/* Wick Wardens motion and look classes (look agent; SPEC §10.4, §10.5, §10.6).
   Everything is a class the renderer / screens toggle. Durations are written as calc(N ms * var(--fx-k, 1)): 1 = 1x, .5 = 2x, 0 = Instant
   (html[data-speed] sets --fx-k below; Instant also gets a 150 ms flash, .fx-flash, from the renderer).
   Reduced motion = html[data-motion="reduced"] (main.js resolves Auto, and the media query covers pages that don't set the attribute):
   no movement, one-shot effects become a 120 ms cross-fade (fx-xfade) and every loop stops.
   SVG transforms use transform-box: fill-box so classes work on <g>, <circle>, <path>.
   Hooks: .fx-nudge-1..6 (die face = wind direction) · .fx-die-tumble · .fx-die-land · .fx-breathe · .fx-twitch · .fx-wobble · .fx-puff(.big) · .fx-drop · .fx-bloom
          · .fx-shudder · .fx-burst · .fx-snuff · .fx-soaked · .fx-spark · .fx-bell-swing · .fx-pulse-coral · .fx-moon-wobble · .fx-spin-once · .fx-lend-hop
          · .fx-rewind · .fx-seal · .fx-card-glow · .fx-beam-sweep · .fx-lit-flash · .fx-bunting · .fx-dusk (+ .fx-dusk-rect) · .fx-dawn · .fx-fogwave · .fx-lamp-out
          · .fx-wave · .fx-flash · .fx-xfade · .wd-walk (Warden legs) · .lamp-flame / .lamp-spark (lamp art, wordmark flame) · .fx-confetti (canvas, confetti.js) */

:root{ --fx-k:1; --fx-pop:cubic-bezier(.3,1.6,.5,1); }
html[data-speed="2x"]{ --fx-k:.5; }
html[data-speed="instant"]{ --fx-k:0; }

/* ---------- always-on small loops (art in code: the flame is 2 frames, 600 ms) ---------- */
.lamp-flame{ transform-box:fill-box; transform-origin:50% 100%; animation:fx-flicker 600ms steps(1,end) infinite; }
@keyframes fx-flicker{ 0%{ transform:scale(1,1) rotate(0deg); } 50%{ transform:scale(.88,1.16) rotate(3deg); } }
.lamp-spark{ transform-box:fill-box; transform-origin:center; animation:fx-twinkle 1800ms ease-in-out infinite; }
@keyframes fx-twinkle{ 0%,100%{ opacity:.35; } 50%{ opacity:1; } }
.wd-walk .wd-leg-l{ animation:wd-legl 360ms steps(1,end) infinite; }
.wd-walk .wd-leg-r{ animation:wd-legr 360ms steps(1,end) infinite; }
.wd-walk .wd-pawn{ animation:wd-bob 360ms steps(1,end) infinite; }
@keyframes wd-legl{ 0%{ transform:rotate(16deg); } 50%{ transform:rotate(-16deg); } }
@keyframes wd-legr{ 0%{ transform:rotate(-16deg); } 50%{ transform:rotate(16deg); } }
@keyframes wd-bob{ 0%{ transform:translateY(0); } 50%{ transform:translateY(-.9px); } }
.fx-wave{ animation:fx-wave 14s linear infinite; }                                   /* sea wave lines, 3 slow horizontal lines */
@keyframes fx-wave{ from{ transform:translateX(0); } to{ transform:translateX(-24px); } }
.fx-breathe{ transform-box:fill-box; transform-origin:center; animation:fx-breathe 3s ease-in-out infinite; }   /* lamp glow, +-4% radius */
@keyframes fx-breathe{ 0%,100%{ transform:scale(.96); } 50%{ transform:scale(1.04); } }
.fx-twitch{ transform-box:fill-box; transform-origin:center; animation:fx-twitch 4s ease-in-out infinite; }       /* the surge forecast star twitches every 4 s */
@keyframes fx-twitch{ 0%,88%,100%{ transform:rotate(0) scale(1); } 91%{ transform:rotate(-14deg) scale(1.18); } 94%{ transform:rotate(12deg) scale(1.12); } 97%{ transform:rotate(-5deg) scale(1.04); } }
.fx-pulse-coral{ animation:fx-pulse 1.1s ease-in-out infinite; }                                                   /* the last free Bell pip pulses coral (never > 3 Hz) */
@keyframes fx-pulse{ 0%,100%{ transform:scale(1); fill:var(--danger); filter:none; } 50%{ transform:scale(1.25); filter:drop-shadow(0 0 4px var(--danger)); } }
.fx-pulse-coral{ transform-box:fill-box; transform-origin:center; }

