/* ===========================================================================
   Pong & Languages — styles

   Written for an audience that runs from a seven-year-old to an eighty-year-old,
   which drove three decisions you will see everywhere below:

   1. NOTHING IS SMALL. The smallest interactive target is 44px; the smallest text
      in the interface is 15px and everything that matters is far larger. A text-size
      control (`[data-text]`) scales the whole interface, not just paragraphs.
   2. NOTHING IS COLOUR-ONLY. Right and wrong carry an icon and a word as well as a
      colour; the two paddles differ in shape; a disabled control says so in text.
      `[data-contrast]` swaps in a stronger palette with heavier outlines.
   3. THE COURT NEVER MOVES. The challenge panel below it is always the same height,
      so a question appearing cannot reflow the thing the player is watching.

   Both themes are complete palettes; nothing is defined only inside a media query.
   =========================================================================== */

:root {
  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --radius: 20px;
  --radius-sm: 14px;
  --radius-xs: 10px;

  --fs-base: 16px;
  --fs-sm: 0.875rem;
  --fs-md: 1rem;
  --fs-lg: 1.15rem;
  --fs-xl: 1.4rem;
  --tap: 48px;

  --panel-h: 278px;
  --dur: 220ms;
  --ease: cubic-bezier(.22, .78, .28, 1);
}

/* Text size is a first-class setting: everything downstream is in rem. */
html[data-text="large"]  { --fs-base: 18.5px; --tap: 52px; --panel-h: 300px; }
html[data-text="xlarge"] { --fs-base: 21px;   --tap: 58px; --panel-h: 322px; }

html { font-size: var(--fs-base); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }

/* ------------------------------------------------------------------ themes */
[data-theme="dark"] {
  --bg: #070c18;
  --bg-2: #0d1526;
  --surface: #131d33;
  --surface-2: #1b2740;
  --surface-3: #24314e;
  --text: #eaf0ff;
  --text-dim: #9aa8c6;
  --border: #26324e;
  --border-soft: #ffffff14;

  --primary: #38bdf8;
  --primary-ink: #04283a;
  --rival: #fb7185;
  --good: #34d399;
  --good-ink: #04291d;
  --bad: #fb7185;
  --warn: #fbbf24;
  --violet: #a78bfa;

  --court-floor: #0a1122;
  --court-floor-2: #101c33;
  --court-line: #31405f;
  --court-player: #38bdf8;
  --court-rival: #fb7185;
  --court-ball: #f8fafc;
  --court-trail: #7dd3fc;
  --court-shield: #34d399;
  --court-slow: #a78bfa;
  --court-edge: #22304c;
  --court-lineweight: 2;

  --shadow-1: 0 1px 2px rgba(0,0,0,.44), 0 6px 18px rgba(0,0,0,.36);
  --shadow-2: 0 2px 8px rgba(0,0,0,.48), 0 18px 44px rgba(0,0,0,.46);
  --plate: #f4f7ff;          /* drawings are outline art: they need a light ground */
  --plate-ring: #d7e0f5;
  --scroll: #33415f;
  --scroll-hover: #4c5f86;
}

[data-theme="light"] {
  --bg: #e9edf7;
  --bg-2: #f6f8fd;
  --surface: #ffffff;
  --surface-2: #f2f5fc;
  --surface-3: #e6ecf9;
  --text: #101a30;
  --text-dim: #5a6580;
  --border: #d8e0f0;
  --border-soft: #0b132a12;

  --primary: #0284c7;
  --primary-ink: #ffffff;
  --rival: #e11d48;
  --good: #047857;
  --good-ink: #ffffff;
  --bad: #e11d48;
  --warn: #b45309;
  --violet: #6d28d9;

  --court-floor: #ccd8ef;
  --court-floor-2: #e3eaf8;
  --court-line: #94a6c8;
  --court-player: #0369a1;
  --court-rival: #be123c;
  --court-ball: #0f172a;
  --court-trail: #38bdf8;
  --court-shield: #047857;
  --court-slow: #6d28d9;
  --court-edge: #c3cee6;
  --court-lineweight: 2;

  --shadow-1: 0 1px 2px rgba(24,40,80,.09), 0 6px 18px rgba(24,40,80,.10);
  --shadow-2: 0 2px 8px rgba(24,40,80,.12), 0 18px 44px rgba(24,40,80,.16);
  --plate: #ffffff;
  --plate-ring: #dde5f5;
  --scroll: #b9c5dc;
  --scroll-hover: #93a3c4;
}

/* High contrast: fewer surfaces, harder edges, no translucency anywhere. */
[data-contrast="on"][data-theme="dark"] {
  --bg: #000000; --bg-2: #05070d; --surface: #0b1020; --surface-2: #151d33; --surface-3: #1e2841;
  --text: #ffffff; --text-dim: #cbd5e1; --border: #7c8db3; --border-soft: #7c8db3;
  --primary: #7dd3fc; --rival: #fda4af; --good: #6ee7b7; --bad: #fda4af; --warn: #fcd34d;
  --court-floor: #000000; --court-floor-2: #060a14; --court-line: #7c8db3; --court-edge: #9fb0d6;
  --court-lineweight: 3;
  --scroll: #9fb0d6; --scroll-hover: #ffffff;
}
[data-contrast="on"][data-theme="light"] {
  --bg: #ffffff; --bg-2: #ffffff; --surface: #ffffff; --surface-2: #f0f3fa; --surface-3: #e2e8f5;
  --text: #000000; --text-dim: #1f2937; --border: #1f2937; --border-soft: #1f2937;
  --primary: #0b5f8a; --rival: #a1112f; --good: #04593f; --bad: #a1112f; --warn: #7c4a02;
  --court-floor: #ffffff; --court-floor-2: #f2f5fc; --court-line: #334155; --court-edge: #1f2937;
  --court-lineweight: 3;
  --scroll: #475569; --scroll-hover: #000000;
}

