/* The story stage and the passage HUD: the diorama, the crew, the stats chips, the typing box
   and the modifier gauges. Prefixes: .cmp-stage .dio- .comp- .mod- .tv-* (campaign scope only).
   See docs/ARCHITECTURE.md, "Look: the cartoon edition". Tokens only, no hex colors.
   Free-play passages and drills are untouched: every typing-view rule here sits under
   main[data-screen="campaign"].
   Look: a cartoon frame (the .sb-frame band), big friendly speech bubbles, a quiet HUD. */

/* ---------- stage: the picture, in a thick cartoon frame ---------- */

.cmp-stage {
  position: relative;
  margin: 0 0 var(--gap);
  border: 6px solid var(--sb-sun);
  border-radius: var(--radius-lg);
  background: var(--sb-deep);
  background-clip: padding-box;
  box-shadow: 0 0 0 var(--sb-outline-w) var(--sb-outline), 0 6px 0 var(--sb-outline);
  overflow: hidden;
  isolation: isolate;
}
/* the inner ink line of the frame sits over the picture */
.cmp-stage::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: calc(var(--radius-lg) - 6px);
  box-shadow: inset 0 0 0 var(--sb-outline-w) var(--sb-outline);
  pointer-events: none;
}

/* ---------- diorama ---------- */

.dio {
  position: relative;
  width: 100%;
  /* tall enough for the crew and their bubble, short enough that the typing box stays above the fold */
  height: clamp(240px, 32vw, min(360px, 40vh));
  background: var(--sb-deep);
}
.dio-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---------- companions: the crew ---------- */

.comp-layer {
  --badge: 80px;
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.comp-badges {
  position: absolute;
  left: 18px;
  bottom: 20px;
  display: flex;
  gap: 14px;
  align-items: flex-end;
}

/* One crew badge. The colours are fixed cartoon colours (ink, white, a few mixes of the art palette),
   never the page's text colour, so a face looks the same in light and in dark. */
.comp-badge {
  --ink: var(--sb-outline);
  --paper: white;
  --skin: color-mix(in srgb, var(--sb-pink) 16%, color-mix(in srgb, var(--sb-sand) 62%, white));
  --skin-shade: color-mix(in srgb, var(--skin) 55%, var(--sb-orange));
  --lip: color-mix(in srgb, var(--sb-danger) 70%, var(--skin));
  --who: var(--sb-sea);
  --who-ink: var(--sb-outline);
  position: relative;
  flex: none;
  width: var(--badge);
  height: var(--badge);
  transform-origin: 50% 90%;
  transition: transform 0.4s var(--ease-spring), opacity 0.3s ease, filter 0.3s ease;
}
.comp-conseil { --who: var(--sb-sea); }
.comp-ned { --who: var(--sb-orange); }
.comp-nemo { --who: var(--sb-sun); }

/* the medallion: a round sticker with the speaker's colour behind the bust; this is the part that bobs */
.comp-medal {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  border-radius: 50%;
  border: var(--sb-outline-w) solid var(--sb-outline);
  background: radial-gradient(circle at 50% 30%, color-mix(in srgb, var(--who) 30%, white), color-mix(in srgb, var(--who) 80%, var(--sb-sky)) 85%);
  box-shadow: 0 0 0 3px var(--who), 0 0 0 calc(3px + var(--sb-outline-w)) var(--sb-outline), 0 6px 0 calc(3px + var(--sb-outline-w)) var(--sb-outline);
  animation: sb-bob calc(3.2s + var(--i, 0) * 0.55s) ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * -1.1s);
  transition: box-shadow 0.3s ease;
}
.comp-medal svg {
  display: block;
  width: 100%;
  height: 100%;
}

.comp-badge.is-speaking {
  z-index: 2;
  transform: scale(1.16);
}
.comp-badge.is-speaking .comp-medal {
  box-shadow: 0 0 0 3px var(--who), 0 0 0 calc(3px + var(--sb-outline-w)) var(--sb-outline), 0 6px 0 calc(3px + var(--sb-outline-w)) var(--sb-outline), 0 0 0 calc(9px + var(--sb-outline-w)) color-mix(in srgb, var(--sb-glow-color) 85%, transparent);
}
.comp-badges.has-speaker .comp-badge:not(.is-speaking) {
  opacity: 0.72;
  filter: saturate(0.75);
}
.comp-badge[data-mood="alarmed"].is-speaking svg { animation: sb-shake 0.16s linear 4; }

