/* AISU HERO · "AISU Fire Festival" · component library (WP1). SPEC section 2, plus the #stall frame and #cursor.

   How this file is written
   - Components are zero-specificity (:where) so the screen packages (screens/*.css, loaded later) override any of it
     with any selector. State hooks (:hover, :focus-visible, :active, .is-pressed, [aria-*], :disabled, .confirm) keep
     a small specificity so a state always wins over its component's base look.
   - base.css GEOMETRY positions every direct child of a section (`:where(.screen) > :where(*)`, absolute) and a few
     inner parts. This file never sets `position` on something geometry already places (see the guards below).
   - Only transform and opacity animate or transition. Box-shadow and background swap instantly (SPEC 2 global rule).
   - Text is never smaller than .875rem (the pad-glyph font-size: 0 trick of SPEC 2.3 hides a text node, it shows
     nothing at that size).
   - New keyframes are prefixed ffc- and touch transform / opacity only. Loops are declared through --loop
     (motion.css stops them under reduced motion); base.css stops the decorative ones under body.lowfx. */

/* =========================================================================================== keyframes */
@keyframes ffc-drain      { from { transform: scaleX(1); } to { transform: scaleX(0); } }                 /* confirm countdown */
@keyframes ffc-tick       { from { transform: translateX(calc(-1 * var(--dot, .55rem))); } to { transform: translateX(calc(2 * var(--dot, .55rem))); } } /* busy dots (steps 3) */
@keyframes ffc-skew-sway  { from { transform: skewX(-3.5deg); } to { transform: skewX(3.5deg); } }        /* stall lanterns */
@keyframes ffc-hang       { 0% { opacity: 0; transform: translateY(-120%); } 25% { opacity: 1; } 55% { transform: translateY(0) rotate(2.5deg); } 78% { transform: rotate(-1.6deg); } 100% { transform: none; } }
@keyframes ffc-embers     { from { transform: translateY(0); } to { transform: translateY(-50%); } }      /* one tile up: seamless */
@keyframes ffc-delta      { from { opacity: 0; transform: translateY(.3rem); } }

/* =========================================================================================== guards
   Components that need a positioned box get `position: relative` here, and the section-level ones get back the
   geometry's `absolute` right after (same zero specificity, later wins). */
:where(button, .bubble, .band, .ticket, .tabs, .foil, .ribbon, .stamp, .preview, .toast--ema, kbd) { position: relative; }
:where(.screen) > :where(button, .bubble, .band, .ticket, .tabs, .foil, .ribbon, .stamp, .preview, .toast) { position: absolute; }
:where(#hud) > :where(*) { position: absolute; }

/* =========================================================================================== 2.1 surfaces */
:where(.panel) {
  color: var(--ink);
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory);
  border: var(--ow-l) solid var(--ink); border-radius: var(--r-l);
  box-shadow: var(--plate-cobalt), var(--float);
}
:where(.panel--night) {
  color: var(--ivory);
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-night);
  box-shadow: inset 0 0 0 .14rem rgb(246 240 228 / .18),
              .6rem .7rem 0 0 var(--cobalt-deep), .6rem .7rem 0 var(--ow-l) var(--ink), var(--float);
}
:where(.panel--flat) { box-shadow: var(--drop), var(--float); }
:where(.panel--red-plate) { box-shadow: var(--plate-red), var(--float); }

