/* screens/hud.css: the Playing HUD (WP9; SPEC 3.10, 2.10-2.15). Festival gear around the headliner.
   Regions and ids come from base.css (B. geometry) and components.css (shared look); this file finishes them.
   Rules kept here: transform / opacity are the only animated properties; per-frame values arrive as custom properties
   written by hud-view.js only on change (--rock, --f, --fill, --jx; the rail line once per start / resize); no filter,
   no backdrop-filter; text >= .875rem. Entrances use --anim (motion.css turns them into a 120ms fade under reduced
   motion), loops use --loop (stopped there). Entrance delays count from --hud-at (hud-view: the camera move's moveAt,
   written once per entry). Infinite loops while playing (budget 9): bichito idle / dance (mascot.js), bulb flame, rope
   head flame, ema sway (shown), lanterns breathe (ready), edge glow (Star Power), danger pulse + red edge (danger),
   MAX shine (x4). One-shots: the drum's number per hit, the drum on milestones, the gold edge flash, the ×N hanko. */

#hud { --hud-at: 0ms; }
/* #hud > * are absolutely placed by base.css with zero specificity; .tk (#endcap) would make it relative again */
#hud > #endcap { position: absolute; }

/* =========================================================================================== song plate (top-left) */
#hud .song-plate {
  grid-template-columns: 3.3rem minmax(0, 1fr); grid-template-rows: auto auto; align-content: center;
  column-gap: .6rem; row-gap: .2rem; padding: .4rem .95rem .45rem .45rem;
  border: var(--ow) solid var(--ink); border-radius: 1.6rem .5rem 1.6rem .5rem;
  background: url(../../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory);
  box-shadow: inset 0 -.2rem 0 rgb(205 187 156 / .55), .32rem .38rem 0 0 var(--red), .32rem .38rem 0 var(--ow-s) var(--ink);
  --anim: ff-slide-l 380ms var(--spring-soft) calc(var(--hud-at) + 80ms) backwards; --slide: 60%;
  animation: var(--anim, none);
}
/* difficulty disc: red seigaiha (cobalt for Expert) with the heat icon, an ivory inner rule and a small drop */
#hud .song-plate__icon {
  --dif-icon: url(../../img/svg/dif-medium.svg);
  grid-row: 1 / 3; align-self: center; position: relative; display: block;
  width: 3.3rem; height: 3.3rem; border-radius: 50%; border: var(--ow-s) solid var(--ink);
  background: var(--dif-icon) 50% 58% / 64% auto no-repeat, #D11A2A url(../../img/svg/pat-seigaiha-red.svg) 0 0 / 1.5rem .75rem;
  box-shadow: inset 0 0 0 .15rem #D11A2A, inset 0 0 0 .27rem rgb(246 240 228 / .8), 0 .16rem 0 var(--ink);
  rotate: -6deg;
}
#hud .song-plate__icon[data-dif="easy"]   { --dif-icon: url(../../img/svg/dif-easy.svg); }
#hud .song-plate__icon[data-dif="hard"]   { --dif-icon: url(../../img/svg/dif-hard.svg); }
#hud .song-plate__icon[data-dif="expert"] {
  --dif-icon: url(../../img/svg/dif-expert.svg);
  background: var(--dif-icon) 50% 58% / 64% auto no-repeat, var(--cobalt) url(../../img/svg/pat-seigaiha-cobalt.svg) 0 0 / 1.5rem .75rem;
  box-shadow: inset 0 0 0 .15rem var(--cobalt), inset 0 0 0 .27rem rgb(246 240 228 / .8), 0 .16rem 0 var(--ink);
}
#hud #hud-song {
  grid-column: 2; align-self: end; min-width: 0; margin: 0;
  font: 700 var(--fs-h3)/1.1 var(--font-display); font-stretch: 75%; letter-spacing: .01em; color: var(--ink);
}
#hud .song-plate__meta {
  grid-column: 2; align-self: start; min-width: 0; margin: 0; display: flex; align-items: center; gap: .3rem;
  overflow: hidden; white-space: nowrap;
  font: 900 var(--fs-small)/1.2 var(--font-body); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-2);
}
/* the artist and the pills never shrink: the plate grows to fit them (min 17rem, base.css) and only the title gives
   (ellipsis). The title adds no intrinsic width (width 0 + min-width 100%), so a long title never widens the plate. */
#hud .song-plate { width: max-content; min-width: 17rem; max-width: 26rem; }
#hud #hud-song { width: 0; min-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#hud .song-plate__meta { overflow: visible; }
#hud #hud-artist { flex: none; margin-right: .1rem; }
#hud .song-plate__meta .pill { flex: none; min-height: 1.35rem; padding: 0 .45rem; letter-spacing: .05em; box-shadow: 0 .12rem 0 var(--ink); }

/* the song's progress lives in the plate: a slim rope with the flame head and the time beside it */
#hud .song-plate { grid-template-rows: auto auto auto; }
#hud .song-plate__rope {
  grid-column: 1 / 3; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: .55rem;
  margin: .12rem 0 0 .15rem;
}
#hud .song-plate__rope .rope { height: .62rem; border-width: max(1.5px, .12rem); }
#hud .song-plate__rope #hud-progress::after { right: -.4rem; bottom: -.12rem; width: .8rem; height: 1.05rem; }
#hud .rope__knot { width: .18rem; height: .42rem; border-radius: .1rem; background: var(--ink); box-shadow: none; opacity: .55; }
#hud #hud-time { margin: 0; white-space: nowrap; font: 900 var(--fs-small)/1 var(--font-body); font-variant-numeric: tabular-nums; color: var(--ink-2); }

/* =========================================================================================== lyric pill (top centre) */
#hud .lyrics {
  row-gap: .05rem; padding: .15rem 1.7rem .25rem;
  border: var(--ow) solid var(--ink); border-radius: var(--r-pill);
  background:
    radial-gradient(60% 120% at 50% -20%, rgb(124 142 255 / .16), transparent 70%),
    linear-gradient(180deg, rgb(36 29 61 / .94), rgb(13 11 22 / .94));
  box-shadow: inset 0 0 0 .14rem rgb(246 240 228 / .2), .3rem .35rem 0 0 var(--cobalt-deep), .3rem .35rem 0 var(--ow-s) var(--ink);
  --anim: ff-fade 240ms linear calc(var(--hud-at) + 120ms) backwards;
  animation: var(--anim, none);
  transition: opacity 200ms linear;
}
/* a flame on the pill's top rim, and two red lantern beads on its ends */
#hud .lyrics::before {
  content: ''; position: absolute; left: 50%; top: -.95rem; width: 1.3rem; height: 1.55rem; translate: -50% 0;
  background: url(../../img/svg/flame-flame-rim.svg) 50% 100% / contain no-repeat; pointer-events: none;
}
#hud .lyrics::after {
  content: ''; position: absolute; inset: 50% -.6rem auto; height: 1rem; translate: 0 -50%; pointer-events: none;
  background:
    radial-gradient(circle at .5rem 50%, var(--red-hi) 0 .22rem, var(--red) .28rem .36rem, var(--ink) .37rem .5rem, transparent .52rem),
    radial-gradient(circle at calc(100% - .5rem) 50%, var(--red-hi) 0 .22rem, var(--red) .28rem .36rem, var(--ink) .37rem .5rem, transparent .52rem);
}
#hud #lyric-now {
  position: relative; margin: 0; padding: 0 .12em;
  font: 700 var(--fs-lyric)/1.18 var(--font-display); font-stretch: 75%; letter-spacing: .01em; color: var(--ivory);
  -webkit-text-stroke: max(1.5px, .08em) var(--ink); paint-order: stroke fill;
}
#hud #lyric-now.len-m { font-size: var(--fs-lyric-m); }
#hud #lyric-now.len-l { font-size: var(--fs-lyric-l); }
/* the sung part: the same line in gold on top, clipped at --fill (one custom-property write per 1 %) */
#hud #lyric-now::after {
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; padding: inherit; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  color: var(--f2); -webkit-text-stroke: inherit; paint-order: stroke fill; pointer-events: none;
  clip-path: inset(-.3em calc(100% - var(--fill, 0%)) -.3em 0);
}
#hud #lyric-now:empty::before {
  content: '♪  ♪  ♪'; content: '♪  ♪  ♪' / ""; color: var(--ivory-3); white-space: pre; -webkit-text-stroke: 0; font-family: var(--font-symbols);
  font-size: 1.2rem;
}
#hud #lyric-next { margin: 0; font: 800 var(--fs-small)/1.25 var(--font-body); letter-spacing: .02em; color: var(--ivory-3); }
#hud:has(#endcap.on) .lyrics { opacity: 0; }