/* bust parts: ink outlines, flat fills */
.comp-medal :is(.c-skin, .c-coat, .c-shirt, .c-hair, .c-beard, .c-cap, .c-cap-band, .c-visor, .c-accent, .c-trim, .c-gold, .c-steel, .c-eye, .c-bulb, .c-mouth, .c-teeth, .c-drop, .c-streak) {
  stroke: var(--ink);
  stroke-width: 1.15;
  stroke-linejoin: round;
}
.comp-medal :is(.c-stroke, .c-brow, .c-lip, .c-nose, .c-shock, .c-coat-hi, .c-beard-hi, .c-hair-hi, .c-cap-band-line, .c-cap-pin, .c-strap, .c-shaft, .c-specs *) {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.comp-medal .c-skin { fill: var(--skin); }
.comp-medal .c-bulb { fill: color-mix(in srgb, var(--sb-danger) 24%, var(--skin)); }
.comp-medal .c-nose { stroke: var(--skin-shade); stroke-width: 1.3; }
.comp-medal .c-eye { fill: var(--paper); stroke-width: 0.8; }
.comp-medal .c-pupil { fill: var(--ink); }
.comp-medal .c-shine { fill: var(--paper); }
.comp-medal .c-stroke { stroke: var(--ink); stroke-width: 1.7; }
.comp-medal .c-brow { stroke: var(--brow, var(--hair)); stroke-width: 1.9; }
.comp-medal .c-brow.is-thick { stroke-width: 2.5; }
.comp-medal .c-mouth { fill: color-mix(in srgb, var(--sb-danger) 55%, var(--ink)); }
.comp-medal .c-teeth { fill: var(--paper); stroke: none; }
.comp-medal .c-lip { stroke: var(--lip); stroke-width: 1.9; }
.comp-medal .c-blush { fill: var(--sb-danger); opacity: 0.3; }
.comp-medal .c-blush.is-strong { opacity: 0.55; }
.comp-medal .c-drop { fill: var(--sb-sky); stroke-width: 0.8; }
.comp-medal .c-shock { stroke: var(--sb-danger); stroke-width: 1.8; }
.comp-medal .c-shirt { fill: var(--paper); }
.comp-medal .c-accent { fill: var(--cloth); }
.comp-medal .c-gold { fill: var(--sb-sun); }
.comp-medal .c-steel { fill: color-mix(in srgb, var(--paper) 70%, var(--sb-sky)); }
.comp-medal .c-shaft { stroke: var(--sb-orange); stroke-width: 2.6; }
.comp-medal .c-coat { fill: var(--coat); }
.comp-medal .c-coat-hi { stroke: color-mix(in srgb, var(--coat) 65%, var(--paper)); stroke-width: 1.3; opacity: 0.85; }
.comp-medal .c-strap { stroke: color-mix(in srgb, var(--coat) 55%, var(--ink)); stroke-width: 2.2; }
.comp-medal .c-hair { fill: var(--hair); }
.comp-medal .c-hair-hi { stroke: color-mix(in srgb, var(--hair) 60%, var(--paper)); stroke-width: 1.3; }
.comp-medal .c-beard { fill: var(--beard); }
.comp-medal .c-beard-hi { stroke: color-mix(in srgb, var(--beard) 62%, var(--paper)); stroke-width: 1.2; opacity: 0.8; }
.comp-medal .c-streak { fill: color-mix(in srgb, var(--paper) 90%, var(--sb-sky)); }
.comp-medal .c-cap { fill: var(--cap); }
.comp-medal .c-cap-band { fill: var(--cap-band); }
.comp-medal .c-cap-band-line { stroke: color-mix(in srgb, var(--cap-band) 55%, var(--ink)); stroke-width: 0.9; opacity: 0.7; }
.comp-medal .c-visor { fill: color-mix(in srgb, var(--cap) 70%, var(--ink)); }
.comp-medal .c-cap-pin { stroke: var(--sb-orange); stroke-width: 1; }
.comp-medal .c-trim { fill: var(--sb-sun); }
.comp-medal .c-specs * { stroke: var(--ink); stroke-width: 1.4; }
.comp-medal .c-specs circle { fill: color-mix(in srgb, var(--paper) 35%, transparent); }

/* each crew member's palette (fixed: a face never turns white in dark mode) */
.comp-conseil {
  --coat: color-mix(in srgb, var(--sb-sea) 52%, var(--sb-outline));
  --hair: color-mix(in srgb, var(--sb-orange) 38%, var(--sb-outline));
  --cloth: var(--sb-danger);
}
.comp-ned {
  --coat: color-mix(in srgb, var(--sb-danger) 62%, var(--sb-outline));
  --hair: color-mix(in srgb, var(--sb-orange) 66%, var(--sb-outline));
  --beard: color-mix(in srgb, var(--sb-orange) 74%, var(--sb-outline));
  --cap: color-mix(in srgb, var(--sb-sea) 52%, var(--sb-outline));
  --cap-band: var(--sb-sand);
  --cloth: var(--sb-sea);
}
.comp-nemo {
  --coat: color-mix(in srgb, var(--sb-grape) 48%, var(--sb-outline));
  --hair: color-mix(in srgb, var(--sb-grape) 22%, var(--sb-outline));
  --beard: color-mix(in srgb, var(--sb-grape) 26%, var(--sb-outline));
  --brow: color-mix(in srgb, var(--sb-grape) 22%, var(--sb-outline));
  --cap: color-mix(in srgb, var(--sb-sea) 42%, var(--sb-outline));
  --cap-band: var(--sb-sand);
  --cloth: var(--sb-sun);
}

/* one SVG, four faces: only the speaker's current mood shows */
.comp-medal .m { display: none; }
.comp-badge[data-mood="neutral"] .m-neutral,
.comp-badge[data-mood="pleased"] .m-pleased,
.comp-badge[data-mood="worried"] .m-worried,
.comp-badge[data-mood="alarmed"] .m-alarmed { display: inline; }

.comp-mono {
  display: grid;
  place-items: center;
  height: 100%;
  font: 700 1.2rem var(--font-display);
  color: var(--sb-outline);
}

/* ---------- the speech bubble ---------- */

.comp-bubble {
  --who: var(--sb-sea);
  --who-ink: var(--sb-outline);
  position: absolute;
  left: 16px;
  /* clear of the speaker, who swells (x1.16) and glows (12px) while talking */
  bottom: calc(var(--badge) * 1.15 + 50px);
  width: max-content;
  max-width: min(calc(100% - 32px), 500px);
  padding: 24px 22px 15px;
  background: var(--sb-card);
  color: var(--sb-card-ink);
  border: var(--sb-outline-w) solid var(--sb-outline);
  border-radius: 28px;
  box-shadow: var(--sb-pop);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1.34;
  text-wrap: pretty;
  opacity: 0;
  transform: translateY(10px) scale(0.55);
  transform-origin: var(--tail, 30px) 100%;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.comp-bubble[data-who="conseil"] { --who: var(--sb-sea); }
.comp-bubble[data-who="ned"] { --who: var(--sb-orange); }
.comp-bubble[data-who="nemo"] { --who: var(--sb-sun); }
.comp-bubble.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.2s ease, transform 0.5s var(--ease-spring);
}
.comp-bubble.is-out {
  opacity: 0;
  transform: translateY(-6px) scale(0.97);
  transition: opacity 0.4s ease, transform 0.4s ease;
}
/* the tail points down at the speaker's medallion */
.comp-bubble::after {
  content: '';
  position: absolute;
  left: var(--tail, 30px);
  bottom: -12px;
  width: 20px;
  height: 20px;
  margin-left: -10px;
  background: var(--sb-card);
  border-right: var(--sb-outline-w) solid var(--sb-outline);
  border-bottom: var(--sb-outline-w) solid var(--sb-outline);
  border-bottom-right-radius: 5px;
  transform: rotate(45deg);
}
/* the name tag sits across the bubble's top edge in the speaker's colour */
.comp-who {
  position: absolute;
  top: -17px;
  left: 18px;
  padding: 2px 14px 3px;
  border: 2px solid var(--sb-outline);
  border-radius: 999px;
  background: var(--who);
  color: var(--who-ink);
  box-shadow: 0 2px 0 var(--sb-outline);
  font: 600 0.95rem/1.3 var(--font-display);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.comp-text { display: block; }

/* ---------- HUD: a quiet WPM, a compact accuracy + progress row, the Nautilus bar ---------- */

main[data-screen="campaign"] .tv { gap: 12px; }

main[data-screen="campaign"] .tv-stats.tv-chips {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
}
main[data-screen="campaign"] .tv-chips .tv-stat {
  flex: none;
  min-width: 0;
  min-height: 36px;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  padding: 3px 12px 3px 9px;
  border-width: 2px;
  box-shadow: 0 2px 0 var(--sb-outline);
}
main[data-screen="campaign"] .tv-chips .tv-stat-label {
  font: 700 0.9rem/1 var(--font-body);
  letter-spacing: 0;
  text-transform: none;
  color: var(--sb-card-muted);
}
main[data-screen="campaign"] .tv-chips .tv-stat-value {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.2;
  text-align: right;
  min-width: 3ch;
}
/* fixed value widths: a chip never changes size while numbers tick */
main[data-screen="campaign"] .tv-chips .tv-stat:nth-child(2) .tv-stat-value { min-width: 4.5ch; }
main[data-screen="campaign"] .tv-chips .tv-stat:nth-child(3) .tv-stat-value { min-width: 3.5ch; }
main[data-screen="campaign"] .tv-chips .tv-stat:nth-child(2) .sb-icon { color: var(--good); }
main[data-screen="campaign"] .tv-chips .tv-stat:nth-child(3) .sb-icon { color: var(--accent); }
main[data-screen="campaign"] .tv-chips .tv-stat .sb-icon { width: 1.2em; height: 1.2em; }

/* WPM steps back: no sticker shadow, no ink line, small and calm */
main[data-screen="campaign"] .tv-chips .tv-stat:first-child {
  min-height: 30px;
  padding: 2px 10px 2px 8px;
  border-color: var(--sb-card-line);
  background: transparent;
  box-shadow: none;
  order: 3;
  margin-left: auto;
}
main[data-screen="campaign"] .tv-chips .tv-stat:first-child .sb-icon { color: var(--sb-card-muted); width: 1.05em; height: 1.05em; }
main[data-screen="campaign"] .tv-chips .tv-stat:first-child .tv-stat-label { font-size: 0.85rem; color: var(--ink-muted); }
main[data-screen="campaign"] .tv-chips .tv-stat:first-child .tv-stat-value { font-size: 0.95rem; font-weight: 500; color: var(--ink-muted); min-width: 2.2ch; }

/* streak: always laid out, only revealed after STREAK_WORDS clean words */
main[data-screen="campaign"] .tv-chips .tv-streak {
  order: 2;
  margin-left: 0;
  visibility: hidden;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.35s ease, transform 0.35s var(--ease-spring), visibility 0s linear 0.35s;
}
main[data-screen="campaign"] .tv-chips .tv-streak.is-on {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition: opacity 0.2s ease, transform 0.4s var(--ease-spring), visibility 0s;
  background: color-mix(in srgb, var(--sb-glow-color) 55%, var(--sb-chip));
  color: var(--sb-primary-ink);
  box-shadow: 0 2px 0 var(--sb-outline), 0 0 0 4px color-mix(in srgb, var(--sb-glow-color) 40%, transparent);
}
main[data-screen="campaign"] .tv-chips .tv-streak .sb-icon {
  color: var(--sb-danger);
  fill: color-mix(in srgb, var(--sb-orange) 75%, transparent);
}
main[data-screen="campaign"] .tv-chips .tv-streak.is-on .tv-stat-label,
main[data-screen="campaign"] .tv-chips .tv-streak.is-on .tv-stat-value { color: var(--sb-primary-ink); }
main[data-screen="campaign"] .tv-chips .tv-streak.is-bump { animation: sb-pop 0.35s var(--ease-spring); }

/* progress bar: a rounded sticker channel, a glossy sea fill, and the Nautilus riding the fill's end */
main[data-screen="campaign"] .tv-bar {
  --p: 0;
  position: relative;
  box-sizing: border-box;
  height: 16px;
  margin: 10px 0 8px;
  border: var(--sb-outline-w) solid var(--sb-outline);
  border-radius: 999px;
  background: var(--sb-card-2);
  box-shadow: 0 3px 0 var(--sb-outline);
  overflow: visible;
}
main[data-screen="campaign"] .tv-bar-fill {
  width: calc(22px + var(--p) * (100% - 44px));
  height: 100%;
  border-radius: inherit;
  background:
    linear-gradient(180deg, rgb(255 255 255 / 0.45) 0 38%, transparent 38%),
    var(--sb-sea);
  transform: none;
  transition: width 0.25s linear;
}
main[data-screen="campaign"] .tv-ride {
  position: absolute;
  left: 0;
  top: 50%;
  width: calc(100% - 44px);
  height: 0;
  transform: translateX(calc(var(--p) * 100%));
  transition: transform 0.25s linear;
}
main[data-screen="campaign"] .tv-ship {
  position: absolute;
  left: 0;
  top: -14px;
  width: 52px;
  height: 26px;
  filter: drop-shadow(0 2px 0 var(--sb-outline));
}
main[data-screen="campaign"] .tv.is-running .tv-ship { animation: sb-bob 2.6s ease-in-out infinite; }
main[data-screen="campaign"] .tv-ship svg { display: block; width: 100%; height: 100%; overflow: visible; }
main[data-screen="campaign"] .tv-ship :is(.ns-hull, .ns-fin, .ns-port, .ns-lamp) { stroke: var(--sb-outline); stroke-width: 1.3; stroke-linejoin: round; }
main[data-screen="campaign"] .tv-ship .ns-hull { fill: var(--sb-sun); }
main[data-screen="campaign"] .tv-ship .ns-fin { fill: var(--sb-orange); }
main[data-screen="campaign"] .tv-ship .ns-ridge { fill: none; stroke: white; stroke-width: 1.5; stroke-linecap: round; opacity: 0.8; }
main[data-screen="campaign"] .tv-ship .ns-port { fill: var(--sb-sky); }
main[data-screen="campaign"] .tv-ship .ns-lamp { fill: white; }

/* ---------- the typing box: a white card ---------- */

main[data-screen="campaign"] .tv-box.sb-card {
  padding: 18px 26px;
  cursor: text;
}
main[data-screen="campaign"] .tv-text {
  max-width: 62ch;
  height: calc(1.95em * 3);
  padding: 0 6px;
  font-family: var(--font-mono);
  font-size: clamp(1.2rem, 0.8rem + 1vw, 1.5rem);
  line-height: 1.95;
  letter-spacing: 0.01em;
}
main[data-screen="campaign"] .tv-char {
  color: var(--sb-card-muted);
  border-radius: 4px;
}
main[data-screen="campaign"] .tv-char.is-correct { color: var(--sb-card-ink); }
main[data-screen="campaign"] .tv-char.is-wrong {
  /* a solid coral chip with the page's ink-on-accent text: AA for large bold text in both themes */
  color: var(--accent-ink);
  font-weight: 700;
  background: var(--sb-danger);
  left: 0;
  animation: tv-wiggle 0.2s ease-out;
}
/* The learn track's waiting character: typing.css wiggles it (tv-miss); only the colour is set here,
   because the campaign's muted letter colour would otherwise win over it. */
main[data-screen="campaign"] .tv-char.is-miss { color: var(--bad); font-weight: 700; }
main[data-screen="campaign"] .tv-sp.is-wrong::after { color: var(--sb-danger); }
/* inline text can't take transforms, so the wrong letter shakes with `left` */
@keyframes tv-wiggle {
  0%, 100% { left: 0; }
  25% { left: -2px; }
  75% { left: 2px; }
}

/* the word being typed sits in a soft sunny pill (a pseudo-element: nothing reflows) */
main[data-screen="campaign"] .tv-word.is-active {
  position: relative;
  isolation: isolate;
}
main[data-screen="campaign"] .tv-word.is-active::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0.3em -0.2em 0.3em -0.2em;
  border-radius: 0.55em;
  background: color-mix(in srgb, var(--sb-glow-color) 30%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--sb-primary-lo) 45%, transparent);
}
/* the pill hugs the letters, not the space that ends the word */
main[data-screen="campaign"] .tv-word.is-active:not(:last-child)::before { right: calc(1ch - 0.2em); }