/* ---------- one-shot effects (1x durations from SPEC §10.4) ---------- */
.fx-nudge-1,.fx-nudge-2,.fx-nudge-3,.fx-nudge-4,.fx-nudge-5,.fx-nudge-6{ animation:fx-nudge calc(180ms * var(--fx-k,1)) ease-out; }
.fx-nudge-1{ --nx:3px;  --ny:-5.2px; } .fx-nudge-2{ --nx:6px;  --ny:0; } .fx-nudge-3{ --nx:3px;  --ny:5.2px; }   /* NE E SE */
.fx-nudge-4{ --nx:-3px; --ny:5.2px; }  .fx-nudge-5{ --nx:-6px; --ny:0; } .fx-nudge-6{ --nx:-3px; --ny:-5.2px; }  /* SW W NW */
@keyframes fx-nudge{ 0%{ transform:translate(0,0); } 35%{ transform:translate(var(--nx,0),var(--ny,0)); } 100%{ transform:translate(0,0); } }
.fx-die-tumble{ transform-box:fill-box; transform-origin:center; animation:fx-tumble calc(600ms * var(--fx-k,1)) cubic-bezier(.2,.7,.3,1) both; }
@keyframes fx-tumble{ 0%{ transform:rotate(0) scale(1.2); } 33%{ transform:rotate(240deg) scale(.86); } 66%{ transform:rotate(480deg) scale(1.1); } 100%{ transform:rotate(720deg) scale(1); } }
.fx-die-land{ transform-box:fill-box; transform-origin:center; animation:fx-land calc(160ms * var(--fx-k,1)) ease-out; }
@keyframes fx-land{ 0%{ transform:scale(1.14); } 100%{ transform:scale(1); } }
.fx-wobble{ transform-box:fill-box; transform-origin:50% 100%; animation:fx-wobble calc(280ms * var(--fx-k,1)) ease-out; }   /* a wisp curl lands */
@keyframes fx-wobble{ 0%{ transform:rotate(0) scale(.6); } 30%{ transform:rotate(-9deg) scale(1.1); } 60%{ transform:rotate(7deg) scale(.96); } 100%{ transform:rotate(0) scale(1); } }
.fx-puff{ transform-box:fill-box; transform-origin:center; animation:fx-puff calc(220ms * var(--fx-k,1)) ease-out both; }   /* Clear: the fog pattern puffs outward */
.fx-puff.big{ animation-name:fx-puff-big; animation-duration:calc(320ms * var(--fx-k,1)); }                               /* Clearing Murk: a bigger puff */
@keyframes fx-puff{ 0%{ transform:scale(1); opacity:1; } 100%{ transform:scale(1.5); opacity:0; } }
@keyframes fx-puff-big{ 0%{ transform:scale(1); opacity:1; } 100%{ transform:scale(1.9); opacity:0; } }
.fx-drop{ transform-box:fill-box; transform-origin:50% 100%; animation:fx-drop calc(300ms * var(--fx-k,1)) var(--fx-pop) both; }   /* Hang a Lamp: drops in with a bounce */
@keyframes fx-drop{ 0%{ transform:translateY(-16px) scale(.8); opacity:0; } 60%{ transform:translateY(1px) scale(1.04); opacity:1; } 100%{ transform:translateY(0) scale(1); opacity:1; } }
.fx-bloom{ transform-box:fill-box; transform-origin:center; animation:fx-bloom calc(400ms * var(--fx-k,1)) ease-out both; }   /* the glow blooms */
@keyframes fx-bloom{ 0%{ transform:scale(.2); opacity:0; } 100%{ transform:scale(1); opacity:1; } }
.fx-shudder{ animation:fx-shudder calc(420ms * var(--fx-k,1)) linear; }                                                   /* surge: the district shudders */
@keyframes fx-shudder{ 0%,100%{ transform:translate(0,0); } 10%{ transform:translate(-2px,1px); } 25%{ transform:translate(2px,-1px); } 40%{ transform:translate(-2px,-1px); } 55%{ transform:translate(2px,1px); } 70%{ transform:translate(-1px,0); } 85%{ transform:translate(1px,0); } }
.fx-burst{ transform-box:fill-box; transform-origin:center; animation:fx-burst calc(420ms * var(--fx-k,1)) ease-out both; }   /* the star burst */
@keyframes fx-burst{ 0%{ transform:scale(.3) rotate(0); opacity:1; } 100%{ transform:scale(1.8) rotate(40deg); opacity:0; } }
.fx-snuff{ animation:fx-snuff calc(300ms * var(--fx-k,1)) steps(1,end) both; }                                            /* snuff: flickers twice and goes dark */
@keyframes fx-snuff{ 0%{ opacity:1; } 20%{ opacity:.2; } 40%{ opacity:1; } 60%{ opacity:.2; } 100%{ opacity:0; } }
.fx-soaked{ transform-box:fill-box; transform-origin:center; animation:fx-soaked calc(500ms * var(--fx-k,1)) ease-out both; }   /* a surge line vanishes: the "soaked" sparkle */
@keyframes fx-soaked{ 0%{ transform:scale(.4); opacity:0; } 40%{ transform:scale(1.3); opacity:1; } 100%{ transform:scale(1.8); opacity:0; } }
.fx-spark{ transform-box:fill-box; transform-origin:center; animation:fx-spark calc(260ms * var(--fx-k,1)) ease-out both; }   /* an ember spark rises / absorb sparkle */
@keyframes fx-spark{ 0%{ transform:translateY(0) scale(1); opacity:1; } 100%{ transform:translateY(-12px) scale(.4); opacity:0; } }
.fx-bell-swing{ transform-box:fill-box; transform-origin:50% 0; animation:fx-bell calc(700ms * var(--fx-k,1)) ease-out; }   /* each Bell ring swings the brass bell once */
@keyframes fx-bell{ 0%{ transform:rotate(0); } 15%{ transform:rotate(-22deg); } 35%{ transform:rotate(18deg); } 55%{ transform:rotate(-11deg); } 75%{ transform:rotate(5deg); } 100%{ transform:rotate(0); } }
.fx-moon-wobble{ transform-box:fill-box; transform-origin:center; animation:fx-moonw calc(1000ms * var(--fx-k,1)) ease-in-out; }   /* Moonrise: the moon wobbles as it rises */
@keyframes fx-moonw{ 0%{ transform:translateY(8px) rotate(0); } 30%{ transform:translateY(2px) rotate(-8deg); } 60%{ transform:translateY(-2px) rotate(6deg); } 100%{ transform:translateY(0) rotate(0); } }
.fx-spin-once{ transform-box:fill-box; transform-origin:center; animation:fx-spin calc(1000ms * var(--fx-k,1)) ease-in-out; }     /* the Hollow ring spins once */
@keyframes fx-spin{ from{ transform:rotate(0); } to{ transform:rotate(360deg); } }
.fx-lend-hop{ animation:fx-lend calc(420ms * var(--fx-k,1)) ease-in-out both; }                                            /* Lend a hand: a little lantern hops to the action pips */
@keyframes fx-lend{ 0%{ transform:translate(0,0) scale(1); } 50%{ transform:translate(calc(var(--lx,60px) * .5),-18px) scale(1.15); } 100%{ transform:translate(var(--lx,60px),var(--ly,0px)) scale(.6); opacity:0; } }
.fx-rewind{ animation:fx-rewind calc(160ms * var(--fx-k,1)) ease-in both; }                                                /* Undo: the last change rewinds visibly (160 ms reverse) */
@keyframes fx-rewind{ 0%{ transform:scale(1); opacity:1; } 100%{ transform:scale(.88); opacity:.2; } }
.fx-seal{ transform-box:fill-box; transform-origin:center; animation:fx-seal calc(260ms * var(--fx-k,1)) var(--fx-pop) both; }   /* the wax-seal stamp on a truth-checked Signal */
@keyframes fx-seal{ 0%{ transform:scale(2) rotate(-20deg); opacity:0; } 70%{ transform:scale(.92) rotate(4deg); opacity:1; } 100%{ transform:scale(1) rotate(0); opacity:1; } }
.fx-card-glow{ filter:drop-shadow(0 0 5px var(--seat, var(--lamp-glow))); }                                                 /* stoked card backs glow faintly in the stoker's colour */
.fx-beam-sweep{ transform-box:fill-box; transform-origin:0 50%; animation:fx-beam calc(900ms * var(--fx-k,1)) ease-in-out both; }   /* LIT: the beam sweeps */
@keyframes fx-beam{ 0%{ transform:scaleX(0); opacity:.9; } 55%{ transform:scaleX(1); opacity:1; } 100%{ transform:scaleX(1); opacity:.35; } }
.fx-lit-flash{ animation:fx-litflash calc(500ms * var(--fx-k,1)) ease-out; }                                               /* every district on the line flashes warm */
@keyframes fx-litflash{ 0%{ filter:brightness(1); } 25%{ filter:brightness(1.7) saturate(1.2); } 100%{ filter:brightness(1); } }
.fx-bunting{ transform-box:fill-box; transform-origin:50% 0; animation:fx-bunting calc(380ms * var(--fx-k,1)) var(--fx-pop) both; }   /* the tower's flag bunting pops */
@keyframes fx-bunting{ 0%{ transform:scaleY(0); } 100%{ transform:scaleY(1); } }
.fx-flash{ animation:fx-flash 150ms ease-out; }                                                                            /* Instant speed: the 150 ms flash that replaces the animation */
@keyframes fx-flash{ 0%{ filter:brightness(1.8); } 100%{ filter:brightness(1); } }
.fx-lamp-lit{ animation:fx-lamplit calc(400ms * var(--fx-k,1)) ease-out both; }
@keyframes fx-lamplit{ from{ opacity:.2; } to{ opacity:1; } }