/* =========================================================================================== section ema (top-right) */
#hud #section-toast {
  min-height: 0; margin-top: 1.15rem; padding: 1.35rem .7rem .5rem;
  border: var(--ow) solid var(--ink); border-radius: .3rem .3rem .6rem .6rem;
  background: url(../../img/svg/paper-grain.webp) 0 0 / 16rem, var(--grad-ivory);
  box-shadow: inset 0 -.22rem 0 rgb(205 187 156 / .6), .28rem .32rem 0 0 var(--red), .28rem .32rem 0 var(--ow-s) var(--ink);
  font: 700 var(--fs-h3)/1.1 var(--font-display); font-stretch: 85%; color: var(--ink); text-align: center;
  overflow-wrap: anywhere; transform-origin: 50% -1.15rem;
}
/* the roof: a night-seigaiha cap wider than the plaque, carrying the "Now" kicker */
#hud #section-toast::before {
  content: 'Now'; position: absolute; left: -.6rem; right: -.6rem; top: -.55rem; height: 1.45rem; margin: 0;
  display: grid; place-items: center; box-sizing: border-box;
  border: var(--ow) solid var(--ink); border-radius: .55rem .55rem .3rem .3rem;
  background: var(--night-3) url(../../img/svg/pat-seigaiha-night.svg) 0 0 / 1.6rem .8rem;
  box-shadow: inset 0 .12rem 0 rgb(246 240 228 / .18), 0 .16rem 0 var(--ink);
  font: 900 var(--fs-small)/1 var(--font-body); letter-spacing: .18em; text-transform: uppercase; color: var(--f2);
}
#hud #section-toast::after { left: 1.5rem; right: 1.5rem; height: calc(1.15rem + var(--ow)); }    /* the two red cords */
#hud #section-toast.on {
  --anim: ffc-hang 620ms var(--spring-soft) backwards;
  --loop: ff-sway 3.4s var(--ease-inout) 620ms infinite alternate; --sway: 1.1deg;
  animation: var(--anim, none), var(--loop, none);
}
/* hint mode (ui.hint): wider paper slip, body text, a star on the roof */
#hud #section-toast.hint {
  width: 15rem; padding: 1.3rem .8rem .55rem; text-align: left; overflow-wrap: normal; text-wrap: balance;
  font: 800 var(--fs-small)/1.3 var(--font-body); letter-spacing: .01em;
}
#hud #section-toast.hint::before { content: '★  ★  ★'; content: '★  ★  ★' / ""; white-space: pre; letter-spacing: .1em; color: var(--gold); font-family: var(--font-num); }

/* =========================================================================================== the rail cluster
   Everything the player reads while playing hugs the highway's right rail, bottom-up: the streak drum beside the strike
   line, the score over it, the Star Power lanterns, the crowd head; the crowd tube runs along the rail itself.
   hud-view (_layout) writes the rail's screen line on #hud once per start / resize: --rail-x (px, the rail's outer edge
   at the window's bottom), --rail-k (dx/dy), --rail-sec (sqrt(1 + k^2)) and --rail-tilt (-atan k). The rail leans in
   going up, so each row sits --cl-gap right of the rail at its own bottom edge B: left = k * (--cl-h - B) + --cl-gap,
   relative to the dock (whose left edge is the rail at the cluster's top). No panel: the stage stays visible behind. */
#hud {
  --rail-x: calc(var(--play-dock-l) + 3vh); --rail-k: .3; --rail-sec: 1.044; --rail-tilt: -16.7deg;
  --cl-h: 22rem; --cl-gap: 2.7rem;
}
#hud .dock {
  left: calc(var(--rail-x) - var(--rail-k) * var(--cl-h)); right: 0; top: auto; bottom: 0; width: auto; height: var(--cl-h);
  display: block; padding: 0; container-type: normal; border: 0; border-radius: 0; background: none; box-shadow: none;
  --anim: ff-slide-r 460ms var(--spring-soft) calc(var(--hud-at) + 130ms) backwards; --slide: 40%;
  animation: var(--anim, none);
}
#hud .dock > * { position: absolute; }
#hud .dock .label { font: 900 var(--fs-small)/1.2 var(--font-body); letter-spacing: .14em; text-transform: uppercase; text-shadow: var(--hint-shadow); }
/* the stand for the bichito: beside the score, at the right edge (mascot.js moves it here on Star Power, on every 100
   streak and near the end) */
#hud .dock > .mascot-anchor { display: block; left: auto; right: 1rem; top: auto; bottom: 10.3rem; width: 5.6rem; height: 5rem; }