/* a chunky ink caret */
main[data-screen="campaign"] .tv-char.is-current::before {
  top: 14%;
  bottom: 14%;
  left: -1px;
  width: 3px;
  border-radius: 2px;
  background: var(--sb-outline);
  box-shadow: none;
}

main[data-screen="campaign"] .tv-focus-hint {
  background: color-mix(in srgb, var(--sb-card) 84%, transparent);
  color: var(--sb-card-muted);
}

/* "+1" particles that float up from each clean word (campaign.js adds and removes them) */
.tv-spark {
  position: absolute;
  z-index: 4;
  margin-left: -0.9em;
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  font: 700 1rem/1 var(--font-display);
  color: var(--sb-primary-ink);
  text-shadow: 0 0 6px var(--sb-card), 0 0 2px var(--sb-card);
  pointer-events: none;
  animation: sb-sparkle 0.8s ease-out forwards;
}
.tv-spark::before {
  content: '';
  width: 0.9em;
  height: 0.9em;
  background: var(--sb-glow-color);
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
}
.reduced-motion .tv-spark { display: none; }

/* ---------- modifiers ---------- */

/* Air: a round bubble gauge that drains while you type. */
.mod-air {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 4;
  width: 84px;
  height: 84px;
  border: var(--sb-outline-w) solid var(--sb-outline);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--sb-card);
  box-shadow: var(--sb-pop);
  pointer-events: none;
}
.mod-air-ring {
  --p: 1;
  position: relative;
  width: 66px;
  height: 66px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at center, var(--sb-card) 0 58%, transparent 60%),
    conic-gradient(var(--mod-air-color, var(--sb-sea)) calc(var(--p) * 1turn), var(--sb-card-line) 0);
}
.mod-air-num {
  font: 600 1rem/1 var(--font-display);
  color: var(--sb-card-ink);
}
.mod-air-label {
  position: absolute;
  bottom: -4px;
  left: 50%;
  transform: translate(-50%, 100%);
  padding: 1px 12px 2px;
  border-radius: 999px;
  background: var(--sb-chip);
  border: 2px solid var(--sb-outline);
  box-shadow: 0 2px 0 var(--sb-outline);
  color: var(--sb-chip-ink);
  font: 600 0.9rem/1.2 var(--font-display);
}
.mod-air.is-ok { --mod-air-color: var(--sb-sea); }
.mod-air.is-mid { --mod-air-color: var(--sb-sun); }
.mod-air.is-low { --mod-air-color: var(--sb-danger); animation: mod-pulse 0.7s ease-in-out infinite; }
.mod-air.is-gain .mod-air-ring { animation: mod-gain 0.35s ease-out; }
@keyframes mod-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.07); box-shadow: var(--sb-pop), 0 0 0 7px color-mix(in srgb, var(--sb-danger) 50%, transparent); }
}
@keyframes mod-gain {
  0% { filter: brightness(1.4); }
  100% { filter: none; }
}

