/* =====================================================================================
   ✨ Show FX — the big screen's show effects, countdown and announcement (2026-09-30).
   Driven by static/js/display/show-fx-engine.js (window.ShowFxEngine), which both the Write-In stage
   (static/js/display/writein-stage.js) and the Show FX overlay over every other screen
   (static/js/display/show-fx.js) use.

   Scoping: every effect selector starts at .sfx-scope — a class both surfaces carry (the stage's .wi-stage
   element and the overlay's .sfx-stage). @keyframes are global by nature and carry a wi- prefix; the ones
   the stage's own decorations share with the effects (the cheese rain's fall, the chasing mouse, the stamp,
   the quake) live here too, so writein-stage.css needs this file loaded alongside it.

   The effects are the Bright Ideas deck's (C:\Python\bright ideas\deck\deck.css), as they are there: the same
   elements, sizes, colours, blurs, timings and keyframes, with the selectors scoped and the cz- keyframes
   renamed wi- (2026-10-04: the first port had simplified most of them). On purpose, still:
     - the siren's full-screen colour wash swaps under three times a second (the deck: 4.5; plan Q23);
     - the fog bands drift there and back (the deck's loop jumped back to the start mid-effect).
   No external URLs. No backdrop blur (it froze Android phones before). Effects arrive at once.
   ===================================================================================== */

/* ---------------------------------------------------------------- the marquee bulbs ---- */
/* deck.css #marquee: bulbs round the edge of the frame. The scoreboard's own chase is writein-stage.css; this
   is the marquee show control (on until the host switches it off). */
.sfx-scope .wi-marquee { position: absolute; inset: 0; pointer-events: none; z-index: 38; display: none; }
.sfx-scope .wi-mq-bulb {
  position: absolute; width: 16px; height: 16px; border-radius: 50%; transform: translate(-50%, -50%);
  border: 2px solid #5C2600; background: #FFA000; opacity: .35;
}
.sfx-scope.wi-mq-manual .wi-marquee { display: block; }
.sfx-scope.wi-mq-manual .wi-mq-bulb.p0 { animation: wi-mq-chase .6s steps(1) 0s infinite; }
.sfx-scope.wi-mq-manual .wi-mq-bulb.p1 { animation: wi-mq-chase .6s steps(1) .2s infinite; }
.sfx-scope.wi-mq-manual .wi-mq-bulb.p2 { animation: wi-mq-chase .6s steps(1) .4s infinite; }
/* while the marquee control is on, the Write-In scoreboard's own bulbs (the same places) step aside */
html.sfx-marquee-on .wi-stage .wi-marquee { display: none !important; }

/* ---------------------------------------------------------------- particles ---- */
.sfx-scope .wi-fwp { position: absolute; left: 0; top: 0; will-change: transform; }
.sfx-scope .wi-drop {
  position: absolute; top: 0; will-change: transform;
  animation-name: wi-fall; animation-timing-function: linear; animation-fill-mode: both;
}
.sfx-scope .wi-cf { position: absolute; left: 0; top: 0; will-change: transform; }
.sfx-scope .wi-cf.wi-paper-cf { border-radius: 4px; }
.sfx-scope .wi-cf.wi-ribbon { border-radius: 3px; }
.sfx-scope .wi-cf.wi-dot { border-radius: 50%; }
.sfx-scope .wi-cf.wi-star { clip-path: polygon(50% 0,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%); }

/* ---------------------------------------------------------------- right / wrong, drumroll, balloons, spotlights ---- */
/* deck .sting-fx: a stamp lands in the middle with its own colour wash and a ring; the ✗ shakes */
.sfx-scope .wi-sting { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; }
.sfx-scope .wi-sting-wash { position: absolute; inset: 0; animation: wi-sting-wash 1.8s ease-out both; }
.sfx-scope .wi-sting-ok .wi-sting-wash { background: radial-gradient(circle at 50% 50%, rgba(139,195,74,.5), rgba(139,195,74,0) 62%); }
.sfx-scope .wi-sting-no .wi-sting-wash { background: radial-gradient(circle at 50% 50%, rgba(229,57,53,.5), rgba(229,57,53,0) 62%); }
.sfx-scope .wi-sting-ring { position: absolute; width: 420px; height: 420px; border-radius: 50%; border: 16px solid;
  animation: wi-sting-ring 1.1s cubic-bezier(.2,.8,.3,1) both; }