/* ------------------------------------------------------------- scroll bars */
/*
   The browser's default bar — stepper arrows, a hard track, a square thumb — is the one
   piece of Windows left on screen in a game that draws everything else itself.
   Replaced with a thumb and nothing else.

   The trick that matters is `border: 4px solid transparent` with `background-clip:
   content-box`: the thumb PAINTS as a 6px pill but the grab area is the full 14px, so it
   looks minimal and is still catchable by someone whose hands are not steady. Thin looks
   are not worth an unusable target.

   `.scrolling` is set on <html> for a moment after any scrolling — the thumb steps
   forward while you are moving and settles back when you stop.
*/
/* The standard properties drive Firefox and Chrome 121+; the ::-webkit- rules below drive
   the older engines that still paint stepper arrows — the ones in the reference. Width is
   left at `auto` on a desktop on purpose: `thin` is about 11px there, and shaving three
   pixels off the only thing on screen you have to catch with a mouse is not a trade worth
   making. Phones get `thin`, where the bar is an overlay and never in the way anyway. */
* { scrollbar-width: auto; scrollbar-color: var(--scroll) transparent; }

::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }
/* No steppers. This is the pair of arrows in the reference screenshot. */
::-webkit-scrollbar-button { display: none; width: 0; height: 0; }

::-webkit-scrollbar-thumb {
  background-color: var(--scroll);
  border: 4px solid transparent;
  background-clip: content-box;
  border-radius: 999px;
  transition: background-color 160ms var(--ease);
}
::-webkit-scrollbar-thumb:hover { background-color: var(--scroll-hover); border-width: 3px; }
::-webkit-scrollbar-thumb:active { background-color: var(--scroll-hover); border-width: 2px; }

/* While scrolling, the thumb is easier to see; a moment later it recedes again. */
html.scrolling ::-webkit-scrollbar-thumb { background-color: var(--scroll-hover); border-width: 3px; }
html.scrolling { scrollbar-color: var(--scroll-hover) transparent; }

/* A phone hides its bar anyway; on a small screen the thinner one is plenty. */
@media (max-width: 620px) {
  * { scrollbar-width: thin; }
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-thumb { border-width: 3px; }
}

@media (prefers-reduced-motion: reduce) {
  ::-webkit-scrollbar-thumb { transition: none; }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  font-family: var(--font);
  background: radial-gradient(1200px 700px at 50% -10%, var(--bg-2), var(--bg) 62%);
  color: var(--text);
  min-height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
}
button, input, select { font: inherit; color: inherit; }
h1, h2, h3, p { margin: 0; }

.hidden { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 100;
  background: var(--primary); color: var(--primary-ink);
  padding: .7rem 1rem; border-radius: 0 0 var(--radius-xs) 0; font-weight: 700;
}
.skip-link:focus { left: 0; }

/* The illustration sprite is a real <svg> in the document, and an <svg> with no width or
   height is 300×150 by default — it was sitting at the top of the page pushing the whole
   game down by 150px, which put the answer buttons below the fold on a laptop. It holds
   only <symbol>s, which never render; it just must not take up space. */
#ill-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

:where(button, [tabindex], a):focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 3px;
}

/* Every vendored icon carries this class and its own em sizing, so an icon is the same
   size wherever it is inserted — from the HTML or from JavaScript. `flex: none` stops a
   flex row from squashing one. */
.ico { width: 1em; height: 1em; display: block; flex: none; }

/* An element hidden with the attribute must actually be hidden. Half the HUD sets
   `hidden` in JavaScript, and every one of those chips is `display: flex` here — without
   this the combo chip sat on screen reading "×0" and the blitz clock showed up in
   practice mode. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------------ buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  min-height: var(--tap); padding: .7rem 1.25rem;
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text);
  font-size: var(--fs-md); font-weight: 650; cursor: pointer;
  transition: transform var(--dur) var(--ease), background var(--dur), border-color var(--dur);
}
.btn [data-icon], .btn > span[data-icon] { font-size: 1.25em; }
.btn:hover { background: var(--surface-3); }
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: var(--primary); color: var(--primary-ink); border-color: transparent;
  box-shadow: var(--shadow-1);
}
.btn-primary:hover { filter: brightness(1.06); background: var(--primary); }
.btn-ghost { background: transparent; }
.btn-lg { width: 100%; min-height: calc(var(--tap) + 10px); font-size: var(--fs-lg); font-weight: 800; }
.btn-danger { border-color: var(--bad); color: var(--bad); background: transparent; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap); flex: none;
  border: 1.5px solid var(--border); border-radius: var(--radius-xs);
  background: var(--surface); cursor: pointer; font-size: 1.25rem;
  transition: background var(--dur), border-color var(--dur);
}
.icon-btn:hover { background: var(--surface-3); }

/* ------------------------------------------------------------------- setup */
.screen { height: 100dvh; width: 100%; }
.setup { overflow-y: auto; display: flex; justify-content: center; padding: clamp(.75rem, 2vw, 2rem); }
.setup-card {
  /* `margin: auto` centres it while leaving it reachable: `align-items: center` on an
     overflowing flex child pushes the top of the card above the scroll origin, where no
     amount of scrolling can get to it. */
  width: min(940px, 100%); margin: auto;
  background: var(--surface); border: 1.5px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-2);
  padding: clamp(1rem, 2.4vw, 1.9rem);
  display: flex; flex-direction: column; gap: clamp(1rem, 2vw, 1.5rem);
}
/* The header is allowed to WRAP, and that is the whole fix for the settings button
   leaving the screen. Everything in this row grows with the text-size setting — the title,
   and the three buttons through `--tap` (48 / 52 / 58px) — while the phone it is drawn on
   does not. A rigid row has only one way to answer that, which is to push its last item
   out of the viewport, and it did: measured, the gear sat 9px past the right edge at 412px
   wide and 101px past it at 320px, with the chart button following it out. Raising the
   floor on the title's share and letting the row break instead means the buttons drop to a
   line of their own exactly when they no longer fit, at any text size, without the type
   being quietly shrunk to buy the room. */
.brand { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.brand-mark { font-size: 2.6rem; color: var(--primary); display: block; }
/* A basis rather than a bare `flex: 1`: the basis is what the row consults when it decides
   whether the buttons still fit beside the text, and in `rem` it tracks the text size on
   its own. `min-width: 0` keeps the long words breakable once the row has settled. */
.brand-text { flex: 1 1 12rem; min-width: 0; }
.brand-text h1 { font-size: clamp(1.5rem, 3.6vw, 2.1rem); font-weight: 900; letter-spacing: -.02em; }
.tagline { color: var(--text-dim); font-size: var(--fs-md); margin-top: .15rem; }
/* Never squashed and never split: if the three do not fit beside the title they move
   together, and they stay against the right edge where they have always been. */
.brand-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .5rem; flex: none; margin-left: auto; }