/* Current: the stage is pushed back and streaks run against you. */
.mod-cue {
  position: absolute;
  top: 14px;
  left: 50%;
  z-index: 4;
  transform: translate(-50%, -8px);
  padding: 6px 18px;
  border-radius: 999px;
  border: 2px solid var(--sb-outline);
  background: var(--sb-primary);
  box-shadow: 0 3px 0 var(--sb-outline);
  color: var(--sb-primary-ink);
  font: 600 1rem var(--font-display);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s var(--ease-spring);
}
.mod-cue.is-on {
  opacity: 1;
  transform: translate(-50%, 0);
}
.mod-streaks {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  background: repeating-linear-gradient(0deg, transparent 0 17px, color-mix(in srgb, white 30%, transparent) 17px 19px);
  background-size: 140px 100%;
  mask-image: linear-gradient(90deg, transparent, var(--sb-deep) 25%, var(--sb-deep) 75%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--sb-deep) 25%, var(--sb-deep) 75%, transparent);
}
.mod-pulled .mod-streaks {
  opacity: 1;
  animation: mod-streak 0.7s linear infinite reverse;
}
.mod-pulled .dio-canvas {
  transform: scale(1.04) translateX(calc(min(var(--mod-drift, 12px), 1.8vw) * -1));
  transition: transform 0.6s ease;
}
.cmp-stage .dio-canvas { transition: transform 0.5s ease; }
@keyframes mod-streak {
  from { background-position: 0 0; }
  to { background-position: 140px 0; }
}

