/* =============================================================================
   Sigildream Tech Wiki — Animated CSS Component Library
   -----------------------------------------------------------------------------
   Author: Senior Visual Engineer S1 (Motion)
   Spec:   /tmp/tech-wf/design/design-spec.md  (§1, §2, §3.1, §3.3, §3.4, §3.6, §3.7)
   Depends on: styles.css tokens (--bg, --panel, --accent, --text, --muted,
               --line, --cyan, --green, --neural, --violet, --red, --radius).
   Import order: after styles.css, before per-page overrides.

   Design contract (§0):
   - Every animation must respect prefers-reduced-motion: reduce.
   - Every animation uses only `transform`, `opacity`, `stroke-dashoffset`,
     `filter`, or `box-shadow` — never geometry / layout properties.
   - Every component class is self-contained; safe to drop into any chapter.
   - GPU-friendly hints (will-change, translate3d) applied ONLY to the layers
     that actually animate — never a blanket declaration.

   Motion manual override contract (§2.4):
   - <html data-motion="off"> mirrors prefers-reduced-motion:reduce so the
     in-page toggle produces the identical visual result. All reduced-motion
     rules below are duplicated under html[data-motion="off"].
============================================================================= */


/* -----------------------------------------------------------------------------
   §1  Design tokens — Neural / Flow extensions + timing + gradients
   Append-only. Do NOT overwrite baseline tokens defined in styles.css.
----------------------------------------------------------------------------- */
:root {
  /* --- Neural extensions --- */
  --neural: #b596ff;
  --neural-strong: #9b7cff;
  --neural-glow: rgba(181, 150, 255, 0.32);

  /* --- Flow / pulse (cyan family) --- */
  --flow: #7ec9dc;
  --flow-strong: #5fb6cd;
  --flow-glow: rgba(140, 214, 232, 0.30);

  /* --- Ambient noise / hero background --- */
  --noise-tint-a: rgba(230, 198, 109, 0.09);
  --noise-tint-b: rgba(140, 214, 232, 0.06);
  --noise-tint-c: rgba(181, 150, 255, 0.05);
  --grid-line:    rgba(255, 255, 255, 0.028);

  /* --- Signature gradients --- */
  --grad-hero:
    radial-gradient(1200px 380px at 80% -10%, var(--noise-tint-a), transparent 60%),
    radial-gradient(900px 300px at 10% 110%, var(--noise-tint-b), transparent 55%),
    radial-gradient(700px 260px at 50% 50%, var(--noise-tint-c), transparent 70%);
  --grad-panel-neural: linear-gradient(180deg, rgba(181,150,255,0.10), rgba(181,150,255,0.02) 60%);
  --grad-panel-flow:   linear-gradient(180deg, rgba(140,214,232,0.10), rgba(140,214,232,0.02) 60%);

  /* --- Glow shorthands --- */
  --glow-gold:   0 0 18px rgba(230, 198, 109, 0.35);
  --glow-neural: 0 0 22px var(--neural-glow);
  --glow-flow:   0 0 22px var(--flow-glow);

  /* --- Timing tokens (§2.1) --- */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-flow:  linear;
  --dur-micro:  180ms;
  --dur-short:  320ms;
  --dur-med:    900ms;
  --dur-flow:   1600ms;
  --dur-pulse:  3500ms;
  --dur-noise:  22s;
}


/* -----------------------------------------------------------------------------
   §2  @property registrations — animatable custom properties
   Progressive enhancement: browsers without @property fall back to a plain
   fill on the terminal frame; the counter still displays the numeric value
   because we always render the digits in a real DOM text node too.
----------------------------------------------------------------------------- */
@property --sw-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 0;
}
@property --sw-fill {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}


/* -----------------------------------------------------------------------------
   §3  Keyframe catalogue (§2.2)
   Every keyframe touches transform / opacity / stroke-dashoffset only —
   the sole exception is sw-pulse which animates box-shadow (compositor-only
   on modern Chromium/WebKit; still cheap on Gecko).
----------------------------------------------------------------------------- */
@keyframes sw-flow      { to { stroke-dashoffset: -20; } }