/* header band: seigaiha strip with a scalloped lower edge (lobes in the band colour, ink rims, cusps on ink) */
:where(.band) {
  --lobe: #D11A2A;
  display: flex; align-items: center; gap: .6rem; min-height: 3.4rem; padding: .3rem 1.2rem .2rem;
  margin-bottom: .8rem;                               /* the lobes hang .8rem below the strip */
  background: #D11A2A url(../img/svg/pat-seigaiha-red.svg) 0 0 / 2.4rem 1.2rem;
  color: var(--ivory); border-radius: var(--r-m) var(--r-m) 0 0;
  box-shadow: inset 0 var(--ow) 0 rgb(255 255 255 / .12);
}
:where(.band)::after, :where(.scallop-b)::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: .8rem; pointer-events: none;
  background: radial-gradient(circle at 50% 0, var(--lobe) 0 calc(.8rem - var(--ow)), var(--ink) calc(.8rem - var(--ow) + .5px) .8rem, transparent calc(.8rem + .5px)) 0 0 / 1.6rem .8rem;
  background-repeat: round no-repeat;
}
:where(.scallop-t)::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 100%; height: .8rem; pointer-events: none;
  background: radial-gradient(circle at 50% 100%, var(--lobe, var(--ivory)) 0 calc(.8rem - var(--ow)), var(--ink) calc(.8rem - var(--ow) + .5px) .8rem, transparent calc(.8rem + .5px)) 0 0 / 1.6rem .8rem;
  background-repeat: round no-repeat;
}
:where(.panel, .notice__paper, .ema__paper, .stall__body) :where(h2.tk) { --tk-fill: var(--red); --tk-shadow: var(--cobalt); --tk-drop: .07em; }   /* titles on paper */
:where(.band h2, .band .tk) { --tk-fill: var(--ivory); --tk-shadow: rgb(70 20 10 / .35); }
:where(.band--night) { --lobe: #14102A; background: #14102A url(../img/svg/pat-seigaiha-night.svg) 0 0 / 2.4rem 1.2rem; }
:where(.band--night h2, .band--night .tk) { --tk-fill: var(--f2); }
:where(.band--cobalt) { --lobe: #1D35E0; background: #1D35E0 url(../img/svg/pat-seigaiha-cobalt.svg) 0 0 / 2.4rem 1.2rem; }
:where(.band--cobalt h2, .band--cobalt .tk) { --tk-shadow: var(--red); }

/* ---------------------------------------------------------------- 2.1 the stall (yatai), in #backdrop
   A lacquered beam with gold caps, a shaded striped awning and its valance of rounded scallops. The awning and the
   valance are SVG tiles of one stripe pair (4.4rem, rounded to a whole count), so every lobe hangs under its stripe.
   Three fabric layers cross-fade by screen (220ms opacity): the roof itself is night/ivory (settings, calibrate),
   .stall__stripes--a red/ivory (select, welcome, code), --b cobalt/ivory (board). Nothing reaches left of the frame. */
:where(.stall__roof) {
  z-index: 1;                                          /* the valance hangs over the panel's top edge */
  border: var(--ow-l) solid var(--ink); border-block: 0; border-radius: 0;
  background: url(../img/stall/awning-night.svg) 0 0 / 4.4rem 100%;
  background-repeat: round no-repeat;
}
:where(.stall__stripes) {
  border: 0; border-radius: 0;
  background: url(../img/stall/awning-red.svg) 0 0 / 4.4rem 100%;
  background-repeat: round no-repeat;
  opacity: 0; transition: opacity var(--t-base) linear;
}
:where(.stall__stripes--b) { background-image: url(../img/stall/awning-cobalt.svg); }
:where(.stall__roof)::before, :where(.stall__stripes)::after {          /* valance: one lobe per stripe + its shadow */
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 1.55rem; pointer-events: none;
  background: url(../img/stall/valance-night.svg) 0 0 / 4.4rem 100%;
  background-repeat: round no-repeat;
}
:where(.stall__stripes--a)::after { background-image: url(../img/stall/valance-red.svg); }
:where(.stall__stripes--b)::after { background-image: url(../img/stall/valance-cobalt.svg); }
:where(.stall__roof)::after {                         /* the beam: lacquer with gold end caps, over every fabric layer */
  content: ''; position: absolute; z-index: 1; left: calc(-.25rem - var(--ow-l)); right: calc(-.25rem - var(--ow-l));
  top: -.6rem; height: 1.15rem; pointer-events: none;
  border: var(--ow-s) solid var(--ink); border-radius: .4rem;
  background:
    linear-gradient(var(--ink), var(--ink)) 1.4rem 0 / var(--ow-s) 100% no-repeat,
    linear-gradient(var(--ink), var(--ink)) calc(100% - 1.4rem) 0 / var(--ow-s) 100% no-repeat,
    linear-gradient(180deg, #FFF3C2, #EDBE48 42%, #A8741A) 0 0 / 1.4rem 100% no-repeat,
    linear-gradient(180deg, #FFF3C2, #EDBE48 42%, #A8741A) 100% 0 / 1.4rem 100% no-repeat,
    linear-gradient(180deg, rgb(255 222 190 / .5) 0 .14rem, transparent .3rem),
    linear-gradient(180deg, #A3352A, #6E1A14 48%, #3B0C0B);
  box-shadow: 0 .22rem 0 rgb(44 15 21 / .45);
}
:where(body:is([data-screen="select"], [data-screen="welcome"], [data-screen="code"])) .stall__stripes--a,
:where(body[data-screen="board"]) .stall__stripes--b { opacity: 1; }

/* the panel under the awning: opaque paper with a fine inner rule, the cobalt plate, a soft shade under the valance */
:where(.stall__panel) {
  border-radius: var(--r-xs) var(--r-xs) var(--r-l) var(--r-l);
  background:
    linear-gradient(180deg, rgb(44 15 21 / .2), transparent 2.6rem),
    url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory);
  box-shadow: inset 0 0 0 .3rem rgb(255 250 240 / .6), inset 0 0 0 calc(.3rem + 1.5px) rgb(44 15 21 / .1),
              .4rem .45rem 0 0 var(--cobalt), .4rem .45rem 0 var(--ow-l) var(--ink), var(--float);
}

/* two paper lanterns hanging from the beam on the right, over the fabric (the bichito stands on the beam's end);
   one shared sway (skewX keeps both hanging from their own cords) */
:where(.stall__lanterns) {
  top: .3rem; z-index: 2; transform-origin: 50% 0;
  --loop: ffc-skew-sway 3.6s var(--ease-inout) infinite alternate;
  animation: var(--loop);
}
:where(.stall__lantern) {
  position: absolute; top: 0; width: 2.35rem; height: 3.9rem;
  background: url(../img/stall/lantern.svg) 50% 0 / contain no-repeat;
}
:where(.stall__lantern)::before {
  content: ''; position: absolute; left: -70%; right: -70%; top: 5%; bottom: 0; z-index: -1; border-radius: 50%;
  background: radial-gradient(closest-side, rgb(255 150 70 / .65), rgb(255 90 40 / .2) 60%, transparent);
}
:where(.stall__lantern--l) { left: 23rem; }
:where(.stall__lantern--r) { right: 10.5rem; }

/* the hanging sign (each section brings its own .stall__sign with the screen title): a sticker like the Setlist logo,
   cream die-cut border around a dark plate, the title in ivory with a red drop. It hangs over the fabric (its foot on
   the valance's top edge), so the scallops and the body below stay clear */
:where(.stall__sign) {
  bottom: calc(100vh - var(--stall-body-t) - .1rem);
  padding: .35rem 1.1rem .45rem;
  background: radial-gradient(120% 140% at 0 0, rgb(255 255 255 / .1), transparent 50%), var(--ink);
  border: .26rem solid var(--paper); border-radius: var(--r-m);
  box-shadow: 0 0 0 var(--ow-s) var(--ink), .25rem .3rem 0 var(--ow-s) rgb(44 15 21 / .55);
  rotate: -2deg; transform-origin: 30% 0;
}
:where(.stall__sign .stall-title) { --tk-fill: var(--ivory); --tk-shadow: var(--red); --tk-halo: var(--ink); --tk-drop: .07em; font-size: var(--fs-h2); }
:where(.stall__sign .stall-title)::before { text-shadow: .04em .07em 0 var(--tk-shadow); }
:where(.stall__sign .pill) { rotate: 2deg; }

/* arriving from another shot (SPEC 3.5, CONTRACTS 5.3 rule 1): the frame rides in from the right, behind Aisu, on the
   camera's own curve (the critically damped spring of shot-math camSpring, 1 - (1 + wt)e^(-wt) with w = 10/s, sampled
   over 700ms for title->menu and 450ms for play->menu) instead of the G0 fade at --move-at. It starts 50ms after the
   shot (--stall-at, she is never ahead of the camera by more than that) and lasts --move-at + 200ms, so it ends before
   ui.show() clears body[data-move] (--move-at + 260ms). Its left edge stays right of her moving silhouette + 3.56vh
   halo at every frame: with her analytic silhouette at 25ms steps for title->menu and play->menu at 1024x768,
   1100x900, 1280x720, 1440x900 and 2560x1080, and her running 50ms late (stall_race.py + race_check2.py), the
   closest approach is 52px; the start offsets stay 1.45 x (100vw - --stall-l) for title->menu (she starts over the
   whole right side, so the frame starts fully off-screen) and .6 x --stall-w for play->menu. Checked on real frames
   by g0_screens.py --motion-times. Screen packages can move their stall content on the same curve: --stall-dx,
   --ease-stall, --stall-in-ms, --stall-at.
   Reduced motion never sets body[data-move], so there is no slide there. Without linear() (old engines) the
   @supports keeps the G0 fade: an invalid var() easing would drop the animation and show the frame at once. */
@keyframes ffc-stall-in { from { transform: translateX(var(--stall-dx, 0px)); } }
@supports (animation-timing-function: linear(0, 1)) {
  :where(body:is([data-move="title-menu"], [data-move="play-menu"])) {
    --stall-in-ms: calc(var(--move-at, 0ms) + 200ms);
    --stall-at: 50ms;
  }
  :where(body[data-move="title-menu"]) {
    --stall-dx: calc((100vw - var(--stall-l)) * 1.45);
    --ease-stall: linear(0, 0.0354, 0.1173, 0.22, 0.3277, 0.4313, 0.526, 0.6096, 0.6817, 0.7428, 0.7938, 0.836, 0.8705, 0.8985, 0.9211, 0.9392, 0.9537, 0.9652, 0.9743, 0.9815, 0.9872, 0.9916, 0.9951, 0.9979, 1);
  }
  :where(body[data-move="play-menu"]) {
    --stall-dx: calc(var(--stall-w) * .6);
    --ease-stall: linear(0, 0.0165, 0.0586, 0.1169, 0.1846, 0.257, 0.3303, 0.4022, 0.4709, 0.5356, 0.5955, 0.6504, 0.7002, 0.7452, 0.7854, 0.8212, 0.853, 0.881, 0.9056, 0.9272, 0.9461, 0.9625, 0.9768, 0.9893, 1);
  }
  :where(body:is([data-move="title-menu"], [data-move="play-menu"])) :where(#stall) {
    animation: ffc-stall-in var(--stall-in-ms) var(--ease-stall) var(--stall-at) backwards, ff-fade 160ms linear backwards;
  }
}

/* ---------------------------------------------------------------- 2.1 ema (hanging votive plaque), title shot
   .ema__ink (ink pentagon), the grain paper inset by --ow-l (.ema padding), a night-3 seigaiha roof cap along the two
   slopes, a red cord from the peak to the top edge (with the deco.js bunting), and a cobalt registration plate
   (.ema::before ink + ::after cobalt, offset like --plate-cobalt). */
:where(.ema) {
  --ema-p: 3.2rem;                                   /* gable height */
  --ema-clip: polygon(0 var(--ema-p), 50% 0, 100% var(--ema-p), 100% 100%, 0 100%);
  isolation: isolate; padding: var(--ow-l);
}
:where(.ema)::before, :where(.ema)::after {
  content: ''; position: absolute; inset: 0; translate: .6rem .7rem; z-index: -1; clip-path: var(--ema-clip);
}
:where(.ema)::before { background: var(--ink); }
:where(.ema)::after { inset: var(--ow-l); background: var(--cobalt); }
:where(.ema__ink) { inset: 0; background: var(--ink); clip-path: var(--ema-clip); }
:where(body:not([data-shot="menu"])) :where(.ema__paper) {
  clip-path: var(--ema-clip); border: 0; border-radius: 0; box-shadow: none;
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory);
  padding-top: calc(var(--ema-p) + .9rem);
}
:where(.ema__roof) { left: -.8rem; right: -.8rem; top: -.75rem; height: calc(var(--ema-p) + 2.1rem); z-index: 1; }
:where(.ema__roof)::before, :where(.ema__roof)::after { content: ''; position: absolute; inset: 0; }
:where(.ema__roof)::before {                         /* ink chevron */
  background: var(--ink);
  clip-path: polygon(0 calc(var(--ema-p) + .2rem), 50% 0, 100% calc(var(--ema-p) + .2rem), 100% calc(var(--ema-p) + 2.1rem), 50% 1.75rem, 0 calc(var(--ema-p) + 2.1rem));
}
:where(.ema__roof)::after {                          /* night-3 seigaiha chevron inset by the rim */
  background: #241D3D url(../img/svg/pat-seigaiha-night.svg) 0 0 / 2rem 1rem;
  clip-path: polygon(var(--ow-l) calc(var(--ema-p) + .2rem + var(--ow-l)), 50% calc(var(--ow-l) * 1.15), calc(100% - var(--ow-l)) calc(var(--ema-p) + .2rem + var(--ow-l)), calc(100% - var(--ow-l)) calc(var(--ema-p) + 2.1rem - var(--ow-l)), 50% calc(1.75rem - var(--ow-l) * 1.15), var(--ow-l) calc(var(--ema-p) + 2.1rem - var(--ow-l)));
}
:where(.ema__cord) {
  left: 0; right: 0; bottom: calc(100% + .1rem); height: max(6vh, 4rem);
  --bunting-from: 0 .1; --bunting-to: 1 .1; --bunting-sag: 1.4rem; --bunting-size: 1.5rem; --bunting-gap: 2.6rem;
  --bunting-colors: red cobalt ivory gold;
}
:where(.ema__cord)::before {                         /* the red cord, top edge -> the ring on the peak */
  content: ''; position: absolute; left: 50%; top: 0; bottom: -.2rem; width: .3rem; translate: -50% 0;
  background: var(--red); box-shadow: 0 0 0 .1rem var(--ink);
}
:where(.ema__cord)::after {                          /* brass ring on the roof peak (the cord sits above the roof) */
  content: ''; position: absolute; left: 50%; bottom: -1.15rem; translate: -50% 0; width: 1.15rem; height: 1.15rem; z-index: 2;
  border-radius: 50%; border: .28rem solid var(--gold); box-shadow: 0 0 0 .1rem var(--ink), inset 0 0 0 .1rem var(--ink);
}

/* ---------------------------------------------------------------- 2.1 notice (wavy card), title shot
   Deviation from the SPEC's per-side radial-gradient mask: the scalloped paper and its ink outline are one 9-slice
   SVG (notice-frame.svg) drawn by border-image with whole lobes (round), over a flat ivory + grain fill. The cobalt
   plate (.notice__ink) is the same shape in cobalt (notice-plate.svg). Same look, no seams, one element each. */
:where(.notice) { isolation: isolate; }
:where(body:not([data-shot="menu"])) :where(.notice__paper) {
  border: 1.6rem solid transparent; border-radius: 0; box-shadow: none;
  border-image: url(../img/svg/notice-frame.svg) 48 / 1.6rem / 0 round;
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--ivory);
  background-clip: padding-box; background-origin: padding-box;
  padding: .3rem .5rem .5rem;
}
:where(.notice__ink) {
  inset: 0; z-index: -1; translate: .6rem .7rem;
  border: 1.6rem solid transparent; border-image: url(../img/svg/notice-plate.svg) 48 fill / 1.6rem / 0 round;
}
:where(.notice__bunting) {                     /* corner strings tied outside the card (Taiko notice): the pennants hang over the scalloped rim */
  top: -2.2rem; width: 9.5rem; height: 6.6rem; z-index: 1;
  --bunting-sag: .75rem; --bunting-size: 1.3rem; --bunting-gap: 2.1rem; --bunting-colors: red cobalt ivory gold;
}
:where(.notice__bunting--l) { left: -2.4rem; --bunting-from: 0 .8; --bunting-to: .9 0; }
:where(.notice__bunting--r) { right: -2.4rem; --bunting-from: .1 0; --bunting-to: 1 .8; }

/* ---------------------------------------------------------------- 2.1 ticket: two masked layers + a masked drop copy
   (no filter), so the ink rim follows the side notches. .ticket__in is the paper. */
:where(.ticket) { --n: .62rem; isolation: isolate; }
:where(.ticket)::before, :where(.ticket)::after, :where(.ticket__in) {
  -webkit-mask: radial-gradient(circle var(--nr) at var(--nx) 50%, #0000 97%, #000) left / 51% 100% no-repeat,
                radial-gradient(circle var(--nr) at calc(100% - var(--nx)) 50%, #0000 97%, #000) right / 51% 100% no-repeat;
          mask: radial-gradient(circle var(--nr) at var(--nx) 50%, #0000 97%, #000) left / 51% 100% no-repeat,
                radial-gradient(circle var(--nr) at calc(100% - var(--nx)) 50%, #0000 97%, #000) right / 51% 100% no-repeat;
}
:where(.ticket)::before, :where(.ticket)::after {
  content: ''; position: absolute; inset: 0; border-radius: var(--r-m); background: var(--ink); --nr: var(--n); --nx: 0px;
}
:where(.ticket)::before { z-index: -1; }
:where(.ticket)::after { z-index: -2; translate: 0 .3rem; }
:where(.ticket__in) {
  position: relative; margin: var(--ow); border: 0; border-radius: calc(var(--r-m) - var(--ow));
  --nr: calc(var(--n) + var(--ow)); --nx: calc(-1 * var(--ow));
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  display: flex; align-items: center; gap: .8rem; padding: .7rem 0 .7rem 1.3rem; overflow: hidden;
}
:where(.ticket__stub) {
  flex: none; align-self: stretch; display: grid; place-items: center; width: 7.4rem; margin-block: -.7rem;
  padding: .4rem .4rem .4rem .45rem; text-align: center;
  border-left: .2rem dashed var(--ink-2);
  background: #D11A2A url(../img/svg/pat-seigaiha-red.svg) 0 0 / 2.4rem 1.2rem; color: var(--ivory);
  font: 900 var(--fs-small)/1.25 var(--font-body); text-transform: uppercase; letter-spacing: .05em;
  -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill;
}
/* the recovery code: 1.6rem mono on one line; on a narrower card it shrinks (to 1.1rem) before it wraps at a hyphen.
   17em is the width of AISU-XXXX-XXXX-XXXX-XXXX at .1em tracking; 10.4rem is the stub, gaps and margins. */
:where(.ticket--code) { container-type: inline-size; }
:where(.ticket--code .code-value) {
  flex: 1 1 auto; min-width: 0; color: var(--ink); user-select: all; -webkit-user-select: all;
  font: 700 clamp(1.1rem, (100cqi - 10.4rem) / 17, 1.6rem)/1.2 var(--font-mono); letter-spacing: .1em; overflow-wrap: break-word;
}
:where(.ticket.sel)::after { background: var(--f3); translate: .3rem .35rem; }
:where(.ticket.sel) :where(.ticket__in) { background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, linear-gradient(180deg, var(--paper), var(--f1)); }
:where(.ticket.locked) :where(.ticket__in) { background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-night); color: var(--ivory-3); }
:where(.ticket.locked) :where(.ticket__stub) { background: var(--night-3) url(../img/svg/lock.svg) 50% 50% / 1.4rem auto no-repeat; color: transparent; -webkit-text-stroke: 0; }
:where(.ticket.shine) :where(.ticket__in)::after {
  content: ''; position: absolute; inset: -20% 0; pointer-events: none;     /* full width: ff-shine moves it ±120% */
  background: linear-gradient(100deg, transparent 38%, rgb(255 255 255 / .85) 50%, transparent 62%);
  transform: translateX(-120%) skewX(-20deg);
  --anim: ff-shine 1.6s var(--ease-inout) var(--at, 0ms) 1 both; animation: var(--anim);
}

/* ---------------------------------------------------------------- 2.1 tape, foil, stamp, ribbon, burst, crown, medals */
:where(.tape) {
  position: absolute; width: 4.2rem; height: 1.25rem; z-index: 3;
  background: #2A2530 linear-gradient(90deg, rgb(255 255 255 / .07) 0 2px, transparent 2px 5px) 0 0 / 5px 100%;
  -webkit-mask: url(../img/svg/tape-torn.svg) 0 0 / 100% 100%; mask: url(../img/svg/tape-torn.svg) 0 0 / 100% 100%;
  opacity: .95;
}
:where(.tape--a) { left: -1.1rem; top: .2rem; rotate: -38deg; }
:where(.tape--b) { right: -1.1rem; bottom: .2rem; rotate: -38deg; }
:where(.tape--red) { background-color: var(--red); }
:where(.tape--ivory) { background-color: #E9DDC4; }

:where(.foil) {
  overflow: hidden; background: var(--grad-gold); color: var(--ink);
  border: var(--ow-l) solid var(--ink); box-shadow: inset 0 .15rem 0 rgb(255 255 255 / .6), var(--drop);
}
:where(.foil)::after {                                /* the shine: parked off the strip; runs only on records */
  content: ''; position: absolute; inset: -20% 0; pointer-events: none;     /* full width: ff-shine moves it ±120% */
  background: linear-gradient(100deg, transparent 36%, rgb(255 255 255 / .85) 50%, transparent 64%);
  transform: translateX(-120%) skewX(-20deg);
}
:where(.foil.is-record, .foil--shine)::after { --loop: ff-shine 3.2s var(--ease-inout) 1.2s infinite; animation: var(--loop); }

:where(.stamp) {
  display: inline-grid; place-items: center; text-align: center; width: 5.4rem; height: 5.4rem; padding: 0 .65rem; text-wrap: balance;
  color: var(--red); border: .22rem solid currentColor; border-radius: 50%;
  background: rgb(255 250 240 / .55); box-shadow: inset 0 0 0 .2rem var(--paper), inset 0 0 0 .34rem currentColor;
  font: 700 var(--fs-small)/1.05 var(--font-display); font-stretch: 75%; letter-spacing: .04em; text-transform: uppercase;
  rotate: -12deg;
}
:where(.stamp--ok) { color: var(--ok); }
:where(.stamp--red) { color: var(--red); }
:where(.stamp--gold) { color: var(--gold-deep); background: rgb(255 239 176 / .6); }

/* ribbon: gold band with V-tails (ink layer = the element, gold layer = ::before), shine on ::after (records) */
:where(.ribbon) {
  --tail: .7rem;
  display: inline-flex; align-items: center; isolation: isolate; padding: .25rem calc(var(--tail) + .7rem);
  background: var(--ink); color: var(--ink); border: 0; border-radius: 0; box-shadow: none; overflow: hidden;
  font: 900 var(--fs-small)/1.2 var(--font-body); text-transform: uppercase; letter-spacing: .08em; white-space: nowrap;
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--tail)) 50%, 100% 100%, 0 100%, var(--tail) 50%);
  rotate: 6deg;
}
:where(.ribbon)::before {
  content: ''; position: absolute; inset: var(--ow-s) 0; z-index: -1; background: var(--grad-gold);
  clip-path: polygon(calc(var(--ow-s) * 1.6) 0, calc(100% - var(--ow-s) * 1.6) 0, calc(100% - var(--tail) - var(--ow-s)) 50%, calc(100% - var(--ow-s) * 1.6) 100%, calc(var(--ow-s) * 1.6) 100%, calc(var(--tail) + var(--ow-s)) 50%);
}
:where(.ribbon)::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 35%, rgb(255 255 255 / .8) 50%, transparent 65%);
  transform: translateX(-120%) skewX(-20deg);
  --loop: ff-shine 3.2s var(--ease-inout) 1.6s infinite; animation: var(--loop);
}
body.lowfx .ribbon::after { display: none; }

:where(.burst) { background: url(../img/svg/burst-red.svg) 50% 50% / contain no-repeat; }
:where(.burst--cobalt) { background-image: url(../img/svg/burst-cobalt.svg); }
:where(.crown) { display: inline-block; width: 1.5em; height: 1.4em; vertical-align: -.25em; background: url(../img/svg/crown.svg) 50% 50% / contain no-repeat; }
:where(.medal-1, .medal-2, .medal-3) { background: 50% 50% / contain no-repeat; }
:where(.medal-1) { background-image: url(../img/svg/medal-1.svg); }
:where(.medal-2) { background-image: url(../img/svg/medal-2.svg); }
:where(.medal-3) { background-image: url(../img/svg/medal-3.svg); }

/* note cards: .note (paper-2), .warn-note (ember, with the bichito's meh face) */
:where(.note) {
  padding: .5rem .8rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-s);
  background: var(--ivory-2); color: var(--ink); font: 800 var(--fs-small)/1.4 var(--font-body);
}
:where(.warn-note) {
  display: flex; align-items: center; gap: .7rem; background: linear-gradient(180deg, var(--f2), var(--f3)); color: var(--ink);
}
:where(.warn-note)::before {
  content: ''; flex: none; width: 2.2rem; height: 2.2rem; border-radius: 50%;
  background: url(../img/mascot/face-meh.svg) 50% 60% / 74% auto no-repeat, var(--f4);
  box-shadow: 0 0 0 var(--ow-s) var(--ink), inset 0 .25rem 0 rgb(255 255 255 / .25);
}
:where(.panel--night .note, .note--night) { background: var(--night-3); color: var(--ivory); border-color: var(--ivory-3); }

