/* ==========================================================================
   Motion as instrumentation.

   The rule for what earns animation:
     It animates if the motion carries information — direction of data flow,
     arrival of a live event, magnitude resolving, a state changing.
     It does not animate if the motion is only there to be noticed.
   Nothing delays reading: text is legible at frame one, entrances only move
   transform/opacity/filter, and no motion blocks scroll.

   Every motion has a static state that is equally complete. Under
   prefers-reduced-motion the end state is shown, never a skipped element.
   ========================================================================== */

:root {
  /* --- Durations --------------------------------------------------------- */
  --d-instant: 90ms; /* @kind other */    /* colour/opacity on press                          */
  --d-fast: 150ms; /* @kind other */      /* hover, focus, chip states                        */
  --d-base: 250ms; /* @kind other */      /* panel open, tab switch, row arrival              */
  --d-slow: 450ms; /* @kind other */      /* bar growth, band expansion                       */
  --d-enter: 700ms; /* @kind other */     /* section entrance                                 */
  --d-draw: 1200ms; /* @kind other */     /* one chart line drawing itself                    */
  --d-count: 1400ms; /* @kind other */    /* a number counting up. Once, then still.          */
  --d-scan: 1400ms; /* @kind other */     /* one scanline sweep across a detection box        */

  /* --- Easing ------------------------------------------------------------
     expo    entrances: fast out, long settle. Reads responsive.
     quart   draws and sweeps: even, instrument-like.
     snap    detection brackets landing. Slight overshoot, physical.
     soft    ambient loops (forecast band breathing).
     linear  anything conveying constant rate (flow dashes, tickers). */
  --e-expo: cubic-bezier(0.16, 1, 0.3, 1); /* @kind other */
  --e-quart: cubic-bezier(0.25, 1, 0.5, 1); /* @kind other */
  --e-snap: cubic-bezier(0.34, 1.4, 0.64, 1); /* @kind other */
  --e-soft: cubic-bezier(0.65, 0, 0.35, 1); /* @kind other */

  /* --- Stagger intervals -------------------------------------------------
     Held short enough that the last item in a group lands within ~600ms of
     the first, or the group reads as slow rather than sequential. */
  --stagger-tight: 40ms; /* @kind other */   /* swatches, chips, small grids                  */
  --stagger: 70ms; /* @kind other */         /* cards, bars, list rows                        */
  --stagger-wide: 110ms; /* @kind other */   /* diagram tiers, pipeline stages                */
  --stagger-cap: 6; /* @kind other */        /* stop staggering past the 6th item             */
}

/* --- Primitives ----------------------------------------------------------- */

@keyframes sx-reveal {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to { opacity: 1; transform: none; }
}
@keyframes sx-reveal-scale {
  from { opacity: 0; transform: translate3d(0, 18px, 0) scale(0.97); }
  to { opacity: 1; transform: none; }
}
/* Detection box: corner brackets snap in from outside the frame. */
@keyframes sx-bracket-in {
  from { opacity: 0; transform: translate3d(var(--bx, -6px), var(--by, -6px), 0); }
  to { opacity: 1; transform: none; }
}
/* One scanline sweep, top to bottom, then gone. Never loops. */
@keyframes sx-scan {
  from { transform: translateY(0); opacity: 0; }
  12% { opacity: 1; }
  to { transform: translateY(var(--scan-distance, 240px)); opacity: 0; }
}
/* SVG path draw-on. Pair with pathLength="1". */
@keyframes sx-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
/* Bars from the baseline. Set transform-origin per element. */
@keyframes sx-grow-y { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes sx-grow-x { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* Travelling dash: direction of data flow along a connector. Constant rate. */
@keyframes sx-flow { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
/* Forecast band breathing — ±1.5%, 6s. Below the threshold of distraction. */
@keyframes sx-breathe {
  0%, 100% { transform: scaleY(1); }
  50% { transform: scaleY(1.015); }
}
/* Live indicator. The only infinite animation allowed above the fold. */
@keyframes sx-pulse {
  0% { transform: scale(1); opacity: 0.6; }
  70%, 100% { transform: scale(2.4); opacity: 0; }
}
@keyframes sx-marquee {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

.sx-reveal { animation: sx-reveal var(--d-enter) var(--e-expo) both; }
.sx-reveal-scale { animation: sx-reveal-scale var(--d-enter) var(--e-expo) both; }
.sx-draw { stroke-dasharray: 1; animation: sx-draw var(--d-draw) var(--e-quart) both; }

/* --- Reduced motion --------------------------------------------------------
   Entrances resolve to their end state rather than being skipped, so nothing
   is ever stuck at opacity 0. Ambient loops stop outright. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .sx-draw { stroke-dasharray: none; }
}