/* ---- crowd tube along the rail (the fill rises toward the far end of the highway) + its head (Crowd + chip) */
#hud .rock {
  --tube-h: calc(19rem * var(--rail-sec));
  left: calc(var(--rail-k) * (var(--cl-h) - 12.3rem) + 1.15rem); top: auto; bottom: 12.3rem; width: 1.3rem; height: var(--tube-h);
  translate: -50% 50%; rotate: var(--rail-tilt);
}
#hud .rock__tube { top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; border-radius: var(--r-pill); }
#hud #rock-fill { transform: translateY(calc((1 - var(--rock, .5)) * 100%)); }
#hud #rock-fill > i {
  transform: translateY(calc((var(--rock, .5) - 1) * 100%)); border-width: .16rem;
  background-image:
    linear-gradient(90deg, rgb(255 255 255 / .4) 0 .22rem, transparent .22rem),
    repeating-linear-gradient(0deg, transparent 0 calc(100% / 16 - .14rem), var(--night) 0 calc(100% / 16)),
    linear-gradient(0deg, var(--red) 0 25%, var(--ember) 25% 50%, var(--f3) 50% 75%, var(--gold) 75%);
}
#hud .rock__line { left: .1rem; right: .1rem; top: auto; border-left: 0; border-top: .14rem dashed var(--red); }
#hud .rock__line--fail { bottom: 25%; }
#hud .rock__line--fire { bottom: 75%; border-top: .18rem solid var(--gold); }
#hud #rock-needle {
  left: 50%; bottom: 0; width: 1.45rem; height: 1.45rem; translate: -50% calc(50% - var(--rock, .5) * var(--tube-h));
  box-shadow: 0 .14rem 0 var(--ink), 0 0 0 .14rem rgb(246 240 228 / .35);
}
/* printed scale on the tube's outer side: eight ticks, the 25 % / 75 % ones longer */
#hud .rock::after {
  content: ''; position: absolute; left: calc(100% + .18rem); top: 0; width: .5rem; height: 100%; pointer-events: none;
  background:
    linear-gradient(var(--ivory), var(--ivory)) 0 75% / .5rem .16rem no-repeat,
    linear-gradient(var(--ivory), var(--ivory)) 0 25% / .5rem .16rem no-repeat,
    repeating-linear-gradient(180deg, transparent 0 calc(var(--tube-h) / 8 - .12rem), rgb(246 240 228 / .5) 0 calc(var(--tube-h) / 8)) 0 0 / .28rem 100% no-repeat;
}
/* the bulb: the reservoir at the strike-line end, upright (counter-rotated) with a flickering gold flame */
#hud .rock__bulb {
  display: block; left: 50%; top: auto; bottom: -1.75rem; width: 2.5rem; height: 2.5rem; translate: -50% 0;
  rotate: calc(-1 * var(--rail-tilt));
  background: radial-gradient(circle at 38% 30%, #FFE7A0 0, #FF8A1C 38%, #E8202F 72%, #A50F1F 100%);
  box-shadow: inset 0 0 0 .17rem rgb(246 240 228 / .8), 0 0 1.2rem .2rem rgb(255 110 40 / .4), var(--drop-s);
}
#hud .rock__bulb::after {
  content: ''; position: absolute; left: 22%; right: 22%; top: 14%; bottom: 20%; pointer-events: none;
  background: url(../../img/svg/flame-gold-rim.svg) 50% 100% / contain no-repeat; transform-origin: 50% 100%;
  --loop: ff-flicker 380ms ease-in-out infinite alternate;
  animation: var(--loop, none);
}
#hud .rock:has(.rock-fill.high) .rock__bulb { box-shadow: inset 0 0 0 .17rem rgb(246 240 228 / .85), 0 0 1.6rem .45rem rgb(255 190 60 / .55), var(--drop-s); }
#hud .rock:has(.rock-fill.high) .rock__bulb::after { scale: 1.22; background-image: url(../../img/svg/flame-blaze-rim.svg); }
#hud .rock:has(.rock-fill.low) .rock__bulb {
  background: radial-gradient(circle at 38% 30%, #6B5B95 0, var(--night-3) 55%, var(--night-2) 100%);
  box-shadow: inset 0 0 0 .17rem rgb(246 240 228 / .45), var(--drop-s);
}
#hud .rock:has(.rock-fill.low) .rock__bulb::after { background-image: url(../../img/svg/flame-off-rim.svg); animation: none; }
#hud .rock:has(.rock-fill.low) .rock__tube { box-shadow: 0 0 0 .14rem rgb(255 70 80 / .75), 0 0 1rem .2rem rgb(232 32 47 / .55), var(--drop-s); }
#hud .rock__head {
  left: calc(var(--rail-k) * (var(--cl-h) - 19.6rem) + var(--cl-gap) - .4rem); bottom: 19.6rem; right: 7.2rem;
  display: flex; align-items: center; gap: .4rem; margin: 0; min-width: 0;
}
#hud .rock__cap { position: static; display: flex; align-items: center; gap: .25rem; letter-spacing: .14em; }
#hud .rock__cap::before { content: ''; width: .95rem; height: 1.1rem; background: url(../../img/svg/flame-flame-rim.svg) 50% 100% / contain no-repeat; }
#hud .rock__chip { position: static; display: block; padding: .05rem .55rem; }
#hud .rock__chip[data-level="low"] { -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill; }

/* ---- Star Power lanterns. Caps, cord and tassels are the bar's own background (static), so each lantern body (an i
   with overflow: clip) only holds the cobalt light (::before, translateY by --f) and the ribs (::after). */
#hud .dock__star {
  left: calc(var(--rail-k) * (var(--cl-h) - 15.1rem) + var(--cl-gap)); bottom: 15.1rem; right: .8rem;
  display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto auto; align-items: center; justify-content: start;
  column-gap: .6rem; row-gap: .2rem; min-width: 0;
}
#hud .dock__star::before {
  content: 'Star Power'; grid-area: 1 / 2; align-self: end; white-space: nowrap;
  font: 900 var(--fs-small)/1.15 var(--font-body); letter-spacing: .08em; text-transform: uppercase; color: var(--cobalt-hi);
  text-shadow: var(--hint-shadow);
}
#hud .starbar {
  grid-area: 1 / 1 / 3 / 2; display: grid; grid-template-columns: repeat(4, 2.2rem); column-gap: .45rem;
  padding: .42rem 0 .5rem;
  background:
    linear-gradient(90deg, transparent .4rem, var(--ink) .4rem .52rem, var(--gold) .52rem 1.68rem, var(--ink) 1.68rem 1.8rem, transparent 1.8rem) 0 .18rem / 2.65rem .44rem repeat-x,
    linear-gradient(90deg, transparent .4rem, var(--ink) .4rem .52rem, var(--gold) .52rem 1.68rem, var(--ink) 1.68rem 1.8rem, transparent 1.8rem) 0 calc(100% - .2rem) / 2.65rem .44rem repeat-x,
    linear-gradient(90deg, transparent 1rem, var(--red) 1rem 1.2rem, transparent 1.2rem) 0 100% / 2.65rem .3rem repeat-x,
    linear-gradient(90deg, transparent 1.02rem, var(--ink-2) 1.02rem 1.18rem, transparent 1.18rem) 0 0 / 2.65rem .3rem repeat-x;
}
#hud .starbar i {
  position: relative; width: 2.2rem; height: 2.85rem; border: var(--ow-s) solid var(--ink); border-radius: 46% / 36%;
  background: radial-gradient(ellipse at 50% 45%, #54468A 0, var(--night-4) 55%, var(--night-3) 100%);
}
#hud .starbar i::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse 80% 60% at 50% 45%, #FFFFFF 0, #DCE4FF 20%, #7C8EFF 58%, #2340FF 100%);
  box-shadow: inset 0 .18rem 0 rgb(255 255 255 / .75);
  transform: translateY(calc(100% - var(--f, 0%)));
}
#hud .starbar i::after {
  background:
    radial-gradient(circle at 50% 50%, transparent 0 .38rem, rgb(246 240 228 / .55) .4rem .5rem, transparent .52rem),
    url(../../img/svg/lantern-ribs.svg) 0 0 / 100% 100% no-repeat;
}
#hud .starbar i.lit, #hud.star .starbar i { box-shadow: 0 0 .9rem .2rem rgb(124 142 255 / .6), 0 0 0 .12rem var(--cobalt-hi); }
#hud #star-hint {
  grid-area: 2 / 2; justify-self: start; align-self: start; margin: 0; max-width: 100%;
  font: 900 var(--fs-small)/1.2 var(--font-body); color: var(--ivory-2); text-shadow: var(--hint-shadow); text-wrap: balance;
}
#hud #star-hint:empty { display: none; }
#hud .starbar.ready ~ #star-hint, #hud.star #star-hint {
  padding: .12rem .55rem; border: var(--ow-s) solid var(--ink); border-radius: var(--r-pill);
  background: var(--cobalt); color: var(--ivory); box-shadow: 0 .14rem 0 var(--ink); text-shadow: none;
  -webkit-text-stroke: max(1.5px, .1em) var(--ink); paint-order: stroke fill; white-space: nowrap;
}
#hud.star #star-hint { background: var(--grad-blue); color: var(--ink); -webkit-text-stroke: 0; }
#hud #star-hint kbd { margin: 0 0 0 .2rem; -webkit-text-stroke: 0; vertical-align: .05rem; }