@keyframes sw-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(145, 211, 140, 0.55); }
  50%      { box-shadow: 0 0 0 8px rgba(145, 211, 140, 0.00); }
}

@keyframes sw-reveal {
  from { opacity: 0; transform: translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0,   0); }
}

@keyframes sw-tick {
  from { transform: translate3d(0, 6px, 0); opacity: 0; }
  to   { transform: translate3d(0, 0,   0); opacity: 1; }
}

@keyframes sw-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(var(--fill, 1)); }
}

@keyframes sw-drift {
  from { transform: translate3d(0,    0,   0); }
  to   { transform: translate3d(-48px, -32px, 0); }
}

@keyframes sw-ring {
  0%   { transform: scale(0.92); opacity: 0.7; }
  100% { transform: scale(1.15); opacity: 0;   }
}

/* Counter tween — pure CSS via @property. Fallback: browsers ignoring
   @property will simply see the final value paint. */
@keyframes sw-count-up {
  from { --sw-count: 0; }
  to   { --sw-count: var(--sw-count-to, 0); }
}


/* =============================================================================
   §4  Component: AnimatedFlow — pulsing pipeline of boxes  (§3.1)
   /* SW/DS: AnimatedFlow — see design-spec.md §3.1 */
============================================================================= */
.sw-flow {
  position: relative;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius, 12px);
  background: rgba(0, 0, 0, 0.24);
  contain: layout paint;
}
.sw-flow svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.sw-flow-track {
  position: relative;
  display: grid;
  gap: 12px;
  list-style: none;
  margin: 0;
  padding: 0;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  z-index: 1;
}
.sw-flow[data-orient="vertical"] .sw-flow-track {
  grid-auto-flow: row;
  grid-auto-columns: initial;
}

.sw-flow-node {
  --tint: var(--accent);
  padding: 14px 16px;
  border-radius: var(--radius, 12px);
  border: 1px solid color-mix(in oklab, var(--tint) 55%, transparent);
  background: color-mix(in oklab, var(--tint) 8%, var(--panel));
  transition:
    transform  var(--dur-micro) var(--ease-out),
    box-shadow var(--dur-micro) var(--ease-out);
  will-change: transform;
}
.sw-flow-node:hover,
.sw-flow-node:focus-visible {
  transform: translate3d(0, -2px, 0);
  box-shadow: 0 6px 22px color-mix(in oklab, var(--tint) 40%, transparent);
  outline: none;
}
.sw-flow-node b {
  display: block;
  color: var(--text);
  font-size: 14px;
  letter-spacing: 0.02em;
}
.sw-flow-node small {
  color: var(--muted);
  font-size: 12px;
}
.sw-flow-node.is-source::before,
.sw-flow-node.is-sink::before {
  content: attr(data-chip);
  display: inline-block;
  margin-bottom: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tint);
  border: 1px solid color-mix(in oklab, var(--tint) 45%, transparent);
  background: rgba(0, 0, 0, 0.35);
}

/* Connector lines — directional dashed march = "flow travels tail → head". */
.sw-flow-line {
  fill: none;
  stroke: rgba(230, 198, 109, 0.75);
  stroke-width: 1.4;
  stroke-dasharray: 6 4;
  animation: sw-flow var(--dur-flow) var(--ease-flow) infinite;
}
.sw-flow-line.is-feedback {
  stroke: rgba(140, 214, 232, 0.75);
}
/* Solid forward arrow (never animated — direction is already explicit). */
.sw-flow-line.is-solid {
  stroke-dasharray: 0;
  animation: none;
}


/* =============================================================================
   §5  Component: TierLadder — 5-rung vertical stack  (§3.3)
   /* SW/DS: TierLadder — see design-spec.md §3.3 */
============================================================================= */
.sw-tier {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
  counter-reset: sw-tier-rung;
}
.sw-tier-rung {
  --tint: var(--accent);
  --fill: 0;
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  padding: 12px 14px;
  border: 1px solid color-mix(in oklab, var(--tint) 40%, var(--line));
  border-radius: var(--radius, 12px);
  background: rgba(0, 0, 0, 0.28);
  overflow: hidden;
  counter-increment: sw-tier-rung;
  isolation: isolate;
}
/* Fill layer — animated on scaleX, driven by --fill per rung.
   `both` fill-mode pins the ladder to its terminal frame after playing. */
