/*
 * motion.css — vendored motion layer. Loads after tokens, before styles
 * (tokens → motion → styles; motion consumes the tokens below).
 *
 * The repo motion system (`@wa-aas/ui-kit/primitives`) is React-only and this
 * site is vanilla Astro served as a flat nginx web root, so the React wrappers
 * cannot be used here. Instead we vendor the *token* layer: the `:root` custom
 * properties below are copied verbatim from
 * design_system/tokens/CSS/primitives/motion.css (generated from
 * design_system/tokens/JSON/core.tokens.json) — same reason as tokens.css, a
 * cross-repo @import escapes the served root and 404s.
 *
 * If the source motion tokens change, re-sync the :root block below from
 * design_system/tokens/CSS/primitives/motion.css (NOT ui-motion-tokens.json).
 *
 * The utility classes underneath are the vanilla equivalents of the system's
 * RevealOnScroll / Stagger / FadeIn primitives. They animate purely from the
 * vendored tokens and are activated by the IntersectionObserver in public/app.js,
 * which adds `.is-revealed` to `[data-reveal]` elements when they scroll in.
 * See the `sites-animation` skill.
 *
 * Extended 2026-07-21 (Wave-2 / interior uplift port): the hero-template
 * ambient primitives, the media-carousel/coverflow scroll-driven primitives,
 * the decorative parallax primitive, and the scroll-progress rail primitive
 * below are ported from sites/leadcarvan/public/motion.css (its "Hero-template
 * motion primitives", "Carousel motion primitive", "Decorative parallax
 * primitive", and "Scroll-progress rail" sections). Keyframes are renamed
 * brand-neutral, `lc-* → st-*` (see the mapping table in each section's
 * comment). The two derived `--motion-glow-*` custom properties are NOT
 * vendored from design_system — they are computed locally from this site's
 * own `--primary` / `--accent` semantic tokens (tokens.css), so they track
 * whatever brand hue a site built from this starter sets, with no hand-sync
 * required. Consumers: styles.css's "Interior-page visual language (uplift)"
 * section and its `.media-carousel` block.
 */

/* --- Vendored motion tokens (from design_system/tokens/CSS/primitives/motion.css) --- */
:root {
  --motion-distance-lg: 1rem;
  --motion-distance-md: 0.75rem;
  --motion-distance-sm: 0.5rem;
  --motion-duration-fast: 120ms;
  --motion-duration-instant: 0ms;
  --motion-duration-normal: 180ms;
  --motion-duration-reduced: 0.01ms;
  --motion-duration-scroll: 420ms;
  --motion-duration-slow: 260ms;
  --motion-ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
  --motion-ease-in: cubic-bezier(0.4, 0, 1, 1);
  --motion-ease-out: cubic-bezier(0, 0, 0.2, 1);
  --motion-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --motion-spring-damping: 32;
  --motion-spring-mass: 1;
  --motion-spring-stiffness: 420;
  --motion-stagger-fast: 30ms;
  --motion-stagger-normal: 50ms;
  --motion-stagger-slow: 80ms;
}

/* --- Reveal-on-scroll primitive (vanilla RevealOnScroll equivalent) --- */
/*
 * Elements opt in with `data-reveal`. They start hidden + offset and settle to
 * their resting state once app.js adds `.is-revealed`. Direction is controlled
 * with `data-reveal="up|down|left|right|scale"` (default: up). Stagger within a
 * `[data-reveal-group]` is applied as an inline `--reveal-delay` by app.js.
 */
[data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--motion-distance-lg), 0);
  transition:
    opacity var(--motion-duration-scroll) var(--motion-ease-out),
    transform var(--motion-duration-scroll) var(--motion-ease-emphasized);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

[data-reveal="down"] {
  transform: translate3d(0, calc(-1 * var(--motion-distance-lg)), 0);
}

[data-reveal="left"] {
  transform: translate3d(var(--motion-distance-lg), 0, 0);
}

[data-reveal="right"] {
  transform: translate3d(calc(-1 * var(--motion-distance-lg)), 0, 0);
}

