/* ═══════════════════════════════════════════════════════════════════
   Exploding Dots — Fruit3D activity
   Tier 0, fully self-contained. Design tokens below are inlined from the
   Fruit3D design system so the folder runs offline (no CDN fonts/CSS).
   Brand faces (GT Walsheim / Inter) fall back to a warm system stack —
   trial webfonts are not shipped, and rule 1 forbids network fetches.
   ═══════════════════════════════════════════════════════════════════ */

:root {
  /* fruit accents */
  --brand-blue:   #38C3E4;
  --brand-orange: #FF8A20;
  --brand-pink:   #FC74B4;
  --brand-yellow: #FFD401;
  --brand-red:    #EB7CB2; /* Fruit3D bright pink — BANG accents (was #B14884) */
  --brand-green:  #45BA90;

  /* dark plum theme (matches examples.fruit3d.com/maths-3d-solids) */
  --plum:       #2B0326;  /* background + header/footer bars */
  --card:       #3a0a33;  /* pop-up cards / dark panels */
  --card-inset: #4a133f;  /* nested surfaces (answer tiles, speech) */
  --line-dark:  #5a1a4f;  /* plum hairline borders */
  --nav:        #A1FCD2;  /* navigation buttons (mint) */
  --nav-dark:   #6fcea3;  /* mint button shadow / hover */

  /* box colours */
  --ones:      var(--brand-blue);
  --ones-ink:  #0d7f99;
  --tens:      var(--brand-orange);
  --tens-ink:  #b85700;
  /* Hundreds is #D6455C (deep coral-red) — the pink is reserved for nav. */
  --hundreds:  #D6455C;
  --hund-ink:  #a5324a;

  /* purple / aubergine chrome */
  --purple-900: #300028;
  --purple-800: #401E50;
  --purple-700: #4B235D;
  --purple-600: #5D2E73;
  --purple-100: #EDE4F2;
  --purple-50:  #F6F1F9;

  /* ink ramp */
  --ink-900: #1C1726;
  --ink-700: #3A3447;
  --ink-600: #5D5670;
  --ink-400: #938DA3;
  --ink-200: #E8E4EE;
  --white:   #FFFFFF;

  --success: #44B990;
  --warning: #FF8B1F;

  --surface-cream: var(--plum);   /* page background */
  --surface-card:  #FFFFFF;        /* the number-machine frames stay white so the balls pop */

  /* On the dark ground the copy is light; the counters and box labels keep
     their own bright colours. */
  --text-strong: #FFFFFF;
  --text-body:   #FFFFFF;
  --text-soft:   rgba(248, 244, 241, .68);

  /* type */
  /* Raleway throughout — display + body both use it (loaded via Google Fonts in index.html). */
  --font-brand: 'Raleway', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-sans:  'Raleway', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  --font-chrome: 'Raleway', system-ui, -apple-system, sans-serif; /* Fruit3D site chrome (header/footer) */

  /* radius / shadow / motion */
  --radius:      12px;
  --radius-lg:   16px;
  --radius-xl:   24px;
  --radius-full: 9999px;
  --shadow-md: 0 4px 12px rgba(28,23,38,.08), 0 2px 4px rgba(28,23,38,.04);
  --shadow-lg: 0 12px 28px rgba(28,23,38,.14), 0 4px 8px rgba(28,23,38,.06);
  --ease-pop:  cubic-bezier(0.16, 1, 0.3, 1);
  --dur-base:  220ms;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--text-body);
  background: var(--plum);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
  user-select: none;
  /* Stack the Fruit3D header, the activity stage, and the footer.
     Dynamic viewport height so the layout fits the *visible* area on mobile
     (the address bar shows/hides) — 100vh would push the footer/dock below
     the fold when browser chrome is showing. */
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}
h1, h2, h3 { font-family: var(--font-brand); color: var(--text-strong); margin: 0; letter-spacing: -0.01em; }
button { font-family: inherit; }

#app { position: relative; flex: 1 1 auto; min-height: 0; width: 100%; overflow: hidden; }

/* ── % complete status (top-right of the activity, Fruit3D pink) ── */
/* The %-card with its Back and Menu buttons. Desktop: floated top-right, the
   card on top spanning both columns, Back + Menu side by side below it. */