.sw-tier-rung::before {
  content: "";
  position: absolute;
  inset: 0;
  transform-origin: left center;
  transform: scaleX(0);
  background: linear-gradient(
    90deg,
    color-mix(in oklab, var(--tint) 22%, transparent),
    color-mix(in oklab, var(--tint)  6%, transparent)
  );
  animation: sw-fill var(--dur-med) var(--ease-out) both;
  /* Fallback stagger via CSS counter — if --rung-idx isn't set inline. */
  animation-delay: calc((var(--rung-idx, 0)) * 80ms);
  will-change: transform;
  z-index: 0;
}
.sw-tier-rung > *          { position: relative; z-index: 1; }
.sw-tier-rung b            { color: var(--text);  font-size: 14px; letter-spacing: 0.02em; }
.sw-tier-rung span         { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.sw-tier-rung.is-current {
  box-shadow:
    inset 0 0 0 1px var(--tint),
    var(--glow-gold);
}
.sw-tier-rung.is-goal      { opacity: 0.72; }

/* Progressive-enhancement: children set --rung-idx explicitly for perfect
   stagger; the :nth-child fallback covers static markup. */
.sw-tier-rung:nth-child(1) { --rung-idx: 0; }
.sw-tier-rung:nth-child(2) { --rung-idx: 1; }
.sw-tier-rung:nth-child(3) { --rung-idx: 2; }
.sw-tier-rung:nth-child(4) { --rung-idx: 3; }
.sw-tier-rung:nth-child(5) { --rung-idx: 4; }


/* =============================================================================
   §6  Component: StatPanel — animated counter tile  (§3.4)
   Two counter strategies, both JS-free:
   (a) @property tween on --sw-count, exposed via `content: counter(...)`.
       Works in Chromium 85+, WebKit 18+. Requires <strong data-counter>
       with `--sw-count-to: <n>` inline.
   (b) Fallback: `sw-tick` translate-in of the pre-rendered numeric text.
       Every browser gets at least (b).
   /* SW/DS: StatPanel — see design-spec.md §3.4 */
============================================================================= */
.sw-stat {
  --stat-tint: var(--accent);
  position: relative;
  padding: 18px 18px 18px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius, 12px);
  background: var(--panel);
  box-shadow: var(--shadow, 0 6px 24px rgba(0, 0, 0, 0.35));
  display: grid;
  gap: 6px;
  isolation: isolate;
}
.sw-stat[data-tint="cyan"]   { --stat-tint: var(--cyan);   }
.sw-stat[data-tint="green"]  { --stat-tint: var(--green);  }
.sw-stat[data-tint="violet"] { --stat-tint: var(--violet); }
.sw-stat[data-tint="rose"]   { --stat-tint: var(--red);    }
.sw-stat[data-tint="neural"] { --stat-tint: var(--neural); }
.sw-stat[data-tint="flow"]   { --stat-tint: var(--flow);   }

/* Accent bar */
.sw-stat-bar {
  position: absolute;
  left: 0;
  top: 10px;
  bottom: 10px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--stat-tint);
  box-shadow: 0 0 12px color-mix(in oklab, var(--stat-tint) 55%, transparent);
}

/* Value line */
.sw-stat-value strong {
  display: inline-block;
  font-size: clamp(28px, 3.6vw, 44px);
  line-height: 1;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  /* Tick-in on first paint. Component-level opt-in via `.sw-stat` visibility. */
  animation: sw-tick var(--dur-med) var(--ease-out) both;
  will-change: transform, opacity;
}
.sw-stat-value em {
  color: var(--muted);
  font-style: normal;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-left: 8px;
}