/* ---- score: the label and the last jump on one line, the number under them (big, ink rim, cobalt drop) */
#hud .dock__score {
  left: calc(var(--rail-k) * (var(--cl-h) - 10.3rem) + var(--cl-gap)); bottom: 10.3rem; right: .8rem;
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: end; column-gap: .6rem; min-width: 0;
}
#hud .dock__score .label { grid-area: 1 / 1; color: var(--ivory-2); }
#hud #hud-delta {
  grid-area: 1 / 2; justify-self: start; margin: 0; min-height: 1.2em; text-align: left;
  font: 900 var(--fs-small)/1.2 var(--font-num); letter-spacing: .02em; color: var(--f2); text-shadow: var(--hint-shadow);
}
#hud #hud-delta.pop2 { animation: ffh-delta var(--t-base) var(--ease-out), ffh-delta-out 200ms linear 600ms forwards; }
#hud #hud-score {
  grid-area: 2 / 1 / 3 / 3; margin: 0; overflow: hidden; white-space: nowrap;
  font: 700 2.85rem/1.04 var(--font-num); letter-spacing: .02em; color: var(--ivory);
  -webkit-text-stroke: max(2px, .1em) var(--ink); paint-order: stroke fill;
  text-shadow: .05em .065em 0 var(--cobalt);
}
@keyframes ffh-delta { from { opacity: 0; transform: translateY(.3rem); } }
@keyframes ffh-delta-out { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { #hud #hud-delta.pop2 { animation: ffh-delta-out 200ms linear 600ms forwards; } }

/* ---- the streak drum beside the strike line (taiko skin, red band with gold rivets, fire ring) + COMBO / best / player */
#hud .dock__streak {
  left: calc(var(--rail-k) * (var(--cl-h) - 2.1rem) + var(--cl-gap) + .5rem); bottom: 2.1rem; right: .8rem;
  display: flex; align-items: center; gap: .75rem; min-width: 0;
}
#hud .mult.medal {
  flex: none;
  background: radial-gradient(circle, transparent 0 2.78rem, var(--ink) 2.8rem 3.72rem, transparent 3.74rem);
  border-radius: 50%;
}
#hud .mult .ring-bg { stroke: var(--night-3); stroke-width: 3.6; }
#hud .mult .ring { stroke-width: 3.6; }
#hud .mult.medal::before {
  inset: 1.06rem;                                    /* radius 2.74rem + its rim = the ring's inner edge (2.89rem) */
  background:
    radial-gradient(circle at 40% 32%, rgb(255 255 255 / .9) 0, rgb(255 255 255 / 0) 1.2rem),
    radial-gradient(circle, transparent 0 2.08rem, var(--ink) 2.1rem 2.22rem, transparent 2.24rem),
    radial-gradient(circle, var(--paper) 0 1rem, var(--ivory) 1.6rem, var(--ivory-2) 2.1rem, transparent 2.12rem),
    radial-gradient(circle, #FF4650 2.1rem, var(--red) 2.4rem, var(--red-deep) 2.74rem);
  box-shadow: 0 0 0 var(--ow-s) var(--ink), 0 .2rem 0 var(--ink);
}
/* the drum's rivets: a dotted gold ring along the red band */
#hud .mult.medal::after {
  content: ''; position: absolute; inset: 1.3rem; border-radius: 50%; pointer-events: none;
  border: .17rem dotted var(--gold-hi);
}
#hud #hud-streak {
  margin: 0; font: 700 2.5rem/1 var(--font-num); letter-spacing: .01em; color: var(--ink);
  -webkit-text-stroke: max(2px, .08em) var(--paper); paint-order: stroke fill;
}
#hud #hud-streak[data-len="3"] { font-size: 2.05rem; }
#hud #hud-streak[data-len="4"] { font-size: 1.6rem; }
#hud .mult > #hud-mult { left: -.55rem; bottom: -.25rem; box-shadow: inset 0 0 0 .2rem var(--red), inset 0 0 0 .32rem rgb(246 240 228 / .8), 0 .2rem 0 var(--ink); }
#hud.star .mult > #hud-mult { box-shadow: inset 0 0 0 .2rem var(--cobalt), inset 0 0 0 .32rem rgb(246 240 228 / .8), 0 .2rem 0 var(--ink); }
#hud .mult__max {
  left: auto; right: -.35rem; bottom: -.15rem; top: auto; z-index: 3; overflow: hidden; rotate: 6deg;
  box-shadow: 0 .14rem 0 var(--ink);
}
#hud .mult.max .mult__max::after {
  content: ''; position: absolute; inset: 0 auto 0 0; width: 60%; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / .85), transparent);
  --loop: ff-shine 3.2s var(--ease-inout) infinite;
  animation: var(--loop, none);
}
#hud .dock__streak-info { display: grid; gap: .25rem; min-width: 0; justify-items: start; }
#hud .dock__streak-info .label {
  font: 700 1.55rem/1 var(--font-display); font-stretch: 85%; letter-spacing: .04em; color: var(--f2);
  -webkit-text-stroke: max(1.5px, .09em) var(--ink); paint-order: stroke fill; text-shadow: .04em .07em 0 var(--red-deep);
}
#hud #hud-streak-best { margin: 0; font: 800 var(--fs-small)/1.2 var(--font-body); color: var(--ivory-2); text-shadow: var(--hint-shadow); }
#hud #hud-streak-best:empty { display: none; }
#hud .dock__plate {
  position: relative; left: auto; top: auto; width: auto; height: auto; max-width: 100%; margin-top: .2rem;
  gap: .4rem; padding: .12rem .7rem .12rem .15rem; rotate: -2deg; font-size: var(--fs-small);
}
#hud .dock__plate .avatar { width: 1.7rem; height: 1.7rem; font-size: var(--fs-small); }
#hud #hud-name { font: 900 var(--fs-small)/1.15 var(--font-body); }

/* every hit the drum's number jumps; the drum itself stamps on a milestone (10, 25, every 50: hud-view flips the
   classes) and shudders when a long streak breaks. The ×N hanko pops whenever its value changes (the animation name
   follows data-text, so no class is needed). */
