/* signal-base pillar motion: the nine motions Cody chose (2026-10-17, "Pillar
   Motion"). Transform and opacity only, motion tokens, ease-out. Each motion
   is a class on a .signal-pillars (or the launch mark); motion/signal-motion.js
   drives the stateful ones. Use them where they carry state a responder needs
   (how much is left, is it live, did it save, a new call), never as idle
   decoration or hover effects. Every motion has a reduced-motion still state
   at the bottom of this file that says the same thing. */

/* 1 Readiness Fill: the fills' scaleY transition in css/signal-base.css;
   SignalMotion.fill(el, fraction) sets the levels. */

/* 2 Ready Moment: the last pillar fills, all three dip once. Plays once. */
.sb-m-ready .signal-pillars__fill { animation: sb-dip var(--motion-slow) var(--ease-out) 360ms 1; }
@keyframes sb-dip { 50% { opacity: 0.25; } }

/* 3 Working: replaces spinners. JS stops it at 20 s ("Still Working"). */
.sb-m-working .signal-pillars__fill { animation: sb-work 900ms var(--ease-out) infinite alternate; }
.sb-m-working .signal-pillars__fill:nth-of-type(2) { animation-delay: 150ms; }
.sb-m-working .signal-pillars__fill:nth-of-type(3) { animation-delay: 300ms; }
@keyframes sb-work { from { transform: scaleY(0.22); } to { transform: scaleY(1); } }

/* 4 Live Pulse: the centre pillar breathes while the feed is fresh. Stale is
   grey and still; no pulse means no proof (an unknown feed is never live). */
.sb-m-live .signal-pillars__fill:nth-of-type(2) { animation: sb-breathe 2400ms ease-out infinite; }
@keyframes sb-breathe { 50% { opacity: 0.45; } }
.sb-m-stale { --pillar-fill: var(--text-muted); }
.sb-m-stale .signal-pillars__fill { animation: none; }
/* The same pulse on the Signal mark (mark(), tiled or bare), for a surface
   whose one mark says the feed is live (the station board header). Stale
   stills it and keeps the pillars red: the mark is never recoloured, so the
   age label beside it is what says stale. */
.sb-m-live .sb-mark__pillar:nth-of-type(2) { animation: sb-breathe 2400ms ease-out infinite; }
.sb-m-stale .sb-mark__pillar { animation: none; }

/* 5 Tone-Out Strike: left, centre, right, three cycles (under 3 flashes a
   second per pillar), then hold solid (.sb-m-held) until acknowledged. */
.sb-m-strike .signal-pillars__fill { animation: sb-strike 360ms steps(1, end) 3; }
.sb-m-strike .signal-pillars__fill:nth-of-type(2) { animation-delay: 120ms; }
.sb-m-strike .signal-pillars__fill:nth-of-type(3) { animation-delay: 240ms; }
@keyframes sb-strike { 0% { opacity: 1; } 34% { opacity: 0.18; } }
.sb-m-held .signal-pillars__fill { opacity: 1; animation: none; transform: scaleY(1); }

/* 6 Done Mark: the open count leaves and the mark pops in where it was. */
.sb-m-pop { animation: sb-pop var(--motion-base) var(--ease-out) both; }
@keyframes sb-pop { from { opacity: 0; transform: scale(0.6); } }

/* 7 Hollow Until Confirmed: outlines while a write is in flight; filled only
   when the server confirms; dashed grey and "Not Confirmed" if it never does. */
.sb-m-pending .signal-pillars__track,
.sb-m-unconfirmed .signal-pillars__track { fill: none; stroke: var(--pillar-fill); stroke-width: 28; }
.sb-m-pending .signal-pillars__fill,
.sb-m-unconfirmed .signal-pillars__fill { transform: scaleY(0); }
.sb-m-unconfirmed { --pillar-fill: var(--text-muted); }
.sb-m-unconfirmed .signal-pillars__track { stroke-dasharray: 56 36; }

/* 8 Turnout Clock: each pillar a third of the turnout target, in ink; red
   once the target passes. The number beside it stays the main read. */
.sb-m-turnout { --pillar-fill: var(--text); }
.sb-m-turnout .signal-pillars__fill { transition: transform 100ms linear; }
.sb-m-turnout.sb-m-over { --pillar-fill: var(--signal-red); }

/* 9 Launch: on a cold start the tile settles and the pillars rise, 60 ms
   apart, 440 ms in all, once per session (SignalMotion.launch). */
.sb-m-launch .sb-mark__tile { transform-box: fill-box; transform-origin: center; animation: sb-tile var(--motion-slow) var(--ease-out) both; }
.sb-m-launch .sb-mark__pillar { transform-box: fill-box; transform-origin: 50% 100%; animation: sb-rise var(--motion-slow) var(--ease-out) both; }
.sb-m-launch .sb-mark__pillar:nth-of-type(2) { animation-delay: 60ms; }
.sb-m-launch .sb-mark__pillar:nth-of-type(3) { animation-delay: 120ms; }
@keyframes sb-tile { from { opacity: 0; transform: scale(0.92); } }
@keyframes sb-rise { from { transform: scaleY(0); } }

/* Still states (MOTION-04): the device's Reduce Motion unless the person
   chose More, or the person's Less Motion. Same meaning, no movement. */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="more"]) [class*="sb-m-"],
  :root:not([data-motion="more"]) [class*="sb-m-"] * { animation: none !important; }
  :root:not([data-motion="more"]) .sb-m-working .signal-pillars__fill { transform: scaleY(0.6) !important; }
}
:root[data-motion="less"] [class*="sb-m-"],
:root[data-motion="less"] [class*="sb-m-"] * { animation: none !important; }
:root[data-motion="less"] .sb-m-working .signal-pillars__fill { transform: scaleY(0.6) !important; }