[data-reveal="scale"] {
  transform: scale(0.96);
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/*
 * No-JS / pre-hydration safety net: if app.js never runs (script blocked, JS
 * disabled), reveal content after a short delay so nothing stays invisible.
 * `.reveal-ready` is set on <html> by app.js the moment it boots, which cancels
 * this fallback and hands control to the observer.
 */
@media (prefers-reduced-motion: no-preference) {
  html:not(.reveal-ready) [data-reveal] {
    animation: reveal-failsafe 0ms linear 1200ms forwards;
  }
}

@keyframes reveal-failsafe {
  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Reduced motion: respect the system's reduced duration token --- */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    transition-duration: var(--motion-duration-reduced);
    transition-delay: 0ms;
    animation: none;
  }
}

/* --- Derived glow tokens (2026-07-21) ---------------------------------------
 * NOT vendored — computed here from this site's own semantic --primary /
 * --accent (tokens.css) via color-mix, so a soft "glow" colour is available to
 * the ambient/decorative rules below without inventing a raw hex or a brand-
 * specific token, and it re-tints itself automatically if a site built from
 * this starter changes --brand-h. Reused by the hero-ambient aurora layer,
 * the carousel's current-dot ring, and anything else that wants "a soft wash
 * of the brand colour" rather than the flat colour itself.
 */
:root {
  --motion-glow-primary: color-mix(in srgb, var(--primary) 20%, transparent);
  --motion-glow-accent: color-mix(in srgb, var(--accent) 20%, transparent);
}

/* --- Hero-template ambient motion primitives (2026-07-21, ported from
 * sites/leadcarvan/public/motion.css "Hero-template motion primitives") ------
 * Rename map (lc-* → st-*, brand-neutral "site template" prefix):
 *   lc-drift → st-drift | lc-aurora → st-aurora | lc-pulse → st-pulse |
 *   lc-sweep → st-sweep | lc-gradient-pan → st-gradient-pan
 * All keyframes live inside the no-preference media query, so under reduced
 * motion the animations simply don't exist and every element rests at its
 * final state (reduced-motion strategy: css-prefers-reduced-motion). Intents
 * (motion-intent-map): drift/aurora/sweep = delight (decorative idle loops,
 * transform-only), pulse = wait (live status, opacity-only). Consumers live
 * in styles.css: `.hero-ambient`, `.hero-drift`, `.hero-highlight`,
 * `.hero-edge-sweep`, `.status-dot--live`.
 */
@media (prefers-reduced-motion: no-preference) {
  /* Gentle float for decorative hero cards (transform-only). */
  @keyframes st-drift {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50% { transform: translate3d(0, -0.4rem, 0); }
  }

  /* Slow-moving glow mass behind panel content — the CSS "shader". */
  @keyframes st-aurora {
    0%, 100% { transform: translate3d(-6%, -6%, 0) rotate(0deg) scale(1); }
    50% { transform: translate3d(6%, 6%, 0) rotate(10deg) scale(1.08); }
  }

  /* Soft status pulse (opacity-only) for "live" chips. */
  @keyframes st-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
  }

  /* Accent line sweep across a band edge (transform-only). */
  @keyframes st-sweep {
    0% { transform: translateX(-100%); }
    55%, 100% { transform: translateX(100%); }
  }

  /* Paint-only gradient pan for highlighted headline words. */
  @keyframes st-gradient-pan {
    to { background-position: 200% 50%; }
  }
}

/* --- Carousel motion primitive (2026-07-21, ported from
 * sites/leadcarvan/public/motion.css "Carousel motion primitive") -----------
 * Rename map: lc-coverflow → st-coverflow | lc-scroll-progress →
 * st-scroll-progress. Scroll-DRIVEN keyframes for the `.media-carousel`
 * section (see styles.css) — attached to a `view()` / `scroll()` timeline, so
 * every frame is a function of the track's scroll position: the motion is
 * scrubbed by the user, never played at them, and no JS drives a single pixel
 * of it. Intent: orient (the depth cue tells you where you are in the set) +
 * delight. Transform/opacity only, so the whole effect stays on the
 * compositor. Progressive enhancement: browsers without scroll-driven
 * animations never match the @supports gate in styles.css and keep a plain,
 * fully functional scroll-snap carousel — the correct resting state, not a
 * broken one. Reduced-motion strategy: css-prefers-reduced-motion (styles.css
 * turns the animation off and the carousel stays fully usable).
 */