.setup-group h2 { font-size: var(--fs-md); font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--text-dim); margin-bottom: .6rem; }
/* Side by side only when the two rows of pills genuinely fit three across; below that the
   pair stacks and each row gets the full width of the card, which is what keeps the three
   languages on one line for far longer than squeezing them into half a card ever did. */
.lang-pair { display: grid; grid-template-columns: 1fr; gap: .75rem; align-items: end; }
@media (min-width: 1000px) {
  .lang-pair { grid-template-columns: 1fr auto 1fr; }
}
.lang-arrow { color: var(--text-dim); font-size: 1.5rem; padding-bottom: .7rem; }
/* Three identical boxes, always. Sized by the grid rather than by their contents, because
   "Português" is a longer word than "English" and letting content decide made its button
   23px wider than its neighbours — which reads as a bigger button, then as bigger text.
   The type size flexes with the viewport so the longest of the three keeps real breathing
   room inside the box instead of filling it edge to edge: at 16px "Português PT" had six
   pixels of slack against English's twenty-nine, and that difference alone is what made
   it look oversized. Nothing here is per-language; the same rule fits all three. */
.pill-row {
  /* Three across when they fit, stacked when they do not. The pill width never followed
     the viewport — the row sits in one of two columns on a tablet and alone on a phone —
     so sizing the type from `vw` was guesswork, and shrinking it to fit "Português" made
     the type illegible at the tight sizes and clipped it anyway. The row asks its own
     width instead, in `em`, so the answer also tracks the text-size setting: at "very
     large" the three simply stack, each full width, nothing cropped. */
  display: grid; grid-template-columns: 1fr; gap: .5rem;
}
/* The container is the GROUP, not the row: an element cannot answer its own container
   query, so putting `container-type` on `.pill-row` and then querying `.pill-row` silently
   did nothing and left the three stacked at every width. */