/* Dark: the text box is dark except a lit patch around the caret. */
@property --mod-x { syntax: '<length>'; inherits: false; initial-value: 50px; }
@property --mod-y { syntax: '<length>'; inherits: false; initial-value: 30px; }
.mod-dark {
  --mod-shade: color-mix(in srgb, var(--sb-deep) 45%, black); /* a deep shade in either theme: the box goes dark, never fogged */
  --mod-lamp: color-mix(in srgb, var(--sb-glow-color) 78%, var(--sb-primary)); /* warm light in either theme */
  --mod-x: 50px;
  --mod-y: 30px;
  --mod-rx: 150px;
  --mod-ry: 74px;
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  background:
    radial-gradient(ellipse var(--mod-rx) var(--mod-ry) at var(--mod-x) var(--mod-y),
      color-mix(in srgb, var(--mod-lamp) 38%, transparent) 0,
      color-mix(in srgb, var(--mod-lamp) 24%, transparent) 42%,
      color-mix(in srgb, var(--mod-shade) 60%, transparent) 72%,
      color-mix(in srgb, var(--mod-shade) 96%, transparent) 100%);
  transition: --mod-x 0.14s ease-out, --mod-y 0.18s ease-out;
}
.mod-dark::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(ellipse var(--mod-rx) var(--mod-ry) at var(--mod-x) var(--mod-y), color-mix(in srgb, var(--sb-glow-color) 16%, transparent), transparent 70%);
  mix-blend-mode: soft-light;
}

