/* ==========================================================================
   not a square — motion (CANONICAL)
   Source of truth: ~/.claude/skills/design/code/nas-motion.css
   Do not edit values inside projects — edit here, then re-copy.
   Usage: import after nas-tokens.css. Reference var(--nas-dur-*) and
   var(--nas-ease-*), or apply the .nas-* utilities. Never retype raw values.

   The physical feel: interfaces are objects. Things decelerate into place
   with weight — Apple, not bouncy. Entrances travel short distances
   (4–12px), hovers lift by a hair, presses compress and settle back.
   Only transform and opacity are ever animated (compositor-friendly);
   never animate layout properties (width, height, margin, top/left).
   One sanctioned exception: the R-007 single-value count-up mutates text
   content, not CSS properties — see the reference block at end, which
   requires the final width be reserved so no layout shifts.
   ========================================================================== */

:root {
  /* Duration scale */
  --nas-dur-fast: 120ms;      /* presses, taps, instant feedback */
  --nas-dur-base: 200ms;      /* hovers, toggles, small state changes */
  --nas-dur-slow: 320ms;      /* panels, drawers, larger movements */
  --nas-dur-entrance: 480ms;  /* page and section entrances */

  /* Easing */
  --nas-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);    /* swift-out — everyday state changes */
  --nas-ease-entrance: cubic-bezier(0.16, 1, 0.3, 1);   /* strong deceleration — arrives with weight */
  --nas-ease-spring: cubic-bezier(0.22, 1.12, 0.36, 1); /* gentle spring feel — settles, never bounces */

  /* Stagger */
  --nas-stagger-step: 60ms;   /* override per group: style="--nas-stagger-step: 90ms" */
}

/* --------------------------------------------------------------------------
   Entrances
   Keyframes define only the `from` state, so every animation resolves to
   the element's own resting style — no fighting with hover transforms.
   -------------------------------------------------------------------------- */

@keyframes nas-fade {
  from { opacity: 0; }
}

@keyframes nas-rise {
  from { opacity: 0; transform: translateY(10px); }
}

@keyframes nas-scale-in {
  from { opacity: 0; transform: scale(0.97); }
}

.nas-enter {
  animation: nas-fade var(--nas-dur-entrance) var(--nas-ease-entrance) both;
}

.nas-enter-rise {
  animation: nas-rise var(--nas-dur-entrance) var(--nas-ease-entrance) both;
}

.nas-enter-scale {
  animation: nas-scale-in var(--nas-dur-entrance) var(--nas-ease-entrance) both;
}

/* --------------------------------------------------------------------------
   Stagger
   Put .nas-stagger on the parent; give children an entrance class.
   Children arrive one step apart, capped after eight so long lists
   do not queue forever.
   -------------------------------------------------------------------------- */

.nas-stagger > :nth-child(1) { animation-delay: calc(var(--nas-stagger-step) * 0); }
.nas-stagger > :nth-child(2) { animation-delay: calc(var(--nas-stagger-step) * 1); }
.nas-stagger > :nth-child(3) { animation-delay: calc(var(--nas-stagger-step) * 2); }
.nas-stagger > :nth-child(4) { animation-delay: calc(var(--nas-stagger-step) * 3); }
.nas-stagger > :nth-child(5) { animation-delay: calc(var(--nas-stagger-step) * 4); }
.nas-stagger > :nth-child(6) { animation-delay: calc(var(--nas-stagger-step) * 5); }
.nas-stagger > :nth-child(7) { animation-delay: calc(var(--nas-stagger-step) * 6); }
.nas-stagger > :nth-child(8) { animation-delay: calc(var(--nas-stagger-step) * 7); }
.nas-stagger > :nth-child(n + 9) { animation-delay: calc(var(--nas-stagger-step) * 7); }