#hud #hud-streak.pop  { animation: ffh-num-a 150ms var(--ease-out); }
#hud #hud-streak.pop2 { animation: ffh-num-b 150ms var(--ease-out); }
@keyframes ffh-num-a { from { transform: scale(1.16); } }
@keyframes ffh-num-b { from { transform: scale(1.16); } }
#hud .dock .mult.tier  { animation: ffh-tier-a 560ms var(--spring-pop); }
#hud .dock .mult.tier2 { animation: ffh-tier-b 560ms var(--spring-pop); }
@keyframes ffh-tier-a { from { transform: scale(1.3) rotate(-8deg); } }
@keyframes ffh-tier-b { from { transform: scale(1.3) rotate(8deg); } }
#hud .dock .mult.crack, #hud .dock .mult.crack2 { animation: ffh-crack 340ms var(--ease-out); }
#hud .dock .mult.crack2 { animation-name: ffh-crack-b; }
@keyframes ffh-crack { 0% { transform: translateX(-.3rem) rotate(-4deg); } 30% { transform: translateX(.25rem) rotate(3deg); } 60% { transform: translateX(-.12rem); } }
@keyframes ffh-crack-b { 0% { transform: translateX(.3rem) rotate(4deg); } 30% { transform: translateX(-.25rem) rotate(-3deg); } 60% { transform: translateX(.12rem); } }
#hud #hud-mult[data-text="×2"] { animation: ffh-hanko-2 360ms var(--spring-pop); }
#hud #hud-mult[data-text="×3"] { animation: ffh-hanko-3 360ms var(--spring-pop); }
#hud #hud-mult[data-text="×4"] { animation: ffh-hanko-4 360ms var(--spring-pop); }
#hud #hud-mult[data-text="×6"] { animation: ffh-hanko-6 360ms var(--spring-pop); }
#hud #hud-mult[data-text="×8"] { animation: ffh-hanko-8 360ms var(--spring-pop); }
@keyframes ffh-hanko-2 { from { transform: scale(1.6); } }
@keyframes ffh-hanko-3 { from { transform: scale(1.6); } }
@keyframes ffh-hanko-4 { from { transform: scale(1.6); } }
@keyframes ffh-hanko-6 { from { transform: scale(1.6); } }
@keyframes ffh-hanko-8 { from { transform: scale(1.6); } }
@media (prefers-reduced-motion: reduce) {
  #hud #hud-streak.pop, #hud #hud-streak.pop2, #hud .dock .mult.tier, #hud .dock .mult.tier2,
  #hud .dock .mult.crack, #hud .dock .mult.crack2, #hud #hud-mult[data-text] { animation: none; }
}

/* Star Power on the cluster: cobalt drum band and ring, cobalt-hi lyric ring */
#hud.star .mult .ring { stroke: var(--cobalt-hi); }
#hud.star .mult.medal::before {
  background:
    radial-gradient(circle at 40% 32%, rgb(255 255 255 / .9) 0, rgb(255 255 255 / 0) 1.2rem),
    radial-gradient(circle, transparent 0 2.08rem, var(--ink) 2.1rem 2.22rem, transparent 2.24rem),
    radial-gradient(circle, #FFFFFF 0 1rem, #E6ECFF 1.6rem, #B9C8FF 2.1rem, transparent 2.12rem),
    radial-gradient(circle, var(--cobalt-hi) 2.1rem, var(--cobalt) 2.4rem, var(--cobalt-deep) 2.74rem);
}
#hud.star .lyrics {
  box-shadow: inset 0 0 0 .2rem var(--cobalt-hi), 0 0 1.4rem .2rem rgb(124 142 255 / .4), .3rem .35rem 0 0 var(--cobalt), .3rem .35rem 0 var(--ow-s) var(--ink);
}

/* =========================================================================================== screen-edge light
   i x2 = Star Power (cobalt, breathing on the beat); ::before = danger (red, the crowd is leaving); ::after = the gold
   flash of a streak milestone (hud-view flips .tier / .tier2 on #edge-glow). Opacity only. */