.setup-group { container-type: inline-size; }
@container (min-width: 24em) {
  .pill-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@supports not (container-type: inline-size) {
  .pill-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pill {
  min-width: 0; min-height: var(--tap);
  /* A two-column grid, not a centred row: the code sits in a column of its own and the
     name is centred in what is left. Equal boxes were not enough on their own — the words
     are different lengths, so "Português" had six pixels of air where "English" had
     twenty-nine, and crowding reads as bigger type. Now the three share one geometry and
     the codes line up down the row. */
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: .35rem;
  padding: .5rem .5rem;
  border: 1.5px solid var(--border); border-radius: var(--radius-xs);
  background: var(--surface-2); cursor: pointer;
  font-weight: 700; font-size: var(--fs-md); line-height: 1.15;
  white-space: nowrap;
}
.pill > span:first-child { text-align: center; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.pill .pill-short {
  font-size: .74em; font-weight: 800; color: var(--text-dim); letter-spacing: .05em;
  /* Just wide enough for the widest of EN / PT / ES: the column must be identical across
     the three, and every pixel spent here is a pixel the longest name does not get. */
  min-width: 1.5em; text-align: center;
}
/* Sized here, after `.pill`, and not up in the block that flips the row to three across:
   `@container` adds no specificity, so a rule of equal weight written earlier in the file
   simply loses to `.pill { font-size: … }` below it — which is why the first attempt at
   this parsed, matched, and changed nothing.

   Three across is where the words start competing for room and the box can no longer grow
   to give it to them. Equal boxes and an equal code column were not enough on their own:
   measured at a 450px viewport, "English" floated in 26.6px of air while "Português" had
   2.7px, and a word pressed flat against its own box reads as bigger type — which is
   precisely what was reported. So the type answers the CONTAINER rather than the viewport
   (the row sits in one of two columns on a desktop and alone on a phone, so `vw` never
   described its width), and it answers on behalf of the LONGEST of the three: whatever
   size leaves "Português" real air on both sides is the size all three take. Nothing is
   per-language — one number for the row. Stacked, the box grows instead and the type
   stays full size; below the floor the row stacks, where there is room to spare. */
@container (min-width: 24em) {
  .pill { font-size: clamp(0.8rem, calc(5.385cqi - 0.272rem - 3.72px), var(--fs-md)); }
}
.pill[aria-checked="true"] {
  background: var(--primary); color: var(--primary-ink); border-color: transparent;
}
.pill[aria-checked="true"] .pill-short { color: inherit; opacity: .8; }

.chip-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.chip {
  display: inline-flex; align-items: center; gap: .5rem;
  min-height: var(--tap); padding: .5rem .9rem;
  border: 1.5px solid var(--border); border-radius: 999px;
  background: var(--surface-2); cursor: pointer; font-weight: 700;
}
.chip [data-icon] { font-size: 1.2em; color: var(--text-dim); }
.chip[aria-checked="true"] { background: var(--primary); color: var(--primary-ink); border-color: transparent; }
.chip[aria-checked="true"] [data-icon] { color: inherit; }
.chip-hint { margin-top: .5rem; color: var(--text-dim); font-size: var(--fs-sm); min-height: 1.4em; }

.choice-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: .65rem; }
.choice {
  text-align: left; padding: .9rem 1rem; cursor: pointer;
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
  display: grid; grid-template-columns: auto 1fr; gap: .3rem .75rem; align-items: start;
}
.choice-icon { font-size: 1.5rem; color: var(--primary); grid-row: span 2; }
.choice-title { font-weight: 800; font-size: var(--fs-md); }
.choice-desc { color: var(--text-dim); font-size: var(--fs-sm); line-height: 1.45; }
.choice[aria-checked="true"] { border-color: var(--primary); background: var(--surface-3); box-shadow: inset 0 0 0 1.5px var(--primary); }
.choice[aria-checked="true"] .choice-title::after { content: ""; }

.warn {
  display: flex; align-items: center; gap: .5rem; color: var(--warn);
  font-weight: 650; font-size: var(--fs-sm);
}
.fineprint { display: flex; align-items: center; gap: .45rem; color: var(--text-dim); font-size: var(--fs-sm); }

.setup-profile { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: .5rem; }
.mini-stat {
  background: var(--surface-2); border: 1.5px solid var(--border); border-radius: var(--radius-xs);
  padding: .6rem .75rem; display: flex; flex-direction: column; gap: .1rem;
}
.mini-stat-value { font-size: var(--fs-lg); font-weight: 900; }
.mini-stat-label { font-size: var(--fs-sm); color: var(--text-dim); }

/* -------------------------------------------------------------------- play */
.play { display: flex; flex-direction: column; height: 100dvh; padding: clamp(.4rem, 1.2vw, .9rem); gap: .6rem; }
.topbar { display: flex; align-items: center; gap: .75rem; flex: none; }
.topbar-left, .topbar-right { display: flex; align-items: center; gap: .5rem; }
.topbar-left { flex: 1; min-width: 0; }
.topbar-right { flex: none; }

.scoreboard {
  display: flex; align-items: center; gap: .7rem;
  background: var(--surface); border: 1.5px solid var(--border);
  border-radius: var(--radius-xs); padding: .35rem .9rem; min-height: var(--tap);
}
.score-side { display: flex; align-items: baseline; gap: .45rem; }
.score-name { font-size: var(--fs-sm); color: var(--text-dim); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.score-value { font-size: 1.6rem; font-weight: 900; font-variant-numeric: tabular-nums; line-height: 1; }
.score-you .score-value { color: var(--primary); }
.score-rival .score-value { color: var(--rival); }
.score-sep { color: var(--text-dim); }

.hud { display: flex; gap: .4rem; flex: none; }
.hud-item {
  display: flex; align-items: center; gap: .4rem; min-height: var(--tap);
  padding: .35rem .7rem; border-radius: var(--radius-xs);
  background: var(--surface); border: 1.5px solid var(--border);
  font-weight: 800; font-variant-numeric: tabular-nums;
}
.hud-icon { font-size: 1.05rem; color: var(--text-dim); }
.hud-combo { border-color: var(--warn); color: var(--warn); }
.hud-combo .hud-icon { color: var(--warn); }
/* The speed chip fills up as the match speeds up: the number says how much, the bar
   underneath says how close to the ceiling, and neither depends on noticing a colour. */
.hud-speed { position: relative; overflow: hidden; }
.hud-speed::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 3px;
  width: calc(var(--speed-progress, 0) * 100%);
  background: var(--primary); transition: width 400ms var(--ease);
}
.hud-speed.at-max { border-color: var(--primary); color: var(--primary); }
.hud-speed.at-max .hud-icon { color: var(--primary); }

.play-main { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: .6rem; }
.court-wrap { position: relative; flex: 1; min-height: 0; touch-action: none; }
#court { width: 100%; height: 100%; display: block; border-radius: var(--radius-sm); }

.court-overlay { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
.slow-badge {
  position: absolute; top: .55rem; left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .3rem .75rem; border-radius: 999px;
  background: var(--surface); border: 1.5px solid var(--violet); color: var(--violet);
  font-size: var(--fs-sm); font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  box-shadow: var(--shadow-1);
}
.slow-badge .slow-badge-icon { font-size: 1.15em; display: block; }
/* Held is a stronger state than easing down, and says so. */
.slow-badge.held { border-color: var(--warn); color: var(--warn); }
.big-note {
  background: var(--surface); border: 2px solid var(--border); box-shadow: var(--shadow-2);
  border-radius: var(--radius); padding: .8rem 1.6rem;
  font-size: clamp(1.5rem, 5vw, 2.6rem); font-weight: 900; letter-spacing: -.02em;
}
.big-note.count { font-variant-numeric: tabular-nums; min-width: 4.2rem; text-align: center; }
.big-note-hint {
  display: block; margin-top: .3rem;
  font-size: var(--fs-sm); font-weight: 650; letter-spacing: .02em; color: var(--text-dim);
}
.big-note.good { border-color: var(--good); color: var(--good); }
.big-note.bad { border-color: var(--rival); color: var(--rival); }

/* Pausing banks the answer clock, so leaving the question readable would turn the pause
   button into unlimited thinking time. The panel is blurred out and stops taking clicks;
   the pause MENU stays fully interactive, which is the part that matters. */
.play.is-paused .panel { filter: blur(7px) saturate(.55); opacity: .55; pointer-events: none; }
.play.is-paused .panel * { transition: none; }

.pause-veil {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  border-radius: var(--radius-sm); z-index: 5;
}
.pause-card { text-align: center; display: flex; flex-direction: column; gap: .7rem; align-items: center; padding: 1rem; }
.veil-title { font-size: var(--fs-xl); font-weight: 900; }
.veil-sub { color: var(--text-dim); max-width: 42ch; }

/* ------------------------------------------------------------------- panel */
.panel {
  flex: none; height: var(--panel-h);
  background: var(--surface); border: 1.5px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-1);
  padding: clamp(.6rem, 1.4vw, 1rem);
  display: flex; flex-direction: column; overflow: hidden;
}

.panel-idle { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: .8rem; height: 100%; }
.panel-hint { display: flex; align-items: center; gap: .55rem; color: var(--text-dim); font-size: var(--fs-md); font-weight: 600; }
.panel-hint [data-icon] { font-size: 1.2em; }

.rewards { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; }
.reward-chip {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .45rem .8rem; border-radius: 999px;
  border: 1.5px solid var(--good); color: var(--good);
  font-weight: 750; font-size: var(--fs-sm);
}
.reward-chip .chip-icon { font-size: 1.2em; }
.reward-chip .chip-time { font-variant-numeric: tabular-nums; opacity: .8; }

/* --- the question --- */
.panel-challenge { display: flex; flex-direction: column; gap: .55rem; height: 100%; }
.q-head { display: flex; align-items: center; gap: .6rem; }
.q-icon { font-size: 1.35rem; color: var(--violet); flex: none; }
.q-text { font-size: var(--fs-lg); font-weight: 800; flex: 1; min-width: 0; }
.btn-replay { min-height: 40px; padding: .35rem .7rem; font-size: var(--fs-sm); flex: none; }
.btn-replay[disabled] { opacity: .5; cursor: not-allowed; }
.btn-replay-count { font-variant-numeric: tabular-nums; color: var(--text-dim); }

.q-timer { display: flex; align-items: center; gap: .6rem; }
.q-timer-track { flex: 1; height: 10px; border-radius: 999px; background: var(--surface-3); overflow: hidden; border: 1px solid var(--border); }
.q-timer-fill { height: 100%; width: 100%; background: var(--good); border-radius: 999px; transition: width 90ms linear, background 200ms; }
.q-timer.low .q-timer-fill { background: var(--warn); }
.q-timer.critical .q-timer-fill { background: var(--bad); }
.q-timer-num { font-weight: 900; font-variant-numeric: tabular-nums; min-width: 1.4em; text-align: right; font-size: var(--fs-lg); }

.q-prompt { display: flex; align-items: center; justify-content: center; gap: .8rem; min-height: 2.4rem; text-align: center; }
/* A picture question is laid out differently, because the picture IS the question: the
   drawing takes the full remaining height on the left and the four options sit beside it.
   Stacking them the way a word question does either shrinks the drawing to a thumbnail or
   pushes it over the timer — both of which happened before this. */
.panel-challenge.has-art {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr);
  grid-template-areas: "head head" "timer timer" "prompt options";
  column-gap: clamp(.6rem, 2vw, 1.2rem);
}
.panel-challenge.has-art .q-head { grid-area: head; }
.panel-challenge.has-art .q-timer { grid-area: timer; }
.panel-challenge.has-art .q-prompt { grid-area: prompt; min-height: 0; }
.panel-challenge.has-art .q-options { grid-area: options; }
.panel-challenge.has-art .prompt-art { height: 100%; width: auto; max-width: 34vw; }
/* The word being asked stays big beside its four pictures, and the pictures get the whole
   height of the row instead of the strip left under a centred prompt. */
.panel-challenge.has-art .q-prompt { padding-right: .4rem; }
.panel-challenge.has-art .q-options.images { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.panel-challenge.has-art .opt.image .opt-art { max-height: 100%; }
.prompt-word { font-size: clamp(1.4rem, 4.2vw, 2.1rem); font-weight: 900; letter-spacing: -.01em; }
.prompt-sentence {
  font-size: clamp(1.05rem, 3vw, 1.5rem); font-weight: 750; line-height: 1.3;
  /* Capped so a long sentence can never eat the height the four answers need. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.prompt-gap {
  display: inline-block; min-width: 3.5em; border-bottom: 3px solid var(--primary);
  margin: 0 .2em; vertical-align: baseline;
}
/* On a picture question the drawing IS the question, so it gets the room to say so. */
.prompt-art {
  width: clamp(92px, 19vh, 150px); aspect-ratio: 1;
  border-radius: var(--radius-sm); background: var(--plate);
  border: 1.5px solid var(--plate-ring); padding: 8px; box-shadow: var(--shadow-1);
}
.prompt-art svg { width: 100%; height: 100%; }
.btn-hear {
  display: inline-flex; align-items: center; gap: .6rem; min-height: var(--tap);
  padding: .6rem 1.2rem; border-radius: 999px; cursor: pointer;
  border: 2px solid var(--primary); background: transparent; color: var(--primary);
  font-weight: 800; font-size: var(--fs-md);
}
.btn-hear [data-icon] { font-size: 1.4em; }

/* Explicit rows, not auto rows. With auto rows a long option grew its row to fit its
   text and the grid quietly became taller than the panel — the third and fourth answers
   ended up below the fold, scrollable, during a five-second question. `minmax(0, 1fr)`
   makes the four buttons share exactly the height that exists; --panel-h above is sized
   so that share is never less than a 44px tap target. */
.q-options {
  flex: 1; min-height: 0; display: grid; gap: .5rem;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, minmax(0, 1fr));
  overflow-y: auto;
}
.q-options.images { grid-template-columns: repeat(4, 1fr); grid-template-rows: minmax(0, 1fr); }
/* Whole sentences: smaller type and never more than three lines each. They stay two-up on
   a wide screen — one column would need four rows the fixed panel does not have. */
.q-options.long .opt { font-size: calc(var(--fs-md) * .92); }
.opt {
  position: relative; display: flex; align-items: center; gap: .6rem;
  padding: .6rem .8rem; cursor: pointer; text-align: left;
  border: 2px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); font-weight: 700; font-size: var(--fs-md);
  min-height: 44px; overflow: hidden;
  transition: border-color var(--dur), background var(--dur), transform var(--dur) var(--ease);
}
.opt:hover:not([disabled]) { border-color: var(--primary); background: var(--surface-3); }
.opt-key {
  flex: none; width: 1.7em; height: 1.7em; border-radius: 8px;
  display: grid; place-items: center; background: var(--surface-3);
  border: 1.5px solid var(--border); font-size: .8em; font-weight: 900; color: var(--text-dim);
}
.opt-label {
  flex: 1; min-width: 0; line-height: 1.25;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.opt-mark { flex: none; font-size: 1.3em; }
.opt.image { flex-direction: column; gap: .3rem; padding: .5rem; justify-content: center; }
.opt.image .opt-art {
  /* Square, centred, as tall as the tile allows: a full-width plate turns into a letterbox
     and the drawing inside it shrinks to fit the short side. */
  height: 100%; width: auto; aspect-ratio: 1; max-width: 100%; margin: 0 auto;
  flex: 1; min-height: 0; background: var(--plate);
  border-radius: var(--radius-xs); border: 1.5px solid var(--plate-ring); padding: 6px;
}
.opt.image .opt-art svg { width: 100%; height: 100%; }
.opt.image .opt-key { position: absolute; top: .35rem; left: .35rem; }

.opt.correct { border-color: var(--good); background: color-mix(in srgb, var(--good) 16%, var(--surface-2)); color: var(--text); }
.opt.correct .opt-mark { color: var(--good); }
.opt.wrong { border-color: var(--bad); background: color-mix(in srgb, var(--bad) 14%, var(--surface-2)); }
.opt.wrong .opt-mark { color: var(--bad); }
.opt[disabled] { cursor: default; }
.opt.dim { opacity: .5; }

/* --- feedback --- */
.panel-feedback { height: 100%; display: flex; align-items: center; gap: clamp(.7rem, 2vw, 1.3rem); }
.fb-mark { font-size: clamp(2.2rem, 7vw, 3.2rem); flex: none; line-height: 1; }
.fb-mark.good { color: var(--good); }
.fb-mark.bad { color: var(--warn); }
.fb-art { width: clamp(56px, 12vh, 88px); aspect-ratio: 1; background: var(--plate); border: 1.5px solid var(--plate-ring); border-radius: var(--radius-xs); padding: 5px; flex: none; }
.fb-art svg { width: 100%; height: 100%; }
.fb-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .25rem; }
.fb-title { font-size: var(--fs-lg); font-weight: 900; }
.fb-pair { font-size: clamp(1.1rem, 3.2vw, 1.6rem); font-weight: 800; line-height: 1.25; }
.fb-pair .fb-eq { color: var(--text-dim); margin: 0 .45rem; font-weight: 700; }
.fb-note { color: var(--text-dim); font-size: var(--fs-md); line-height: 1.4; }
.fb-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: .35rem; }
.fb-points { font-size: var(--fs-lg); font-weight: 900; color: var(--good); font-variant-numeric: tabular-nums; }
.fb-reward {
  display: inline-flex; align-items: center; gap: .45rem; padding: .4rem .75rem;
  border-radius: 999px; border: 2px solid var(--good); color: var(--good); font-weight: 800; font-size: var(--fs-sm);
}
.fb-combo { display: inline-flex; align-items: center; gap: .35rem; color: var(--warn); font-weight: 800; font-size: var(--fs-sm); }
/* The ball getting faster is a reward, so it is announced next to the points, not as a
   warning. Easing back down is announced too — quietly, and never as a failure. */