/* ---------- the board's moods ---------- */
.fx-dusk{ position:relative; }                                                                                              /* Dusk: the board tints 15% bluer over 300 ms */
.fx-dusk::after{ content:''; position:absolute; inset:0; pointer-events:none; background:#2F5FC2; mix-blend-mode:soft-light; opacity:.15; animation:fx-dusk-in calc(300ms * var(--fx-k,1)) ease-out both; z-index:3; }
@keyframes fx-dusk-in{ from{ opacity:0; } to{ opacity:.15; } }
.fx-dusk-rect{ fill:#2F5FC2; opacity:.15; pointer-events:none; animation:fx-dusk-in calc(300ms * var(--fx-k,1)) ease-out both; }   /* the same tint for an SVG <rect> overlay */
.fx-dawn{ position:relative; }                                                                                              /* Dawn: the sky band warms to peach over 1.5 s */
.fx-dawn::before{ content:''; position:absolute; left:0; right:0; top:0; height:42%; pointer-events:none; z-index:2; background:linear-gradient(to bottom, rgba(255,184,138,.6), rgba(255,217,168,.28) 55%, rgba(255,217,168,0)); animation:fx-dawn-in calc(1500ms * var(--fx-k,1)) ease-in-out both; }
@keyframes fx-dawn-in{ from{ opacity:0; } to{ opacity:1; } }
.fx-fogwave{ opacity:0; animation:fx-fogwave calc(420ms * var(--fx-k,1)) ease-out calc(var(--wave-i,0) * 60ms * var(--fx-k,1)) forwards; }   /* Loss: fog rolls over every district in a wave from the cause */
@keyframes fx-fogwave{ from{ opacity:0; } to{ opacity:var(--wave-o,.62); } }
.fx-lamp-out{ animation:fx-lampout calc(260ms * var(--fx-k,1)) ease-in calc(var(--out-i,0) * 140ms * var(--fx-k,1)) forwards; }       /* the lamps go dark one by one */
@keyframes fx-lampout{ to{ opacity:.08; filter:grayscale(1); } }
.fx-xfade{ animation:fx-xfade 120ms linear both; }
@keyframes fx-xfade{ from{ opacity:.35; } to{ opacity:1; } }

/* ---------- reduced motion: no movement, a 120 ms cross-fade; every loop stops (app.css already shortens all durations) ---------- */
html[data-motion="reduced"] .lamp-flame, html[data-motion="reduced"] .lamp-spark, html[data-motion="reduced"] .wd-pawn, html[data-motion="reduced"] .wd-leg,
html[data-motion="reduced"] .fx-wave, html[data-motion="reduced"] .fx-breathe, html[data-motion="reduced"] .fx-twitch, html[data-motion="reduced"] .fx-pulse-coral{ animation:none!important; }
html[data-motion="reduced"] .fx-pulse-coral{ filter:drop-shadow(0 0 4px var(--danger)); }
html[data-motion="reduced"] :is(.fx-nudge-1,.fx-nudge-2,.fx-nudge-3,.fx-nudge-4,.fx-nudge-5,.fx-nudge-6,.fx-die-tumble,.fx-die-land,.fx-wobble,.fx-puff,.fx-drop,.fx-bloom,.fx-shudder,.fx-burst,.fx-snuff,.fx-soaked,.fx-spark,.fx-bell-swing,.fx-moon-wobble,.fx-spin-once,.fx-lend-hop,.fx-rewind,.fx-seal,.fx-beam-sweep,.fx-lit-flash,.fx-bunting,.fx-lamp-lit){ animation:fx-xfade 120ms linear both!important; transform:none!important; }
html[data-motion="reduced"] :is(.fx-fogwave,.fx-lamp-out,.fx-dusk-rect), html[data-motion="reduced"] .fx-dusk::after, html[data-motion="reduced"] .fx-dawn::before{ animation-duration:120ms!important; animation-delay:0s!important; }
@media (prefers-reduced-motion:reduce){
  html:not([data-motion]) .lamp-flame, html:not([data-motion]) .lamp-spark, html:not([data-motion]) .wd-pawn, html:not([data-motion]) .wd-leg,
  html:not([data-motion]) .fx-wave, html:not([data-motion]) .fx-breathe, html:not([data-motion]) .fx-twitch, html:not([data-motion]) .fx-pulse-coral{ animation:none!important; }
  html:not([data-motion]) :is(.fx-nudge-1,.fx-nudge-2,.fx-nudge-3,.fx-nudge-4,.fx-nudge-5,.fx-nudge-6,.fx-die-tumble,.fx-die-land,.fx-wobble,.fx-puff,.fx-drop,.fx-bloom,.fx-shudder,.fx-burst,.fx-snuff,.fx-soaked,.fx-spark,.fx-bell-swing,.fx-moon-wobble,.fx-spin-once,.fx-lend-hop,.fx-rewind,.fx-seal,.fx-beam-sweep,.fx-lit-flash,.fx-bunting,.fx-lamp-lit){ animation:fx-xfade 120ms linear both!important; transform:none!important; }
  html:not([data-motion]) :is(.fx-fogwave,.fx-lamp-out,.fx-dusk-rect), html:not([data-motion]) .fx-dusk::after, html:not([data-motion]) .fx-dawn::before{ animation-duration:120ms!important; animation-delay:0s!important; }
}

/* ---------- board papers (§7.10): the full token sets, generated from ui/warden.js paperTokens() by the look agent; contrast numbers in notes/look.md ---------- */
/*PAPERS-START*/
html[data-paper="navy"]{ --bg:#0E1A2B; --bg-deep:#08111D; --sea:#0B2237; --sea-wave:#16405E; --paper:#1C2E47; --paper-hi:#28426A; --edge:#E9DDBE; --ink:#F6EBD0; --ink-muted:#A9B6C8; --fog:#B4C7DB; --fog-ink:#DCE6F0; --fog-ink-hc:#FFFFFF; --amber:#F2A93B; --teal:#3CC6B5; --violet:#B9A2F2; --any:#F6EBD0; --lamp-core:#FFD27A; --lamp-glow:#FF9E3D; --beam:#FFE7A8; --danger:#FF7A66; --brass:#D9B26A; --moon:#E8E3C8; --card:#F3E6C8; --card-ink:#2A1E10; --good:#9BE39B; }
html[data-paper="navy"][data-hcfog]{ --fog:#B4C7DB; --fog-ink:#FFFFFF; }
html[data-paper="drift"]{ --bg:#1E1A16; --bg-deep:#12100D; --sea:#16232B; --sea-wave:#2C4552; --paper:#3A3128; --paper-hi:#54463A; --edge:#E9DDBE; --ink:#F6EBD0; --ink-muted:#C9BCA8; --fog:#CBC3B4; --fog-ink:#F1ECDF; --fog-ink-hc:#FFFFFF; --amber:#F2A93B; --teal:#3CC6B5; --violet:#B9A2F2; --any:#F6EBD0; --lamp-core:#FFD27A; --lamp-glow:#FF9E3D; --beam:#FFE7A8; --danger:#FF7A66; --brass:#D9B26A; --moon:#E8E3C8; --card:#F3E6C8; --card-ink:#2A1E10; --good:#9BE39B; }
html[data-paper="drift"][data-hcfog]{ --fog:#CBC3B4; --fog-ink:#FFFFFF; }
html[data-paper="glass"]{ --bg:#0C2022; --bg-deep:#061416; --sea:#0A2B2E; --sea-wave:#17525A; --paper:#1D3F40; --paper-hi:#2C5D5C; --edge:#E3F1E6; --ink:#F2F7E8; --ink-muted:#B1CAC4; --fog:#A9C4C0; --fog-ink:#E6F3F0; --fog-ink-hc:#FFFFFF; --amber:#F2A93B; --teal:#3CC6B5; --violet:#B9A2F2; --any:#F6EBD0; --lamp-core:#FFD27A; --lamp-glow:#FF9E3D; --beam:#FFE7A8; --danger:#FF7A66; --brass:#D9B26A; --moon:#E8E3C8; --card:#F3E6C8; --card-ink:#2A1E10; --good:#9BE39B; }
html[data-paper="glass"][data-hcfog]{ --fog:#A9C4C0; --fog-ink:#FFFFFF; }
html[data-paper="ink"]{ --bg:#07070F; --bg-deep:#030308; --sea:#0A0E1E; --sea-wave:#1B2549; --paper:#151633; --paper-hi:#28295C; --edge:#E9DDBE; --ink:#FFF3D6; --ink-muted:#B8BDD4; --fog:#B9BEDB; --fog-ink:#E6E9F8; --fog-ink-hc:#FFFFFF; --amber:#F2A93B; --teal:#3CC6B5; --violet:#B9A2F2; --any:#F6EBD0; --lamp-core:#FFD27A; --lamp-glow:#FF9E3D; --beam:#FFE7A8; --danger:#FF7A66; --brass:#D9B26A; --moon:#E8E3C8; --card:#F3E6C8; --card-ink:#2A1E10; --good:#9BE39B; }
html[data-paper="ink"][data-hcfog]{ --fog:#B9BEDB; --fog-ink:#FFFFFF; }
/*PAPERS-END*/