/* =========================================================================================== 2.3 keys and pad glyphs */
:where(kbd) {
  display: inline-grid; place-items: center; min-width: 1.6rem; height: 1.5rem; padding: 0 .4rem; margin-inline: .15rem;
  border: var(--ow-s) solid var(--ink); border-radius: var(--r-xs);
  background: linear-gradient(180deg, var(--paper), var(--ivory-2)); color: var(--ink);
  font: 900 var(--fs-small)/1 var(--font-body); letter-spacing: 0; text-transform: none; font-stretch: normal;
  box-shadow: 0 .18rem 0 var(--ink); text-shadow: none; -webkit-text-stroke: 0; vertical-align: middle; white-space: nowrap;
}
:where(.kbd--dark) { background: var(--night-3); color: var(--ivory); border-color: var(--ivory); box-shadow: 0 .18rem 0 var(--ink); }
/* pad mode (body[data-input=pad], input.js): the key text hides (tests still read it), the pad label shows */
body[data-input="pad"] kbd[data-pad-label] { font-size: 0; }
body[data-input="pad"] kbd[data-pad-label]::after { content: attr(data-pad-label); font: 900 var(--fs-small)/1 var(--font-body); }
body[data-input="pad"] kbd[data-pad-label="none"] { display: none; }
body[data-input="pad"] kbd:is([data-pad-label="A"], [data-pad-label="B"], [data-pad-label="X"], [data-pad-label="Y"]) {
  width: 1.5rem; min-width: 1.5rem; padding: 0; border-radius: 50%; box-shadow: 0 .16rem 0 var(--ink);
}
body[data-input="pad"] kbd[data-pad-label="A"] { background: #22d65a; color: var(--ink); }
body[data-input="pad"] kbd[data-pad-label="B"] { background: #ff2b38; color: var(--ivory); }
body[data-input="pad"] kbd[data-pad-label="X"] { background: #2d6bff; color: var(--ivory); }
body[data-input="pad"] kbd[data-pad-label="Y"] { background: #ffd21f; color: var(--ink); }
body[data-input="pad"] kbd[data-pad-label="Start"] { background: var(--night); color: var(--ivory); border-color: var(--ivory); border-radius: var(--r-pill); padding: 0 .55rem; }
body[data-input="pad"] kbd:is([data-pad-label="◀▶"], [data-pad-label="▲▼"]) { background: var(--night-3); color: var(--ivory); border-color: var(--ivory); border-radius: var(--r-s); padding: 0 .3rem; }
body[data-input="pad"] kbd:is([data-pad-label="◀▶"], [data-pad-label="▲▼"])::after { font-family: var(--font-symbols); letter-spacing: -.05em; }

/* =========================================================================================== 2.2 buttons
   Generic look: every <button> that is not a hint item, link, chip, ticket, tab, radio card or keycap. */
:where(button:not(.hint-btn, .link, .chip, .song-thumb, [role="tab"], [role="radio"], [role="option"], .keys button)) {
  --btn-drop: .35rem;
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 2.8rem; padding: .3rem 1.15rem .38rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-pill);
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  font-family: var(--font-display); font-weight: 700; font-stretch: 85%; font-size: var(--fs-btn); line-height: 1.1;
  letter-spacing: .01em; text-align: center; white-space: nowrap;
  box-shadow: 0 var(--btn-drop) 0 var(--ink);
  transition: transform var(--t-quick) var(--ease-snap);
  -webkit-tap-highlight-color: transparent; user-select: none; -webkit-user-select: none;
}
/* primary: fire gradient, ivory label with one ink stroke, dark key caps */
:where(button.primary, .play, .prompt, #btn-resume, #btn-again) {
  background: var(--grad-fire-btn); color: var(--ivory);
  box-shadow: inset 0 .2rem 0 rgb(255 255 255 / .3), inset 0 -.28rem 0 rgb(80 0 10 / .3), 0 var(--btn-drop) 0 var(--ink);
  -webkit-text-stroke: max(1.5px, .12em) var(--ink); paint-order: stroke fill;
}
:where(button.primary, .play, .prompt, #btn-resume, #btn-again) :where(kbd) {
  background: var(--night-3); color: var(--ivory); border-color: var(--ivory); box-shadow: 0 .18rem 0 var(--ink);
}
:where(.prompt) { font-size: 1.3rem; min-height: 3.6rem; padding-inline: 1.4rem; }
:where(.play) { min-height: 4.4rem; padding: .2rem 1.1rem .3rem 1.5rem; gap: .7rem; }
:where(.play .tk) { font-size: var(--fs-play); --tk-fill: var(--ivory); }
/* two flame tips on the top-right corner: static on the prompt, flickering on Play */
:where(.play, .prompt)::before {
  content: ''; position: absolute; right: .9rem; top: -1.45rem; width: 2.4rem; height: 1.9rem; pointer-events: none;
  background: url(../img/svg/flame-flame-rim.svg) 100% 100% / auto 1.9rem no-repeat, url(../img/svg/flame-blaze-rim.svg) 0 100% / auto 1.3rem no-repeat;
  transform-origin: 70% 100%;
}
:where(.play)::before { --loop: ff-flicker 380ms ease-in-out infinite alternate; animation: var(--loop); }

/* secondary is the generic look. Ghost / danger / link: */
:where(button.ghost) {
  min-height: 2.2rem; padding: .2rem .95rem .25rem; background: transparent; box-shadow: none;
  font-size: var(--fs-btn);
}
:where(.panel--night) :where(button.ghost) { border-color: var(--ivory); color: var(--ivory); }
:where(button.ghost.danger) { border-color: var(--red); color: var(--red-deep); }
:where(.link) {
  display: inline; min-height: 0; padding: 0; border: 0; background: none; box-shadow: none;
  color: var(--cobalt-deep); font: 900 var(--fs-small)/1.3 var(--font-body); text-align: left; white-space: normal;
  text-decoration: underline dotted 2px; text-underline-offset: .22em; cursor: pointer;
}
@media (hover: hover) { :where(.link):hover { color: var(--cobalt); text-decoration-style: solid; } }

/* kbd inside a button sits on the right, .5rem from the label */
:where(button) :where(kbd) { margin-inline: .1rem 0; }

/* ---------------------------------------------------------------- button states (all buttons) */
@media (hover: hover) {
  :where(button:not(.hint-btn, .link, .chip, .song-thumb, [role="tab"], [role="radio"], [role="option"], .keys button)):hover:not(:disabled, [aria-disabled="true"], [aria-busy="true"]) {
    transform: translateY(-.12rem); --btn-drop: .47rem;
  }
  :where(button.ghost):hover:not(:disabled, [aria-busy="true"], .confirm) { background: rgb(44 15 21 / .06); }
  :where(.panel--night) :where(button.ghost):hover:not(:disabled, .confirm) { background: rgb(246 240 228 / .1); }
}
:where(button:not(.hint-btn, .link, .chip, .song-thumb, [role="tab"], [role="radio"], [role="option"], .keys button)):active:not(:disabled, [aria-disabled="true"], [aria-busy="true"]),
:where(button:not(.hint-btn, .link, .chip, .song-thumb, [role="tab"], [role="radio"], [role="option"], .keys button)).is-pressed {
  transform: translateY(.3rem); --btn-drop: 0rem; transition-duration: var(--t-instant);
}
:where(button.ghost):active:not(:disabled), :where(button.ghost).is-pressed { transform: translateY(.14rem); }
:where(button:not(.hint-btn, .link, [role="tab"], [role="radio"], [role="option"], .keys button)):is(:disabled, [aria-disabled="true"]) {
  background: var(--ivory-3); color: rgb(91 58 64 / .7); border-color: rgb(44 15 21 / .7);
  box-shadow: none; -webkit-text-stroke: 0; cursor: default; transform: none;
}
:where(button):is(:disabled, [aria-disabled="true"]) :where(kbd) { opacity: .6; }

/* busy (SPEC 2.2): label at 45%, three flame dots ticking (a bright dot stepping over three dim ones, steps(3)).
   The dots never sit on the label. With a key cap, they take the cap's slot on the right (its text hides; "Let's
   play" stays readable at 45%). Without one (Save, Copy, Sign out; or a cap hidden in pad mode), the label hides and
   the dots sit centred in its place. */
:where(button)[aria-busy="true"], :where(form[aria-busy="true"]) :where(button[type="submit"]) { cursor: progress; }
:where(button)[aria-busy="true"] > :not(kbd), :where(form[aria-busy="true"]) :where(button[type="submit"]) > :not(kbd) { opacity: .45; }
:where(button)[aria-busy="true"] > kbd, :where(form[aria-busy="true"]) :where(button[type="submit"]) > kbd {
  --dot: .55rem; min-width: 2.4rem; color: transparent; border-color: var(--ink);
  background:
    radial-gradient(circle .2rem at calc(50% - var(--dot)) 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    radial-gradient(circle .2rem at 50% 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    radial-gradient(circle .2rem at calc(50% + var(--dot)) 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    rgb(44 15 21 / .88);
}
/* no key cap: the label goes, a small ink pill with the three dots takes its place */
:where(button)[aria-busy="true"]:not(:has(> kbd)), :where(form[aria-busy="true"]) :where(button[type="submit"]):not(:has(> kbd)),
body[data-input="pad"] :where(button)[aria-busy="true"]:has(> kbd[data-pad-label="none"]),
body[data-input="pad"] :where(form[aria-busy="true"]) :where(button[type="submit"]):has(> kbd[data-pad-label="none"]) {
  color: transparent; -webkit-text-stroke-color: transparent;
}
:where(button)[aria-busy="true"]:not(:has(> kbd)) > *, :where(form[aria-busy="true"]) :where(button[type="submit"]):not(:has(> kbd)) > *,
body[data-input="pad"] :where(button)[aria-busy="true"]:has(> kbd[data-pad-label="none"]) > *,
body[data-input="pad"] :where(form[aria-busy="true"]) :where(button[type="submit"]):has(> kbd[data-pad-label="none"]) > * { opacity: 0; }
:where(button)[aria-busy="true"]:not(:has(> kbd))::after, :where(form[aria-busy="true"]) :where(button[type="submit"]):not(:has(> kbd))::after,
body[data-input="pad"] :where(button)[aria-busy="true"]:has(> kbd[data-pad-label="none"])::after,
body[data-input="pad"] :where(form[aria-busy="true"]) :where(button[type="submit"]):has(> kbd[data-pad-label="none"])::after {
  content: ''; position: absolute; left: 50%; top: 50%; pointer-events: none;
  width: 2.6rem; height: 1.15rem; margin: -.575rem 0 0 -1.3rem; border-radius: var(--r-pill);
  --dot: .55rem;
  background:
    radial-gradient(circle .2rem at calc(50% - var(--dot)) 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    radial-gradient(circle .2rem at 50% 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    radial-gradient(circle .2rem at calc(50% + var(--dot)) 50%, rgb(255 210 58 / .5) 0 calc(100% - .5px), transparent 100%),
    rgb(44 15 21 / .85);
  box-shadow: 0 0 0 .1rem rgb(246 240 228 / .5);
  animation: none;
}
/* the bright dot: on the key cap (::before), or on the button itself when there is no cap */
:where(button)[aria-busy="true"] > kbd::before, :where(form[aria-busy="true"]) :where(button[type="submit"]) > kbd::before,
:where(button)[aria-busy="true"]:not(:has(> kbd))::before, :where(form[aria-busy="true"]) :where(button[type="submit"]):not(:has(> kbd))::before,
body[data-input="pad"] :where(button)[aria-busy="true"]:has(> kbd[data-pad-label="none"])::before,
body[data-input="pad"] :where(form[aria-busy="true"]) :where(button[type="submit"]):has(> kbd[data-pad-label="none"])::before {
  content: ''; position: absolute; left: 50%; top: 50%; right: auto; z-index: 1; pointer-events: none;
  width: .62rem; height: .62rem; margin: -.31rem 0 0 -.31rem; border-radius: 50%;
  --dot: .55rem;
  background: radial-gradient(circle at 40% 35%, var(--f1), var(--f2) 45%, var(--f3) 100%);
  box-shadow: 0 0 .4rem rgb(255 190 60 / .9);
  --loop: ffc-tick 420ms steps(3, jump-end) infinite; animation: var(--loop);
}
body[data-input="pad"] :is(button[aria-busy="true"], form[aria-busy="true"] button[type="submit"]) > kbd[data-pad-label]::after { content: none; }

/* confirm step (main.js confirmTwice): solid red, ivory plain-text question, one shake, a 5s countdown strip */
:where(button).confirm, :where(button.ghost).confirm {
  background: var(--red); color: var(--ivory); border-color: var(--ink); --btn-drop: .25rem;
  box-shadow: 0 var(--btn-drop) 0 var(--ink);
  white-space: normal; height: auto; min-height: 2.2rem; padding: .35rem .9rem .55rem; border-radius: var(--r-m);
  line-height: 1.15; text-align: center; overflow: hidden; text-wrap: balance;
  display: -webkit-inline-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
:where(button).confirm::after {
  content: ''; position: absolute; left: .7rem; right: .7rem; bottom: .22rem; height: .25rem; border-radius: var(--r-pill);
  background: var(--ivory); transform-origin: 0 50%; animation: ffc-drain 5s linear forwards;
}
@media (prefers-reduced-motion: no-preference) { :where(button).confirm { animation: ff-shake var(--t-base) linear; } }

/* ---------------------------------------------------------------- hint-bar buttons and the profile chip */
:where(.hint-btn) {
  position: relative; transition: transform var(--t-quick) var(--ease-snap); -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) { :where(.hint-btn):hover { transform: translateY(-.12rem); } }
:where(.hint-btn):active, :where(.hint-btn).is-pressed { transform: translateY(.12rem); transition-duration: var(--t-instant); }

/* =========================================================================================== 2.4 pills */
:where(.pill) {
  display: inline-flex; align-items: center; gap: .35em; min-height: 1.5rem; padding: 0 .6rem;
  border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: var(--night); color: var(--ivory);
  font: 900 var(--fs-small)/1.1 var(--font-body); text-transform: uppercase; letter-spacing: .06em; white-space: nowrap;
}
:where(.pill--red) { background: var(--red); color: var(--ivory); }
:where(.pill--cobalt) { background: var(--cobalt); color: var(--ivory); }
:where(.pill--gold) { background: var(--grad-gold); color: var(--ink); }
:where(.pill--ivory) { background: var(--paper); color: var(--ink); }
:where(.pill--night) { background: var(--night); color: var(--ivory); }
:where(.pill--ok) { background: var(--ok); color: var(--ink); }
:where(.pill--warn) { background: var(--ember); color: var(--ink); }
:where(.pill--dif) { background: var(--dif-color); color: var(--ivory); -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill; }
:where(.pill[data-mode="tap"], .pill--tap) { background: var(--night); color: var(--ivory); }
:where(.pill[data-mode="clone"], .pill--strum) { background: var(--cobalt); color: var(--ivory); }
:where(.pill:empty) { display: none; }      /* not filled yet (e.g. the select sign's .setlist-count): no black dot */

/* =========================================================================================== 2.5 tabs */
:where(.tabs) { isolation: isolate; }
:where(.tabs [role="tab"]) {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem; min-width: 0; height: 2.6rem; min-height: 0;
  padding: 0 .8rem; border: var(--ow) solid var(--ink); border-radius: var(--r-m) var(--r-m) var(--r-xs) var(--r-xs);
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  font: 700 1.15rem/1 var(--font-display); font-stretch: 85%; white-space: nowrap;
  box-shadow: var(--drop-s); cursor: pointer;
  transition: transform var(--t-base) var(--ease-snap);
}
:where(.tabs [role="tab"])::before {
  content: ''; flex: none; width: 1.4rem; height: 1.4rem; background: url(../img/svg/dif-easy.svg) 50% 50% / contain no-repeat;
}
:where(.tabs [role="tab"]:nth-child(1), .tabs [role="tab"][data-dif="easy"]) { --dif-color: var(--dif-easy); }
:where(.tabs [role="tab"]:nth-child(2), .tabs [role="tab"][data-dif="medium"]) { --dif-color: var(--dif-medium); }
:where(.tabs [role="tab"]:nth-child(2), .tabs [role="tab"][data-dif="medium"])::before { background-image: url(../img/svg/dif-medium.svg); }
:where(.tabs [role="tab"]:nth-child(3), .tabs [role="tab"][data-dif="hard"]) { --dif-color: var(--dif-hard); }
:where(.tabs [role="tab"]:nth-child(3), .tabs [role="tab"][data-dif="hard"])::before { background-image: url(../img/svg/dif-hard.svg); }
:where(.tabs [role="tab"]:nth-child(4), .tabs [role="tab"][data-dif="expert"]) { --dif-color: var(--dif-expert); }
:where(.tabs [role="tab"]:nth-child(4), .tabs [role="tab"][data-dif="expert"])::before { background-image: url(../img/svg/dif-expert.svg); }
@media (hover: hover) { :where(.tabs [role="tab"]):hover:not([aria-selected="true"]) { transform: translateY(-.1rem); } }
:where(.tabs [role="tab"]):is(:active, .is-pressed) { transform: translateY(.1rem); transition-duration: var(--t-instant); }
:where(.tabs [role="tab"])[aria-selected="true"] {
  background: linear-gradient(180deg, color-mix(in oklab, var(--dif-color) 72%, #fff), var(--dif-color) 52%, color-mix(in oklab, var(--dif-color) 74%, #000));
  color: var(--ivory); -webkit-text-stroke: max(1.5px, .12em) var(--ink); paint-order: stroke fill;
  transform: translateY(-.2rem); box-shadow: none;
}
/* the sliding selection fill (WP8 adds .tabs__ink and FLIPs it): the selected tab turns see-through above it */
:where(.tabs__ink) {
  position: absolute; left: 0; top: 0; height: 2.6rem; z-index: -1; pointer-events: none; transform-origin: 0 50%;
  border: var(--ow) solid var(--ink); border-radius: var(--r-m) var(--r-m) var(--r-xs) var(--r-xs);
  background: linear-gradient(180deg, color-mix(in oklab, var(--dif-color) 72%, #fff), var(--dif-color) 52%, color-mix(in oklab, var(--dif-color) 74%, #000));
  translate: 0 -.2rem;
}
:where(.tabs:has(.tabs__ink)) [role="tab"][aria-selected="true"] { background: transparent; border-color: transparent; }

/* =========================================================================================== 2.6 segmented controls */
/* heat cards (#difficulty, role=radio, data-dif) */
:where(.seg.four [role="radio"], .seg--heat [role="radio"]) {
  display: grid; grid-template-columns: 2.4rem minmax(0, 1fr); grid-template-rows: auto auto auto; align-content: center;
  column-gap: .5rem; row-gap: .05rem; min-width: 0; height: 100%; min-height: 0; padding: .4rem .6rem .45rem .5rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-m); text-align: left; cursor: pointer;
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  box-shadow: var(--drop);
  transition: transform var(--t-base) var(--spring-pop);
}
:where(.seg.four [role="radio"], .seg--heat [role="radio"])::before {
  content: ''; grid-column: 1; grid-row: 1 / span 3; align-self: center; width: 2.4rem; height: 2.4rem;
  background: url(../img/svg/dif-medium.svg) 50% 50% / contain no-repeat;
}
:where([role="radio"][data-dif="easy"])::before { background-image: url(../img/svg/dif-easy.svg); }
:where([role="radio"][data-dif="hard"])::before { background-image: url(../img/svg/dif-hard.svg); }
:where([role="radio"][data-dif="expert"])::before { background-image: url(../img/svg/dif-expert.svg); }
:where(.seg.four [role="radio"] b, .seg--heat [role="radio"] b) {
  grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 700 1.3rem/1.05 var(--font-display); font-stretch: 85%;
}
:where(.seg.four [role="radio"] small, .seg--heat [role="radio"] small) {
  grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 800 var(--fs-small)/1.2 var(--font-body); color: var(--ink-2);
}
:where(.seg.four [role="radio"] .pips, .seg--heat [role="radio"] .pips) { grid-column: 2; }
@media (hover: hover) { :where(.seg.four [role="radio"], .seg--heat [role="radio"]):hover:not([aria-checked="true"]) { transform: translateY(-.12rem); } }
:where(.seg.four [role="radio"], .seg--heat [role="radio"]):is(:active, .is-pressed):not([aria-checked="true"]) { transform: translateY(.15rem); transition-duration: var(--t-instant); }
:where(.seg.four [role="radio"], .seg--heat [role="radio"])[aria-checked="true"] {
  background: radial-gradient(120% 90% at 30% 0, rgb(255 255 255 / .28), transparent 60%),
              linear-gradient(180deg, color-mix(in oklab, var(--dif-color) 70%, #fff), var(--dif-color) 50%, color-mix(in oklab, var(--dif-color) 72%, #000));
  color: var(--ivory); transform: translateY(-.3rem) scale(1.04); z-index: 1;
  box-shadow: inset 0 .18rem 0 rgb(255 255 255 / .3), var(--drop);
}
:where(.seg.four [role="radio"], .seg--heat [role="radio"])[aria-checked="true"] :where(b) { -webkit-text-stroke: max(1.5px, .12em) var(--ink); paint-order: stroke fill; }
:where(.seg.four [role="radio"], .seg--heat [role="radio"])[aria-checked="true"] :where(small) { color: var(--ivory); -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill; }
:where(.seg.four [role="radio"], .seg--heat [role="radio"])[aria-checked="true"]::after {   /* flame tips, top-right */
  content: ''; position: absolute; right: .3rem; top: -1.2rem; width: 2rem; height: 1.6rem; pointer-events: none;
  background: url(../img/svg/flame-flame-rim.svg) 100% 100% / auto 1.6rem no-repeat, url(../img/svg/flame-blaze-rim.svg) 0 100% / auto 1.1rem no-repeat;
}
:where([role="radio"][data-dif="expert"])[aria-checked="true"]::after {
  background: url(../img/svg/flame-blue-rim.svg) 100% 100% / auto 1.6rem no-repeat, url(../img/svg/flame-blue-rim.svg) 0 100% / auto 1.1rem no-repeat;
}

/* heat pips: 5 flames of .9rem */
:where(.pips) { display: flex; align-items: flex-end; gap: .1rem; height: .95rem; }
:where(.pips i) {
  flex: none; width: .74rem; height: .9rem; border-radius: 0;
  background: url(../img/svg/flame-off.svg) 50% 100% / contain no-repeat; opacity: .5;
}
:where(.pips i.on) { background-image: url(../img/svg/flame-blaze.svg); opacity: 1; }
:where([data-dif="expert"] .pips i.on) { background-image: url(../img/svg/flame-blue.svg); }
:where([aria-checked="true"] .pips i:not(.on)) { opacity: .7; }

/* option cards (#modes, role=radio, each keeps its <small>; ::after is left free for data-tip) */
:where(.seg.two [role="radio"], .seg--opt [role="radio"]) {
  display: grid; align-content: center; gap: .1rem; min-width: 0; height: 100%; min-height: 0; padding: .35rem .9rem .4rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-m); text-align: left; cursor: pointer;
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  box-shadow: var(--drop-s); transition: transform var(--t-base) var(--ease-snap);
}
:where(.seg.two [role="radio"] b, .seg--opt [role="radio"] b) { font: 700 1.3rem/1.05 var(--font-display); font-stretch: 85%; }
:where(.seg.two [role="radio"] small, .seg--opt [role="radio"] small) {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 800 var(--fs-small)/1.25 var(--font-body); color: var(--ink-2);
}
@media (hover: hover) { :where(.seg.two [role="radio"], .seg--opt [role="radio"]):hover:not([aria-checked="true"]) { transform: translateY(-.12rem); } }
:where(.seg.two [role="radio"], .seg--opt [role="radio"]):is(:active, .is-pressed) { transform: translateY(.12rem); transition-duration: var(--t-instant); }
:where(.seg.two [role="radio"], .seg--opt [role="radio"])[aria-checked="true"] {
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-night); color: var(--ivory);
  box-shadow: inset 0 0 0 .14rem rgb(246 240 228 / .25), var(--drop-s);
}
:where(.seg.two [role="radio"], .seg--opt [role="radio"])[aria-checked="true"] :where(small) { color: var(--ivory-3); }
:where(.seg.two [role="radio"], .seg--opt [role="radio"])[aria-checked="true"]::before {   /* the flame check */
  content: ''; position: absolute; right: -.55rem; top: -.65rem; width: 1.55rem; height: 1.55rem; pointer-events: none;
  background: url(../img/svg/check.svg) 50% 50% / contain no-repeat;
}

/* =========================================================================================== 2.7 switch */
:where(.switch) {
  gap: .7rem; min-height: 2rem; color: inherit; cursor: pointer;
  font: 900 var(--fs-body)/1.25 var(--font-body);
}
:where(.switch) > :where(span:last-child) { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
:where(.switch .track) {
  width: 2.8rem; height: 1.6rem; border: 0; border-radius: var(--r-pill);
  background: var(--ink); box-shadow: 0 0 0 var(--ow-s) var(--ink), inset 0 .16rem 0 rgb(0 0 0 / .35);
}
:where(.switch .track)::after {
  left: .2rem; width: 1.2rem; height: 1.2rem; border-radius: 50%;
  background: radial-gradient(circle at 40% 30%, #fff, var(--ivory) 55%, var(--ivory-2));
  box-shadow: 0 .1rem 0 rgb(0 0 0 / .35);
  transition: transform var(--t-base) var(--spring-pop);
}
:where(.switch input:checked + .track) { background: var(--grad-fire-btn); box-shadow: 0 0 0 var(--ow-s) var(--ink), inset 0 .16rem 0 rgb(255 255 255 / .3); }
:where(.switch input:checked + .track)::after {
  transform: translateX(1.2rem);
  background: url(../img/svg/flame-flame.svg) 50% 45% / auto 72% no-repeat, radial-gradient(circle at 40% 30%, #fff, var(--ivory) 55%, var(--ivory-2));
}
:where(.switch input:is(:active, .is-pressed) + .track)::after { scale: .9; }
:where(.switch input:disabled + .track), :where(.switch:has(input:disabled)) > :where(span:last-child) { opacity: .5; }
:where(.switch:has(input:disabled)) { cursor: default; }

/* =========================================================================================== 2.8 slider */
:where(.slider) { column-gap: .7rem; }
:where(.slider) > :where(span) { font: 900 var(--fs-small)/1.2 var(--font-body); text-transform: uppercase; letter-spacing: .06em; }
:where(.slider input[type="range"]) {
  -webkit-appearance: none; appearance: none; height: 1.6rem; margin: 0; padding: 0; background: none; cursor: pointer;
  border-radius: var(--r-pill);
}
:where(.slider input[type="range"])::-webkit-slider-runnable-track {
  height: .6rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--f3) 0 var(--v, 0%), transparent 0), var(--ivory-2);
  box-shadow: inset 0 .1rem 0 rgb(44 15 21 / .2);
}
:where(.slider input[type="range"])::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 1.5rem; height: 1.5rem; margin-top: calc((.6rem - 2 * var(--ow-s) - 1.5rem) / 2);
  border: var(--ow-s) solid var(--ink); border-radius: 50%;
  background: url(../img/svg/flame-flame.svg) 50% 45% / auto 64% no-repeat, radial-gradient(circle at 40% 30%, #fff, var(--ivory) 55%, var(--ivory-2));
  box-shadow: 0 .14rem 0 var(--ink); transition: transform var(--t-quick) var(--ease-snap);
}
:where(.slider input[type="range"]):active::-webkit-slider-thumb { transform: scale(1.15); }
:where(.slider input[type="range"])::-moz-range-track {
  height: .6rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill); background: var(--ivory-2); box-sizing: border-box;
}
:where(.slider input[type="range"])::-moz-range-progress { height: .6rem; border-radius: var(--r-pill); background: var(--f3); }
:where(.slider input[type="range"])::-moz-range-thumb {
  width: 1.5rem; height: 1.5rem; border: var(--ow-s) solid var(--ink); border-radius: 50%; box-sizing: border-box;
  background: url(../img/svg/flame-flame.svg) 50% 45% / auto 64% no-repeat, var(--ivory); box-shadow: 0 .14rem 0 var(--ink);
  transition: transform var(--t-quick) var(--ease-snap);
}
:where(.slider input[type="range"]):active::-moz-range-thumb { transform: scale(1.15); }
:where(.slider output) {
  display: inline-grid; place-items: center; min-width: 4.2rem; height: 1.6rem; padding: 0 .5rem;
  border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill); background: var(--night); color: var(--ivory);
  font: 700 var(--fs-small)/1 var(--font-num); white-space: nowrap;
}

/* =========================================================================================== 2.9 text inputs */
:where(.field-text) { gap: .3rem; }
:where(.field-text) > :where(span) { font: 900 var(--fs-small)/1.2 var(--font-body); text-transform: uppercase; letter-spacing: .08em; }
:where(.field-text small) { font: 800 var(--fs-small)/1.2 var(--font-body); color: var(--ink-2); text-transform: none; letter-spacing: 0; }
:where(.field-text input) {
  width: 100%; height: 3rem; padding: 0 .9rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-s);
  background: var(--paper); color: var(--ink); caret-color: var(--red);
  font: 800 var(--fs-btn)/1.2 var(--font-body);
  box-shadow: inset 0 .22rem 0 rgb(44 15 21 / .09);
  outline: none;
}
:where(.field-text input)::placeholder { color: var(--ink-2); opacity: .55; }
:where(.field-text input):focus { border-color: var(--cobalt); box-shadow: inset 0 .22rem 0 rgb(35 64 255 / .1), 0 0 0 .14rem rgb(35 64 255 / .25); }
:where(.field-text input)[aria-invalid="true"] { border-color: var(--red); box-shadow: 0 0 0 .2rem rgb(232 32 47 / .35); }
@media (prefers-reduced-motion: no-preference) { :where(.field-text input)[aria-invalid="true"] { animation: ff-shake var(--t-base) linear; } }
:where(.field-text input):disabled { background: var(--ivory-2); color: var(--ink-2); box-shadow: none; }
:where(.profile-form, .settings-body) :where(.field-text input) { height: 2.6rem; }
:where(.mono, .field-text input.mono) { font-family: var(--font-mono); font-weight: 700; font-size: var(--fs-btn); text-transform: uppercase; letter-spacing: .08em; }
/* ticket-style recovery-code input: a red stub with a perforation on the left */
:where(.field-text--ticket input) {
  padding-left: 2.6rem;
  background: linear-gradient(90deg, transparent 1.55rem, var(--ink-2) 0 calc(1.55rem + 2px), transparent 0) 0 0 / 100% .5rem repeat-y,
              linear-gradient(90deg, #D11A2A 0 1.6rem, var(--paper) 0);
}
/* the valid tick sits inside the input's right edge */
:where(.field-ok) {
  position: absolute; right: .7rem; bottom: .55rem; width: 1.9rem; height: 1.9rem; pointer-events: none;
  background: url(../img/svg/check.svg) 50% 50% / contain no-repeat;
}
:where(.field-text.is-valid, .field:has(> .field-help.ok)) :where(.field-ok) { display: block; }
:where(.field-text:has(.field-ok)) :where(input) { padding-right: 3rem; }
/* help line and form messages */
:where(.field-help) { display: flex; align-items: center; gap: .4rem; min-height: 1.3em; font: 800 var(--fs-small)/1.25 var(--font-body); color: var(--ink-2); }
:where(.field-help.ok)::before { content: ''; flex: none; width: 1.05rem; height: 1.05rem; background: url(../img/svg/check.svg) 50% 50% / contain no-repeat; }
:where(.field-help.warn)::before { content: ''; flex: none; width: .65rem; height: .65rem; border-radius: 50%; background: var(--ember); box-shadow: 0 0 0 .1rem var(--ink); }
:where(.field-help.warn) { color: var(--red-deep); }
:where(.form-msg) { display: flex; align-items: center; gap: .45rem; font: 800 var(--fs-small)/1.3 var(--font-body); color: var(--ink); }
:where(.form-msg.err) { color: var(--red-deep); }
:where(.form-msg.err)::before {
  content: ''; flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: url(../img/mascot/face-squish.svg) 50% 60% / 76% auto no-repeat, var(--f4);
  box-shadow: 0 0 0 var(--ow-s) var(--ink);
}
:where(.form-msg.ok)::before { content: ''; flex: none; width: 1.1rem; height: 1.1rem; background: url(../img/svg/check.svg) 50% 50% / contain no-repeat; }
/* live preview strip (welcome): label + night plate on a night seigaiha strip */
:where(.preview) {
  display: flex; align-items: center; flex-wrap: wrap; gap: .4rem .9rem; padding: .5rem .8rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-m);
  background: #14102A url(../img/svg/pat-seigaiha-night.svg) 0 0 / 2.4rem 1.2rem;
}
:where(.preview__label) { color: var(--ivory-3); font: 900 var(--fs-small)/1.2 var(--font-body); text-transform: uppercase; letter-spacing: .1em; }

/* =========================================================================================== 2.10 gauges */
/* ---- crowd thermometer (#rock, HUD) */
:where(.rock__cap, .rock__chip) {
  font: 900 var(--fs-small)/1.15 var(--font-body); text-transform: uppercase; letter-spacing: .06em; color: var(--ivory);
  text-shadow: var(--hint-shadow); white-space: nowrap;
}
:where(.rock__chip) {
  padding: .1rem .5rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: var(--ember); color: var(--ink); text-shadow: none; box-shadow: var(--drop-s);
}
:where(.rock__chip[data-level="low"]) { background: var(--red); color: var(--ivory); }
:where(.rock__chip[data-level="warm"]) { background: var(--ember); color: var(--ink); }
:where(.rock__chip[data-level="hyped"]) { background: var(--f3); color: var(--ink); }
:where(.rock__chip[data-level="fire"]) { background: var(--grad-gold); color: var(--ink); }
:where(.rock__tube) {
  border: var(--ow) solid var(--ink); border-radius: var(--r-pill);
  background: linear-gradient(90deg, rgb(255 255 255 / .06), transparent 40%), var(--night);
  box-shadow: 0 0 0 .14rem rgb(246 240 228 / .35), var(--drop-s);
}
:where(.rock-fill > i) {
  border: .2rem solid transparent; background-clip: padding-box; background-origin: padding-box;
  border-radius: var(--r-pill);
  background-image:
    linear-gradient(90deg, rgb(255 255 255 / .38) 0 .32rem, transparent .32rem),
    repeating-linear-gradient(0deg, transparent 0 calc(100% / 16 - .18rem), var(--night) 0 calc(100% / 16)),
    linear-gradient(0deg, var(--red) 0 25%, var(--ember) 25% 50%, var(--f3) 50% 75%, var(--gold) 75%);
}
:where(.rock__line) { left: .15rem; right: .15rem; border-top: .16rem dashed var(--red); z-index: 1; }
:where(.rock__line--fire) { border-top-style: solid; border-top-color: var(--gold); }
:where(.rock__tube > b) {
  width: 1.8rem; height: 1.8rem; z-index: 2; border: var(--ow-s) solid var(--ink); border-radius: 50%;
  background: url(../img/svg/flame-flame.svg) 50% 45% / auto 70% no-repeat, var(--ivory); box-shadow: var(--drop-s);
}
:where(.rock__bulb) {
  border: var(--ow) solid var(--ink); border-radius: 50%;
  background: url(../img/svg/flame-blaze.svg) 50% 42% / auto 66% no-repeat, radial-gradient(circle at 40% 30%, #fff, var(--ivory) 60%, var(--ivory-2));
  box-shadow: 0 0 0 .14rem rgb(246 240 228 / .35), var(--drop-s);
}
:where(.rock:has(.rock-fill.low)) :where(.rock__bulb) { background-image: url(../img/svg/flame-off.svg), radial-gradient(circle at 40% 30%, #fff, var(--ivory) 60%, var(--ivory-2)); }
:where(.rock-fill.low) > :where(i) { --loop: ff-pulse-danger var(--beat) ease-in-out var(--beat-delay) infinite alternate; animation: var(--loop); }
@media (max-aspect-ratio: 149/100) {
  :where(.rock-fill > i) {
    background-image:
      linear-gradient(180deg, rgb(255 255 255 / .38) 0 .3rem, transparent .3rem),
      repeating-linear-gradient(90deg, transparent 0 calc(100% / 16 - .18rem), var(--night) 0 calc(100% / 16)),
      linear-gradient(90deg, var(--red) 0 25%, var(--ember) 25% 50%, var(--f3) 50% 75%, var(--gold) 75%);
  }
  :where(.rock__line) { top: .15rem; bottom: .15rem; right: auto; border-top: 0; border-left: .16rem dashed var(--red); }
  :where(.rock__line--fail) { left: 25%; }
  :where(.rock__line--fire) { left: 75%; border-left-style: solid; border-left-color: var(--gold); }
  :where(.rock__tube > b) { width: 1.6rem; height: 1.6rem; }
}

/* ---- Star Power lanterns (#starbar, 4 i; --f per lantern) */
:where(.starbar) { grid-template-columns: repeat(4, 2.4rem); gap: .35rem; transform-origin: 50% 70%; }
:where(.starbar i) {
  width: 2.4rem; height: 3rem; border: var(--ow-s) solid var(--ink); border-radius: 45% / 40%;
  background: var(--night-3); overflow: clip;
}
:where(.starbar i)::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: url(../img/svg/lantern-ribs.svg) 0 0 / 100% 100% no-repeat;
}
:where(.starbar i.lit, #hud.star .starbar i, .is-star .starbar i) { box-shadow: 0 0 .9rem .25rem rgb(124 142 255 / .65), 0 0 0 .12rem var(--cobalt-hi); }
:where(.starbar.ready) { --loop: ff-breathe calc(var(--beat) * 2) ease-in-out var(--beat-delay) infinite alternate; animation: var(--loop); }
:where(#hud.star, .is-star) :where(.starbar) { animation: none; }
:where(.star-hint) { font: 900 var(--fs-small)/1.2 var(--font-body); color: var(--cobalt-hi); text-shadow: var(--hint-shadow); }
:where(.star-hint) :where(kbd) { margin-inline: .25rem; }

/* ---- progress rope (#hud-progress, #pause-bar, #load-bar) */
:where(.rope) {
  height: 1rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: repeating-linear-gradient(45deg, var(--ivory-2) 0 .32rem, var(--ivory-3) .32rem .5rem, var(--ivory-2) .5rem .64rem);
  box-shadow: inset 0 .12rem 0 rgb(44 15 21 / .25);
}
:where(.rope > i, #hud-progress, #pause-bar, #load-bar) {
  position: absolute; inset: 0; display: block; width: 100%; transform-origin: 0 50%;
  background:
    repeating-linear-gradient(45deg, rgb(255 255 255 / .18) 0 .32rem, transparent .32rem .64rem),
    var(--grad-fire);
  border-radius: inherit;
}
:where(.dock__rope .rope > i, #hud-progress)::after {                      /* the flame head at the fill's right end */
  content: ''; position: absolute; right: -.55rem; bottom: -.1rem; width: 1.1rem; height: 1.4rem; pointer-events: none;
  background: url(../img/svg/flame-flame-rim.svg) 50% 100% / contain no-repeat; transform-origin: 50% 100%;
  --loop: ff-flicker 360ms ease-in-out infinite alternate; animation: var(--loop);
}
:where(.rope__knot) {
  position: absolute; top: 50%; width: .55rem; height: .55rem; translate: -50% -50%; z-index: 1; border-radius: 50%;
  background: var(--ink-2); box-shadow: 0 0 0 .1rem var(--ivory-2);
}
:where(#hud-time) { font: 900 var(--fs-small)/1.2 var(--font-body); font-variant-numeric: tabular-nums; color: var(--ivory-3); }

/* ---- multiplier hanko, ring and streak drum (.mult.medal) */
:where(.mult.medal) { width: 7.6rem; height: 7.6rem; }
:where(.mult.medal)::before {                       /* the taiko drum: ivory skin, ink rim, red band with rivets */
  content: ''; position: absolute; inset: .55rem; border-radius: 50%;
  background:
    radial-gradient(circle, transparent 0 2.62rem, var(--ink) 2.62rem 2.78rem, transparent 2.78rem),
    radial-gradient(circle, var(--paper) 0 1.2rem, var(--ivory) 1.9rem, var(--ivory-2) 2.62rem, transparent 2.62rem),
    repeating-conic-gradient(var(--gold) 0 4deg, var(--red) 4deg 30deg);
  box-shadow: 0 0 0 var(--ow) var(--ink), var(--drop-s);
}
:where(.mult .ring-bg) { stroke: rgb(13 11 22 / .55); stroke-width: 3.4; }
:where(.mult .ring) { stroke-width: 3.4; stroke-linecap: round; }
:where(.mult.medal > p, #hud-streak) {
  position: relative; z-index: 1; color: var(--ink); font: 700 2.5rem/1 var(--font-num); text-align: center;
}
:where(.mult > .reg, #hud-mult) {
  position: absolute; left: -.3rem; bottom: -.1rem; z-index: 2; display: grid; place-items: center; width: 3.2rem; height: 3.2rem; padding: 0;
  border: var(--ow) solid var(--ink); border-radius: var(--r-xs); rotate: -8deg;
  background: var(--red); color: var(--ivory);
  box-shadow: inset 0 0 0 .2rem var(--red), inset 0 0 0 .32rem rgb(246 240 228 / .75), var(--drop-s);
  font: 700 1.75rem/1 var(--font-display); font-stretch: 85%; letter-spacing: -.02em;
  -webkit-text-stroke: max(2px, .1em) var(--ink); paint-order: stroke fill;
}
:where(#hud.star, .is-star) :where(.mult > .reg) { background: var(--cobalt); box-shadow: inset 0 0 0 .2rem var(--cobalt), inset 0 0 0 .32rem rgb(246 240 228 / .75), var(--drop-s); }
:where(.mult__max) {
  position: absolute; left: .1rem; bottom: -1.3rem; z-index: 2; padding: 0 .45rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: var(--grad-gold); color: var(--ink); font: 900 var(--fs-small)/1.35 var(--font-body); text-transform: uppercase; letter-spacing: .08em;
}
:where(.mult.max) :where(.mult__max) { display: block; }
@media (prefers-reduced-motion: no-preference) { :where(.mult.is-don) { animation: ff-don var(--t-base) var(--spring-pop); } }

/* ---- results gauges */
:where(.breakdown) { height: 1.4rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill); background: var(--ivory-2); box-shadow: inset 0 .12rem 0 rgb(44 15 21 / .2); }
:where(.breakdown i + i) { box-shadow: inset .14rem 0 0 var(--ink); }
:where(.res-ring) {
  display: inline-block; flex: none; width: 2.6rem; height: 2.6rem; border-radius: 50%;
  background: radial-gradient(circle, var(--paper) 0 54%, var(--ink) 55% 60%, transparent 61%),
              conic-gradient(var(--ok) calc(var(--acc, 0) * 1turn), var(--ivory-3) 0);
  box-shadow: 0 0 0 var(--ow-s) var(--ink);
}
:where(.res-meter__track) {
  height: .7rem; border: var(--ow-s) solid var(--ink);
  background: linear-gradient(90deg, var(--early), var(--ivory-2) 45% 55%, var(--late));
}
:where(.res-meter__track)::after { content: ''; position: absolute; left: 50%; top: -.25rem; bottom: -.25rem; width: 2px; translate: -50% 0; background: var(--ink); }
:where(.res-meter__mark) { width: 1rem; height: 1rem; z-index: 1; background: var(--paper); border: var(--ow-s) solid var(--ink); box-shadow: var(--drop-s); }

/* =========================================================================================== 2.11 name plate */
:where(.plate) {
  display: inline-flex; align-items: center; gap: .5rem; min-width: 0; max-width: 100%;
  padding: .3rem .95rem .3rem .3rem; border: var(--ow) solid var(--ink); border-radius: 1.4rem .4rem 1.4rem .4rem;
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory); color: var(--ink);
  box-shadow: var(--drop-s);
}
:where(.avatar) {
  display: inline-grid; place-items: center; flex: none; width: 2rem; height: 2rem; border-radius: 50%;
  border: var(--ow-s) solid var(--ink); background: var(--red) url(../img/svg/pat-seigaiha-red.svg) 0 0 / 1.6rem .8rem;
  color: var(--ivory); font: 700 var(--fs-btn)/1 var(--font-display); font-stretch: 100%;
  -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill;
  box-shadow: inset 0 0 0 .1rem rgb(246 240 228 / .6);
}
:where(.plate__name, .chip-name, #hud-name) {
  min-width: 0; max-width: 14ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; unicode-bidi: isolate;
  font: 900 var(--fs-body)/1.2 var(--font-body);
}
:where(.plate__name.is-placeholder) { color: var(--ivory-3); }
/* the @handle gives way first, all of it, before the name loses a pixel. Flex shrinking is weighted by the basis, so
   a shrink factor of 24 still took 1px from a 9-letter name (an ellipsis, "G0 Test…"). With 1e5 the name's share is
   under 1/64px (one layout unit) until the handle hits min-width 0 and freezes; only then does the name shrink.
   No percentage involved, so auto-width plates (the chip, the preview) size the same as before. */
:where(.plate__x, #hud-x) { flex: 0 100000 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 800 var(--fs-small)/1.2 var(--font-body); color: var(--cobalt); }
:where(.plate__x:empty, #hud-x:empty) { display: none; }
:where(.plate__kicker) { font: 900 var(--fs-small)/1 var(--font-body); color: var(--red); text-transform: uppercase; letter-spacing: .1em; }
:where(.plate__text) { display: grid; min-width: 0; }
:where(.plate--night) { background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-night); color: var(--ivory); box-shadow: inset 0 0 0 .1rem rgb(246 240 228 / .2), var(--drop-s); }
:where(.plate--night) :where(.plate__x, #hud-x) { color: var(--cobalt-hi); }
:where(.plate--chip) {
  min-height: 2.6rem; padding: .2rem .95rem .2rem .25rem; cursor: pointer;
  transition: transform var(--t-quick) var(--ease-snap);
}
@media (hover: hover) { :where(.plate--chip):hover { transform: translateY(-.12rem); box-shadow: 0 .32rem 0 var(--ink); } }
:where(.plate--chip):is(:active, .is-pressed) { transform: translateY(.18rem); box-shadow: none; transition-duration: var(--t-instant); }
:where(.plate--row) { border-radius: var(--r-s); box-shadow: none; }

/* =========================================================================================== 2.12 score boxes */
:where(.dock__score) :where(.label) { color: var(--ivory-3); }
:where(.dock__score .score, #hud-score) { font: 700 var(--fs-score)/1.05 var(--font-num); color: var(--ivory); }
:where(.dock__delta, #hud-delta) { min-height: 1.2em; text-align: right; font: 900 var(--fs-small)/1.2 var(--font-body); color: var(--gold); text-shadow: var(--hint-shadow); }
:where(.dock__delta.pop, #hud-delta.pop) { animation: ffc-delta var(--t-base) var(--ease-out), ff-fade-out 200ms linear 600ms forwards; }
@media (prefers-reduced-motion: reduce) { :where(.dock__delta.pop, #hud-delta.pop) { animation: ff-fade-out 200ms linear 600ms forwards; } }
:where(.res-score-box) { align-items: baseline; gap: .8rem; padding: .25rem 1.3rem .35rem; border-radius: var(--r-pill); }
:where(.res-score-box__label) { font: 900 var(--fs-small)/1 var(--font-body); text-transform: uppercase; letter-spacing: .1em; }
:where(.res-score) {
  font: 700 var(--fs-score-res)/1.05 var(--font-num); color: var(--ink);
  -webkit-text-stroke: max(2px, .1em) var(--ivory); paint-order: stroke fill;
}
:where(.res-delta) { font: 900 var(--fs-small)/1.2 var(--font-body); color: var(--ivory-3); }
:where(.res-delta.up, .res-delta.ok) { color: var(--ok); }

/* =========================================================================================== 2.13 judgement words
   p.judge[data-kind] + data-text (set with textContent by hud-view). The outline technique of .tk is rebuilt here
   because the pool nodes carry no .tk class. Placement (--jx) and the one-shot stay as in base.css; WP9 may tune. */
:where(.judge) {
  isolation: isolate; padding: .05em .18em; line-height: 1.1; letter-spacing: .02em; text-transform: uppercase;
  color: var(--ivory); -webkit-text-stroke: .11em var(--ink); paint-order: stroke fill;
  transform-origin: 50% 60%;
}
:where(.judge)::before {
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; padding: inherit; z-index: -1; color: var(--ivory);
  -webkit-text-stroke: .26em var(--ivory); paint-order: stroke fill; text-shadow: 0 .08em 0 var(--ink);
}
:where(.judge[data-kind="perfect"], .judge[data-kind="great"])::after {
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; padding: inherit; -webkit-text-stroke: 0; color: transparent;
  background: var(--grad-gold-fire) content-box; -webkit-background-clip: text; background-clip: text;   /* the ramp spans the word, not the fan above it */
}
:where(.judge[data-kind="great"])::after { background-image: var(--grad-blue); }
/* PERFECT / GREAT: an upward half-fan of firework rays behind the word (SPEC 2.13; ref 13-B, 21-B), drawn as the node's
   own background (the pool nodes have no children; ::before / ::after carry the halo and the gradient fill). Opaque
   rays with ink rims, brightest at the tips, plus a warm glow and twinkles; the lower half stays empty so the fret rings
   below the word stay clear. The SVG's viewBox is the node's padding box: a fixed width, the fan above the word, the
   word's centre 1.8em (1.4em) from the top. translate keeps that centre on 73vh; the pop scales around it. The node box
   holds the whole fan, so hud-view's clamp (offsetWidth x JUDGE_POP) keeps it clear of Aisu (CONTRACTS 5.2). */
:where(.judge[data-kind="perfect"]) {
  box-sizing: border-box; width: 6em; padding: 1.25em 0 .12em; text-align: center;
  translate: -50% calc(-50% - .565em); transform-origin: 50% 1.8em;
  background: url(../img/svg/judge-fan-gold.svg) 50% 0 / 100% 100% no-repeat;
}
:where(.judge[data-kind="great"]) {
  box-sizing: border-box; width: 4.6em; padding: .85em 0 .12em; text-align: center;
  translate: -50% calc(-50% - .365em); transform-origin: 50% 1.4em;
  background: url(../img/svg/judge-fan-cobalt.svg) 50% 0 / 100% 100% no-repeat;
}
:where(.judge:is([data-kind="good"], [data-kind="miss"])) { font-size: var(--fs-judge-s); }
:where(.judge[data-kind="miss"]) { color: var(--miss); rotate: -8deg; translate: -50% calc(-50% + .4rem); }
:where(.judge[data-kind="miss"])::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 1.8em; height: 1.8em; margin: -.9em 0 0 -.9em; z-index: -2;
  background: url(../img/fx/poof-3.webp) 50% 50% / contain no-repeat; opacity: 0;
}
/* the poof sprite is keyed per hit, never by position: ui.judge() moves the node to the end of #judge-host, so an
   :nth-child sprite was always the same one for the newest word and switched mid-poof when the next word came.
   hud-view (WP9) writes data-poof="1".."12" on each MISS (SPEC: random poof-1..12); without it, poof-3. */
:where(.judge[data-poof="1"])::after { background-image: url(../img/fx/poof-1.webp); }
:where(.judge[data-poof="2"])::after { background-image: url(../img/fx/poof-2.webp); }
:where(.judge[data-poof="3"])::after { background-image: url(../img/fx/poof-3.webp); }
:where(.judge[data-poof="4"])::after { background-image: url(../img/fx/poof-4.webp); }
:where(.judge[data-poof="5"])::after { background-image: url(../img/fx/poof-5.webp); }
:where(.judge[data-poof="6"])::after { background-image: url(../img/fx/poof-6.webp); }
:where(.judge[data-poof="7"])::after { background-image: url(../img/fx/poof-7.webp); }
:where(.judge[data-poof="8"])::after { background-image: url(../img/fx/poof-8.webp); }
:where(.judge[data-poof="9"])::after { background-image: url(../img/fx/poof-9.webp); }
:where(.judge[data-poof="10"])::after { background-image: url(../img/fx/poof-10.webp); }
:where(.judge[data-poof="11"])::after { background-image: url(../img/fx/poof-11.webp); }
:where(.judge[data-poof="12"])::after { background-image: url(../img/fx/poof-12.webp); }
:where(.judge.on[data-kind="miss"])::after { animation: ff-poof 600ms var(--ease-out) forwards; }
@media (prefers-reduced-motion: reduce) { :where(.judge)::after { animation: none; } }
:where(.timing) {
  padding: .05rem .6rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill); box-shadow: var(--drop-s);
  font: 900 var(--fs-small)/1.3 var(--font-body); text-transform: uppercase; letter-spacing: .08em; color: var(--ink);
}
:where(.timing.early) { background: var(--cobalt-hi); }
:where(.timing.late) { background: #FF5A5F; }

/* =========================================================================================== 2.14 combo toasts (#combo[data-kind]) */
:where(.combo) {
  --arrow: polygon(1.1rem 0, 100% 0, calc(100% - .7rem) 50%, 100% 100%, 1.1rem 100%, 0 50%);
  display: flex; align-items: center; gap: .5rem; min-height: 3.2rem; padding: .25rem 1.3rem .3rem 1.6rem;
  isolation: isolate; border: 0; border-radius: 0; background: var(--ink); color: var(--ink);
  font: 700 var(--fs-h3)/1.1 var(--font-display); font-stretch: 85%; white-space: nowrap;
  clip-path: var(--arrow);
}
:where(.combo)::before {
  content: ''; position: absolute; inset: var(--ow); z-index: -1; background: var(--paper);
  clip-path: polygon(calc(1.1rem - var(--ow) * .4) 0, 100% 0, calc(100% - .7rem + var(--ow) * .2) 50%, 100% 100%, calc(1.1rem - var(--ow) * .4) 100%, 0 50%);
}
:where(.combo[data-kind="streak"])::before { background: var(--grad-gold); }
:where(.combo[data-kind="star"])::before { background: url(../img/svg/pat-seigaiha-cobalt.svg) 0 0 / 2rem 1rem; }
:where(.combo[data-kind="star"]) { color: var(--ivory); -webkit-text-stroke: max(2px, .1em) var(--ink); paint-order: stroke fill; }
:where(.combo[data-kind="phrase"]) { padding-left: 1rem; clip-path: none; border: var(--ow) solid var(--ink); border-radius: var(--r-m); background: var(--paper); }
:where(.combo[data-kind="phrase"])::before { content: '★'; position: static; inset: auto; z-index: auto; background: none; clip-path: none; color: var(--gold); -webkit-text-stroke: max(2px, .08em) var(--ink); paint-order: stroke fill; font-family: var(--font-num); }
:where(.combo[data-kind="mult"]) {
  padding: .25rem 1rem; clip-path: none; border: var(--ow) solid var(--ink); border-radius: var(--r-xs); rotate: -4deg;
  background: var(--red); color: var(--ivory); box-shadow: inset 0 0 0 .2rem var(--red), inset 0 0 0 .32rem rgb(246 240 228 / .75);
  -webkit-text-stroke: max(2px, .1em) var(--ink); paint-order: stroke fill; font-size: 1.75rem;
}
:where(.combo[data-kind="mult"])::before { content: none; }

/* =========================================================================================== 2.15 toasts */
:where(.toast) {
  max-width: 22rem; padding: .6rem 1rem; border: var(--ow) solid var(--ink); border-radius: var(--r-m);
  background: url(../img/svg/paper-grain.webp) 0 0 / 16rem, var(--paper); color: var(--ink);
  box-shadow: var(--drop); font: 800 var(--fs-body)/1.35 var(--font-body);
}
/* section ema (#section-toast): plaque hanging on two red cords. Name mode shows a "Now" kicker; hint mode (.hint) is wider */
:where(.toast--ema) {
  margin-top: 1.7rem; min-height: 0; padding: .4rem .8rem .5rem; text-align: center; transform-origin: 50% -1.7rem;
  border-radius: var(--r-xs); box-shadow: .3rem .35rem 0 0 var(--red), .3rem .35rem 0 var(--ow-s) var(--ink);
  font: 700 var(--fs-h3)/1.1 var(--font-display); font-stretch: 85%; overflow-wrap: anywhere;
  transition: opacity 200ms linear;
}
:where(.toast--ema)::before {
  content: 'Now'; display: block; margin-bottom: .15rem;
  font: 900 var(--fs-small)/1 var(--font-body); text-transform: uppercase; letter-spacing: .12em; color: var(--red);
}
:where(.toast--ema)::after {                          /* the two cords */
  content: ''; position: absolute; left: 1.2rem; right: 1.2rem; bottom: 100%; height: calc(1.7rem + var(--ow)); pointer-events: none;
  background:
    linear-gradient(90deg, var(--ink) 0 .1rem, var(--red) .1rem .32rem, var(--ink) .32rem .42rem, transparent .42rem) 0 0 / 100% 100% no-repeat,
    linear-gradient(270deg, var(--ink) 0 .1rem, var(--red) .1rem .32rem, var(--ink) .32rem .42rem, transparent .42rem) 0 0 / 100% 100% no-repeat;
}
:where(.toast--ema.hint) { width: 15rem; font: 800 var(--fs-small)/1.3 var(--font-body); text-align: left; }
:where(.toast--ema.hint)::before { content: none; }
:where(.toast--ema.on) { --anim: ffc-hang 600ms var(--spring-soft) backwards; animation: var(--anim); }
/* fatal error: red band header "Oops!" (CSS content), message in ink on paper, the squish face */
:where(.toast--error) {
  max-width: none; padding: 0 1.1rem .8rem 4.6rem; overflow: hidden; border-width: var(--ow-l); border-radius: var(--r-m);
  box-shadow: var(--plate-red), var(--float); font: 800 var(--fs-body)/1.4 var(--font-body);
}
:where(.toast--error)::before {
  content: 'Oops!'; display: block; margin: 0 -1.1rem .6rem -4.6rem; padding: .3rem 1rem .3rem 4.6rem;
  background: #D11A2A url(../img/svg/pat-seigaiha-red.svg) 0 0 / 2.4rem 1.2rem; border-bottom: var(--ow) solid var(--ink);
  color: var(--ivory); font: 700 var(--fs-h3)/1.2 var(--font-display); font-stretch: 85%;
  -webkit-text-stroke: max(2px, .12em) var(--ink); paint-order: stroke fill;
}
:where(.toast--error)::after {
  content: ''; position: absolute; left: .8rem; top: 1.25rem; width: 3rem; height: 3rem; border-radius: 50%;
  background: url(../img/mascot/face-squish.svg) 50% 62% / 74% auto no-repeat, radial-gradient(circle at 45% 30%, var(--f2), var(--f4) 70%);
  box-shadow: 0 0 0 var(--ow) var(--ink), var(--drop-s);
}
:where(.toast--error.on) { --anim: ff-drop-in 420ms var(--spring-soft) backwards; animation: var(--anim); }
:where(.toast--info) {
  padding: .2rem .9rem; border-width: var(--ow-s); border-radius: var(--r-pill);
  background: var(--night); color: var(--ivory); font: 900 var(--fs-small)/1.4 var(--font-body);
}

/* =========================================================================================== 2.16 hint bar */
:where(.hintbar) {
  gap: 1.1rem; color: var(--ivory); text-shadow: var(--hint-shadow); white-space: nowrap;
  font: 900 var(--fs-small)/1 var(--font-body); text-transform: uppercase; letter-spacing: .06em;
}
:where(.hintbar .hint) { gap: .3rem; }
:where(.hintbar .hint__label) { padding-left: .15rem; }
:where(.hint-btn) { cursor: pointer; }
:where(.hint--go .hint__label) { color: var(--f2); }
:where(.hint--note) { text-transform: none; letter-spacing: .02em; color: var(--ivory-2); font-weight: 800; }

/* =========================================================================================== 2.17 focus cursor
   One global #cursor (in #fx, z 6), driven by cursor.js: it writes left / top / width / height and the radius per
   move and runs the 140ms FLIP. Glow and tips loop only while it is on (2 infinite animations); static on lowfx. */
:where(.cursor) {
  box-sizing: border-box; border: .22rem solid var(--focus); border-radius: .5rem;
  box-shadow: 0 0 0 .14rem var(--ink), inset 0 0 0 .14rem var(--ink);
  transform-origin: 0 0; pointer-events: none; transition: opacity var(--t-instant) linear;
  will-change: transform;          /* keeps "has a transform" constant, so the end of a FLIP costs no extra layout */
}
:where(.cursor)::before {                            /* pre-blurred glow, opacity pulse only */
  content: ''; position: absolute; inset: -.36rem; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 1.2rem .3rem rgb(255 210 58 / .55); opacity: .8;
}
:where(.cursor)::after {                             /* two flame tips: top-left and bottom-right corners */
  content: ''; position: absolute; inset: -1.05rem; pointer-events: none;
  background: url(../img/svg/flame-tip-tl.svg) 0 0 / 1.55rem 1.55rem no-repeat, url(../img/svg/flame-tip-br.svg) 100% 100% / 1.55rem 1.55rem no-repeat;
}
/* text fields (cursor.js data-frame="input"): the field's label sits .3rem above the input, right where the top-left
   tip reaches (1.4rem out), so only the bottom-right tip stays */
:where(.cursor[data-frame="input"])::after { background: url(../img/svg/flame-tip-br.svg) 100% 100% / 1.55rem 1.55rem no-repeat; }
.cursor.on { opacity: 1; }
.cursor.on::before { --loop: ff-glow calc(var(--beat) * 2) ease-in-out var(--beat-delay) infinite alternate; animation: var(--loop); }
.cursor.on::after { --loop: ff-tip calc(var(--beat) * 2) var(--ease-inout) var(--beat-delay) infinite alternate; animation: var(--loop); }
body.lowfx .cursor::before, body.lowfx .cursor::after { animation: none; }
body.lowfx .cursor::before { opacity: 1; }
/* the cursor replaces the native outline; body.no-cursor (cursor.js not running) keeps the 3px fallback */
body:not(.no-cursor) :focus-visible { outline: none; }
body.no-cursor :focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
body:not(.no-cursor) .switch input:focus-visible + .track { outline: none; }   /* base.css fallback ring; the cursor frames the label */
/* selected but not focused: the selected fill + an inner ivory ring, no flame tips */
:where([aria-checked="true"], [aria-selected="true"], .is-selected):not(:focus-visible, .ticket) { outline: max(2px, .16rem) solid var(--ivory); outline-offset: -.4rem; }
:where(.tabs [role="tab"][aria-selected="true"]):not(:focus-visible) { outline-offset: -.32rem; }

/* =========================================================================================== 2.18 tooltips ([data-tip])
   A bubble in ::after (only on elements that do not use ::after already). Sections are isolated (z 3), so the tip
   stacks above its own section only (CONTRACTS 5.5); it opens .6rem away from the control, clear of the cursor ring.
   The bubble has no box until it shows (display: none, then a 140ms opacity fade-in: after 450ms on hover, at once on
   :focus-visible). A hidden tip that only had opacity 0 still added scrollable overflow to its scroll container
   (.stall__body, the board list), so a stall could scroll sideways for nothing. A shown tip is still clipped by
   them: inside a scroll container, open it inward (data-tip-pos). */
:where([data-tip]:not(.screen > *)) { position: relative; }
:where([data-tip])::after {
  content: attr(data-tip); position: absolute; z-index: 7; left: 50%; bottom: calc(100% + .6rem); translate: -50% 0;
  width: max-content; max-width: 18rem; padding: .35rem .6rem; pointer-events: none;
  border: var(--ow-s) solid var(--ink); border-radius: var(--r-s); background: var(--paper); color: var(--ink); box-shadow: var(--drop-s);
  font: 800 var(--fs-small)/1.3 var(--font-body); text-align: left; text-transform: none; letter-spacing: 0; white-space: normal;
  text-shadow: none; -webkit-text-stroke: 0; font-stretch: normal;
  display: none;
}
:where([data-tip-pos="bottom"])::after { top: calc(100% + .6rem); bottom: auto; }
:where([data-tip-pos="left"])::after { left: auto; right: calc(100% + .6rem); top: 50%; bottom: auto; translate: 0 -50%; }
:where([data-tip-pos="right"])::after { left: calc(100% + .6rem); top: 50%; bottom: auto; translate: 0 -50%; }
@media (hover: hover) { [data-tip]:hover::after { display: block; animation: ff-fade var(--t-quick) linear 450ms backwards; } }
[data-tip]:focus-visible::after { display: block; animation: ff-fade var(--t-quick) linear backwards; }
#hud [data-tip]::after { display: none; }

/* =========================================================================================== 2.19 speech bubbles */
:where(.bubble) {
  --tail-x: 1.4rem; --tail-y: 1rem;
  max-width: 17.5rem; padding: .5rem .8rem; border: var(--ow) solid var(--ink); border-radius: var(--r-m);
  background: var(--paper); color: var(--ink); box-shadow: var(--drop-s);
  font: 800 var(--fs-body)/1.3 var(--font-body); overflow-wrap: anywhere;
  transform-origin: var(--tail-x) 100%;
}
:where(.bubble)::after {                              /* tail: a rotated square with the ink rim on two sides (down by default) */
  content: ''; position: absolute; width: .9rem; height: .9rem; left: var(--tail-x); bottom: calc(-.45rem - var(--ow) / 2);
  background: var(--paper); border: solid var(--ink); border-width: 0 var(--ow) var(--ow) 0; rotate: 45deg;
}
:where(.bubble--tail-left)::after { left: calc(-.45rem - var(--ow) / 2); bottom: auto; top: var(--tail-y); rotate: 135deg; }
:where(.bubble--tail-right)::after { left: auto; right: calc(-.45rem - var(--ow) / 2); bottom: auto; top: var(--tail-y); rotate: -45deg; }
:where(.bubble--tail-left) { transform-origin: 0 var(--tail-y); }
:where(.bubble--tail-right) { transform-origin: 100% var(--tail-y); }
:where(.bubble--aisu) { padding-top: .8rem; }
:where(.bubble--aisu)::before {
  content: 'Aisu'; position: absolute; left: .6rem; top: -.85rem; padding: .05rem .55rem;
  border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill); background: var(--red); color: var(--ivory);
  font: 900 var(--fs-small)/1.3 var(--font-body); text-transform: uppercase; letter-spacing: .1em;
  -webkit-text-stroke: max(1.5px, .08em) var(--ink); paint-order: stroke fill; rotate: -3deg;
}
:where(.bubble:not([hidden], :empty)) { --anim: ff-pop 380ms var(--spring-pop) backwards; animation: var(--anim); }
:where(.bubble.is-out) { opacity: 0; transition: opacity 200ms linear; }
:where(#hud .bubble) { max-width: 8.5rem; font-size: var(--fs-small); }

/* =========================================================================================== 2.20 decorations (deco.js) */
/* rays: static, never spinning */
:where(.rays) {
  border-radius: 50%; pointer-events: none;
  background: repeating-conic-gradient(from 0deg, rgb(255 90 31 / .5) 0 6deg, rgb(232 32 47 / 0) 6deg 15deg);
  -webkit-mask: radial-gradient(closest-side, #000 22%, transparent 100%); mask: radial-gradient(closest-side, #000 22%, transparent 100%);
}
:where(.lockup__rays) {
  position: absolute; left: -12%; top: -20%; width: 124%; height: 140%; z-index: -1;
  background: repeating-conic-gradient(from 4deg, rgb(232 32 47 / .08) 0 7deg, rgb(246 240 228 / .08) 7deg 14deg);
}
:where(#title-logo) { isolation: isolate; }
:where(.res-field__rays) {
  background: repeating-conic-gradient(from 0deg at calc(2.5vw + var(--res-w) / 2) 42%, rgb(255 190 80 / .1) 0 5deg, transparent 5deg 13deg);
}
/* edge flames: the flame strip as a mask over a fire gradient */
:where(.edge-flames, .res-field__flames) {
  background: linear-gradient(0deg, var(--red), var(--f3) 55%, var(--f2));
  -webkit-mask: url(../img/svg/edge-flames.svg) 0 100% / 6rem 100% repeat-x; mask: url(../img/svg/edge-flames.svg) 0 100% / 6rem 100% repeat-x;
  opacity: .85;
}
:where(.res-field__flames) { top: auto; height: 4.5rem; }

/* lines (cords) drawn by deco.js: an SVG in host %, non-scaling strokes */
:where(.deco-line) { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
:where(.deco-line__ink) { fill: none; stroke: var(--ink); stroke-width: .3rem; stroke-linecap: round; }
:where(.deco-line__hi) { fill: none; stroke: var(--ivory); stroke-width: .1rem; stroke-linecap: round; opacity: .65; }
:where(.deco-line--red .deco-line__hi) { stroke: var(--red); stroke-width: .16rem; opacity: 1; }

/* lantern cord (title): one group, one sway loop; lanterns hang from the cord, glows are static */
:where(.deco-swing) {
  position: absolute; inset: 0; transform-origin: 50% 0;
  --loop: ff-sway 4.2s var(--ease-inout) infinite alternate; --sway: 1.5deg; animation: var(--loop);
}
:where(.deco-lantern) { position: absolute; width: var(--lw, 2.6rem); aspect-ratio: 100 / 156; translate: -50% -.15rem; }
:where(.deco-lantern img) { position: relative; z-index: 1; display: block; width: 100%; height: 100%; }
:where(.deco-lantern__glow) {
  position: absolute; left: -75%; right: -75%; top: -15%; bottom: -30%; border-radius: 50%;
  background: radial-gradient(closest-side, rgb(255 110 50 / .5), rgb(255 80 40 / .16) 55%, transparent);
}
:where(.deco-lantern--ivory .deco-lantern__glow) { background: radial-gradient(closest-side, rgb(255 225 150 / .45), rgb(255 200 120 / .14) 55%, transparent); }
:where(.deco-lantern--cobalt .deco-lantern__glow) { background: radial-gradient(closest-side, rgb(90 120 255 / .5), rgb(60 80 255 / .14) 55%, transparent); }

/* bunting: static pennants along a catenary */
:where(.deco-pennant) {
  position: absolute; width: var(--pw, 1.5rem); aspect-ratio: 44 / 56; translate: -50% -.1rem; rotate: var(--a, 0deg); transform-origin: 50% 0;
}
:where(.deco-pennant img) { display: block; width: 100%; height: 100%; }

/* crowd (title): two layers of red-lit silhouettes bobbing on the beat (2 loops). Each layer is clipped to the host's
   width and fades out on its right end, so no silhouette ever reaches Aisu (the host stops at 46vw). */
:where(.deco-crowd) {
  position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: flex-end; overflow: hidden; transform-origin: 50% 100%;
  -webkit-mask: linear-gradient(90deg, #000 72%, transparent); mask: linear-gradient(90deg, #000 72%, transparent);
}
:where(.deco-crowd img) { display: block; flex: none; height: 100%; width: auto; margin-right: -9%; }
:where(.deco-crowd--back) { height: 82%; opacity: .7; --loop: ff-bob var(--beat) ease-in-out var(--beat-delay) infinite alternate; animation: var(--loop); }
:where(.deco-crowd--front) { height: 100%; left: 4%; --loop: ff-bob var(--beat) ease-in-out calc(var(--beat-delay) - var(--beat) / 2) infinite alternate; animation: var(--loop); }

/* embers (loading): a fixed window over the lower 62vh (clipped, with a static mask that lights the embers up at the
   bottom and lets them die out at the top) and one drifting layer inside it: two identical tiles of ten dots, moved up
   by exactly one tile per loop at constant opacity, so the loop has no seam and the screen is never empty. */
:where(.deco-embers) {
  position: absolute; left: 0; right: 0; bottom: 0; height: 62vh; overflow: hidden; pointer-events: none;
  -webkit-mask: linear-gradient(0deg, transparent, #000 14%, #000 46%, transparent 96%);
  mask: linear-gradient(0deg, transparent, #000 14%, #000 46%, transparent 96%);
}
:where(.deco-embers__drift) {
  position: absolute; left: 0; right: 0; top: 0; height: 200%;
  background: var(--embers, none); background-size: 100% 50%; background-repeat: repeat-y;
  --loop: ffc-embers 9s linear infinite; animation: var(--loop);
}

/* fireworks: B's bursts, single-shot (deco.js removes each one on animationend) */
:where(.deco-firework) { position: absolute; width: var(--fw, 11rem); aspect-ratio: 1; translate: -50% -50%; pointer-events: none; opacity: 0; animation: ff-burst 900ms var(--ease-out) forwards; }

/* split letters ([data-letters]) keep their own static pose; screen packages stagger them with --i */
:where(.deco-letter) { display: inline-block; }

/* =========================================================================================== reduced motion extras */
@media (prefers-reduced-motion: reduce) {
  :where(.cursor) { transition-duration: var(--t-instant); }
  :where(.deco-firework) { display: none; }
}