/* Delta chip */
.sw-stat-delta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  border: 1px solid var(--line);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  width: max-content;
}
.sw-stat-delta.is-up {
  color: var(--green);
  border-color: color-mix(in oklab, var(--green) 40%, transparent);
}
.sw-stat-delta.is-down {
  color: var(--red);
  border-color: color-mix(in oklab, var(--red) 40%, transparent);
}
.sw-stat p {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

/* --- JS-free CSS counter tween (@property strategy).
   Author writes:
       <strong data-counter style="--sw-count-to: 454000">
         <span class="sw-counter-tween" aria-hidden="true"></span>
         <span class="sw-counter-static">454K</span>
       </strong>
   The tween pseudo is decorative; the static span is what screen readers read
   and what old browsers show. Only the tween moves. */
.sw-counter-tween {
  display: inline-block;
  counter-reset: sw-c var(--sw-count);
  animation: sw-count-up var(--dur-med) var(--ease-out) both;
}
.sw-counter-tween::after {
  content: counter(sw-c);
  font-variant-numeric: tabular-nums;
}
/* When the @property tween is active, hide the static echo so we don't
   double-render. Selector guard: only if the browser understands @property
   (feature-detectable via @supports (background: paint(x)) heuristics is
   brittle — instead we let JS-free authors opt in explicitly by adding
   .sw-counter-tween-active on the parent). */
[data-counter].sw-counter-tween-active .sw-counter-static { display: none; }


/* =============================================================================
   §7  Component: SigilPulse — concentric ring pulse for callouts  (§3.7)
   /* SW/DS: SigilPulse — see design-spec.md §3.7 */
============================================================================= */
.sw-sigil-pulse {
  --pulse-tint: var(--accent);
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  border-radius: 999px;
  isolation: isolate;
}
.sw-sigil-pulse[data-tint="green"]  { --pulse-tint: var(--green);  }
.sw-sigil-pulse[data-tint="neural"] { --pulse-tint: var(--neural); }
.sw-sigil-pulse[data-tint="flow"]   { --pulse-tint: var(--flow);   }
.sw-sigil-pulse[data-tint="rose"]   { --pulse-tint: var(--red);    }

.sw-sigil-pulse-ring {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid color-mix(in oklab, var(--pulse-tint) 60%, transparent);
  animation: sw-ring var(--dur-pulse) var(--ease-out) infinite;
  transform-origin: center center;
  pointer-events: none;
  will-change: transform, opacity;
  z-index: 0;
}
.sw-sigil-pulse-ring.is-lag {
  animation-delay: calc(var(--dur-pulse) / 2);
}
.sw-sigil-pulse-core {
  position: relative;
  z-index: 1;
  color: var(--pulse-tint);
  font-weight: 800;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-shadow: 0 0 12px color-mix(in oklab, var(--pulse-tint) 45%, transparent);
}

/* Shipped-badge liveness variant — inherits sw-pulse (box-shadow ripple). */
.sw-shipped-badge {
  --pulse-tint: var(--green);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid color-mix(in oklab, var(--pulse-tint) 55%, transparent);
  color: var(--pulse-tint);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.35);
  animation: sw-pulse var(--dur-pulse) var(--ease-inout) infinite;
  will-change: box-shadow;
}


/* =============================================================================
   §8  Component: NoiseBackground — ambient hero layer  (§3.6)
   Three layers stacked, GPU-friendly:
   1. static radial gradients (no animation).
   2. 48px grid overlay drifting via translate3d — isolated in its own layer.
   3. optional canvas particle field (rendered by JS; CSS just positions).
   /* SW/DS: NoiseBackground — see design-spec.md §3.6 */
============================================================================= */
.sw-noise-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
  contain: strict;
}
.sw-noise-gradient {
  position: absolute;
  inset: 0;
  background: var(--grad-hero);
}
.sw-noise-grid {
  position: absolute;
  /* Oversize by 48px so the drift never exposes a hard edge. */
  inset: -48px;
  opacity: 0.55;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 48px 48px;
  /* Feather the grid toward the edges so it reads as ambient, not architectural. */
  -webkit-mask-image: radial-gradient(60% 60% at 50% 40%, black, transparent 75%);
          mask-image: radial-gradient(60% 60% at 50% 40%, black, transparent 75%);
  animation: sw-drift var(--dur-noise) linear infinite;
  will-change: transform;
  transform: translateZ(0); /* Promote to its own compositor layer. */
}
.sw-noise-particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.35;
  mix-blend-mode: screen;
}
.sw-noise-bg[data-particles="false"] .sw-noise-particles,
.sw-noise-bg:not([data-particles]) .sw-noise-particles { display: none; }