/* ---------- small screens ---------- */

@media (max-width: 640px) {
  .cmp-stage { border-width: 5px; }
  .cmp-stage::after { border-radius: calc(var(--radius-lg) - 5px); }
  .cmp-stage .comp-layer { --badge: 60px; }
  .comp-badges { left: 14px; bottom: 16px; gap: 12px; }
  .comp-bubble {
    left: 10px;
    bottom: calc(var(--badge) * 1.15 + 44px);
    max-width: calc(100% - 20px);
    padding: 22px 16px 12px;
    font-size: 1.05rem;
    line-height: 1.3;
  }
  .comp-who { top: -16px; left: 14px; }
  .mod-air { width: 68px; height: 68px; top: 10px; right: 10px; }
  .mod-air-ring { width: 54px; height: 54px; }
  .mod-air-num { font-size: 0.9rem; }
  .mod-cue { top: 10px; font-size: 0.95rem; white-space: normal; text-align: center; width: max-content; max-width: 62%; }

  /* HUD: four equal chips; icons carry the labels */
  main[data-screen="campaign"] .tv-stats.tv-chips {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
  }
  main[data-screen="campaign"] .tv-chips .tv-stat {
    min-width: 0;
    justify-content: center;
    gap: 5px;
    padding: 3px 4px;
  }
  main[data-screen="campaign"] .tv-chips .tv-stat:first-child,
  main[data-screen="campaign"] .tv-chips .tv-streak { order: 0; margin-left: 0; }
  main[data-screen="campaign"] .tv-chips .tv-stat-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  main[data-screen="campaign"] .tv-chips .tv-stat-value { font-size: 1rem; min-width: 0 !important; }
  main[data-screen="campaign"] .tv-chips .tv-stat:first-child .tv-stat-value { font-size: 0.95rem; }
  main[data-screen="campaign"] .tv-chips .tv-stat .sb-icon { width: 1.2em; height: 1.2em; }

  main[data-screen="campaign"] .tv-box.sb-card { padding: 16px 10px; }
  main[data-screen="campaign"] .tv-text { font-size: 1.18rem; padding: 0 4px; }
}

.reduced-motion .comp-medal,
.reduced-motion .comp-badge[data-mood="alarmed"].is-speaking svg { animation: none; }
.reduced-motion .mod-air.is-low { animation: none; box-shadow: var(--sb-pop), 0 0 0 4px var(--sb-danger); }
.reduced-motion .mod-pulled .mod-streaks { animation: none; }
.reduced-motion main[data-screen="campaign"] .tv-char.is-wrong { animation: none; }
.reduced-motion main[data-screen="campaign"] .tv-ship { animation: none; }
