/* ==========================================================================
   Stillroom moving-type engine — stylesheet half.
   Pair with stillroom-type.js. Theme by overriding the --st-* custom
   properties on :root (or any ancestor) — never edit the JS to re-theme.
   18 moves: roll, decode, pass, typeon, bracket-grow, mx-decode, shimmer,
   field, rain, rain-lock, flip-cut, grid-pop, block-wipe, crt-print,
   dial, flood, block-build, ring.
   See motion/README.md for the full move table and the act card.
   ========================================================================== */

:root{
  --st-ink:#0a0a0a;
  --st-ink-2:#141414;
  --st-paper:#f4f1ea;
  --st-paper-dim:#cfc8b8;
  --st-bone:#a8a194;
  --st-accent:#b85c2c;      /* rust — default "hot" colour for heads/flashes/locks */
  --st-accent-2:#5fae6e;    /* committed green variant, opt-in per venture/move */
  --st-line: rgba(244,241,234,.18);
  --st-glyphs:"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789§/+#";
  --st-mono-font: 'Space Mono', monospace;
  --st-tick-fast: 40ms;
  --st-tick-mid: 90ms;
  --st-hold-short: 300ms;
}

/* Stillroom's own theme: map the engine tokens onto the guidelines' real tokens. */
.stillroom-theme, [data-st-theme="stillroom"]{
  --st-ink:#0a0a0a; --st-ink-2:#141414;
  --st-paper:#f4f1ea; --st-paper-dim:#cfc8b8; --st-bone:#a8a194;
  --st-accent:#b85c2c; --st-accent-2:#5fae6e;
  --st-line: rgba(244,241,234,.18);
  --st-mono-font: var(--f-mono, 'Space Mono', monospace);
}

[data-st]:not([data-st="off"]){ position:relative; }  /* an opted-out element keeps its own positioning (2026-09-27: absolutely placed labels were being dragged into flow) */
[data-st] .st-a11y{
  position:absolute !important; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0; padding:0; margin:-1px;
}
[data-st][data-st-applied] { /* stamped once the engine has run this element */ }

/* -------------------------------------------------------------------------
   A · small print — DOM/char moves (roll, decode, typeon, bracket-grow,
   mx-decode share a common "reel/cell" visual grammar).
   ------------------------------------------------------------------------- */

/* !important on the layout-critical rules below: a host page's own class on
   the same element (a card, a stage, a flex row) can share equal specificity
   and win on source order alone — these display values are load-bearing for
   the move to render at all, not cosmetic, so they must not lose that fight. */
/* flex-wrap: long lines wrap inside their box on a 390px phone instead of pushing the page sideways (2026-09-27).
   Known gap: a wrap can fall mid-word; the fix is grouping chars into word spans in charSpanBuild(). */
.st-chars{ display:inline-flex !important; flex-wrap:wrap; max-width:100%; }
.st-char{ display:inline-block; white-space:pre; font-variant-numeric:tabular-nums; }

/* A1 · roll — chase-roll odometer reel */
.st-reel{ display:inline-block; overflow:hidden; height:1em; vertical-align:-0.08em; line-height:1; }
.st-reel .st-reel-strip{ display:block; will-change:transform; }
.st-reel .st-reel-strip span{ display:block; height:1em; line-height:1; }

/* A2/A6 · decode & mx-decode — glyph-cycle lock, colour ramps toward accent as it locks.
   white-space:pre + inline-block, matching .st-char: a lone space character
   in a plain inline span can collapse to near-zero width (confirmed live —
   rain-lock's "MOVING TYPE" rendered as "MOVINGTYPE", the space silently
   swallowed), which is exactly the kind of thing the "resting state is the
   plain, exact string" rule exists to forbid. */
.st-cell{ color:var(--st-bone); white-space:pre; display:inline-block; font-variant-numeric:tabular-nums; }
.st-cell.st-cell--cycling{ color:var(--st-accent); text-shadow:0 0 6px color-mix(in srgb, var(--st-accent) 55%, transparent); font-weight:700; }
.st-cell.st-cell--locked{ color:inherit; font-weight:inherit; }
/* Bloom on the instant of lock: a brief glow that decays over ~450ms, not a
   permanent effect (a locked line settles flat, per "every move's resting
   state is the plain, exact string") -- but the LANDING itself gets the same
   saturated-fill punch the reference clips build every beat on, addressing
   the "flat rust-on-black, no bloom anywhere" note across every judging pass.
   Applied via a short-lived class the JS adds/removes at the lock moment. */
.st-cell.st-cell--just-locked{ color:var(--st-paper); text-shadow:0 0 3px var(--st-paper), 0 0 12px color-mix(in srgb, var(--st-accent) 80%, transparent), 0 0 26px color-mix(in srgb, var(--st-accent) 45%, transparent); transition:text-shadow 450ms ease-out, color 450ms ease-out; }

/* A3 · pass — phosphor wash band */
.st-pass{ position:relative; }
.st-pass .st-char{ color:inherit; transition:none; }
.st-pass.st-pass--washing .st-char.st-char--lit{ color:var(--st-accent); text-shadow:0 0 6px color-mix(in srgb, var(--st-accent) 70%, transparent); }

