/* motion-system-v1 — W-61-1 — Saadjie motion utility layer + reduced-motion guards. */
/*
 * Planning: .archon/queued-builds/WAVE-61-motion-system.md
 *
 * WHY this file exists: public/css/tokens/motion.css (W-56-3) defines a
 * generic duration / easing palette ("fastest / fast / base / slow /
 * slower" × five named cubic-beziers). That layer is the atomic
 * vocabulary. THIS file is the W61 *system* layer — the opinionated
 * three-rung pairing the brief mandates ("Easing: cubic-bezier(0.16, 1,
 * 0.3, 1) Material easeOut. Durations: 150/250/400ms. All animations
 * respect prefers-reduced-motion."). Component CSS reads from the
 * --motion-* tokens and the .motion-* / .transition-* utility classes
 * below; the W56 atoms remain available for the rare case a component
 * needs a non-standard duration.
 *
 * WHY a separate --motion-* namespace (not overwrite --duration-* /
 * --ease-*): a hard rename would break every existing component that
 * already reads var(--duration-fast). Shipping the W61 system alongside
 * the W56 atoms means components migrate at their own pace — the
 * default for NEW work is the --motion-* tokens defined here, the
 * legacy --duration-* tokens remain untouched until each component is
 * audited and rewired.
 *
 * WHY exactly 150 / 250 / 400 ms (the brief's three rungs):
 *   --motion-duration-fast (150ms) — micro-interactions: hover/focus
 *                                    tint, button press, focus-ring
 *                                    fade. Below ~120ms reads as
 *                                    instant; above ~180ms hovers feel
 *                                    sticky.
 *   --motion-duration-base (250ms) — standard component transitions:
 *                                    dropdown reveal, accordion expand,
 *                                    tab switch, card hover lift.
 *   --motion-duration-slow (400ms) — page-level: modal enter/exit,
 *                                    sheet slide, route transition.
 *                                    Long enough to read as a scene
 *                                    change, short enough never to
 *                                    block input.
 *
 * WHY cubic-bezier(0.16, 1, 0.3, 1) for --motion-ease-out (the brief's
 * "Material easeOut"): the curve front-loads the motion — element
 * reaches ~80% of its destination in the first third of the duration,
 * then settles slowly. Reads as "alive but not bouncy". Subjectively
 * matches Material Design's spec for "elements ENTERING the screen"
 * (the brief's reference); mathematically it is the popular "ease-out
 * expo" variant used by Vercel / Linear / Stripe surfaces. Component
 * CSS should NOT write raw cubic-bezier() values — always reference
 * --motion-ease-out.
 *
 * WHY both --motion-* tokens AND .motion-* / .transition-* utility
 * classes ship in the same file: 80% of consumers want a class
 * (<button class="transition-base">) rather than to compose duration
 * + easing per declaration. The token layer stays available for
 * authors who need a one-off (e.g. transition: opacity
 * var(--motion-duration-slow) var(--motion-ease-out)).
 *
 * GOTCHA: NO raw hex / rgb() / rgba() / hsl() / hsla() values appear
 * in this file. The W56 ANTI-RAW-VALUE gate (stylelint config +
 * `git diff` grep in CI) ignores public/css/tokens/** but ENFORCES on
 * this file. Easing curves and millisecond durations are NOT colour
 * values and so are exempt from the gate.
 *
 * GOTCHA: any component that hard-codes `transition: opacity 200ms` or
 * `animation: foo 250ms ease-out` LITERALLY (instead of through the
 * --motion-* tokens or the .transition-* / .motion-* classes here) will
 * NOT be neutralised by the reduced-motion override at the bottom of
 * this file. Component CSS must always route motion through the
 * --motion-* tokens OR through the universal selector override below.
 * The universal selector block is the safety net for legacy / vendor
 * CSS that can't be retrofitted.
 *
 * CONSTRAINT: WCAG 2.3.3 (Animation from Interactions) — any
 * non-essential animation that fires on user input must respect
 * prefers-reduced-motion. The override at the bottom of this file
 * satisfies that programmatically for every consumer that uses these
 * tokens / classes.
 *
 * CONSTRAINT: the file is a UTILITY layer. Multi-property "component"
 * classes (e.g. .dropdown { transition + transform + opacity }) belong
 * in component CSS, not here. Mixing the two layers is how utility
 * CSS systems collapse into specificity wars (cf. W60 surface-* notes).
 *
 * FLOW: cascade order is tokens.css → tokens/motion.css → motion.css
 *       → component CSS. The W61 utilities sit above the W56 atoms so
 *       a component can override a class with its own rule if it
 *       genuinely needs to.
 */

:root {
  /* ============================================================
   * W61 system durations — the three rungs every standard
   * component should pick from. 150 / 250 / 400 ms.
   * ============================================================ */
  --motion-duration-fast: 150ms;
  --motion-duration-base: 250ms;
  --motion-duration-slow: 400ms;

  /* ============================================================
   * W61 system easing — Material's easeOut (per spec).
   * cubic-bezier(0.16, 1, 0.3, 1) — fast accelerate, slow settle.
   * Component CSS should reference this token rather than writing
   * raw cubic-bezier() values.
   * ============================================================ */
  --motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ============================================================
   * Compound transition presets — duration + easing wired together
   * so component declarations don't repeat the pair.
   *
   * WHY four presets (color / transform / opacity / all): these
   * four categories cover every property our components actually
   * transition. Anything outside this set should be a deliberate
   * choice that earns its own line in the component CSS.
   * ============================================================ */
  --motion-transition-color:
      color var(--motion-duration-fast) var(--motion-ease-out),
      background-color var(--motion-duration-fast) var(--motion-ease-out),
      border-color var(--motion-duration-fast) var(--motion-ease-out);
  --motion-transition-transform:
      transform var(--motion-duration-base) var(--motion-ease-out);
  --motion-transition-opacity:
      opacity var(--motion-duration-base) var(--motion-ease-out);
  --motion-transition-all:
      all var(--motion-duration-base) var(--motion-ease-out);
}