.hud-group {
  position: absolute; top: 12px; right: 12px; z-index: 30;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas: "card card" "back menu";
  gap: 8px; width: 190px;
}
#progress-hud { grid-area: card; }
#btn-back { grid-area: back; }
#btn-menu { grid-area: menu; }
.progress-hud {
  background: var(--card); border: 2px solid #A1FCD2; border-radius: 16px;
  padding: 11px 16px 13px;
  box-shadow: var(--shadow-md);
  font-family: var(--font-brand);
}
.progress-hud[hidden] { display: none; }
.hud-btn {
  font-family: var(--font-brand); font-weight: 800;
  font-size: 15px;                 /* same size as the "% complete" text */
  color: #A1FCD2; background: transparent;
  border: 2px solid #A1FCD2; border-radius: 14px;
  padding: 8px 6px; cursor: pointer; width: 100%;
  transition: background .15s, color .15s, transform .1s;
  touch-action: manipulation;
}
.hud-btn:hover { background: #A1FCD2; color: var(--plum); }
.hud-btn:active { transform: translateY(1px) scale(.98); }
.progress-level {
  font-size: 20px; font-weight: 800; letter-spacing: .01em;
  color: #A1FCD2; text-align: center; margin-bottom: 3px;
}
.progress-label {
  font-size: 15px; font-weight: 800; letter-spacing: .01em;
  color: #A1FCD2; text-align: center; margin-bottom: 9px;
}
.progress-track {
  height: 14px; border-radius: var(--radius-full); overflow: hidden;
  background: rgba(161, 252, 210, .22);
}
.progress-fill {
  height: 100%; width: 0%; border-radius: var(--radius-full);
  background: #A1FCD2;
  transition: width .45s var(--ease-pop);
}
@media (max-width: 640px) {
  /* Mobile: full-width row — Back anchored left, the %-card centred, Menu
     anchored right (like the Sign Up button in the header). */
  .hud-group {
    position: static; top: auto; right: auto; width: 100%;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "back card menu";
    align-items: center; gap: 8px;
  }
  #progress-hud { justify-self: center; }
  #btn-menu { justify-self: end; }
  .hud-btn { font-size: 13px; padding: 6px 12px; width: auto; border-radius: 11px; }
  .progress-hud {
    min-width: 0; padding: 5px 12px 7px; border-radius: 12px;
    display: flex; align-items: center; gap: 8px;
  }
  /* Compact: "Level N" and "N%" sit on one line beside a short bar. */
  .progress-level { font-size: 13px; margin-bottom: 0; }
  .progress-word { display: none; }
  .progress-label { font-size: 13px; margin-bottom: 0; }
  .progress-track { height: 8px; width: 56px; }
}

/* ═══════════════════════════════════════════════════════════════════
   Fruit3D site chrome — fixed header & footer that bound the stage.
   Cloned from the reference example (examples.fruit3d.com): the Fruit3D
   wordmark + an activity pill in the header; copyright, a "Created by"
   credit and a "Made with" wordmark in the footer. Raleway throughout,
   Fruit3D plum (#2b0326) on the warm off-white (#f8f4f1) bar.
   ═══════════════════════════════════════════════════════════════════ */
.site-header,
.site-footer {
  flex: none;
  background: var(--plum);
  z-index: 10;
  font-family: var(--font-chrome);
}
/* Keep the bars clear of the notch / Dynamic Island / home indicator
   (viewport-fit=cover lets content reach the physical edges). */
.site-header { border-bottom: 1px solid var(--line-dark); padding-top: env(safe-area-inset-top); }
.site-footer { border-top: 1px solid var(--line-dark); padding-bottom: env(safe-area-inset-bottom); }
.site-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 22px;
}
/* Header bar: fixed 60px, centred. */
.site-header .site-bar {
  height: 60px;
  padding: 0 20px;
  gap: 16px;
}
.site-logo-link {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  transition: opacity .15s ease;
}
.site-logo-link:hover { opacity: .85; }
.site-logo {
  /* The Fruit3D wordmark is a wide transparent PNG, so no blend is needed. */
  height: 30px;
  width: auto;
  display: block;
}
/* Header title, beside the wordmark. */
.site-title {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}
.site-title h1 {
  margin: 0;
  font-size: clamp(14px, 2vw, 20px);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.1;
  color: #fff;
}
.site-title h1 em { font-style: normal; color: #fff; }
.fruit3d-pill {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  border-radius: 11px;
  background: var(--card-inset);
  border: 1px solid var(--line-dark);
  padding: 6px 13px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
  color: #f8f4f1;
  text-decoration: none;
  transition: opacity .16s;
}
.fruit3d-pill:hover { opacity: .9; }
.fruit3d-pill .pill-logo { display: block; height: 16px; width: auto; }
/* Sign Up CTA — solid pink, same height as the Interactive Activity pill. */
.signup-btn {
  display: inline-flex; align-items: center;
  border: 1px solid transparent; border-radius: 11px;
  background: #EB7CB2; color: #fff;
  padding: 6px 16px;
  font-family: var(--font-brand);
  font-size: 13px; font-weight: 700; letter-spacing: .02em; line-height: 16px;
  white-space: nowrap; cursor: pointer; text-decoration: none;
  transition: filter .16s;
}
.signup-btn:hover { filter: brightness(1.06); }
.site-footer .copyright {
  margin-right: auto;
  white-space: nowrap;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: #f8f4f1;
}
.site-footer .made-with {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: #f8f4f1;
}
/* Centred "Created by" credit (persona icon + name), Fruit3D plum. */
.site-footer .site-bar { position: relative; }
.site-footer .created-by {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: .01em;
  color: #f8f4f1;
}
.site-footer .created-by svg { display: block; flex: none; }
.site-footer .wordmark-link {
  display: inline-flex;
  align-items: center;
  transition: opacity .16s;
}
.site-footer .wordmark-link:hover { opacity: .8; }
.site-footer .wordmark { height: 22px; width: auto; }
@media (max-width: 560px) {
  .fruit3d-pill { font-size: 0; gap: 0; padding: 7px; }
  .fruit3d-pill .pill-logo { height: 17px; }
  .site-footer .made-with { display: none; }
  .site-footer .created-by { display: none; }
}
@media (max-width: 640px) {
  /* Header: wordmark + pill on the first row, title wraps to a second row. */
  .site-header .site-bar { height: auto; flex-wrap: wrap; padding: 10px 12px; gap: 10px 12px; }
  .site-logo-link { order: 1; }
  .fruit3d-pill { order: 2; }
  .signup-btn { order: 3; }
  .site-title { order: 4; flex-basis: 100%; }
  .site-title h1 { font-size: 17px; }
}

/* ── screen switching ── */
.screen {
  position: absolute; inset: 0;
  display: none;
  flex-direction: column;
  padding: clamp(12px, 2.4vh, 26px) clamp(14px, 3vw, 40px);
  overflow: hidden;
}
.screen.is-active { display: flex; }

/* ── shared buttons ── */
.btn {
  border: none;
  cursor: pointer;
  border-radius: var(--radius-full);
  font-weight: 700;
  transition: transform var(--dur-base) var(--ease-pop), box-shadow var(--dur-base), background var(--dur-base);
  touch-action: manipulation;
}
.btn:active { transform: scale(.96); }
.btn-start {
  background: var(--nav);
  color: var(--plum);
  font-family: var(--font-brand);
  font-size: clamp(20px, 3.2vh, 28px);
  padding: 18px 54px;
  min-height: 68px;
  box-shadow: 0 10px 0 var(--nav-dark), 0 16px 26px rgba(0,0,0,.4);
}
.btn-start:active { transform: translateY(6px) scale(.99); box-shadow: 0 4px 0 var(--nav-dark), 0 8px 14px rgba(0,0,0,.4); }
.btn-ghost {
  background: transparent;
  color: var(--text-soft);
  border: 2px solid var(--line-dark);
  font-size: 15px;
  font-weight: 600;
  padding: 12px 22px;
  min-height: 48px;
}
.btn-ghost:hover { color: #fff; border-color: var(--nav); background: var(--card); }

.eyebrow {
  font-size: 13px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-400); font-weight: 700; margin: 0 0 6px;
}

/* ═══════════════  TITLE  ═══════════════ */
#screen-title { align-items: center; justify-content: center; }

/* Opening card — matches the Fruit3D reference example: wordmark, bang emoji,
   title, subject/level, hook, and a bright-pink Start button. */
.intro-card {
  width: min(520px, calc(100vw - 32px));
  background: var(--card);
  border: 1px solid var(--line-dark);
  border-radius: 24px;
  box-shadow: var(--shadow-lg);
  padding: clamp(28px, 4vh, 42px) clamp(24px, 4vw, 40px) clamp(26px, 3.4vh, 36px);
  text-align: center;
}
.intro-logo { display: block; height: 34px; width: auto; margin: 0 auto 16px; }
.intro-emoji { font-size: clamp(38px, 6vh, 48px); line-height: 1; }
.intro-card h1 {
  margin: 12px 0 8px;
  font-size: clamp(26px, 4.6vh, 38px);
  line-height: 1.1;
  text-wrap: balance;
  color: #fff;
}
.intro-subhead {
  /* Sits midway between the pink subtitle and the level-picker row, filling the
     space freed by collapsing the buttons to a single row. Symmetric top/bottom
     margins keep it centred in that gap. */
  margin: clamp(24px, 5vh, 46px) auto; max-width: 420px;
  font-size: clamp(16px, 2.6vh, 21px); font-weight: 700; line-height: 1.2;
  text-wrap: balance; color: #fff;
}
.intro-sub {
  font-size: clamp(13px, 1.8vh, 15px); font-weight: 700; letter-spacing: .02em;
  color: #EB7CB2; margin: 0 0 6px;
}
.intro-subtitle {
  font-size: clamp(15px, 2.2vh, 19px); font-weight: 800; letter-spacing: .01em;
  color: #EB7CB2; margin: 0 0 12px;
}
.intro-hook {
  margin: 0 auto 24px; max-width: 400px;
  font-size: clamp(15px, 2vh, 17px); line-height: 1.55; color: #ffffff; font-weight: 700;
}
.btn-intro-start {
  font-family: var(--font-brand);
  font-size: clamp(18px, 3vh, 22px); font-weight: 700;
  color: var(--plum); background: var(--nav);
  border: none; border-radius: var(--radius-full);
  padding: 15px 46px; cursor: pointer;
  box-shadow: 0 8px 0 var(--nav-dark), 0 14px 22px rgba(0, 0, 0, .4);
  transition: transform var(--dur-base) var(--ease-pop), box-shadow var(--dur-base), background var(--dur-base);
  touch-action: manipulation;
}
.btn-intro-start:hover { background: #b6ffdd; }
.btn-intro-start:active { transform: translateY(3px) scale(.98); box-shadow: 0 4px 0 var(--nav-dark), 0 8px 14px rgba(0,0,0,.4); }

/* Level picker: all three levels sit in a single row. Level 1 is the primary
   (filled mint) button; Levels 2 & 3 are secondary outlined buttons, sized to
   match so the row lines up. */
.intro-levels { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; gap: 12px; }
/* Level 1 button in the picker: same footprint as the outlined levels (matching
   padding + a 2px border under border-box), but filled and without the heavy
   3D lift so all three sit flush in the row. */
.intro-levels .btn-intro-start,
.finale-actions .btn-intro-start {
  font-size: clamp(15px, 2.4vh, 18px); font-weight: 800;
  padding: 11px 30px;
  border: 2px solid var(--nav);
  box-shadow: 0 6px 14px rgba(0, 0, 0, .3);
}
.intro-levels .btn-intro-start:active,
.finale-actions .btn-intro-start:active { transform: translateY(2px) scale(.98); box-shadow: 0 3px 8px rgba(0,0,0,.3); }
.btn-intro-level {
  font-family: var(--font-brand);
  font-size: clamp(15px, 2.4vh, 18px); font-weight: 800;
  color: #A1FCD2; background: transparent;
  border: 2px solid #A1FCD2; border-radius: var(--radius-full);
  padding: 11px 30px; cursor: pointer;
  transition: transform var(--dur-base) var(--ease-pop), background .15s, color .15s;
  touch-action: manipulation;
}
.btn-intro-level:hover { background: #A1FCD2; color: var(--plum); }
.btn-intro-level:active { transform: translateY(2px) scale(.98); }
.finale-actions { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; gap: 12px; }
.finale-actions[hidden] { display: none; }   /* the flex display would otherwise override [hidden] */

.title-card { text-align: center; max-width: 760px; }
.title-card h1 {
  font-size: clamp(30px, 6.4vh, 62px);
  line-height: 1.04;
  margin: 8px 0 10px;
  text-wrap: balance;
}
.bang-word {
  color: var(--brand-red);
  display: inline-block;
  animation: bang-pulse 1.6s var(--ease-pop) infinite;
}
/* BANG on the opening card uses the Fruit3D "Fruitful" pink→orange gradient. */
.intro-subhead .bang-word {
  background-image: linear-gradient(110deg, #FF7AA2, #FF4D6D 55%, #FF9D57);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
@keyframes bang-pulse {
  0%, 100% { transform: scale(1) rotate(-2deg); }
  50%      { transform: scale(1.12) rotate(2deg); }
}
.title-sub { font-size: clamp(16px, 2.4vh, 21px); color: var(--text-soft); margin: 0 0 28px; }
.title-dots {
  position: relative; height: 92px; margin: 0 auto 6px; width: 240px;
}
.title-dots .td {
  position: absolute; width: 26px; height: 26px; border-radius: 50%;
  background: var(--brand-blue);
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.12);
  animation: td-float 2.4s ease-in-out infinite;
}
.title-dots .td::after {
  content: ''; position: absolute; top: 5px; left: 6px; width: 7px; height: 7px;
  border-radius: 50%; background: rgba(255,255,255,.7);
}
/* nine small dots clustering toward one big centre dot */
.td1{left:8px;  top:34px} .td2{left:36px; top:12px} .td3{left:34px; top:58px}
.td4{left:66px; top:30px} .td5{left:60px; top:64px} .td6{left:150px;top:10px}
.td7{left:182px;top:34px} .td8{left:158px;top:60px} .td9{left:206px;top:56px}
.title-dots .td10{
  left:98px; top:20px; width:54px; height:54px; background: var(--brand-yellow);
  animation: td-big 2.4s ease-in-out infinite;
}
.td2{animation-delay:.1s}.td3{animation-delay:.2s}.td4{animation-delay:.3s}
.td5{animation-delay:.4s}.td6{animation-delay:.5s}.td7{animation-delay:.6s}
.td8{animation-delay:.7s}.td9{animation-delay:.8s}
@keyframes td-float { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-6px)} }
@keyframes td-big { 0%,100%{transform:scale(1)} 50%{transform:scale(1.08)} }

/* ═══════════════  PLAY  ═══════════════ */
#screen-play { align-items: stretch; }

.play-top { display: flex; flex-direction: column; gap: 8px; }
.stage-pips { display: flex; gap: 8px; justify-content: center; }
.stage-pips .pip {
  width: 30px; height: 8px; border-radius: var(--radius-full);
  background: rgba(255,255,255,.2); transition: background var(--dur-base);
}
.stage-pips .pip.done { background: var(--nav); }
.stage-pips .pip.active { background: #ffffff; }

.mascot-row { display: flex; align-items: center; gap: 12px; justify-content: center; }
/* The 3D lime (kiwi) mascot — a rotatable model-viewer. */
.mascot {
  width: clamp(64px, 10vh, 94px); height: clamp(64px, 10vh, 94px); flex: 0 0 auto;
  background: transparent;
  --poster-color: transparent;
  --progress-bar-color: transparent;
  cursor: grab;
  /* The GLB renders a little bleached under the neutral lighting — boost
     saturation and knock brightness back so the lime reads vivid chartreuse. */
  filter: saturate(1.6) brightness(0.9);
}
.mascot::part(default-progress-bar) { display: none; }
.mascot.cheer { animation: cheer .5s var(--ease-pop); }
@keyframes cheer { 0%{transform:translateY(0) rotate(0)} 30%{transform:translateY(-10px) rotate(-6deg)} 60%{transform:translateY(-4px) rotate(5deg)} 100%{transform:translateY(0) rotate(0)} }

.speech {
  position: relative;
  background: #E1E34F; border: 2px solid #E1E34F;
  border-radius: var(--radius-lg);
  padding: 8px 16px; box-shadow: var(--shadow-md);
  max-width: min(72vw, 560px);
}
.speech::before {
  content: ''; position: absolute; left: -9px; top: 50%; transform: translateY(-50%) rotate(45deg);
  width: 14px; height: 14px; background: #E1E34F;
  border-left: 2px solid #E1E34F; border-bottom: 2px solid #E1E34F;
}
.speech p { margin: 0; font-size: clamp(15px, 2.1vh, 18px); font-weight: 600; line-height: 1.35; color: #2B0326; }
/* Play-screen bubble: a fixed width so it doesn't resize per message — that
   kept re-centring the row and made the lime jump left/right. Longer messages
   now wrap to a second line instead of widening. Text centred so short lines
   don't hug the left of the wider bubble. */
.mascot-row .speech { width: min(72vw, 420px); }
.mascot-row .speech p { text-align: center; }

.stage-instruction {
  text-align: center;
  font-size: clamp(16px, 2.5vh, 22px);
  font-weight: 700;
  color: #ffffff;
  margin: clamp(4px, 1.2vh, 10px) auto 2px;
  min-height: 1.4em;
  max-width: 820px;
  transition: color var(--dur-base);
}
/* Instruction lines stay white whatever the tone — the ones/tens/hundreds/
   thousands words keep their own colours (set on inner spans), so the line
   isn't a wash of orange or green. */
.stage-instruction.amber { color: #fff; }
.stage-instruction.green { color: #fff; }
/* When the instruction is cleared (e.g. the worth-card stages, where the panel
   carries the question and the split-line), take up no space. */
.stage-instruction:empty { min-height: 0; margin: 0; }
/* "Too many" — the one warning that keeps a colour, to flag the overshoot. */
.stage-instruction.toomany { color: #D6455C; }
/* "tap" in Fruit3D pink to match the wobbling clump (.digit.ten / .box.full),
   linking the word to the thing to tap. "BANG" carries the opening card's
   pink→orange gradient. */
.stage-instruction .hl-tap { color: var(--brand-red); font-weight: 800; }
.stage-instruction .hl-bang {
  font-weight: 800;
  background-image: linear-gradient(110deg, #FF7AA2, #FF4D6D 55%, #FF9D57);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* Place-value words carry their column/ball colour so the language links to
   the machine (ones=blue, tens=orange, hundreds=pink, thousands=green). The
   tone colours (.green/.amber) still win via inheritance elsewhere, but these
   keep the words matched to the balls throughout. */
/* The "target" number to build — a pink→yellow gradient to draw the eye. */
.stage-instruction .target-num {
  font-weight: 800;
  background-image: linear-gradient(100deg, #FF6FB5, #FFD24D);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.stage-instruction .pv-th { color: var(--brand-green); font-weight: 800; }
.stage-instruction .pv-h  { color: var(--hundreds);    font-weight: 800; }
.stage-instruction .pv-t  { color: var(--tens);        font-weight: 800; }
.stage-instruction .pv-o  { color: var(--ones);        font-weight: 800; }

/* ── the machine ── */
.machine-wrap { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; min-height: 0; overflow: hidden; }
.machine {
  display: flex; gap: clamp(24px, 6vw, 72px); align-items: flex-end;
  justify-content: center;
}

.box {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(6px, 1.2vh, 10px);
}
.box.hidden { display: none; }

/* Monument-Valley-style ombre marbles — each ball is a two-tone gradient.
   The frame/digit/label/button keep the single --box-c tone so the three
   place values stay clearly differentiated. */
.box[data-box="o"] { --ball-a: #2BB0E6; --ball-b: #2FE1C6; } /* ones: blue → teal */
.box[data-box="t"] { --ball-a: #FF8E1E; --ball-b: #FFD63C; } /* tens: orange → yellow */
.box[data-box="h"] { --ball-a: #E45A6C; --ball-b: #A62D42; } /* hundreds: coral-red ombre */

.digit {
  font-family: var(--font-brand);
  font-weight: 800;
  font-size: clamp(34px, 7vh, 64px);
  line-height: 1;
  color: var(--box-c, var(--ink-900));
  min-width: 1.1ch; text-align: center;
  transition: transform .18s var(--ease-pop);
}
.digit.flip { animation: flip .26s var(--ease-pop); }
@keyframes flip { 0%{transform:scale(1.4)} 100%{transform:scale(1)} }
.digit.ten { color: var(--brand-red); animation: ten-buzz .5s ease-in-out infinite; }
@keyframes ten-buzz { 0%,100%{transform:rotate(-3deg) scale(1.05)} 50%{transform:rotate(3deg) scale(1.12)} }

.frame {
  position: relative;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: clamp(4px, 0.9vh, 8px);
  padding: clamp(6px, 1.2vh, 11px);
  background: var(--surface-card);
  border: 3px solid var(--box-c);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  width: clamp(100px, 14vw, 132px);
  height: clamp(200px, 40vh, 320px);
  transition: box-shadow var(--dur-base), transform var(--dur-base);
}
/* ten-frame divider between the top five and bottom five */
.frame::after {
  content: ''; position: absolute; left: 8px; right: 8px; top: 50%;
  height: 2px; background: color-mix(in srgb, var(--box-c) 26%, transparent);
  transform: translateY(-1px);
}
.slot, .dot {
  /* Fit the ball inside its grid cell: width-driven with the height capped, so
     it never overflows either edge as the frame changes shape. */
  aspect-ratio: 1 / 1; width: 88%; height: auto; max-height: 88%;
  border-radius: 50%; place-self: center;
}
.slot {
  background: color-mix(in srgb, var(--box-c) 9%, #fff);
  box-shadow: inset 0 2px 4px rgba(0,0,0,.05);
}

/* A glossy 3D marble: ombre body + spherical shading + a specular highlight,
   lifted off the tray with a soft drop shadow. */
.dot {
  position: relative;
  cursor: pointer;
  background:
    radial-gradient(circle at 30% 26%, rgba(255,255,255,.55), rgba(255,255,255,0) 34%),
    radial-gradient(circle at 66% 84%, rgba(0,0,0,.32), rgba(0,0,0,0) 50%),
    linear-gradient(158deg, var(--ball-a, var(--box-c)) 4%, var(--ball-b, var(--box-c)) 96%);
  box-shadow:
    inset 0 -5px 9px rgba(0,0,0,.22),
    inset 2px 3px 8px rgba(255,255,255,.30),
    0 5px 9px -2px rgba(0,0,0,.30);
  animation: dot-pop var(--dur-base) var(--ease-pop);
}
/* the bright glossy catch-light near the top-left */
.dot::after {
  content: ''; position: absolute; top: 11%; left: 17%;
  width: 34%; height: 24%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(255,255,255,.92), rgba(255,255,255,0) 72%);
  transform: rotate(-22deg);
}
@keyframes dot-pop { from { transform: scale(0); } to { transform: scale(1); } }

/* box that has ten dots: whole frame becomes one big BANG button */
.box.full .frame {
  cursor: pointer;
  border-color: var(--brand-red);
  /* Ring + red border for emphasis, but no wobble rotation — that enlarged the
     frame's box and read as the box changing size. The "Tap to BANG" button
     carries the animation now. */
  box-shadow: 0 0 0 4px rgba(235,124,178,.3), var(--shadow-lg);
}
/* The "Tap to BANG" call-to-action now lives on the Drop button (see below),
   so the frame no longer needs its own hint text. */
.box.full .dot { cursor: pointer; }
@keyframes wobble {
  0%,100% { transform: rotate(-1.5deg); }
  50%     { transform: rotate(1.5deg); }
}

.box-label {
  font-family: var(--font-brand);
  font-weight: 700;
  font-size: clamp(15px, 2.3vh, 20px);
  color: var(--box-c);
  text-transform: capitalize;
  letter-spacing: .01em;
}

.drop-btn {
  border: none; cursor: pointer;
  background: var(--box-c);
  color: #fff;
  font-family: var(--font-brand);
  font-weight: 800;
  font-size: clamp(14px, 2vh, 17px);
  border-radius: var(--radius-full);
  padding: 0 24px;
  min-height: 64px;
  width: clamp(148px, 20vw, 188px);
  box-shadow: 0 6px 0 var(--box-shadow-c), var(--shadow-md);
  transition: transform .14s var(--ease-pop), box-shadow .14s;
  touch-action: manipulation;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  line-height: 1.05;
  white-space: nowrap;
}
.drop-btn:active { transform: translateY(4px); box-shadow: 0 2px 0 var(--box-shadow-c); }
.drop-btn:disabled { opacity: .4; cursor: default; box-shadow: 0 6px 0 var(--box-shadow-c); transform: none; }
.drop-btn .plus { font-size: 1.25em; }

/* A full box: the Drop button turns white and reads "Tap to BANG", with BANG
   in the opening-card gradient, wobbling to invite the tap. */
.drop-btn.to-bang {
  background: #fff;
  color: var(--plum);
}
.drop-btn.to-bang .bang-word-btn {
  display: inline-block; font-weight: 800;
  background-image: linear-gradient(110deg, #FF7AA2, #FF4D6D 55%, #FF9D57);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
  animation: bang-pulse 1.6s var(--ease-pop) infinite;
}

.play-footer { display: flex; justify-content: center; padding-top: 6px; min-height: 54px; }

/* ═══════════════  PANEL (worth-cards / split)  ═══════════════ */
.panel {
  max-width: 880px; margin: 6px auto 0; width: 100%;
  text-align: center;
}
.panel .panel-q { font-size: clamp(16px, 2.4vh, 21px); font-weight: 700; color: var(--text-strong); margin: 0 0 12px; }
.worth-cards { display: flex; gap: clamp(10px, 2vw, 20px); justify-content: center; flex-wrap: wrap; }
.worth-card {
  border: 3px solid var(--line-dark); background: var(--card-inset);
  border-radius: var(--radius-lg);
  font-family: var(--font-brand); font-weight: 800;
  /* Sized to match the quiz answer buttons — the big cards looked oversized. */
  font-size: clamp(16px, 2.6vh, 22px); color: #fff;
  min-width: 88px; min-height: 64px; padding: 12px 20px;
  cursor: pointer; box-shadow: var(--shadow-md);
  transition: transform .16s var(--ease-pop), border-color .16s, background .16s;
}
.worth-card:hover { border-color: var(--brand-red); }
.worth-card:active { transform: scale(.95); }
.worth-card.wrong { animation: shake .4s; border-color: var(--warning); }
.worth-card.right { background: #A1FCD2; color: var(--plum); border-color: #A1FCD2; transform: scale(1.06); }
@keyframes shake { 0%,100%{transform:translateX(0)} 20%{transform:translateX(-8px)} 40%{transform:translateX(8px)} 60%{transform:translateX(-6px)} 80%{transform:translateX(6px)} }

.split-line {
  font-family: var(--font-brand); font-weight: 800;
  font-size: clamp(24px, 4.4vh, 40px); margin: 4px 0;
}
.split-line .hl-th { color: var(--brand-green); }
.split-line .hl-h { color: var(--hundreds); }
.split-line .hl-t { color: var(--tens); }
.split-line .hl-o { color: var(--ones); }

/* ═══════════════  STAGE-CLEAR OVERLAY  ═══════════════ */
.overlay {
  position: absolute; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  background: rgba(15, 0, 12, .62);
  backdrop-filter: blur(3px);
  padding: 20px;
}
.overlay-card {
  background: var(--card); border: 1px solid var(--line-dark); border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: clamp(24px, 5vh, 44px) clamp(24px, 5vw, 52px);
  text-align: center; max-width: 520px;
}
.overlay-badge {
  margin: 0 auto 10px;
  font-size: clamp(38px, 6vh, 48px); line-height: 1;
}
.overlay-card h2 { font-size: clamp(24px, 4.2vh, 38px); margin: 0 0 8px; }
.overlay-card p { font-size: clamp(15px, 2.3vh, 19px); font-weight: 700; color: #fff; margin: 0 0 22px; text-wrap: balance; }
/* Keep the card's action button label on a single line, scaling to fit. */
.overlay-card .btn-start {
  white-space: nowrap;
  max-width: 100%;
  font-size: clamp(15px, 3.4vw, 24px);
  padding: 16px clamp(20px, 4vw, 44px);
}

/* While asking a column's worth, the active column is simply the one that
   isn't dimmed — no extra ring or lift, which read as too busy. */
.box.dim { opacity: .4; }

/* ═══════════════  QUIZ  ═══════════════ */
#screen-quiz { align-items: center; justify-content: center; }
/* The quiz lives in the same box as the opening card, for consistency. */
.quiz-card {
  width: min(520px, calc(100vw - 32px));
  background: var(--card);
  border: 1px solid var(--line-dark);
  border-radius: 24px;
  box-shadow: var(--shadow-lg);
  padding: clamp(28px, 4vh, 42px) clamp(24px, 4vw, 40px) clamp(26px, 3.4vh, 36px);
  text-align: center;
}
.quiz-top { text-align: center; margin-bottom: 6px; }
.quiz-top h2 { font-size: clamp(24px, 4.6vh, 34px); color: #fff; line-height: 1.1; }
.stars, .finale-stars { display: flex; gap: 8px; justify-content: center; margin-top: 8px; min-height: 34px; }
.star {
  width: 30px; height: 30px; display: inline-block;
  background: var(--ink-200);
  clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
  transition: background .3s, transform .3s var(--ease-pop);
}
.star.on { background: var(--brand-yellow); transform: scale(1.15); }
.finale-stars .star { width: 40px; height: 40px; }
/* When a finale has no stars (e.g. Level 1), don't reserve the tray's height —
   otherwise the empty space at the top makes the card text look offset low. */
.finale-stars:empty { min-height: 0; margin-top: 0; }

.quiz-body {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 16px;
  width: 100%; min-height: 0;
}
.quiz-q {
  font-family: var(--font-brand); font-weight: 800;
  font-size: clamp(16px, 2.6vh, 21px); text-align: center; color: #EB7CB2;
  text-wrap: balance;
}
.quiz-scene { display: flex; gap: 16px; justify-content: center; align-items: flex-end; }
.quiz-mini-box { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.quiz-mini-frame {
  display: grid; grid-template-columns: repeat(2, 22px); gap: 4px;
  padding: 8px; border: 3px solid var(--box-c); border-radius: 12px; background: #fff;
}
.quiz-mini-frame .d { width: 22px; height: 22px; border-radius: 50%; background: var(--box-c); }
.quiz-mini-label { font-family: var(--font-brand); font-weight: 700; font-size: 14px; color: var(--box-c); }

.quiz-options { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; width: 100%; }
.quiz-option {
  border: 3px solid var(--line-dark); background: var(--card-inset);
  border-radius: var(--radius-lg);
  font-family: var(--font-brand); font-weight: 800;
  font-size: clamp(16px, 2.6vh, 22px); color: #fff;
  min-width: 88px; min-height: 64px; padding: 12px 20px;
  cursor: pointer; box-shadow: var(--shadow-md);
  transition: transform .16s var(--ease-pop), border-color .16s, background .16s, color .16s;
}
.quiz-option:active { transform: scale(.95); }
.quiz-option.wrong { animation: shake .4s; border-color: var(--warning); opacity: .55; }
.quiz-option.right { background: #A1FCD2; color: var(--plum); border-color: #A1FCD2; transform: scale(1.06); }
.quiz-feedback {
  min-height: 2.4em; text-align: center; font-size: clamp(14px, 2vh, 17px);
  font-weight: 600; color: var(--text-soft); max-width: 100%;
}
.quiz-feedback.good { color: var(--success); }
/* The lime + speech bubble that relays quiz feedback, anchored to the left. */
.quiz-mascot-row { display: flex; align-items: center; gap: 12px; justify-content: flex-start; width: 100%; margin-top: 16px; }
.quiz-mascot-row[hidden] { display: none; }
.quiz-mascot-row .mascot { width: clamp(54px, 8vh, 72px); height: clamp(54px, 8vh, 72px); }
.quiz-mascot-row .speech p { text-align: left; }

/* ═══════════════  FINALE  ═══════════════ */
/* Scrollable so a tall card (the Ball Master feedback form) is reachable on
   short screens; margin-block:auto still centres a short card when it fits. */
#screen-finale { align-items: center; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.finale-card { margin-block: auto; }
/* The level-complete card uses the exact same box as the opening card
   (.intro-card) so the experience feels consistent end to end. */
.finale-card {
  width: min(520px, calc(100vw - 32px));
  background: var(--card);
  border: 1px solid var(--line-dark);
  border-radius: 24px;
  box-shadow: var(--shadow-lg);
  padding: clamp(28px, 4vh, 42px) clamp(24px, 4vw, 40px) clamp(26px, 3.4vh, 36px);
  text-align: center;
}
.finale-card h1 { font-size: clamp(26px, 4.6vh, 40px); line-height: 1.1; margin: 8px 0 10px; text-wrap: balance; }
.takeaway-line {
  font-size: clamp(15px, 2.4vh, 20px); line-height: 1.4; color: var(--text-body);
  /* Extra gap below (10px + 200% = 30px) separates the white "what just
     happened" line from the aqua "what's next" note. The card is vertically
     centred, so widening this gap lifts the white text up. */
  max-width: 100%; margin: 0 auto 30px; text-wrap: balance;
}
.takeaway-line .hl-th { color: var(--brand-green); }
.takeaway-line .hl-h { color: var(--hundreds); }
.takeaway-line .hl-t { color: var(--tens); }
.takeaway-line .hl-o { color: var(--ones); }
/* The follow-up line ("Ready for the hundreds box?") is the next step, so it
   reads as prominently as the takeaway above it — button-mint, bold, same size. */
.finale-note { color: var(--nav); font-size: clamp(15px, 2.4vh, 20px); margin: 0 0 22px; font-weight: 800; }
/* The "next box" word in the note carries its own place-value colour. */
.finale-note .hl-th { color: var(--brand-green); }
.finale-note .hl-h  { color: var(--hundreds); }
/* The final "Ball Master" card: all text bold, body text pink (the thousands
   words keep their green, per the place-value colour scheme). */
.finale-card.champion,
.finale-card.champion h1,
.finale-card.champion .takeaway-line { font-weight: 800; }
.finale-card.champion .takeaway-line { color: #EB7CB2; }

/* ── Ball Master feedback form ── */
#fb-form { text-align: center; }
.fb-ask {
  margin: 6px 0 0; font-family: var(--font-brand); font-weight: 800;
  font-size: clamp(16px, 2.6vh, 21px); color: #EB7CB2; text-wrap: balance;
}
.fb-row { display: flex; gap: 12px; margin-top: 16px; align-items: flex-end; }
.fb-field { display: flex; flex-direction: column; gap: 6px; text-align: left; margin-top: 14px; }
.fb-row .fb-field { margin-top: 0; }
.fb-field--grow { flex: 1 1 auto; }
.fb-field--age { flex: 0 0 88px; }
.fb-label { font-family: var(--font-brand); font-weight: 700; font-size: 13px; color: var(--text-soft); }
.fb-input {
  width: 100%; min-height: 52px; box-sizing: border-box;
  background: var(--card-inset); border: 3px solid var(--line-dark); border-radius: var(--radius-lg);
  padding: 12px 16px; font-family: var(--font-brand); font-weight: 600;
  font-size: clamp(15px, 2.2vh, 18px); color: #fff; transition: border-color .16s;
}
.fb-input::placeholder { color: rgba(248, 244, 241, .4); }
.fb-input:focus { outline: none; border-color: #EB7CB2; }
.fb-select { cursor: pointer; text-align: center; }
.fb-select option { color: #fff; background: var(--plum); }
.fb-textarea { resize: vertical; min-height: 76px; line-height: 1.4; }
.fb-rate { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; margin-top: 16px; }
.fb-rate .quiz-option { min-width: 0; min-height: 56px; padding: 12px 8px; font-size: clamp(15px, 2.4vh, 19px); }
.fb-rate .quiz-option.is-picked { background: #A1FCD2; color: var(--plum); border-color: #A1FCD2; transform: scale(1.04); }
.fb-actions { display: flex; gap: 12px; margin-top: 18px; align-items: stretch; }
.fb-actions .btn-intro-level,
.fb-actions .btn-intro-start { flex: 1 1 0; width: auto; text-decoration: none; text-align: center; }
@media (max-width: 480px) { .fb-actions { flex-direction: column; } }

/* ═══════════════  BURST / CONFETTI / FINALE-F  ═══════════════ */
#burst-layer {
  position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 40;
}
.starburst {
  position: absolute; width: 120px; height: 120px; margin: -60px 0 0 -60px;
  transform: scale(0);
}
.starburst svg { width: 100%; height: 100%; }
.starburst.go { animation: burst .55s var(--ease-pop) forwards; }
@keyframes burst {
  0%   { transform: scale(0) rotate(0); opacity: 1; }
  60%  { transform: scale(1.15) rotate(30deg); opacity: 1; }
  100% { transform: scale(1.4) rotate(45deg); opacity: 0; }
}
.fly-dot {
  position: absolute; width: 44px; height: 44px; margin: -22px 0 0 -22px;
  border-radius: 50%; box-shadow: inset 0 -4px 0 rgba(0,0,0,.14);
  transition: transform .5s var(--ease-pop);
  z-index: 41;
}
.fly-dot::after { content:''; position:absolute; top:16%; left:20%; width:26%; height:26%; border-radius:50%; background:rgba(255,255,255,.75); }

/* Fruit sprinkle — a variety of fruit rains from the top of the page on success. */
.fruit-sprinkle {
  position: fixed;
  inset: 0 0 auto 0;
  height: 0;
  pointer-events: none;
  z-index: 900;            /* above the header/footer (10), below any modal */
}
.fruit-sprinkle-item {
  position: fixed;
  display: block;
  height: auto;
  opacity: 0;               /* hidden until its animation fades it in (no start flash) */
  will-change: transform, opacity;
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .16));
}
/* The burst rides its own layer, above everything. */
.fruit-burst {
  position: fixed;
  inset: 0;
  height: 0;
  pointer-events: none;
  z-index: 950;
}
/* A single piece of an outward fruit explosion (BANG / Start), positioned at
   its origin point via inline left/top and animated by app.js. */
.fruit-burst-item {
  position: fixed;
  display: block;
  height: auto;
  opacity: 0;               /* hidden until its animation fades it in (no start flash) */
  will-change: transform, opacity;
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .18));
}

.finale-dot {
  position: absolute; width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border-radius: 50%; box-shadow: inset 0 -4px 0 rgba(0,0,0,.14);
  transition: transform 1.1s var(--ease-pop), opacity .6s;
}

/* ═══════════════  reduced motion  ═══════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .bang-word, .m-bulb, .box.full .frame, .digit.ten { animation: none !important; }
}

/* ═══════════════  short / landscape phones  ═══════════════ */
@media (max-height: 560px) {
  .frame { height: clamp(200px, 46vh, 300px); }
  .mascot-row { gap: 8px; }
}

/* ═══════════════  mobile play screen  ═══════════════
   Like the Fruit3D reference (examples.fruit3d.com/maths-3d-solids): the
   interactive machine fills the top, and the guidance ("instructions") — the
   mascot, speech bubble and the prompt line — sit in a dock at the bottom. */
@media (max-width: 640px) {
  /* NO scrolling — everything fits the device. The machine (the ones/tens/
     hundreds containers) is the ONE flexible element: it shrinks/grows to fill
     whatever height is left after all the fixed chrome. */
  #screen-play {
    padding: 8px 8px calc(10px + env(safe-area-inset-bottom, 0px));
    gap: 4px; overflow: hidden;
  }

  /* Unwrap .play-top so the progress pips and the mascot can be placed
     independently — pips stay at the top, the mascot moves to the bottom. */
  .play-top { display: contents; }

  /* Every chrome row keeps its natural (fixed) size — only the machine flexes. */
  .stage-pips, .play-footer, .stage-instruction, .mascot-row, .panel { flex: none; }

  .stage-pips     { order: 0; margin-top: 2px; }
  /* The machine is the flexible middle: it fills the leftover height and, if
     that's tight, shrinks itself (and its balls) rather than causing a scroll. */
  .machine-wrap   { order: 1; flex: 1 1 auto; min-height: 0; align-items: center; justify-content: center; overflow: hidden; padding-top: 4px; }
  .panel          { order: 2; }
  .stage-instruction { order: 3; }
  .mascot-row     { order: 4; }
  /* Clear button + compact % bar sit at the very bottom, well clear of the
     "Drop a Ball" / worth-card number buttons above. */
  .play-footer    {
    order: 5; min-height: 0;
    padding: 8px 8px calc(6px + env(safe-area-inset-bottom, 0px));
    display: flex; flex-direction: row; align-items: center; justify-content: center;
    gap: 10px; flex-wrap: wrap;
  }

  /* The instruction dock sits just below the controls (tighter gap). */
  .stage-instruction {
    margin: clamp(14px, 3.4vh, 30px) 0 0; padding: 0 12px;
    font-size: clamp(15px, 4.4vw, 19px);
  }
  /* Left-align so the kiwi stays put; the speech bubble simply shortens/wraps
     to its right instead of re-centring (which shifted the kiwi around). */
  .mascot-row {
    gap: 8px; justify-content: flex-start; flex-wrap: nowrap;
    padding: 4px 8px calc(2px + env(safe-area-inset-bottom, 0px));
  }
  .mascot { width: 54px; height: 54px; }
  .speech { max-width: calc(100vw - 92px); padding: 7px 13px; }
  .speech p { font-size: clamp(14px, 3.7vw, 16px); }

  .machine { gap: clamp(8px, 3vw, 18px); align-items: flex-end; }
  .box { min-width: 0; }
  /* The frame keeps a ten-frame proportion (2 wide : 5.4 tall). Its width is
     the SMALLER of: a per-column width (so 3 fit across), and a height-derived
     width (available height / 5.4 * 2, using dvh minus the fixed chrome ~480px).
     So it shrinks — balls and all — to fit any device, no scrolling. */
  .frame {
    width: max(30px, min(clamp(50px, 26vw, 90px), calc((100dvh - 510px) * 0.37)));
    aspect-ratio: 2 / 5.4;
    height: auto;
    margin: 0 auto;
    border-width: 2.5px;
  }
  .digit { font-size: clamp(20px, 6vw, 34px); }
  .box-label { font-size: clamp(12px, 3.2vw, 15px); }

  /* Four columns (Level 3, thousands box) — tighter still, and the drop button
     collapses to just a "+" so all four fit. */
  .machine.cols-4 { gap: clamp(4px, 1.4vw, 10px); }
  .machine.cols-4 .frame {
    width: max(28px, min(clamp(40px, 20vw, 74px), calc((100dvh - 510px) * 0.37)));
  }
  .machine.cols-4 .digit { font-size: clamp(16px, 5vw, 28px); }
  .machine.cols-4 .box-label { font-size: clamp(9px, 2.5vw, 12px); }
  .machine.cols-4 .drop-btn .drop-txt { display: none; }
  .machine.cols-4 .drop-btn { padding: 0; min-height: 48px; font-size: 20px; }

  /* Buttons fill their column and keep "Drop a Ball" on one line. */
  .drop-btn {
    width: 100%; min-width: 0;
    padding: 0 6px;
    min-height: 54px;
    font-size: clamp(11px, 3vw, 14px);
    box-shadow: 0 5px 0 var(--box-shadow-c), var(--shadow-md);
    gap: 3px;
  }
  .drop-btn .plus { font-size: 1.1em; }
  .drop-btn:active { transform: translateY(3px); box-shadow: 0 2px 0 var(--box-shadow-c); }

  /* Worth-card answers: compact so all choices fit one row without clashing. */
  .worth-cards { gap: 6px; flex-wrap: nowrap; }
  .worth-card { min-width: 0; flex: 1 1 0; min-height: 52px; padding: 8px 6px; font-size: clamp(14px, 4.4vw, 20px); }

  /* When a worth-card panel is on screen there's more chrome below the machine,
     so shrink the columns further so nothing overlaps and it still fits. */
  #screen-play:has(.panel:not([hidden])) .frame {
    width: max(40px, min(clamp(44px, 17vw, 64px), calc((100dvh - 500px) * 0.37)));
  }
  #screen-play:has(.panel:not([hidden])) .digit { font-size: clamp(16px, 5vw, 26px); }
}