.sfx-scope .wi-sting-ok .wi-sting-ring { border-color: #8BC34A; }
.sfx-scope .wi-sting-no .wi-sting-ring { border-color: #E53935; }
.sfx-scope .wi-sting-mark { position: relative; font-family: var(--wi-head); font-size: 380px; line-height: 1;
  text-shadow: 0 14px 0 rgba(0,0,0,.35), 0 30px 60px rgba(0,0,0,.45); }
.sfx-scope .wi-sting-ok .wi-sting-mark { color: #8BC34A; animation: wi-sting-pop 1.7s cubic-bezier(.2,1.6,.4,1) both; }
.sfx-scope .wi-sting-no .wi-sting-mark { color: #E53935; animation: wi-sting-pop 1.7s cubic-bezier(.2,1.6,.4,1) both,
  wi-sting-shake .5s .3s linear both; }

/* deck .roll-fx: the vignette closes in over 2.3 s, then one cream flash as the roll lets go */
.sfx-scope .wi-roll { position: absolute; inset: 0; pointer-events: none; }
.sfx-scope .wi-roll-vig { position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 26%, rgba(0,0,0,.72) 100%);
  animation: wi-roll-vig 2.3s ease-in both; }
.sfx-scope .wi-roll-flash { position: absolute; inset: 0; background: #FFF6DF; opacity: 0;
  animation: wi-roll-flash .5s 2.05s ease-out both; }

/* deck .balloon-fx */
.sfx-scope .wi-balloon-fx { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.sfx-scope .wi-balloon-fx i { position: absolute; top: -320px; will-change: transform;
  border-radius: 50% 50% 48% 48% / 56% 56% 44% 44%;
  background: radial-gradient(circle at 32% 26%, rgba(255,255,255,.9), var(--c) 42%, rgba(0,0,0,.28));
  box-shadow: inset -10px -14px 26px rgba(0,0,0,.28);
  animation-name: wi-balloon; animation-timing-function: cubic-bezier(.42,0,.6,1); animation-fill-mode: both; }
.sfx-scope .wi-balloon-fx i::after { content: ''; position: absolute; left: 50%; bottom: -46px; width: 2px; height: 48px;
  background: rgba(255,255,255,.55); }

/* deck .spots: two beams swing up from below the floor */
.sfx-scope .wi-spots { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen;
  animation: wi-spot-fade 4.4s ease-in-out forwards; }
.sfx-scope .wi-spot { position: absolute; bottom: -200px; width: 520px; height: 2600px; margin-left: -260px;
  background: linear-gradient(to top, rgba(255,245,200,.9), rgba(255,220,120,.4) 55%, rgba(255,200,80,0));
  clip-path: polygon(40% 100%, 60% 100%, 100% 0, 0 0); transform-origin: 50% 100%; filter: blur(6px); }
.sfx-scope .wi-spot-l { left: 14%; animation: wi-spot-l 4.4s ease-in-out infinite; }
.sfx-scope .wi-spot-r { left: 86%; animation: wi-spot-r 4.4s ease-in-out infinite; }

/* the display-only countdown and the announcement banner (the surface puts them in its controls layer) */
.sfx-scope .wi-countdown { position: absolute; top: 45px; right: 55px; padding: 15px 28px; border: 5px solid #ffc84a; border-radius: 20px; background: #241005f2; color: #fff2bd; font: 900 80px/1.1 system-ui; font-variant-numeric: tabular-nums; box-shadow: 0 12px 30px #0006; }
.sfx-scope .wi-announcement { position: absolute; left: 6%; right: 6%; bottom: 55px; padding: 28px 36px; border: 5px solid #ffc84a; border-radius: 22px; background: #241005f5; color: #fff2bd; text-align: center; overflow-wrap: anywhere; font: 800 54px/1.2 system-ui; box-shadow: 0 12px 30px #0006; }

/* ---------------------------------------------------------------- the live keys (one of each at a time) ---- */
.sfx-scope .wi-p2 { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.sfx-scope .wi-p2-lightning { overflow: visible; }
/* Lightning is the deck's strike (deck.css .flash / .bolt, owner 2026-10-02): instant, no fade-in. The flash
   flickers three times in 0.7 s (the three-flashes-a-second limit; one strike at a time); the bolts carry the
   deck's white-gold glow and a per-bolt delay (the stagger). */
.sfx-scope .wi-flash { position: absolute; inset: 0; background: #FFFDF2; opacity: 0; animation: wi-flash .7s ease-out both; }
.sfx-scope .wi-bolt { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; overflow: visible; opacity: 0; animation: wi-boltfade .8s ease-out both; }
.sfx-scope .wi-bolt polyline { fill: none; stroke: #FFFDF2; stroke-linejoin: round; stroke-linecap: round;
  filter: drop-shadow(0 0 10px #FFF6DF) drop-shadow(0 0 30px #FFD873) drop-shadow(0 0 60px #FFB020); }

/* deck .siren-fx (the wash slowed: Q23) */
.sfx-scope .wi-p2-siren { z-index: 42; animation: wi-siren-fade 1.7s ease-out forwards; }
.sfx-scope .wi-siren-flash { position: absolute; inset: 0; animation: wi-siren-strobe .36s linear infinite; }
.sfx-scope .wi-siren-beam { position: absolute; left: 50%; top: 50%; width: 2800px; height: 2800px; margin: -1400px 0 0 -1400px;
  background: conic-gradient(from 0deg at 50% 50%, rgba(255,30,40,.85) 0deg, rgba(255,30,40,0) 45deg, transparent 90deg,
    rgba(30,136,255,.85) 180deg, rgba(30,136,255,0) 225deg, transparent 270deg, rgba(255,30,40,.85) 360deg);
  animation: wi-siren-rot .55s linear infinite; filter: blur(24px); mix-blend-mode: screen; opacity: .85; }
.sfx-scope .wi-siren-bar { position: absolute; top: 0; left: 0; right: 0; height: 48px; display: flex; z-index: 2; }
.sfx-scope .wi-siren-red { flex: 1; background: #FF1744; box-shadow: 0 0 50px #FF1744, 0 0 100px #FF1744; animation: wi-siren-red .22s infinite alternate; }
.sfx-scope .wi-siren-blue { flex: 1; background: #2979FF; box-shadow: 0 0 50px #2979FF, 0 0 100px #2979FF; animation: wi-siren-blue .22s infinite alternate; }

/* deck flyby(): one cheese from one of eight directions (the engine sets the ends and the spin) */
.sfx-scope .wi-flyby-cheese { position: absolute; left: 0; top: 0; will-change: transform;
  transform: translate(var(--x0), var(--y0)); animation-name: wi-flyby;
  animation-timing-function: cubic-bezier(.35,.02,.65,1); animation-fill-mode: both; }

/* deck .laser-fx: thin red and green beams at random angles, screen-blended, over a coloured haze */
.sfx-scope .wi-p2-lasers { animation: wi-laser-fade 2.4s linear forwards; }
.sfx-scope .wi-laser-haze { position: absolute; inset: 0; opacity: 0; mix-blend-mode: screen;
  background: radial-gradient(60% 50% at 30% 40%, rgba(255,30,60,.30), rgba(0,0,0,0) 70%),
    radial-gradient(60% 50% at 70% 60%, rgba(34,255,106,.26), rgba(0,0,0,0) 70%);
  animation: wi-laser-glow 2.4s ease-out forwards; }
.sfx-scope .wi-laser { position: absolute; left: 50%; top: 50%; width: 3400px; border-radius: 99px; opacity: 0;
  transform-origin: 50% 50%; will-change: transform, opacity; }
.sfx-scope .wi-laser-r { background: linear-gradient(90deg, rgba(255,20,50,0) 0%, rgba(255,70,95,.9) 14%,
    #FF3B57 50%, rgba(255,70,95,.9) 86%, rgba(255,20,50,0) 100%);
  box-shadow: 0 0 3px rgba(120,0,20,.55), 0 0 14px rgba(255,30,60,.95), 0 0 46px rgba(255,30,60,.6); }
.sfx-scope .wi-laser-g { background: linear-gradient(90deg, rgba(20,255,90,0) 0%, rgba(80,255,140,.9) 14%,
    #2BFF6E 50%, rgba(80,255,140,.9) 86%, rgba(20,255,90,0) 100%);
  box-shadow: 0 0 3px rgba(0,90,35,.5), 0 0 14px rgba(34,255,106,.9), 0 0 46px rgba(34,255,106,.55); }

/* deck .fizz-fx */
.sfx-scope .wi-p2-fizz i { position: absolute; bottom: -90px; border-radius: 50%; will-change: transform;
  background: radial-gradient(circle at 34% 30%, rgba(255,255,255,.95), rgba(255,244,205,.45) 46%,
    rgba(255,216,115,.16) 70%, rgba(255,216,115,0) 72%);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,.3);
  animation-name: wi-fizz; animation-timing-function: cubic-bezier(.4,.05,.7,.95); animation-fill-mode: both; }

/* deck .disco-fx: the rig drops in, the facet layer inside it turns (two animations on one transform would
   fight, so they live on different elements) */
.sfx-scope .wi-disco-rig { position: absolute; left: 50%; top: 0; width: 0; height: 0;
  animation: wi-disco-drop 7.4s cubic-bezier(.3,1.3,.5,1) both; }
.sfx-scope .wi-disco-wire { position: absolute; left: -2px; top: -420px; width: 4px; height: 480px;
  background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,.5)); }
.sfx-scope .wi-disco-ball { position: absolute; left: -95px; top: 60px; width: 190px; height: 190px;
  border-radius: 50%; overflow: hidden;
  background: radial-gradient(circle at 34% 28%, #fff 0%, #d8d3c6 26%, #8d8577 62%, #4b453e 100%);
  box-shadow: 0 0 70px 22px rgba(255,255,255,.3), inset -22px -22px 46px rgba(0,0,0,.5); }
.sfx-scope .wi-disco-ball::after { content: ''; position: absolute; inset: -25%; border-radius: 50%;
  background: repeating-conic-gradient(rgba(255,255,255,.4) 0 6deg, rgba(0,0,0,.22) 6deg 12deg),
    repeating-linear-gradient(to bottom, rgba(255,255,255,.26) 0 9px, rgba(0,0,0,.2) 9px 18px);
  mix-blend-mode: overlay; animation: wi-disco-spin 3.2s linear infinite; }
.sfx-scope .wi-p2-disco b { position: absolute; border-radius: 50%; filter: blur(9px); opacity: 0;
  mix-blend-mode: screen;
  background: radial-gradient(circle, hsla(var(--h),95%,68%,.95), hsla(var(--h),95%,60%,0) 70%);
  animation-name: wi-disco-dot; animation-timing-function: ease-in-out;
  animation-iteration-count: infinite; animation-fill-mode: both; }

/* deck .dun-fx: three hard slams on the sting's hits (.1 s, .55 s, 1.0 s), the third holding through the tail */
.sfx-scope .wi-p2-dundun { background: radial-gradient(circle at 50% 50%, rgba(0,0,0,0) 22%, rgba(0,0,0,.88) 100%);
  opacity: 0; animation: wi-dun 2.8s linear both; }

/* deck .black-fx: lights out for a beat */
.sfx-scope .wi-p2-blackout { background: #000; animation: wi-black 1.9s ease both; }

/* deck .pap-fx: white pops all over the room */
.sfx-scope .wi-p2-paparazzi i { position: absolute; aspect-ratio: 1; border-radius: 50%; opacity: 0;
  background: radial-gradient(circle, #fff 0%, rgba(255,255,255,.75) 26%, rgba(255,255,255,0) 68%);
  animation: wi-pap .34s ease-out both; }

/* deck .stream-fx (its 70vh / 150vh, in the 1080-high frame) */
.sfx-scope .wi-p2-streamers i { position: absolute; top: -40%; border-radius: 3px; will-change: transform;
  background: repeating-linear-gradient(180deg, var(--c) 0 14px, rgba(0,0,0,.18) 14px 20px);
  animation-name: wi-streamer; animation-timing-function: cubic-bezier(.3,0,.6,1); animation-fill-mode: both; }

/* deck .mice-fx: both ways along the bottom */
.sfx-scope .wi-p2-mice img { position: absolute; animation-timing-function: linear; animation-fill-mode: both; }
.sfx-scope .wi-p2-mice .wi-mouse-r { left: 0; animation-name: wi-mice-r; }
.sfx-scope .wi-p2-mice .wi-mouse-l { right: 0; animation-name: wi-mice-l; }

/* deck .star-fx */
.sfx-scope .wi-p2-starfall { --y0: 10%; --tilt: 24deg; }
.sfx-scope .wi-p2-starfall i { position: absolute; left: -30%; top: var(--y0); transform-origin: 0 50%;
  rotate: var(--tilt); animation: wi-star 1.5s cubic-bezier(.35,.1,.5,1) both; }
.sfx-scope .wi-star-tail { width: 640px; height: 10px; border-radius: 10px; filter: blur(3px);
  background: linear-gradient(90deg, rgba(255,236,170,0), rgba(255,236,170,.85) 72%, #fff); }
.sfx-scope .wi-star-head { width: 34px; height: 34px; margin-left: 626px; margin-top: -12px; border-radius: 50%;
  background: radial-gradient(circle,#fff 0 30%, rgba(255,232,150,.85) 55%, rgba(255,232,150,0) 72%);
  box-shadow: 0 0 34px 12px rgba(255,236,170,.55); }

/* deck .pulse-fx: three slow red beats */
.sfx-scope .wi-p2-pulse { background: radial-gradient(circle at 50% 50%, rgba(229,57,53,0) 34%, rgba(229,57,53,.62) 100%);
  opacity: 0; animation: wi-pulse 3s ease-in-out both; }

/* ---------------------------------------------------------------- the atmosphere ---- */
/* deck .fog-fx / .steam-fx / .wheel-fx, with the deck's blurs */
.sfx-scope .wi-p2-fog { transition: opacity 2.2s ease; }
.sfx-scope .wi-p2-fog.wi-out { opacity: 0; }
.sfx-scope .wi-fog-band {
  position: absolute; left: -40%; width: 180%; height: 520px; border-radius: 50%; filter: blur(46px); will-change: transform;
  background: radial-gradient(ellipse at 50% 60%, rgba(255,246,223,.85), rgba(255,246,223,.35) 45%,
    rgba(255,246,223,0) 72%);
  animation-name: wi-fog; animation-timing-function: linear; animation-iteration-count: infinite;
  animation-direction: alternate;
}
.sfx-scope .wi-steam-wisp {
  position: absolute; bottom: -30%; border-radius: 50%; filter: blur(34px); opacity: 0;
  background: radial-gradient(circle, rgba(255,252,240,.75), rgba(255,252,240,.22) 45%, rgba(255,252,240,0) 70%);
  animation-name: wi-steam; animation-timing-function: ease-out; animation-fill-mode: both;
}
.sfx-scope .wi-wheel {
  position: absolute; bottom: 0; will-change: transform;
  animation-duration: 2.9s; animation-timing-function: linear; animation-fill-mode: both;
}
.sfx-scope .wi-wheel.wi-wheel-lr { left: -16%; animation-name: wi-wheel-lr; }
.sfx-scope .wi-wheel.wi-wheel-rl { right: -16%; animation-name: wi-wheel-rl; }

/* ---------------------------------------------------------------- the cover pieces the effects use ---- */
/* spiral: a cheese wheel spins out until it covers the screen, then spins back down (deck wipe()); the
   Write-In stage's spiral transition uses it too */
.sfx-scope .wi-tr-wheel {
  position: absolute; left: 50%; top: 50%; width: 600px; height: 600px; margin: -300px 0 0 -300px;
  border-radius: 50%; overflow: hidden; transform: scale(0); will-change: transform;
  background: radial-gradient(circle at 40% 35%, var(--wi-wedgeHi) 0%, var(--wi-wedgeMid) 50%, var(--wi-wedgeLo) 100%);
  box-shadow: 0 0 120px rgba(40,14,0,.45);
  animation: wi-tr-spiral 2.14s linear both;
}
.sfx-scope .wi-tr-wheel img { position: absolute; left: -10%; top: -10%; width: 120%; height: 120%; object-fit: contain;
  filter: drop-shadow(0 0 30px rgba(40,14,0,.4)); }
/* swipe: one big cheese sheet sweeps across from a side or a diagonal, holds, and carries on (deck swipe(), its
   3400 px sheet and 3600 px travel); the Write-In stage's swipe transition uses it too */
.sfx-scope .wi-tr-mover {
  position: absolute; left: 50%; top: 50%; width: 3400px; height: 3400px; margin: -1700px 0 0 -1700px;
  transform: rotate(var(--wi-rot, 0deg)) translateY(-3600px); will-change: transform;
  animation: wi-tr-swipe 1.5s cubic-bezier(.6,0,.4,1) both;
}
.sfx-scope .wi-tr-sheet {
  position: absolute; inset: 0;
  background-image: radial-gradient(circle, rgba(200,120,10,.38) 0 34px, rgba(0,0,0,0) 35px),
    radial-gradient(circle, rgba(200,120,10,.26) 0 18px, rgba(0,0,0,0) 19px),
    radial-gradient(ellipse at 50% 50%, var(--wi-wedgeHi) 0%, var(--wi-wedgeMid) 45%, var(--wi-wedgeLo) 100%);
  background-size: 260px 260px, 260px 260px, 100% 100%; background-position: 0 0, 130px 130px, 0 0;
}
.sfx-scope .wi-tr-mover img { position: absolute; left: 50%; top: 100%; width: 820px; transform: translate(-50%, -50%);
  filter: drop-shadow(0 20px 40px rgba(40,14,0,.5)); }
/* melt: cheese syrup, simulated per frame by the engine's runMelt(); the deck's drop shadow under the edge and
   its blurred gloss */
.sfx-scope .wi-melt-svg { position: absolute; left: 0; top: -400px; width: 1920px; height: 2000px; overflow: visible;
  filter: drop-shadow(0 10px 12px rgba(150,100,0,.28)); }
.sfx-scope .wi-melt-sheen {
  position: absolute; left: -34%; top: 0; width: 32%; height: 100%; opacity: .45; will-change: transform;
  background: linear-gradient(104deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.7) 46%, rgba(255,255,255,0) 100%);
  filter: blur(24px); animation: wi-tr-sheen 2.8s cubic-bezier(.5,.02,.4,1) both;
}

/* =====================================================================================
   The overlay (static/js/display/show-fx.js): a transparent 1920x1080 frame over every screen of the TV
   page (lobby, question, reveal, scoreboard, mini-game, show loop). It hangs off <html>, beside <body>, so
   the "board" effects can move the page (body) without moving it. Above every game screen (the full
   scoreboard and the popups sit at 9999-10000), below the celebration takeovers (100000) and the
   preview badge; pointer-events none throughout.
   ===================================================================================== */
.sfx-root { position: fixed; inset: 0; z-index: 10500; pointer-events: none; overflow: hidden; }
.sfx-stage {
  position: absolute; left: 50%; top: 50%; width: 1920px; height: 1080px; overflow: visible;
  transform: translate(-50%, -50%) scale(1); transform-origin: center center; pointer-events: none;
  background: transparent; line-height: 1.2; text-align: left; direction: ltr;
  -webkit-user-select: none; user-select: none;
  /* the few palette tokens the effects read (Cheese Board, the stage's default look) */
  --wi-head: 'WriteIn Titan One', 'Fredoka', 'Arial Rounded MT Bold', 'Trebuchet MS', sans-serif;
  --wi-wedgeHi: #FFD84A; --wi-wedgeMid: #FFC02E; --wi-wedgeLo: #F0A012;
}
.sfx-stage *, .sfx-stage *::before, .sfx-stage *::after { box-sizing: border-box; }
.sfx-stage img { max-width: none; max-height: none; border: 0; pointer-events: none; -webkit-user-drag: none; }
.sfx-stage .sfx-fx { position: absolute; inset: 0; pointer-events: none; z-index: 40; overflow: visible; }
.sfx-stage .sfx-controls { position: absolute; inset: 0; pointer-events: none; z-index: 45; }
/* a blackout covers the whole window, not just the 16:9 frame */
.sfx-stage .wi-p2-blackout { inset: -1500px -2500px; }
.sfx-stage .wi-flash { inset: -1500px -2500px; }

/* "Board" effects in overlay mode: the page itself moves under the overlay (the stage moves its slide).
   <html> goes black meanwhile, so the edge a shaking page uncovers looks like the TV's bezel. */
html.sfx-board-on { background: #000 !important; }
body.sfx-board-quake { animation: wi-quake .85s linear both; }
body.sfx-board-tense { animation: wi-drum-tense 2.2s cubic-bezier(.5,0,.9,.6) both; transform-origin: 50% 50%; }
body.sfx-board-haze { animation: wi-haze 2.4s ease-in-out both; }

/* ---------------------------------------------------------------- reduced motion ---- */
/* The engine draws no effects then; the countdown and the banner still show. */
@media (prefers-reduced-motion: reduce) {
  .sfx-stage .sfx-fx, .sfx-stage .wi-marquee { display: none !important; }
  body.sfx-board-quake, body.sfx-board-tense, body.sfx-board-haze { animation: none !important; }
}

/* ---------------------------------------------------------------- keyframes ---- */
/* the deck's cz-* keyframes, renamed wi-* */
@keyframes wi-fall { 0% { transform: translateY(-380px) rotate(0deg); opacity: 0; } 8% { opacity: 1; }
  100% { transform: translateY(1380px) rotate(420deg); opacity: 1; } }
@keyframes wi-mouse-run { 0% { transform: translateX(2100px); } 100% { transform: translateX(-620px); } }
@keyframes wi-stamp { 0% { transform: scale(2.6) rotate(-9deg); opacity: 0; } 55% { transform: scale(.94) rotate(-3deg); opacity: 1; }
  78% { transform: scale(1.04) rotate(-2deg); } 100% { transform: scale(1) rotate(-2deg); opacity: 1; } }
@keyframes wi-quake { 0%, 100% { transform: translate(0,0); } 10% { transform: translate(-18px,10px) rotate(-.6deg); }
  20% { transform: translate(16px,-12px) rotate(.5deg); } 30% { transform: translate(-14px,8px); }
  40% { transform: translate(14px,10px) rotate(.4deg); } 50% { transform: translate(-12px,-8px); }
  60% { transform: translate(10px,6px) rotate(-.3deg); } 70% { transform: translate(-8px,4px); }
  80% { transform: translate(6px,-4px); } 90% { transform: translate(-3px,2px); } }
@keyframes wi-mq-chase {
  0%, 33.3% { opacity: 1; background: #FFFEE8; box-shadow: 0 0 12px #FFE57F, 0 0 26px #FF9800, 0 0 42px #FF5722; transform: translate(-50%,-50%) scale(1.22); }
  33.4%, 100% { opacity: .35; background: #FFA000; box-shadow: none; transform: translate(-50%,-50%) scale(.9); } }
@keyframes wi-flash { 0% { opacity: .95; } 10% { opacity: .2; } 20% { opacity: .9; } 35% { opacity: .15; } 50% { opacity: .7; } 100% { opacity: 0; } }
@keyframes wi-boltfade { 0% { opacity: 0; } 6% { opacity: 1; } 22% { opacity: .6; } 32% { opacity: 1; } 55% { opacity: .8; } 100% { opacity: 0; } }
/* the stinger */
@keyframes wi-sting-wash { 0% { opacity: 0; } 12% { opacity: 1; } 66% { opacity: .9; } 100% { opacity: 0; } }
@keyframes wi-sting-ring { 0% { transform: scale(.2); opacity: 0; } 25% { opacity: .9; } 100% { transform: scale(2.1); opacity: 0; } }
@keyframes wi-sting-pop { 0% { transform: scale(.2) rotate(-16deg); opacity: 0; }
  16% { transform: scale(1.12) rotate(3deg); opacity: 1; } 26% { transform: scale(1) rotate(0); }
  82% { transform: scale(1) rotate(0); opacity: 1; } 100% { transform: scale(1.12); opacity: 0; } }
@keyframes wi-sting-shake { 0%, 100% { margin-left: 0; } 20% { margin-left: -26px; } 40% { margin-left: 22px; }
  60% { margin-left: -14px; } 80% { margin-left: 9px; } }
/* the drumroll: the vignette, the release flash, and the screen tightening (deck cz-tense: it holds at 1.06
   until the effect lets go of it) */
@keyframes wi-roll-vig { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes wi-roll-flash { 0% { opacity: 0; } 12% { opacity: .92; } 100% { opacity: 0; } }
@keyframes wi-drum-tense { 0% { transform: scale(1) translate(0,0); }
  70% { transform: scale(1.035) translate(2px,-1px); }
  82% { transform: scale(1.045) translate(-3px,2px); } 90% { transform: scale(1.05) translate(3px,-2px); }
  100% { transform: scale(1.06) translate(0,0); } }
@keyframes wi-balloon { 0% { transform: translate(0,0) rotate(0); } 100% { transform: translate(var(--sway),1500px) rotate(var(--spin)); } }
@keyframes wi-spot-l { 0% { transform: rotate(-42deg); } 50% { transform: rotate(36deg); } 100% { transform: rotate(-42deg); } }
@keyframes wi-spot-r { 0% { transform: rotate(42deg); } 50% { transform: rotate(-36deg); } 100% { transform: rotate(42deg); } }
@keyframes wi-spot-fade { 0% { opacity: 0; } 12% { opacity: 1; } 85% { opacity: 1; } 100% { opacity: 0; } }
/* the siren (the strobe's two colours each hold half of its 0.36 s cycle) */
@keyframes wi-siren-rot { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
@keyframes wi-siren-strobe { 0%, 49% { background: rgba(255,20,30,.35); } 50%, 100% { background: rgba(30,100,255,.35); } }
@keyframes wi-siren-red { 0% { opacity: .95; } 100% { opacity: .15; } }
@keyframes wi-siren-blue { 0% { opacity: .15; } 100% { opacity: .95; } }
@keyframes wi-siren-fade { 0% { opacity: 1; } 75% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wi-flyby { from { transform: translate(var(--x0), var(--y0)) rotate(0deg); }
  to { transform: translate(var(--x1), var(--y1)) rotate(var(--spin)); } }
@keyframes wi-laserbeam {
  0% { opacity: 0; transform: translate(-50%,-50%) translate(var(--lx),var(--ly)) rotate(calc(var(--a) - 7deg)) scaleY(.5); }
  9% { opacity: 1; }
  62% { opacity: .92; }
  100% { opacity: 0; transform: translate(-50%,-50%) translate(var(--lx),var(--ly)) rotate(calc(var(--a) + 7deg)) scaleY(1.15); } }
@keyframes wi-laser-glow { 0% { opacity: 0; } 12% { opacity: 1; } 70% { opacity: .7; } 100% { opacity: 0; } }
@keyframes wi-laser-fade { 0%, 88% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wi-fizz { 0% { transform: translate(0,0) scale(.55); opacity: 0; } 12% { opacity: .95; }
  82% { opacity: .85; } 100% { transform: translate(var(--sway),-1260px) scale(1.18); opacity: 0; } }
@keyframes wi-disco-drop { 0% { transform: translateY(-320px); } 14% { transform: translateY(0); }
  86% { transform: translateY(0); } 100% { transform: translateY(-320px); } }
@keyframes wi-disco-spin { to { transform: rotate(360deg); } }
@keyframes wi-disco-dot { 0%, 100% { opacity: 0; transform: translate(0,0) scale(.7); }
  45% { opacity: .85; transform: translate(90px,-60px) scale(1.15); } }
@keyframes wi-dun {
  0%, 3% { opacity: 0; } 5% { opacity: .85; } 14% { opacity: .08; }
  19% { opacity: .02; } 21% { opacity: .9; } 30% { opacity: .08; }
  35% { opacity: .02; } 37% { opacity: 1; } 60% { opacity: .5; } 100% { opacity: 0; } }
@keyframes wi-black { 0% { opacity: 0; } 5% { opacity: 1; } 78% { opacity: 1; } 100% { opacity: 0; } }
@keyframes wi-pap { 0% { opacity: 0; transform: scale(.3); } 12% { opacity: 1; transform: scale(1.1); }
  100% { opacity: 0; transform: scale(1.35); } }
@keyframes wi-streamer { 0% { transform: translate(0,0) rotate(0) skewX(0); }
  50% { transform: translate(calc(var(--sway) * .6),756px) rotate(140deg) skewX(16deg); }
  100% { transform: translate(var(--sway),1620px) rotate(300deg) skewX(-12deg); } }
@keyframes wi-mice-r { 0% { transform: translateX(-380px); } 100% { transform: translateX(2280px); } }
@keyframes wi-mice-l { 0% { transform: translateX(380px) scaleX(-1); } 100% { transform: translateX(-2280px) scaleX(-1); } }
@keyframes wi-star { 0% { transform: translate(0,0); opacity: 0; } 12% { opacity: 1; }
  88% { opacity: 1; } 100% { transform: translate(2500px,0); opacity: 0; } }
@keyframes wi-pulse { 0%, 100% { opacity: 0; } 12% { opacity: .9; } 24% { opacity: .15; }
  42% { opacity: .95; } 55% { opacity: .15; } 74% { opacity: 1; } 88% { opacity: .1; } }
/* the atmosphere (the steam's 135vh, in the 1080-high frame) */
@keyframes wi-fog { 0% { transform: translateX(calc(var(--wi-dir, 1) * -16%)); }
  100% { transform: translateX(calc(var(--wi-dir, 1) * 16%)); } }
@keyframes wi-steam { 0% { transform: translate(0,0) scale(.6); opacity: 0; } 22% { opacity: .85; }
  100% { transform: translate(var(--wi-sway, 0px), -1458px) scale(1.5); opacity: 0; } }
@keyframes wi-haze { 0%, 100% { transform: none; filter: none; }
  18% { transform: skewX(1.1deg) scale(1.008); filter: blur(1.1px) saturate(1.15); }
  38% { transform: skewX(-1.3deg) scaleY(1.012); filter: blur(.6px); }
  58% { transform: skewX(.9deg) scale(1.006); filter: blur(1.4px) saturate(1.2); }
  78% { transform: skewX(-.6deg); filter: blur(.5px); } }
@keyframes wi-wheel-lr {
  0% { transform: translate(0,-1400px) rotate(0deg); } 18% { transform: translate(320px,0) rotate(180deg); }
  34% { transform: translate(680px,-460px) rotate(340deg); } 52% { transform: translate(1020px,0) rotate(520deg); }
  68% { transform: translate(1360px,-260px) rotate(660deg); } 84% { transform: translate(1700px,0) rotate(800deg); }
  100% { transform: translate(2300px,-120px) rotate(940deg); } }
@keyframes wi-wheel-rl {
  0% { transform: translate(0,-1400px) rotate(0deg); } 18% { transform: translate(-320px,0) rotate(-180deg); }
  34% { transform: translate(-680px,-460px) rotate(-340deg); } 52% { transform: translate(-1020px,0) rotate(-520deg); }
  68% { transform: translate(-1360px,-260px) rotate(-660deg); } 84% { transform: translate(-1700px,0) rotate(-800deg); }
  100% { transform: translate(-2300px,-120px) rotate(-940deg); } }
/* the covers */
@keyframes wi-tr-spiral {
  0% { transform: scale(0) rotate(0deg); animation-timing-function: cubic-bezier(.5,0,.8,.45); }
  46.7% { transform: scale(4.1) rotate(300deg); }
  53.3% { transform: scale(4.1) rotate(300deg); animation-timing-function: cubic-bezier(.2,.55,.4,1); }
  100% { transform: scale(0) rotate(600deg); } }
@keyframes wi-tr-swipe { 0% { transform: rotate(var(--wi-rot, 0deg)) translateY(-3600px); }
  44%, 56% { transform: rotate(var(--wi-rot, 0deg)) translateY(0); }
  100% { transform: rotate(var(--wi-rot, 0deg)) translateY(3600px); } }
@keyframes wi-tr-sheen { 0% { transform: translateX(0); } 100% { transform: translateX(340%); } }