/* ============================================================
 * Transition utility classes
 * WHY these names follow the duration rung (.transition-fast →
 * --motion-duration-fast): zero translation cost between the
 * exposed class and the underlying token. A future re-scale that
 * changes 150 → 120 only touches the token.
 *
 * WHY `all` and not a specific property list: the .transition-*
 * utilities are for the "I want anything that changes to animate"
 * case (button hover, focus ring fade-in). Components that need to
 * pin the transition to a specific property should use the
 * compound presets above directly (e.g.
 * transition: var(--motion-transition-opacity)).
 * ============================================================ */

.transition-fast {
  transition: all var(--motion-duration-fast) var(--motion-ease-out);
}

.transition-base {
  transition: all var(--motion-duration-base) var(--motion-ease-out);
}

.transition-slow {
  transition: all var(--motion-duration-slow) var(--motion-ease-out);
}

/* WHY .transition-colors as a named class: the most common
   transition is "tint change on hover/focus" — colour, background,
   and border together. Component CSS reaches for this constantly;
   one class is cheaper than three. */
.transition-colors {
  transition: var(--motion-transition-color);
}

.transition-transform {
  transition: var(--motion-transition-transform);
}

.transition-opacity {
  transition: var(--motion-transition-opacity);
}

/* ============================================================
 * Keyframe animations
 * WHY a tiny vocabulary (fade-in, fade-out, slide-up, slide-down,
 * scale-in): these five cover every entrance / exit gesture the
 * design system needs (toast appear, modal mount, dropdown reveal,
 * row insert). Larger choreography belongs in component CSS — but
 * routing it through --motion-duration-* keeps the reduced-motion
 * override below effective.
 *
 * WHY translate(0, 8px) not 16px / 24px on slide gestures: small
 * displacements read as "natural settle", larger ones read as a
 * scene cut. 8px matches the project's --space-2 atom — visually
 * tied to the spacing rhythm without bringing tokens/spacing.css
 * into this file's dependency graph.
 * ============================================================ */

@keyframes motion-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes motion-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@keyframes motion-slide-up {
  from {
    opacity: 0;
    transform: translate3d(0, 8px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

@keyframes motion-slide-down {
  from {
    opacity: 0;
    transform: translate3d(0, -8px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

@keyframes motion-scale-in {
  from {
    opacity: 0;
    transform: scale(0.96);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* ============================================================
 * Animation utility classes
 * WHY animation-fill-mode: both — the element keeps the keyframe's
 * end state after the animation completes. Without this, a faded-
 * in element snaps back to opacity:0 the instant the animation
 * stops, which looks like a flash of broken state.
 *
 * WHY one class per keyframe (rather than a single .animate-*
 * helper + a --animation-name var): older Safari versions choke on
 * `animation: var(--name) var(--duration) var(--easing)` when the
 * var holds the keyframe identifier. Explicit class-per-keyframe
 * sidesteps that bug entirely.
 * ============================================================ */

.motion-fade-in {
  animation: motion-fade-in var(--motion-duration-base) var(--motion-ease-out) both;
}

.motion-fade-out {
  animation: motion-fade-out var(--motion-duration-base) var(--motion-ease-out) both;
}

.motion-slide-up {
  animation: motion-slide-up var(--motion-duration-base) var(--motion-ease-out) both;
}

.motion-slide-down {
  animation: motion-slide-down var(--motion-duration-base) var(--motion-ease-out) both;
}

.motion-scale-in {
  animation: motion-scale-in var(--motion-duration-base) var(--motion-ease-out) both;
}

/* ============================================================
 * Reduced-motion override — THE safety net.
 *
 * WHY two layers (token re-mapping AND universal selector kill):
 *   1) Re-mapping --motion-duration-* to 1ms collapses every
 *      transition / animation that routes through the W61 tokens.
 *      This is the "well-behaved component" path.
 *   2) The universal-selector `* , *::before, *::after` block
 *      below force-overrides animation-duration and
 *      transition-duration on EVERY element, regardless of whether
 *      that element opted into the tokens. This is the safety net
 *      for legacy / vendor CSS that hard-codes its own millisecond
 *      values.
 *
 * WHY 1ms (not 0): some screen readers and JS choreography listen
 * for transitionend / animationend events. Setting duration to 0
 * skips the transition entirely and those events never fire.
 * 1ms keeps the event machinery wired while removing the
 * perceived motion.
 *
 * WHY animation-iteration-count: 1 — kills loop animations (e.g. a
 * spinner the brand uses elsewhere). Vestibular triggers come from
 * continuous motion as much as from large displacements.
 *
 * CONSTRAINT: WCAG 2.3.3 — non-essential animation that fires on
 * user input must respect this preference. The token re-map +
 * universal selector kill below satisfies that contract for every
 * consumer of the W61 system.
 * ============================================================ */

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-duration-fast: 1ms;
    --motion-duration-base: 1ms;
    --motion-duration-slow: 1ms;
  }

  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}