@media (prefers-reduced-motion: no-preference) {
  /* Depth-scrub for each slide, driven by its own position in the track. */
  @keyframes st-coverflow {
    0% {
      opacity: 0.25;
      transform: translate3d(0, 0, -18rem) rotateY(32deg) scale(0.82);
    }
    50% {
      opacity: 1;
      transform: translate3d(0, 0, 0) rotateY(0deg) scale(1);
    }
    100% {
      opacity: 0.25;
      transform: translate3d(0, 0, -18rem) rotateY(-32deg) scale(0.82);
    }
  }

  /* Position indicator, driven by the track's scroll offset. */
  @keyframes st-scroll-progress {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
  }
}

/* --- Decorative parallax primitive (2026-07-21, ported from
 * sites/leadcarvan/public/motion.css "Decorative parallax primitive") -------
 * Rename map: lc-parallax → st-parallax. Opt in with `data-parallax` on a
 * DECORATIVE container: as it travels through the viewport it drifts one
 * distance-token against the scroll. Scroll-DRIVEN, not time-based — every
 * frame is a function of scroll position, so it is scrubbed by the user and
 * cannot play at them, and no JS drives it (confirmed against leadcarvan's
 * app.js: this primitive has zero JS counterpart there).
 *
 * Intent: delight (ambient depth cue). Transform-only, so it stays on the
 * compositor and shifts no layout.
 *
 * Rules for applying it:
 *  - Decoration ONLY (ideally aria-hidden). Never on copy, CTAs, or anything
 *    clickable — moving a link under the pointer is a policy violation.
 *  - Never on an element that also carries `data-reveal`: both would drive
 *    `transform` and the reveal would lose.
 *  - Travel stays at one `--motion-distance-lg` either side of rest; this is
 *    a depth cue, not a camera move.
 *
 * Progressive enhancement + accessibility: browsers without scroll-driven
 * animations never match the @supports gate and the element simply sits at
 * rest, which is the correct resting state. Reduced motion is excluded by the
 * media query, so the animation does not exist at all (reduced-motion
 * strategy: css-prefers-reduced-motion).
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-parallax] {
      animation: st-parallax linear both;
      animation-timeline: view();
    }

    @keyframes st-parallax {
      from { transform: translate3d(0, var(--motion-distance-lg), 0); }
      to { transform: translate3d(0, calc(-1 * var(--motion-distance-lg)), 0); }
    }
  }
}

/* --- Scroll-progress rail (2026-07-21, ported from
 * sites/leadcarvan/public/motion.css "Scroll-progress rail") ----------------
 * Scroll choreography built on the observer already shipped (setupRailProgress
 * in app.js — added in an earlier wave) rather than a scroll library. A
 * container opts in with `data-rail`; app.js observes its direct children and
 * advances `--rail-progress` from 0 → 1 as each one comes into view.
 * Consumers (see `.process-rail` in styles.css) paint a spine and scale it by
 * that variable. This CSS half of the primitive was missing until now — the
 * JS was already driving a custom property nothing declared a default for.
 *
 * Intent: guide (scroll segment, ≤700ms cap — the consumer transition runs at
 * --motion-duration-scroll, 420ms). Scroll is never hijacked and no content is
 * hidden: the spine is decoration over content that is readable at rest.
 *
 * Safety nets mirror [data-reveal]: app.js pins progress to 1 immediately
 * under reduced motion or without IntersectionObserver, and the no-JS
 * failsafe below fills the spine if app.js never boots at all (reduced-motion
 * strategy: state-only-feedback — the spine always lands "complete" rather
 * than depending on motion to convey the steps).
 */
[data-rail] {
  --rail-progress: 0;
}

html:not(.reveal-ready) [data-rail] {
  --rail-progress: 1;
}

@media (prefers-reduced-motion: reduce) {
  [data-rail] {
    --rail-progress: 1;
  }
}
