/* ============================================================================
   motion.css — der Bewegungs-Anker der Library
   ----------------------------------------------------------------------------
   Physik wird nie erfunden (§5). Quellen: BUILD-GESETZ.md §5 (die Werte) +
   playbook/01-motion-physics.md (die Mechanik; zitiert als [01-M R<n>] für
   die Rules in Abschnitt 2 und [01-M §<n>] für die Abschnitte).
   Budget: 2 Beziers + 2 Spring-Configs tragen ≥90% der Motion einer Seite —
   ein Reveal-Bezier, ein State-Bezier, ein Layout-Spring, ein Micro-Spring
   [01-M R3]. Gemischte Easing-Vokabulare zerstören das Haus-Gefühl [01-M §5].
   ============================================================================ */

:root {
  /* ---- die 2 Beziers (§5: „2 Beziers — Entrance expo-out, State inout") ---- */
  --ease-entrance: cubic-bezier(0.16, 1, 0.3, 1);  /* §5 Entrance expo-out — Reveals, Odometer, cinematische Entrances, Chart-Bars [01-M §1: --ease-out-expo; newbuilds .7s, marketer 650ms ×60] */
  --ease-state: cubic-bezier(0.65, 0, 0.35, 1);    /* §5 State inout — State-Wechsel: Steps, Grid-Rows, Dots, Crossfades [01-M §1: --ease-inout; manta .25–.55s, null Ausnahmen] */
  /* Exits: ease-in, ~0.75× der Enter-Dauer — ease-in ist nur für Exits legal [01-M R9]; kein drittes Bezier-Token */

  /* ---- die Dauer-Leiter (§5: 150/250/450/700 — aus ihr wählen, nie zwischen ihr) ---- */
  --dur-color: 150ms;   /* §5 Stufe 1 — Hover/Focus-Farbe, Border, Opacity; Farbe antwortet ≤250ms [01-M R1] */
  --dur-state: 250ms;   /* §5 Stufe 2 — Chevrons, Tints, Chips, Tab-Crossfades [01-M §3.1] */
  --dur-plush: 450ms;   /* §5 Stufe 3 — plush Moves: Accordion-Höhe, Swaps, Feed-Reflow (Band 400–650) [01-M §3.1] */
  --dur-reveal: 700ms;  /* §5 Stufe 4 — Scroll-Reveals, Progress-Bars; „a 700ms button or a 150ms reveal is a bug" [01-M R2] */
  /* Schwerere Register (1000ms Load-Choreo · 2000ms genau EIN Cinema-Move · ≥4000ms Ambient-Loops)
     stehen in [01-M R2] — Einsatz nur mit Ableitungszeile nach §0.3, sie sind kein Leiter-Token. */

  /* ---- Entrance-Distanzen + Stagger ---- */
  --rise: 10px;      /* §5 Entrance-Sync: 8–12px Rise — weniger Bewegung bevorzugen, Kurvenlänge unverändert [01-M R15; 10 = Bandmitte] */
  --nudge: 2px;      /* Icon-Nudge 2–4px, diagonal ok [01-M R5 + §3.1] */
  --stagger: 60ms;   /* Stagger je Geschwister — Band 50–120ms, Delay = step × index, immer linear, nie random; Gesamt-Delay ≤ ~500ms [01-M R6 + §3.1] */

  /* ---- die 2 Spring-Configs — Kommentar-Spec, kein CSS-Wert (§5 One-Scalar-Statemachines) ----
     LAYOUT { stiffness: 200, damping: 30, mass: 1 }  — Hover-Layout-Nudges, mounted Nodes, ~0.3s ruhiges Settle [01-M §1 Spring-Library „calm nudge"]
     MICRO  { bounce: 0.2, duration: 0.4 }            — der universelle Micro-Interaction-Default [01-M §1, homekey/Framer-Form]
     Gesetz dazu: Springs sitzen bei oder über kritischer Dämpfung (bounce ≤ .2, ζ ≥ 1);
     under-damped (z. B. 150/10/.4) ist NUR für Cursor-Follow/Magnetik legal; null Elastic/Back-Easings auf UI [01-M R4].
     Dependency-freier Integrator: createSpring() in playbook/01-motion-physics.md §3.3. */
}

/* ============================================================================
   Entrance-Sync-Utilities (§5 Entrance-Sync-Gesetz + [01-M R15]):
   Opacity und Transform starten strikt gleichzeitig, gleiche Dauer, gleiche
   Kurve — nie opacity-first, keine geteilten Timings der zwei Kanäle.
   Hidden-State ist vorgemalt: .reveal ships im HTML, JS flippt nur .in
   [01-M R8]. rAF besitzt nie einen Entrance — Klassen-Flip nach synchronem
   Reflow: void el.offsetHeight (§5).
   ============================================================================ */
.reveal {
  opacity: 0;                                   /* [01-M R7] initial opacity: 0 (SSR/AT-Variante: .001) */
  transform: translateY(var(--rise));           /* §5 Rise 8–12px — nichts sonst: kein Scale, kein Blur [01-M R7] */
  transition:
    opacity var(--dur-reveal) var(--ease-entrance),
    transform var(--dur-reveal) var(--ease-entrance);  /* §5 ein Paar, ein Takt — gleiche Dauer, gleiche Kurve */
  transition-delay: calc(var(--stagger) * var(--i, 0));  /* [01-M R6] Stagger-Leiter: inline style="--i:n" */
}
.reveal.in { opacity: 1; transform: none; }     /* der Endzustand — Reveals feuern einmal, dann disconnect [01-M R7] */

/* Arming (Kommentar-Spec, ein Observer, fire-once — [01-M §3.2]):
   const io = new IntersectionObserver(es => es.forEach(e => {
     if (!e.isIntersecting) return;
     e.target.classList.add('in');
     io.unobserve(e.target);                    // once — nur Daten-Widgets dürfen re-armen [01-M R7]
   }), { threshold: 0.1, rootMargin: '0px 0px -30px 0px' });
   if (matchMedia('(prefers-reduced-motion: reduce)').matches)
     document.querySelectorAll('.reveal').forEach(el => el.classList.add('in'));
   else document.querySelectorAll('.reveal').forEach(el => io.observe(el));
   Load-Entrance ohne IO: rAF → void el.offsetHeight → classList.add('in') (§5). */

/* Two-Speed-Merksatz [01-M R1]: Farbe/Opacity/Border ≤250ms (--dur-color/--dur-state);
   alles, was sich BEWEGT, 300–500ms auf Dezeleration (--dur-plush) — nie Position auf
   Farbtempo, nie Farbe auf Bewegungstempo. Transform-Hovers leben in
   @media (hover: hover) and (pointer: fine); Farb-Hovers dürfen global bleiben [01-M R13]. */

/* ============================================================================
   Reduced Motion = Endzustand, nie leer (§5; [01-M R12]: Freeze, kein Removal —
   Content-Parität ist absolut). Vertrag: jede dekorative Endlos-Animation trägt
   data-ambient, jeder weiterlaufende Stream/Feed trägt data-stream [01-M §3.5].
   JS-Seite: Counter rendern den Endwert, Feeds den letzten Frame —
   REDUCED ? render(endValue) : startRaf() [01-M §3.5].
   ============================================================================ */
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }   /* [01-M R12] Entrance übersprungen — Inhalt sofort da */
  [data-ambient] { animation: none !important; }               /* [01-M §3.5] Pulses, Shines, Marquees: aus */
  [data-stream] { animation-play-state: paused !important; }   /* [01-M §3.5] Bild bleibt stehen, Bewegung stoppt */
}