#hud #edge-glow { z-index: -1; pointer-events: none; }
#hud #edge-glow i { opacity: 0; transition: opacity 260ms linear; }
#hud #edge-glow i:first-child { background: linear-gradient(90deg, rgb(35 64 255 / .95), rgb(124 142 255 / .4) 55%, transparent); }
#hud #edge-glow i:last-child { background: linear-gradient(270deg, rgb(35 64 255 / .95), rgb(124 142 255 / .4) 55%, transparent); }
#hud.star #edge-glow i {
  opacity: .45;
  --loop: ffh-edge var(--beat) ease-in-out var(--beat-delay) infinite alternate;
  animation: var(--loop, none);
}
@keyframes ffh-edge { from { opacity: .45; } to { opacity: .18; } }
#hud #edge-glow::before, #hud #edge-glow::after { content: ''; position: absolute; inset: 0; opacity: 0; pointer-events: none; }
#hud #edge-glow::before {
  background: linear-gradient(90deg, rgb(232 32 47 / .55), transparent 2.6vw), linear-gradient(270deg, rgb(232 32 47 / .55), transparent 2.6vw);
  transition: opacity 260ms linear;
}
#hud.danger #edge-glow::before {
  opacity: .8;
  --loop: ffh-danger var(--beat) ease-in-out var(--beat-delay) infinite alternate;
  animation: var(--loop, none);
}
@keyframes ffh-danger { from { opacity: .85; } to { opacity: .35; } }
#hud #edge-glow::after {
  background: linear-gradient(90deg, rgb(255 210 58 / .75), rgb(255 138 28 / .25) 1.6vw, transparent 3.2vw),
              linear-gradient(270deg, rgb(255 210 58 / .75), rgb(255 138 28 / .25) 1.6vw, transparent 3.2vw);
}
#hud #edge-glow.tier::after  { animation: ffh-flash-a 760ms var(--ease-out); }
#hud #edge-glow.tier2::after { animation: ffh-flash-b 760ms var(--ease-out); }
@keyframes ffh-flash-a { from { opacity: 1; } }
@keyframes ffh-flash-b { from { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { #hud #edge-glow.tier::after, #hud #edge-glow.tier2::after { animation-duration: 400ms; } }

/* =========================================================================================== combo toasts (over the cluster)
   A stamp above the cluster's top row, left of the bichito's stand; never over the video (it ends at 53vh). */
#hud #combo {
  left: calc(var(--rail-x) - var(--rail-k) * 24rem + var(--cl-gap) - .4rem); right: auto; top: auto; bottom: 22.3rem;
  max-width: min(19rem, 100vw - var(--rail-x) + var(--rail-k) * 24rem - var(--cl-gap) - 7.4rem);
  display: flex; align-items: center; gap: .55rem; letter-spacing: .01em; white-space: nowrap; rotate: -3deg; transform-origin: 0 100%;
}
#hud #combo.pop { animation: ffh-combo 340ms var(--spring-pop) backwards, ff-fade-out 220ms linear 1.7s forwards; }
@keyframes ffh-combo { from { opacity: 0; transform: scale(.5) translateY(.8rem); } 30% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { #hud #combo.pop { animation: ff-fade 80ms linear backwards, ff-fade-out 220ms linear 1.7s forwards; } }
#hud #combo[data-kind="streak"] { padding-left: 1.2rem; background: var(--grad-gold); }
#hud #combo[data-kind="streak"]::after, #hud #combo[data-kind="star"]::after {
  content: ''; flex: none; order: -1; width: 1.6rem; height: 2rem; background: url(../../img/svg/flame-blaze-rim.svg) 50% 100% / contain no-repeat;
}
#hud #combo[data-kind="star"]::after { background-image: url(../../img/svg/flame-blue-rim.svg); }
#hud #combo[data-kind="star"] { padding-left: 1.2rem; background: var(--grad-blue); }
#hud #combo[data-kind="phrase"] { box-shadow: inset 0 0 0 .2rem var(--cobalt-hi), 0 .25rem 0 var(--ink); }
#hud #combo[data-kind="mult"] { box-shadow: inset 0 0 0 .2rem var(--red), inset 0 0 0 .32rem rgb(246 240 228 / .75), 0 .25rem 0 var(--ink); }

/* =========================================================================================== judgement words (73vh)
   Pool of three p.judge, look from components.css. One-shot per word; ends invisible. --jx is clamped by hud-view
   (offsetWidth x 1.5), so nothing here may paint wider than the node box or scale above 1.5. */
#hud .judge { opacity: 0; z-index: 2; }
#hud .judge.on { opacity: 1; animation: ffh-judge 300ms var(--spring-pop) backwards, ffh-judge-out 160ms linear 380ms forwards; }
#hud .judge.on[data-kind="perfect"] { animation: ffh-judge-gold 340ms var(--spring-pop) backwards, ffh-judge-out 170ms linear 400ms forwards; }
#hud .judge.on[data-kind="miss"] { animation: ffh-miss 260ms var(--ease-out) backwards, ffh-judge-out 160ms linear 380ms forwards; }
/* superseded by a newer word while still visible (hud-view: .gone): it drops to half and is gone in 70ms, so two words
   never stack (starting at .5 also covers a word already in its own fade-out) */
#hud .judge.on.gone, #hud .judge.on.gone[data-kind] { animation: ffh-judge-gone 70ms linear forwards; }
@keyframes ffh-judge-gone { from { opacity: .5; } to { opacity: 0; } }
#hud .judge[data-kind="good"] { color: var(--ivory); }
#hud .judge[data-kind="great"] { background-size: 92% 92%; background-position: 50% 30%; }
@keyframes ffh-judge { from { opacity: 0; transform: scale(1.5); } }
@keyframes ffh-judge-gold { 0% { opacity: 0; transform: scale(1.5); } 30% { opacity: 1; } }
@keyframes ffh-miss { from { opacity: 0; transform: translateY(-.6rem) scale(1.18); } }
@keyframes ffh-judge-out { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  #hud .judge.on, #hud .judge.on[data-kind] { animation: ff-fade 80ms linear backwards, ffh-judge-out 160ms linear 380ms forwards; }
  #hud .judge.on.gone, #hud .judge.on.gone[data-kind] { animation: ffh-judge-gone 70ms linear forwards; }
}

/* +N after a full sustain (hud-view.hold): pool of three p.hold-pts in #hold-host. hud-view writes the text, --hx (fret
   centre, clamped clear of Aisu like the words) and --hy (strike line) in px; re-appending the node restarts the
   one-shot, which ends invisible. The number sits on the drum's back rim (above the key letter) and floats up ~1.6vh,
   fading out by the judgement band. After a long sustain, data-label ("Nice hold!") puts a small tilted sticker on top in
   the words' look (ivory, ink rim, hard red drop). It rides with the number, not in the band: the next note's word
   would cover it at once. Scale tops out at 1.08 (HOLD_POP in hud-view). Transform and opacity only. */
#hud #hold-host { left: 0; top: 0; width: 100%; height: 0; --hold-safe-l: var(--safe-hold-l); }
/* registered so getComputedStyle gives px: hud-view clamps the +N right of it (never over Aisu's legs + halo) */
@property --hold-safe-l { syntax: '<length>'; inherits: false; initial-value: 0px; }
#hud .hold-pts {
  position: absolute; z-index: 2; left: var(--hx, 50vw); top: calc(var(--hy, 86vh) - 4.6vh); translate: -50% -100%; margin: 0;
  display: flex; flex-direction: column; align-items: center; transform-origin: 50% 100%;
  opacity: 0; white-space: nowrap; font: 700 2rem/1 var(--font-num); letter-spacing: .02em; color: var(--f2);
  -webkit-text-stroke: max(2px, .13em) var(--ink); paint-order: stroke fill; text-shadow: 0 .09em 0 var(--ink);
}
#hud .hold-pts[data-label]:not([data-label=""])::before {
  content: attr(data-label); content: attr(data-label) / "";
  margin-bottom: .1rem; rotate: -5deg;
  font: 700 .95rem/1 var(--font-display); font-stretch: 100%; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ivory); -webkit-text-stroke: max(1.5px, .16em) var(--ink); text-shadow: 0 .12em 0 var(--red);
}
#hud .hold-pts.on { animation: ffh-pts 820ms var(--ease-out) forwards; }
#hud .hold-pts.on[data-label]:not([data-label=""]) { animation-duration: 1s; }
@keyframes ffh-pts {
  0% { opacity: 0; transform: translateY(.5rem) scale(.6); }
  16% { opacity: 1; transform: translateY(0) scale(1.08); }
  62% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-1.6vh) scale(1); }
}
@keyframes ffh-pts-calm { 0% { opacity: 0; } 12%, 62% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { #hud .hold-pts.on { animation: ffh-pts-calm 760ms linear forwards; } }

/* EARLY / LATE pill (77.5vh): pops under the newest word; the text stays 'Early' / 'Late' (uppercase from CSS) */
#hud #timing { z-index: 2; padding: .05rem .6rem; white-space: nowrap; letter-spacing: .1em; }
#hud #timing.early::before { content: '‹ '; content: '‹ ' / ""; }
#hud #timing.late::after { content: ' ›'; content: ' ›' / ""; }
#hud #timing.on { animation: ffh-timing 220ms var(--spring-pop) backwards; }
@keyframes ffh-timing { from { opacity: 0; transform: translateY(-.35rem) scale(.7); } }
@media (prefers-reduced-motion: reduce) { #hud #timing.on { animation: ff-fade 80ms linear backwards; } }

/* =========================================================================================== countdown: sunburst stamp
   The numeral (fire gradient fill, ink rim, cobalt plate) sits on a lemon disc with static gold rays at 50 %. Each new
   number restarts the stamp: the animation name follows data-text (setReg), so no class toggling is needed. The stamp
   starts at 1.3 (not ff-stamp's 1.9) and the rays end at 5.6rem: at 1280x720 the guitar's headstock + halo is ~7.2rem
   from the centre, and the rays scale with the numeral. */
#hud #countdown {
  z-index: 3; isolation: isolate; margin: 0; padding-top: .1em;
  font: 700 var(--fs-count)/1 var(--font-display); font-stretch: 100%; color: var(--f3);
  -webkit-text-stroke: .11em var(--ink); paint-order: stroke fill;
  text-shadow: .05em .07em 0 var(--cobalt);
}
#hud #countdown::after {
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; display: grid; place-items: center; padding: inherit; pointer-events: none;
  -webkit-text-stroke: 0; color: transparent; text-shadow: none;
  background: var(--grad-fire); -webkit-background-clip: text; background-clip: text;
}
#hud #countdown::before {
  content: ''; position: absolute; inset: -1.6rem; z-index: -1; border-radius: 50%; pointer-events: none;
  background:
    radial-gradient(circle, #FFF6C8 0 2.3rem, var(--f2) 2.95rem, var(--ink) 2.97rem 3.2rem, transparent 3.22rem),
    radial-gradient(circle, transparent 0 3.55rem, rgb(255 210 58 / .9) 3.57rem 3.72rem, transparent 3.74rem),
    repeating-conic-gradient(from -4deg, rgb(255 210 58 / .75) 0 7deg, transparent 7deg 22.5deg);
  -webkit-mask: radial-gradient(closest-side, #000 70%, transparent 98%); mask: radial-gradient(closest-side, #000 70%, transparent 98%);
}
#hud #countdown[data-text="3"] { --anim: ffh-stamp-3 420ms var(--spring-stamp) backwards; animation: var(--anim, none); }
#hud #countdown[data-text="2"] { --anim: ffh-stamp-2 420ms var(--spring-stamp) backwards; animation: var(--anim, none); }
#hud #countdown[data-text="1"] { --anim: ffh-stamp-1 420ms var(--spring-stamp) backwards; animation: var(--anim, none); }
@keyframes ffh-stamp-3 { 0% { opacity: 0; transform: scale(1.3) rotate(-10deg); } 14% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes ffh-stamp-2 { 0% { opacity: 0; transform: scale(1.3) rotate(10deg); } 14% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes ffh-stamp-1 { 0% { opacity: 0; transform: scale(1.3) rotate(-10deg); } 14% { opacity: 1; } 100% { opacity: 1; transform: none; } }
body.lowfx #hud #countdown::before {
  background: radial-gradient(circle, #FFF6C8 0 2.3rem, var(--f2) 2.95rem, var(--ink) 2.97rem 3.2rem, transparent 3.22rem);
}

/* =========================================================================================== endcap (top band, end of song) */
#hud #endcap {
  z-index: 3; margin: 0; padding-block: .05em .1em;
  font-size: var(--fs-verdict); font-stretch: 100%; line-height: 1.1;
  --tk-fill: var(--ivory); --tk-shadow: var(--cobalt); --tk-drop: .07em;
}
#hud #endcap::before { text-shadow: .05em .07em 0 var(--tk-shadow); }
#hud #endcap::after {
  content: attr(data-text); content: attr(data-text) / "";
  position: absolute; inset: 0; padding: inherit; pointer-events: none;
  -webkit-text-stroke: 0; color: transparent;
  background: var(--endcap-fill, var(--grad-fire)) content-box; -webkit-background-clip: text; background-clip: text;
}
#hud #endcap[data-kind="fc"] {
  --endcap-fill: var(--grad-gold); --tk-shadow: var(--red);
  padding-top: .62em; background: url(../../img/svg/crown.svg) 50% 0 / 1.05em auto no-repeat;
}
#hud #endcap[data-kind="fail"] { --endcap-fill: var(--grad-ash); --tk-shadow: var(--night-4); font-size: 3.4rem; }
/* a small stamp (1.12, not ff-stamp's 1.9): the top band ends at Aisu's head, so the word must not grow into it */
#hud #endcap.on { --anim: ffh-endcap var(--t-ceremony) var(--spring-stamp) backwards; animation: var(--anim, none); }
@keyframes ffh-endcap { 0% { opacity: 0; transform: scale(1.12) rotate(-5deg); } 18% { opacity: 1; } 100% { opacity: 1; transform: none; } }

/* =========================================================================================== tight cluster
   hud-view sets #hud.tight when less than 19rem are left between the rail and the window's right edge (5 lanes on
   16:10, 4:3 windows): a 5.6rem drum without the best line and the plate, a smaller score, no lantern label. */
#hud.tight { --cl-gap: 2.4rem; }
#hud.tight .dock__plate, #hud.tight #hud-streak-best, #hud.tight .dock__star::before { display: none; }
#hud.tight .dock__star { grid-template-columns: auto; justify-items: start; }
#hud.tight #star-hint { grid-area: 3 / 1; }
#hud.tight #hud-score { font-size: 2.2rem; }
#hud.tight .dock__streak-info .label { font-size: 1.2rem; }
#hud.tight .mult.medal { width: 5.6rem; height: 5.6rem; background: radial-gradient(circle, transparent 0 2.02rem, var(--ink) 2.04rem 2.74rem, transparent 2.76rem); }
#hud.tight .mult.medal::before {
  inset: .78rem;
  background:
    radial-gradient(circle, transparent 0 1.52rem, var(--ink) 1.54rem 1.64rem, transparent 1.66rem),
    radial-gradient(circle, var(--paper) 0 .7rem, var(--ivory) 1.1rem, var(--ivory-2) 1.54rem, transparent 1.56rem),
    radial-gradient(circle, #FF4650 1.52rem, var(--red) 1.75rem, var(--red-deep) 2.02rem);
}
#hud.tight.star .mult.medal::before {
  background:
    radial-gradient(circle, transparent 0 1.52rem, var(--ink) 1.54rem 1.64rem, transparent 1.66rem),
    radial-gradient(circle, #FFFFFF 0 .7rem, #E6ECFF 1.1rem, #B9C8FF 1.54rem, transparent 1.56rem),
    radial-gradient(circle, var(--cobalt-hi) 1.52rem, var(--cobalt) 1.75rem, var(--cobalt-deep) 2.02rem);
}
#hud.tight .mult.medal::after { inset: .96rem; border-width: .14rem; }
#hud.tight #hud-streak { font-size: 1.9rem; }
#hud.tight #hud-streak[data-len="3"] { font-size: 1.55rem; }
#hud.tight #hud-streak[data-len="4"] { font-size: 1.2rem; }
#hud.tight .mult > #hud-mult { width: 2.6rem; height: 2.6rem; font-size: 1.4rem; }
#hud.tight .starbar { grid-template-columns: repeat(4, 1.9rem); column-gap: .35rem; background-size: 2.25rem .42rem, 2.25rem .42rem, 2.25rem .3rem, 2.25rem .3rem; }
#hud.tight .starbar i { width: 1.9rem; height: 2.45rem; }

/* =========================================================================================== the bichito's spots
   home (bottom-left, base.css), the left strip under the song plate (hud-side) and the stand on the cluster (dock).
   Narrow windows (aspect < 1.5) have no left strip: only the stand remains (base.css hides the home anchor). */
#hud > .mascot-anchor[data-mascot-anchor="hud-side"] { left: 1rem; top: calc(36vh - 5rem); width: 5.4rem; height: 5rem; }
@media (max-aspect-ratio: 149/100) {
  #hud > .mascot-anchor[data-mascot-anchor="hud-side"] { display: none; }
}

/* =========================================================================================== the bichito on stage
   mascot.js sets data-mood (groove: streak >= 10; hype: >= 50 or Star Power) and data-ash (the crowd is leaving) on
   #bichito. The dances are beat loops on .sprite__body (2 beats / 1 beat, delay --beat-delay; mascot.js re-phases them
   with beat.align), pivoting on the feet; only while playing (in the pause the song stops) and never under reduced
   motion. The ash is the burnt bichito drawn over the pose (same bare canvas), swapped under a poof. */
#bichito .sprite__ash {
  position: absolute; display: none; max-width: none; pointer-events: none;
  left: calc(-151.2 * var(--u)); top: calc(-252.8 * var(--u)); width: calc(300 * var(--u)); height: calc(256 * var(--u));
}
#bichito[data-rim="none"][data-ash] .sprite__ash { display: block; }
#bichito[data-rim="none"][data-ash] .sprite__pose.on, #bichito[data-strip] .sprite__ash { opacity: 0; }
#bichito[data-anchor="hud-side"] .say { max-width: 7rem; font-size: var(--fs-small); padding: .35rem .6rem; }
@media (prefers-reduced-motion: no-preference) {
  body[data-screen="playing"] #bichito[data-mood="groove"] .sprite__body { animation: ffh-groove calc(var(--beat) * 2) linear var(--beat-delay) infinite; }
  body[data-screen="playing"] #bichito[data-mood="hype"] .sprite__body { animation: ffh-hype var(--beat) linear var(--beat-delay) infinite; }
}
@keyframes ffh-groove {
  0%, 100% { transform: rotate(-6deg) translateY(0) scale(1.08, .9); }
  10%      { transform: rotate(-5deg) translateY(-.35rem) scale(.95, 1.07); }
  22%      { transform: rotate(-3deg) translateY(0) scale(1.02, .98); }
  40%      { transform: rotate(-1deg) translateY(0) scale(1, 1); }
  50%      { transform: rotate(6deg) translateY(0) scale(1.08, .9); }
  60%      { transform: rotate(5deg) translateY(-.35rem) scale(.95, 1.07); }
  72%      { transform: rotate(3deg) translateY(0) scale(1.02, .98); }
  90%      { transform: rotate(1deg) translateY(0) scale(1, 1); }
}
@keyframes ffh-hype {
  0%, 100% { transform: rotate(0deg) translateY(0) scale(1.1, .88); }
  18%      { transform: rotate(-5deg) translateY(-.75rem) scale(.94, 1.08); }
  40%      { transform: rotate(4deg) translateY(-.95rem) scale(1, 1); }
  72%      { transform: rotate(0deg) translateY(0) scale(1.04, .96); }
}