/* Small viewports: kill particles unconditionally regardless of data attr. */
@media (max-width: 900px) {
  .sw-noise-particles { display: none !important; }
}


/* =============================================================================
   §9  Cross-cutting helpers used by chapters
   Kept here (not in styles.css) so the whole motion system rolls back as one
   file if we ever need to disable it.
============================================================================= */
/* Section reveal — IntersectionObserver adds `.is-visible`. Terminal frame
   is the natural CSS default (opacity 1, no translate), so JS-free pages get
   readable text; the fade only plays when the class is added. */
.sw-reveal {
  opacity: 0;
  transform: translate3d(0, 8px, 0);
  will-change: transform, opacity;
}
.sw-reveal.is-visible {
  animation: sw-reveal var(--dur-short) var(--ease-out) forwards;
}

/* Section highlight after deep-link click (§5.4). */
.section.is-focused {
  animation: sw-reveal var(--dur-short) var(--ease-out) both;
}
.section.is-focused > h2::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 10px;
  vertical-align: middle;
  box-shadow: var(--glow-gold);
}


/* =============================================================================
   §9.5  Chapter hero animation containment  (fix 2026-07-09)
   `.ch-hero-anim` was written by chapter designers as a full-bleed animated
   background for the .hero section (Core/Public plates + ring pulses on
   services; feedback loops on neural-ai; etc). Without CSS it defaulted to
   inline block flow and pushed the h1 into an overlap. This block makes it a
   true absolute-positioned ambient layer with reduced opacity so the h1 stays
   readable at every viewport, and makes hero text children stack above it.
============================================================================= */
.hero { position: relative; overflow: hidden; }

.ch-hero-anim {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}

.ch-hero-anim .ch-hero-anim-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.32; /* Ambient — never fights the h1 for legibility. */
  mix-blend-mode: screen;
}

/* Text children of the hero must render above the ambient SVG. */
.hero > .eyebrow,
.hero > h1,
.hero > .hero-lead,
.hero > .hero-meta {
  position: relative;
  z-index: 1;
}

/* Inline variant: the animation as an in-flow framed figure (used when the
   ambient-background treatment would fight the hero text — e.g. services).
   Sits after the hero-meta chips, before the next section's divider. */
.ch-hero-anim.is-inline {
  position: relative;
  inset: auto;
  z-index: auto;
  contain: content;
  width: 100%;
  aspect-ratio: 1440 / 360;
  max-height: 320px;
  margin: 34px 0 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(8, 9, 8, 0.55);
}
.ch-hero-anim.is-inline .ch-hero-anim-svg {
  opacity: 0.9;
  mix-blend-mode: normal;
}

/* Reduced motion + explicit toggle: fade the ambient layer even further so it
   reads as pure decoration. */
@media (prefers-reduced-motion: reduce) {
  .ch-hero-anim .ch-hero-anim-svg { opacity: 0.18; }
}
:root[data-motion="off"] .ch-hero-anim .ch-hero-anim-svg { opacity: 0.18; }


/* Architecture map (ArchitectureMap, .sw-arch) — subtle breathing halo on the
   two hero nodes (combat engine, match servers). The SMIL dash-march lives in
   the SVG itself; this CSS layer only drives the glow pulse, so the global
   reduced-motion overrides in §10 neutralise it automatically. */
.sw-arch-hero-halo {
  animation: swArchHaloPulse 3.6s ease-in-out infinite;
}
@keyframes swArchHaloPulse {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 1; }
}