.fb-speed { display: inline-flex; align-items: center; gap: .35rem; color: var(--primary); font-weight: 800; font-size: var(--fs-sm); }
.fb-speed.easing { color: var(--text-dim); }

/* ------------------------------------------------------------------ modals */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
  background: color-mix(in srgb, #000 62%, transparent); padding: 1rem;
}
.modal {
  width: min(620px, 100%); max-height: min(86dvh, 780px); overflow: auto;
  background: var(--surface); border: 1.5px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-2); padding: clamp(1rem, 2.5vw, 1.6rem);
  display: flex; flex-direction: column; gap: 1rem;
}
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.modal-head h2 { font-size: var(--fs-xl); font-weight: 900; }
.modal-body { display: flex; flex-direction: column; gap: .9rem; }
.modal-actions { display: flex; gap: .6rem; flex-wrap: wrap; }
.modal-actions .btn { flex: 1 1 160px; }

.result-head { text-align: center; display: flex; flex-direction: column; align-items: center; gap: .4rem; }
.result-mark { font-size: 3rem; color: var(--primary); }
.result-head h2 { font-size: clamp(1.5rem, 5vw, 2.1rem); font-weight: 900; }
.result-sub { color: var(--text-dim); font-size: var(--fs-md); }

/* Sound settings: a switch and its own volume on one line. */
.sound-control { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.vol-wrap { display: inline-flex; align-items: center; gap: .5rem; color: var(--text-dim); }
.vol-wrap input[type="range"] { width: min(180px, 44vw); }
.result-best { display: inline-flex; align-items: center; gap: .45rem; color: var(--warn); font-weight: 800; }
.result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: .55rem; }
.achievement-row { display: flex; gap: .5rem; flex-wrap: wrap; }
.achievement {
  display: inline-flex; align-items: center; gap: .5rem; padding: .5rem .8rem;
  border-radius: 999px; border: 2px solid var(--warn); color: var(--warn); font-weight: 800; font-size: var(--fs-sm);
}