/* =========================================================================================== HYPE (hype.js)
   #hud[data-hype] = the streak's tier: 1 warm (>= 10), 2 hot (>= 25), 3 fire (>= 50), 4 inferno (>= 100), 5 legend
   (>= 200, blue fire). The drum's number grows and burns (scale + a static colour); .mult__fire (built by hype.js) is a
   ring of fire behind the drum that beats with the song (transform / opacity only). The INFERNO! / LEGEND! stamps are
   #combo kinds. #bichito[data-heat] (mascot.js) dances harder. body.calmfx ("Reduced effects"), body.lowfx and reduced
   motion keep only the number's colour and size. */
#hud .mult.medal { isolation: isolate; }
#hud #hud-streak { transition: scale 260ms var(--spring-pop); }
#hud[data-hype="1"] #hud-streak { scale: 1.05; }
#hud[data-hype="2"] #hud-streak { scale: 1.1; color: var(--red-deep); }
#hud[data-hype="3"] #hud-streak, #hud[data-hype="4"] #hud-streak { scale: 1.15; color: var(--red); text-shadow: 0 0 .45rem rgb(255 138 28 / .9); }
#hud[data-hype="5"] #hud-streak { scale: 1.15; color: var(--cobalt); text-shadow: 0 0 .45rem rgb(124 142 255 / .95); }
#hud[data-hype] #hud-streak[data-len="3"], #hud[data-hype] #hud-streak[data-len="4"] { scale: 1.04; }
#hud .mult__fire {
  position: absolute; inset: -.55rem; z-index: -1; border-radius: 50%; pointer-events: none; opacity: 0;
  background: radial-gradient(circle closest-side, transparent 0 82%, rgb(255 236 150 / .95) 86%, rgb(255 138 28 / .85) 91%, rgb(232 32 47 / .5) 96%, transparent 100%);
  transition: opacity 300ms linear;
}
#hud[data-hype="2"] .mult__fire { opacity: .55; }
#hud[data-hype="3"] .mult__fire { opacity: .8; }
#hud[data-hype="4"] .mult__fire, #hud[data-hype="5"] .mult__fire { opacity: 1; }
#hud[data-hype="5"] .mult__fire {
  background: radial-gradient(circle closest-side, transparent 0 82%, rgb(236 242 255 / .95) 86%, rgb(124 142 255 / .9) 91%, rgb(35 64 255 / .55) 96%, transparent 100%);
}
#hud[data-hype] .mult__fire { --loop: ffh-burn var(--beat) ease-out var(--beat-delay) infinite; animation: var(--loop, none); }
@keyframes ffh-burn { from { transform: scale(1.07); } to { transform: scale(.97); } }
body.calmfx #hud .mult__fire { display: none; }