/* =============================================================================
   §10  Reduced-motion contract (§2.4)
   Two triggers, one outcome:
   (a) OS-level `prefers-reduced-motion: reduce`
   (b) Manual toggle `<html data-motion="off">` from the language switcher.
   Both must collapse every keyframe to its terminal frame and disable
   ambient background drift entirely.
============================================================================= */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration:    0.001ms !important;
    animation-iteration-count: 1   !important;
    animation-delay:       0ms     !important;
    transition-duration:   0.001ms !important;
    scroll-behavior:       auto    !important;
  }

  /* Static, no march. */
  .sw-flow-line,
  .sw-flow-line.is-feedback,
  .sw-arch-wire.is-flow,
  .sw-arch-wire.is-feedback,
  .sw-arch-wire.is-learn,
  .sw-arch-wire.is-media,
  .sw-nn-wire {
    stroke-dasharray: 0;
    animation: none !important;
  }

  /* Kill ambient drift; keep gradient visible but slightly dimmed so the
     hero still reads as a hero without any motion cue. */
  .sw-noise-grid {
    animation: none !important;
    transform: none !important;
  }
  .sw-noise-bg { opacity: 0.5; }
  .sw-noise-particles { display: none !important; }

  /* Reveal — pin to terminal frame. */
  .sw-reveal {
    opacity: 1;
    transform: none;
  }

  /* Counter — final value only, no tick-in. */
  .sw-stat-value strong,
  .sw-counter-tween {
    animation: none !important;
    opacity: 1;
    transform: none;
  }

  /* Tier ladder — instantly at final fill. */
  .sw-tier-rung::before {
    animation: none !important;
    transform: scaleX(var(--fill, 1));
  }

  /* Pulse rings — invisible in reduced-motion. The core text still reads. */
  .sw-sigil-pulse-ring { display: none !important; }
  .sw-shipped-badge {
    animation: none !important;
    box-shadow: 0 0 0 2px color-mix(in oklab, var(--pulse-tint) 40%, transparent);
  }
}

/* Manual toggle mirror — identical rule-set behind html[data-motion="off"].
   Kept as a duplicate block (rather than a shared placeholder) so it composes
   cleanly whether or not `@media` also matches. */
html[data-motion="off"] *,
html[data-motion="off"] *::before,
html[data-motion="off"] *::after {
  animation-duration:    0.001ms !important;
  animation-iteration-count: 1   !important;
  animation-delay:       0ms     !important;
  transition-duration:   0.001ms !important;
  scroll-behavior:       auto    !important;
}
html[data-motion="off"] .sw-flow-line,
html[data-motion="off"] .sw-flow-line.is-feedback,
html[data-motion="off"] .sw-arch-wire.is-flow,
html[data-motion="off"] .sw-arch-wire.is-feedback,
html[data-motion="off"] .sw-arch-wire.is-learn,
html[data-motion="off"] .sw-arch-wire.is-media,
html[data-motion="off"] .sw-nn-wire {
  stroke-dasharray: 0;
  animation: none !important;
}
html[data-motion="off"] .sw-noise-grid {
  animation: none !important;
  transform: none !important;
}
html[data-motion="off"] .sw-noise-bg { opacity: 0.5; }
html[data-motion="off"] .sw-noise-particles { display: none !important; }
html[data-motion="off"] .sw-reveal {
  opacity: 1;
  transform: none;
}
html[data-motion="off"] .sw-stat-value strong,
html[data-motion="off"] .sw-counter-tween {
  animation: none !important;
  opacity: 1;
  transform: none;
}
html[data-motion="off"] .sw-tier-rung::before {
  animation: none !important;
  transform: scaleX(var(--fill, 1));
}
html[data-motion="off"] .sw-sigil-pulse-ring { display: none !important; }
html[data-motion="off"] .sw-shipped-badge {
  animation: none !important;
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--pulse-tint) 40%, transparent);
}


/* =============================================================================
   §11  Print — collapse every motion primitive to a static, legible layout.
============================================================================= */
@media print {
  .sw-noise-bg,
  .sw-sigil-pulse-ring,
  .sw-shipped-badge { display: none !important; }
  .sw-flow-line,
  .sw-nn-wire { stroke-dasharray: 0; animation: none !important; }
  .sw-tier-rung::before {
    animation: none !important;
    transform: scaleX(var(--fill, 1));
  }
  .sw-reveal { opacity: 1; transform: none; }
}


/* =============================================================================
   End of animations.css
============================================================================= */