.word-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.word-row {
  display: flex; align-items: center; gap: .75rem; width: 100%; text-align: left;
  padding: .6rem .8rem; border-radius: var(--radius-xs);
  border: 1.5px solid var(--border); background: var(--surface-2); cursor: pointer; min-height: var(--tap);
}
.word-row .word-art { width: 42px; height: 42px; flex: none; background: var(--plate); border: 1px solid var(--plate-ring); border-radius: 8px; padding: 3px; }
.word-art svg { width: 100%; height: 100%; }
.word-main { flex: 1; min-width: 0; }
.word-term { font-weight: 800; }
.word-translation { color: var(--text-dim); font-size: var(--fs-sm); }
.word-tag { flex: none; display: inline-flex; align-items: center; gap: .35rem; font-size: var(--fs-sm); font-weight: 800; }
.word-tag.missed { color: var(--bad); }
.word-tag.got { color: var(--good); }

/* ---------------------------------------------------- how to play / intro */
.modal-howto { width: min(680px, 100%); }
.intro-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .55rem; }
.intro-steps li { display: flex; align-items: flex-start; gap: .7rem; font-size: var(--fs-md); line-height: 1.4; }
.intro-num {
  flex: none; width: 1.85em; height: 1.85em; border-radius: 50%;
  display: grid; place-items: center; font-weight: 900; font-size: .82em;
  background: var(--primary); color: var(--primary-ink);
}
.intro-block {
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); padding: .9rem 1rem;
  display: flex; flex-direction: column; gap: .6rem;
}
.intro-block > h3 {
  display: flex; align-items: center; gap: .5rem;
  font-size: var(--fs-sm); font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--text-dim);
}
.intro-block > h3 [data-icon] { font-size: 1.25em; }
.intro-device { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 700; }

/* Controls, drawn rather than described: key caps for a keyboard, a finger between two
   arrows for a touch screen. Both are icons and shapes, never a picture of a keyboard. */