#hud #combo[data-kind="inferno"], #hud #combo[data-kind="legend"] {
  padding-left: 1.2rem; letter-spacing: .06em; background: linear-gradient(180deg, #FFD23A, #FF8A1C 45%, var(--red));
}
#hud #combo[data-kind="legend"] { background: var(--grad-blue); }
#hud #combo[data-kind="inferno"]::after, #hud #combo[data-kind="legend"]::after {
  content: ''; flex: none; order: -1; width: 1.6rem; height: 2rem; background: url(../../img/svg/flame-blaze-rim.svg) 50% 100% / contain no-repeat;
}
#hud #combo[data-kind="legend"]::after { background-image: url(../../img/svg/flame-blue-rim.svg); }
#hud #combo.pop:is([data-kind="inferno"], [data-kind="legend"]) { animation: ffh-stamp 460ms var(--spring-pop) backwards, ff-fade-out 260ms linear 2.1s forwards; }
@keyframes ffh-stamp { from { opacity: 0; transform: scale(1.7) rotate(-9deg); } 35% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  #hud #combo.pop:is([data-kind="inferno"], [data-kind="legend"]) { animation: ff-fade 80ms linear backwards, ff-fade-out 220ms linear 2.1s forwards; }
}

@media (prefers-reduced-motion: no-preference) {
  body[data-screen="playing"]:not(.calmfx) #bichito[data-mood="groove"]:is([data-heat="2"], [data-heat="3"]) .sprite__body { animation-duration: var(--beat); }
  body[data-screen="playing"]:not(.calmfx) #bichito[data-mood="hype"]:is([data-heat="4"], [data-heat="5"]) .sprite__body { animation-name: ffh-frenzy; }
}
@keyframes ffh-frenzy {
  0%, 100% { transform: rotate(0deg) translateY(0) scale(1.14, .84); }
  15%      { transform: rotate(-9deg) translateY(-1.15rem) scale(.9, 1.12); }
  35%      { transform: rotate(8deg) translateY(-1.4rem) scale(1, 1); }
  60%      { transform: rotate(-3deg) translateY(-.2rem) scale(1.02, .98); }
  80%      { transform: rotate(0deg) translateY(0) scale(1.08, .92); }
}

/* =========================================================================================== bichito rain (ui/rain.js)
   While you hit well the crowd throws bichitos from the ceiling (a 1-3 drizzle with the heat of your Perfects, a 5-8
   shower on every 25 / Perfect run / Star Power, the full rain on every 100), over the whole screen: the owner wants
   them everywhere, also over the highway and this HUD ("es parte del juego"), so the layer sits on top of the HUD
   (z 6; the pause card and the #fx mascot stay above it). rain.js animates only transform / opacity with WAAPI (x on
   .rain__drop, y + squash on .rain__fall, spin on .rain__bug) and sets each drop's size (--rw / --rh) and z-index (the
   big, near ones in front) when it starts. Idle drops are display:none; .rain__fall rests at opacity 0 so a finished
   drop never flashes. Off under reduced motion, "Reduced effects" (body.calmfx) and body.lowfx. */
#hud > .rain { inset: 0; z-index: 6; overflow: hidden; pointer-events: none; contain: strict; }
#hud .rain__drop { position: absolute; left: 0; top: 0; width: var(--rw, 7rem); height: var(--rh, 6rem); display: none; will-change: transform, opacity; }
#hud .rain__drop.on { display: block; }
#hud .rain__fall { position: absolute; inset: 0; opacity: 0; transform-origin: 50.375% 95.22%; will-change: transform, opacity; }
#hud .rain__bug { position: absolute; inset: 0; display: block; width: 100%; height: 100%; transform-origin: 50% 50%; will-change: transform; user-select: none; }
body.calmfx #hud > .rain, body.lowfx #hud > .rain { display: none; }
@media (prefers-reduced-motion: reduce) { #hud > .rain { display: none; } }