/* A4 · typeon — bracket type-on with blinking cursor */
.st-typeon .st-bracket{ color:var(--st-accent); }
.st-typeon .st-cursor{ display:inline-block; width:.5em; background:var(--st-accent); margin-left:1px; animation:st-blink 0.5s steps(1) 6; }
.st-typeon .st-cursor.st-cursor--done{ animation:none; opacity:0; }
@keyframes st-blink{ 50%{ opacity:0; } }

/* A5 · bracket-grow — centre-out reveal, unrevealed = dim placeholder */
.st-bgrow .st-bracket{ color:var(--st-accent); }
.st-bgrow .st-gap{ opacity:.18; }

/* A7 · shimmer idle flicker */
.st-shimmer-hit{ color:var(--st-accent); }

/* -------------------------------------------------------------------------
   B · text-as-texture — canvas moves
   ------------------------------------------------------------------------- */
.st-canvas-host{ position:relative; overflow:hidden; }
.st-canvas-host canvas{ display:block; width:100%; height:100%; }
.st-canvas-host[data-st="field"]{ min-height:64px; }
.st-canvas-host[data-st="rain"], .st-canvas-host[data-st="rain-lock"]{ background:var(--st-ink); min-height:120px; }
.st-rainlock-label{ position:absolute; inset:0; display:flex !important; align-items:center; justify-content:center;
  font-family:var(--st-mono-font); font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--st-paper);
  opacity:0; text-shadow:0 0 10px rgba(10,10,10,.9), 0 0 3px rgba(10,10,10,1); z-index:1; pointer-events:none; }
.st-rainlock-label.st-rainlock-label--on{ opacity:1; }

/* -------------------------------------------------------------------------
   C · cuts, flips & wipes
   ------------------------------------------------------------------------- */
.st-flipcut-host{ position:relative; }
/* Driven by JS setTimeout hard-toggles on el.style.opacity, not a CSS
   @keyframes animation: a single animation-timing-function (even steps(1))
   applies per-segment across the WHOLE keyframe list, which collapses a
   multi-beat flash-hold-flash into effectively one jump — JS timers give
   exact, debuggable, hard-cut control instead (still no transition, so
   every change is instant, per G1). */
.st-flipcut-flash{ position:absolute; inset:0; background:var(--st-accent); opacity:0; pointer-events:none; transition:none; }

.st-gridpop{ position:relative; display:grid !important; align-items:stretch !important; justify-items:stretch !important; }
.st-gridpop-cell{ position:relative; border:1px solid var(--st-line); }
.st-gridpop-cell::after{ content:"+"; position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-size:8px; color:var(--st-line); }
.st-gridpop-cell.st-gridpop-cell--fill{ background:var(--st-accent); }
.st-gridpop-cell.st-gridpop-cell--fill::after{ content:""; }
.st-gridpop-cell.st-gridpop-cell--fill2{ background:var(--st-ink-2); }
.st-gridpop-cell.st-gridpop-cell--fill2::after{ content:""; }

.st-blockwipe{ position:relative; overflow:hidden; }
.st-blockwipe .st-bw-a, .st-blockwipe .st-bw-b{ position:absolute; inset:0; background-size:cover; background-position:center; }
.st-blockwipe .st-bw-b{ display:grid !important; }
.st-blockwipe-cell{ position:relative; overflow:hidden; }
/* Each cell's OWN background is already image B (set inline per-cell by the
   engine); the overlay hiding it must show image A underneath, not another
   copy of B — "background:inherit" here inherited the cell's own B, so
   removing it revealed... B again, and the wipe was a no-op from frame one
   (confirmed against the reference: the composite never visibly changed).
   --bw-cell-a is set inline by the engine alongside background-image/-size/
   -position; background-size/-position are inherited from the cell (they're
   identical for A and B, since both cover the same crop). */
.st-blockwipe-cell::before{ content:""; position:absolute; inset:0; background-image:var(--bw-cell-a); background-size:inherit; background-position:inherit; background-repeat:no-repeat; }
.st-blockwipe-cell.st-bw-revealed::before{ opacity:0; }
.st-blockwipe-cell.st-bw-edge{ outline:2px solid var(--st-accent); outline-offset:-2px; }

/* -------------------------------------------------------------------------
   D/E · instrument, radial, block-built display type — canvas hosts
   ------------------------------------------------------------------------- */
.st-canvas-host[data-st="dial"]{ aspect-ratio:1/1; max-width:320px; }
.st-canvas-host[data-st="crt-print"]{ min-height:180px; background:var(--st-ink); }
.st-canvas-host[data-st="block-build"]{ min-height:110px; background:var(--st-ink); display:flex; align-items:center; justify-content:center; }
.st-canvas-host[data-st="block-build"][data-st-mode="bleed"]{ min-height:220px; }

/* F · Hal's ring (SVG) */
.st-ring{ display:block; }
.st-ring .st-ring-track{ fill:none; stroke:var(--st-line); stroke-width:1; }
.st-ring .st-ring-chase{ fill:none; stroke:var(--st-accent); stroke-linecap:round; }

/* -------------------------------------------------------------------------
   Reduced motion — belt & braces at the CSS layer too (JS is authoritative)
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  .st-typeon .st-cursor{ animation:none; opacity:0; }
  .st-flipcut-flash{ opacity:0 !important; }
}