.ctrl-row { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.keycaps { display: flex; gap: .4rem; }
.keycap {
  min-width: 2.6em; height: 2.6em; padding: 0 .5em;
  display: grid; place-items: center;
  border: 1.5px solid var(--border); border-bottom-width: 3px; border-radius: 9px;
  background: var(--surface); font-weight: 800; font-size: var(--fs-md);
}
.keycap [data-icon] { font-size: 1.15em; }
.ctrl-text { flex: 1 1 220px; font-weight: 650; }
.ctrl-note { color: var(--text-dim); font-size: var(--fs-sm); }
.swipe {
  display: flex; flex-direction: column; align-items: center; gap: .15rem;
  color: var(--primary); font-size: 1.4rem;
  border: 1.5px dashed var(--primary); border-radius: 999px; padding: .5rem .7rem;
}
.swipe .swipe-hand { font-size: 1.6rem; }

.intro-seq { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; color: var(--text-dim); font-weight: 700; font-size: var(--fs-sm); }
.intro-seq [data-icon] { font-size: 1.15em; }
.intro-seq .seq-arrow { opacity: .6; }

/* The Start button never scrolls out of reach: at the largest text size the instructions
   are twice as tall as a phone, and the one thing the player needs is at the bottom. */
.intro-actions {
  display: flex; flex-direction: column; gap: .6rem;
  position: sticky; bottom: calc(clamp(1rem, 2.5vw, 1.6rem) * -1);
  background: var(--surface); padding: .7rem 0 clamp(1rem, 2.5vw, 1.6rem);
  margin-bottom: calc(clamp(1rem, 2.5vw, 1.6rem) * -1);
  box-shadow: 0 -12px 18px -12px rgba(0,0,0,.5);
}
/* A button element, so it needs every default stripped — otherwise it renders as a white
   bar with dark text in the middle of a dark modal. */
.check-row {
  display: inline-flex; align-items: center; gap: .6rem; cursor: pointer;
  min-height: var(--tap); font-weight: 650; text-align: left;
  background: transparent; border: 0; padding: .2rem .1rem; color: var(--text-dim);
}
.check-row:hover { color: var(--text); }
.check-box {
  width: 1.5em; height: 1.5em; flex: none; border-radius: 7px;
  border: 2px solid var(--border); display: grid; place-items: center; color: transparent;
}
.check-row[aria-checked="true"] .check-box { border-color: var(--good); color: var(--good); }

/* ------------------------------------------------------------- settings ui */
.set-group { display: flex; flex-direction: column; gap: .6rem; }
.set-group > h3 { font-size: var(--fs-sm); font-weight: 800; text-transform: uppercase; letter-spacing: .09em; color: var(--text-dim); }
.set-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.set-label { font-weight: 700; }
.set-desc { color: var(--text-dim); font-size: var(--fs-sm); max-width: 46ch; }
.segmented { display: inline-flex; gap: .3rem; background: var(--surface-2); border: 1.5px solid var(--border); border-radius: var(--radius-xs); padding: .25rem; }
.segmented button {
  min-height: 40px; padding: .3rem .8rem; border: 0; border-radius: 9px;
  background: transparent; cursor: pointer; font-weight: 750; display: inline-flex; align-items: center; gap: .4rem;
}
.segmented button[aria-pressed="true"] { background: var(--primary); color: var(--primary-ink); }
.switch {
  display: inline-flex; align-items: center; gap: .5rem; min-height: var(--tap);
  padding: .3rem .8rem; border-radius: 999px; cursor: pointer;
  border: 2px solid var(--border); background: var(--surface-2); font-weight: 800;
}
.switch[aria-pressed="true"] { border-color: var(--good); color: var(--good); }
.switch .switch-state { font-size: var(--fs-sm); letter-spacing: .04em; }
input[type="range"] { width: min(220px, 60vw); accent-color: var(--primary); height: var(--tap); }

/* ------------------------------------------------------------------ toasts */
/* Top, not bottom: a toast at the bottom of the screen lands exactly on top of the
   answer buttons, which is the one place it must never be. */
.toasts { position: fixed; left: 50%; top: .8rem; transform: translateX(-50%); display: flex; flex-direction: column; gap: .5rem; z-index: 80; pointer-events: none; align-items: center; }
.toast {
  display: flex; align-items: center; gap: .6rem; padding: .7rem 1.1rem;
  border-radius: 999px; background: var(--surface); border: 1.5px solid var(--border);
  box-shadow: var(--shadow-2); font-weight: 750;
  animation: toast-in var(--dur) var(--ease);
}
.toast [data-icon] { font-size: 1.2em; }
.toast.good { border-color: var(--good); color: var(--good); }
.toast.warn { border-color: var(--warn); color: var(--warn); }
@keyframes toast-in { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }

.pulse { animation: pulse var(--dur) var(--ease); }
@keyframes pulse { 0% { transform: scale(1); } 45% { transform: scale(1.09); } 100% { transform: scale(1); } }

/* --------------------------------------------------------------- responsive */
/* Phones in portrait.
   The court stops stretching and takes exactly its own 5:3 box, and everything left over
   goes to the panel. Letting the court have the height instead left a tall, narrow
   letterbox with a small court floating in the middle of it, and pushed the answers off
   the bottom of the screen — the panel is where the learning happens, so the panel gets
   the room. The top bar splits into two rows for the same reason: six controls do not fit
   across 390px, and squeezing them made the score unreadable. */
@media (max-width: 620px) {
  :root { --panel-h: 240px; }

  .topbar {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "left score right" "hud hud hud";
    gap: .4rem;
  }
  .topbar-left { grid-area: left; }
  .scoreboard { grid-area: score; justify-self: stretch; justify-content: center; padding: .3rem .5rem; }
  .topbar-right { grid-area: right; }
  .hud { grid-area: hud; justify-content: center; flex-wrap: wrap; }
  .topbar-left .icon-btn, .topbar-right .icon-btn { width: 44px; height: 44px; }
  #btn-howto { display: none; }            /* the same help sits on the setup screen */
  .score-name { font-size: .72rem; }
  .score-value { font-size: 1.3rem; }
  .hud-item { padding: .25rem .55rem; min-height: 40px; }

  .court-wrap { flex: none; width: 100%; aspect-ratio: 5 / 3; }
  .panel { flex: 1 1 auto; height: auto; min-height: var(--panel-h); }

  /* The question keeps its icon on the same line; only the replay button is allowed to
     wrap, and on a phone it drops its label so it usually does not have to. Every row
     saved here is a row the fourth answer needs. */
  .q-head { flex-wrap: wrap; }
  .btn-replay-text { display: none; }
  .btn-replay { margin-left: auto; }
  .opt.image .opt-art { max-height: 118px; }
  /* Four word-buttons stretched over a tall phone panel look like empty boxes; capped and
     centred they stay comfortably tappable without swallowing the screen. */
  /* Explicit rows override grid-auto-rows, so the cap has to live on the same property
     that sets them — otherwise four buttons stretch to 300px each on a tall phone. */
  .q-options:not(.images):not(.long) {
    /* The cap scales with the text size — a fixed 104px clipped the third line at the
       largest setting, which is the setting that needs the room most. */
    grid-template-rows: repeat(2, minmax(56px, calc(var(--tap) * 2)));
    align-content: center;
  }
  /* Four sentences stacked: the floor is the 44px tap target and not a pixel more, because
     at the largest text size those four extra pixels per row were enough to push the last
     answer out of its box. */
  .q-options.long { grid-template-columns: 1fr; grid-template-rows: repeat(4, minmax(44px, 1fr)); }
  .slow-badge { font-size: .7rem; padding: .22rem .6rem; }

  /* The header stops fighting for width: mark, title and the three buttons share the
     first row, and the tagline gets the full width underneath instead of wrapping into a
     five-line column.

     A grid held that arrangement but could not give it up: `auto 1fr auto` has no way to
     say "and if the three of you do not fit, the buttons take their own line", so at the
     larger text sizes the third column simply left the screen. The same arrangement built
     out of a wrapping flex row keeps the layout identical while it fits and breaks in the
     one place it should when it does not. `order` puts the buttons ahead of the tagline so
     the tagline's full-width line still comes last, which is where the grid put it. */
  .brand { display: flex; flex-wrap: wrap; align-items: center; column-gap: .6rem; row-gap: .35rem; }
  .brand-mark { font-size: 2rem; }
  .brand-text { display: contents; }
  /* 6rem is deliberately below what the title usually wants: it is the point at which the
     title would rather have a second line than lose the buttons off the edge, so every
     width that fits today keeps fitting and only the ones that overflowed break. */
  .brand-text h1 { flex: 1 1 6rem; order: 0; }
  .brand-actions { order: 1; margin-left: auto; }
  .tagline { flex: 1 0 100%; order: 2; }

  .lang-arrow { display: none; }
  .q-options.images { grid-template-columns: repeat(2, 1fr); }
  /* The grammar note is the whole lesson on a gap-fill, so it is clamped on a phone
     rather than hidden — two lines still say why the answer is the answer. */
  .fb-note { -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
}

/* Landscape phones and short windows: the court would be a stripe, so the panel
   moves beside it instead of below it. */
@media (max-height: 560px) and (orientation: landscape) {
  .play-main { flex-direction: row; }
  .panel { height: auto; width: min(46vw, 460px); }
  .q-options { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, minmax(0, 1fr)); }
  .q-options.images { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, minmax(0, 1fr)); }
  .q-options.long { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, minmax(0, 1fr)); }
  .topbar { gap: .5rem; }
  .panel-feedback { flex-direction: column; align-items: flex-start; justify-content: center; }
  .fb-side { flex-direction: row; align-items: center; }
}