/* --------------------------------------------------------------------------
   Hover / press affordances
   Transform + opacity only. The lift shadow is a pseudo-element whose
   opacity fades in — the shadow itself is never animated, so the whole
   affordance stays on the compositor.
   -------------------------------------------------------------------------- */

.nas-lift {
  position: relative;
  transition: transform var(--nas-dur-base) var(--nas-ease-standard);
}

.nas-lift::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--nas-shadow-md);
  opacity: 0;
  transition: opacity var(--nas-dur-base) var(--nas-ease-standard);
  pointer-events: none;
}

@media (hover: hover) {
  .nas-lift:hover { transform: translateY(-2px); }
  .nas-lift:hover::after { opacity: 1; }
}

.nas-press {
  transition: transform var(--nas-dur-base) var(--nas-ease-spring);
}

.nas-press:active {
  transform: scale(0.98);
  transition-duration: var(--nas-dur-fast);
  transition-timing-function: var(--nas-ease-standard);
}

/* --------------------------------------------------------------------------
   Reduced motion — neutralise everything above.
   Content appears in place; affordances change state without movement.
   Keep this block last so it wins on order.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .nas-enter,
  .nas-enter-rise,
  .nas-enter-scale {
    animation: none;
  }

  .nas-stagger > * {
    animation-delay: 0s;
  }

  .nas-lift,
  .nas-lift::after,
  .nas-press {
    transition: none;
  }

  .nas-lift:hover { transform: none; }
  .nas-press:active { transform: none; }
}

/* ==========================================================================
   REFERENCE PATTERNS — JS-side motion, documentation only (28 Jul 2026)
   No general-purpose JS motion utility exists by ruling (R-005); the only
   JS motion code in the system is the scoped nas-indicator.js (R-006).
   Do not scaffold a shared nas-motion.js until a second real production
   use exists. Full case law: ../rulings.md (R-005, R-007).

   1) Frame-rate-independent damping (ruling R-005)
      lerp(x, y, 1 - Math.exp(-lambda * dtSeconds))
      Citation: Rory Driscoll, "Frame Rate Independent Damping Using Lerp"
      (2016); observed in Lenis maths.ts (MIT, darkroom.engineering).
      Naive per-frame lerp(x, y, 0.1) is frame-rate-dependent: it runs ~2x
      faster on a 120Hz ProMotion display than on a 60Hz test screen. Use
      the exp form for ANY JS interpolation toward a continuously moving
      target (pointer-follow, drag settle, scroll-linked element values) —
      the one behaviour CSS transitions cannot express.
      Conditions: element-level motion only — native scroll is NEVER
      replaced (smooth-scroll hijacking is banned, R-002); transform and
      opacity only; every use site gates behind
      matchMedia('(prefers-reduced-motion: reduce)') and snaps to target
      when reduced; rAF loops terminate once settled — no perpetual loops;
      lambda is a per-piece art-direction knob, no imported defaults.

   2) KPI count-up, single-value settle (ruling R-007)
      If a piece's art direction calls for a stat to settle into place:
      ~25 lines of owned vanilla JS — IntersectionObserver + rAF +
      Intl.NumberFormat(undefined, ...) — easing/duration from
      --nas-ease-entrance / --nas-dur-entrance, numeral carries
      font-variant-numeric: tabular-nums (which belongs on every KPI
      numeral statically anyway).
      Conditions: fires ONCE per view on first entrance, never on data
      refresh or polling; opt-in per piece, never a default across stat
      tiles (count-up-everything is a template-dashboard tell); the true
      final value sits in the accessible name from first paint (animated
      span aria-hidden), no live-region spam; reduced-motion and no-JS
      both render the final value instantly; reserve the final width
      before animating (size the span from the final formatted value,
      e.g. min-width in ch) so digit-count growth and separators cause
      zero layout shift during the count. Per-digit odometers (springs
      per digit) are excluded outright — overdecorated data. Import ban:
      no animate-ui / motion-react / react-use-measure code, permanent.
   ========================================================================== */