/* Wide desktops: one comfortable column, panel a little roomier. */
@media (min-width: 1200px) {
  :root { --panel-h: 268px; }
  .play { padding: 1rem clamp(1rem, 4vw, 3rem); }
}

/* Very short windows — a phone held sideways, a small floating window. Every fixed cost
   above the answers is trimmed so all four still fit on screen at the largest text size,
   because someone who chose "very large text" needs it most exactly here. The answers
   themselves keep their 44px. */
@media (max-height: 460px) {
  .play { padding: .35rem .5rem; gap: .4rem; }
  .panel { padding: .5rem; }
  .panel-challenge { gap: .35rem; }
  /* In a 390px-tall window the panel is narrow, and at the largest text size the question
     wrapped to four lines and ate the answers. Two lines is enough for every question the
     game asks. */
  .q-text {
    font-size: var(--fs-md);
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  }
  .q-icon { font-size: 1.15rem; }
  .btn-replay { min-height: 38px; }
  .q-prompt { min-height: 0; }
  .prompt-word { font-size: clamp(1.05rem, 3.4vw, 1.5rem); }
  .prompt-sentence { font-size: clamp(.92rem, 2.6vw, 1.15rem); }
  .prompt-art { width: clamp(72px, 22vh, 110px); }
  /* A square plate whose height comes from its width overflows a short row; here it is
     the row that decides, and the plate fits inside it. */
  .opt.image { padding: .35rem; }
  .opt.image .opt-art { height: auto; min-height: 0; max-height: 100%; flex: 1 1 auto; }
  .q-timer-track { height: 8px; }
  .q-timer-num { font-size: var(--fs-md); }
  .topbar .icon-btn { width: 42px; height: 42px; }
  .hud-item { min-height: 42px; padding: .2rem .5rem; }
  .scoreboard { min-height: 42px; }
  .slow-badge { font-size: .68rem; }

  /* The teaching card, compact: in a 390px-tall window at the largest text size the
     drawing is what has to go — the pair of words IS the lesson, and it stays big. */
  .panel-feedback { flex-direction: row; flex-wrap: wrap; align-items: center; align-content: center; gap: .35rem .6rem; }
  .panel-feedback .fb-art { display: none; }
  .fb-mark { font-size: 1.9rem; }
  .fb-title { font-size: var(--fs-md); }
  .fb-pair { font-size: clamp(1rem, 2.6vw, 1.3rem); }
  .fb-note { display: none; }
  .fb-body { flex: 1 1 50%; }
  .fb-side { flex: 1 1 100%; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: .3rem .5rem; }
}

/* "Ponto seu" sits ON the court, so on a phone held sideways its size is a question about
   the scarce dimension — height — not about width. It used to be compacted inside
   `@media (max-height: 460px)`, while the query that actually knows the phone is sideways
   and moves the panel beside the court is the 560px one. Every device whose landscape
   viewport lands between those two numbers therefore got the sideways layout with the
   portrait banner, and the jump was a cliff: measured, 19.32px of type at 460px tall
   against 41.6px at 461px, the banner going from 14% of the court's height to 27% in one
   pixel of viewport. A 1080p phone at a device pixel ratio of 2 reports 540 and lands in
   the middle of it.

   So the size is a curve rather than a step, and the curve is chosen to meet the values
   already in the design at BOTH ends: 4.2vh is exactly what the old compact rule gave, and
   4.2vh reaches the base 2.6rem cap at 990px of height — which is where this query stops.
   Above it nothing changes at all; below it there is no boundary to see. Everything else
   is in `em`, so padding, radius, border and the hint all follow the type down on their
   own instead of each needing its own breakpoint. Portrait is untouched: it never matches.

   The second selector keeps the old `max-height: 460px` case, for a window short enough to
   need this while still being taller than it is wide. */
@media (max-height: 990px) and (orientation: landscape), (max-height: 460px) {
  .big-note {
    font-size: clamp(1rem, min(5vw, 4.2vh), 2.6rem);
    padding: .31em .62em;
    border-width: max(1.5px, .05em);
    border-radius: min(var(--radius), .5em);
  }
  .big-note.count { min-width: 1.62em; }
  /* The countdown passes a hint, so this one is on screen often enough to matter. Each
     `em` is the base value divided by the size it must match at the top of the range, and
     the two divisors are DIFFERENT on purpose: `font-size: em` resolves against the parent
     (.875rem / 2.6rem), while `margin-top: em` resolves against this element's own, already
     reduced, size (.3rem / .875rem). Reading both as "a share of the banner" left the
     margin at a third of its value. The `rem` floors stop the hint shrinking past legible
     on a very short window. */
  .big-note-hint { font-size: max(.62rem, .3365em); margin-top: max(.1rem, .343em); }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
