/* ======================================================================
   Design tokens (Roadmap #153, "Chunky & Friendly")
   The one place colours, type, spacing, radii, shadows and motion are set.
   Ownership: the colour mode (light here, dark in the "Dark mode" section)
   owns the neutrals and feedback colours; a Shop theme (the "Cosmetic
   themes" section) owns only the five brand tokens --accent, --primary,
   --primary-edge, --primary-tint and --accent-ink. New screens use these
   tokens and the shared components below, never raw colours.
   ====================================================================== */
:root {
  /* Neutrals (owned by colour mode) */
  --bg: #f4f5fb;            /* page, lightly tinted */
  --surface: #ffffff;       /* cards */
  --surface-2: #eef0f8;     /* insets, tracks, unselected chips, keypad tray */
  --ink: #1b1e2e;           /* body text */
  --ink-soft: #565c75;      /* secondary text */
  --border: #e3e5f0;
  --border-strong: #cfd3e4; /* secondary-button edge, key edge, dividers */

  /* Brand (owned by Shop theme; Classic shown) */
  --accent: #6d7cff;        /* DECORATION ONLY: glows, rings, swatches. Never behind or as text. */
  --primary: #4c5fd5;       /* fills behind white text, >=4.5:1 with --on-primary */
  --primary-edge: #3a49b0;  /* button bottom edge, pressed fill, hero gradient end */
  --primary-tint: #eceefe;  /* selected chip, active nav, soft fills */
  --accent-ink: #3a49b0;    /* brand-coloured text on --surface and --bg */
  --on-primary: #ffffff;
  --on-primary-line: rgba(255, 255, 255, 0.28); /* rules and tracks on the hero */

  /* Feedback: only the "-ink" shades are used for text */
  --good: #22a06b;  --good-ink: #146c43;  --good-bg: #e3f6ec;
  --bad:  #e5534b;  --bad-ink:  #b42318;  --bad-bg:  #fdecea;
  --warn: #f5a524;  --warn-ink: #8a5300;  --warn-bg: #fff4dc;
  --gold: #ffc53d;  /* stars, goal ring, rewards (graphics only) */

  /* Type. The three stacks are the Shop fonts; body[data-font] picks one
     into --font-body (see "Cosmetic fonts"), and the Shop's "Aa" preview
     uses the same variables. All are installed on iPad: nothing downloads. */
  --font-stack-classic: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont,
                        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-stack-bubbly: "Comic Sans MS", "Chalkboard SE", "Segoe Print", cursive, sans-serif;
  --font-stack-robot: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
  --font-body: var(--font-stack-classic);
  --font-display: var(--font-body);
  --fs-xs: 0.8125rem;    /* 13px: captions, tags. Floor for any UI text */
  --fs-sm: 0.9rem;       /* 14.4px: secondary lines */
  --fs-base: 1rem;       /* 16px: body */
  --fs-md: 1.125rem;     /* 18px: card titles, buttons */
  --fs-lg: 1.375rem;     /* 22px: section headings, question prompt */
  --fs-xl: 1.75rem;      /* 28px: screen titles, big stats */
  --fs-display: 2.75rem; /* 44px: score, timer, verdict */
  --fw-regular: 500; --fw-bold: 700; --fw-heavy: 800;
  --lh-tight: 1.2; --lh-body: 1.45;

  /* Space (4px base) */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px;

  /* Radii */
  --radius-sm: 10px;    /* chips, inputs, icon tiles */
  --radius-md: 14px;    /* buttons, keypad keys */
  --radius-lg: 20px;    /* cards */
  --radius-xl: 28px;    /* hero card, overlays */
  --radius-pill: 999px; /* HUD pills, progress tracks */

  /* Elevation. A pressable thing has a solid "edge" under it, drawn as a
     blur-free box-shadow --press-depth (keys: --key-depth) tall; on :active
     it moves down by the same amount and the edge collapses, like a key. */
  --shadow-1: 0 1px 2px rgba(22, 24, 50, 0.06), 0 2px 6px rgba(22, 24, 50, 0.06);  /* cards */
  --shadow-2: 0 6px 16px rgba(22, 24, 50, 0.10), 0 2px 4px rgba(22, 24, 50, 0.06); /* hero, sheets */
  --shadow-3: 0 16px 40px rgba(22, 24, 50, 0.18);                                   /* overlays */
  --press-depth: 4px;
  --key-depth: 3px;

  /* Sizing */
  --tap-min: 44px; --btn-h: 56px; --icon-sm: 20px; --icon-md: 28px; --icon-lg: 40px;

  /* Motion */
  --dur-fast: 120ms; --dur-base: 200ms; --dur-slow: 320ms;
  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-spring: cubic-bezier(0.2, 1.4, 0.5, 1);

  /* Lets the browser draw its own bits (number/text fields, scrollbars) to
     match; dark mode flips it below. */
  color-scheme: light;
}

/* Tokens that are built from theme or mode tokens are declared on body, not
   :root: a custom property's var() is resolved where it is declared, and
   the Shop theme and dark mode both set their values on body. */
body {
  /* Cards float on a shadow in light mode. The border stays 1px (just
     invisible) so sizes don't shift when dark mode draws it. */
  --card-border: transparent;
  --hero-bg: linear-gradient(135deg, var(--primary), var(--primary-edge));
  /* Selection ring (selected chip, active nav). Dark mode swaps it to the
     theme's light --accent-ink: --primary is under 3:1 on dark greys. */
  --ring: var(--primary);
  --disabled-bg: #c4c8e6; /* disabled buttons (dark mode sets its own) */
}

/* ---------- Cosmetic themes (Shop) ----------
   A theme sets only the five brand tokens (#153). Each --primary is dark
   enough for white text (>=4.5:1); the bright shade lives on as --accent
   and as the Shop swatch (js/shop.js). Classic Blue is the :root default. */
body[data-theme="theme-sunset"] {
  --accent: #ff8a5b;
  --primary: #c2410c;
  --primary-edge: #9a3412;
  --primary-tint: #ffede4;
  --accent-ink: #9a3412;
}
body[data-theme="theme-forest"] {
  --accent: #34c47c;
  --primary: #15803d;
  --primary-edge: #166534;
  --primary-tint: #e3f6ea;
  --accent-ink: #166534;
}
body[data-theme="theme-berry"] {
  --accent: #b86ef0;
  --primary: #9333ea;
  --primary-edge: #6b21a8;
  --primary-tint: #f3e8ff;
  --accent-ink: #7e22ce;
}

/* ---------- Dark mode (Roadmap #87) ----------
   Chosen in Settings > Appearance; app.js resolves Light / Dark / Match
   device into data-mode on <html>. Only the neutrals and feedback shades
   change: --primary, --primary-edge and --on-primary stay, since white on
   a filled button reads the same on any page. These rules sit on body (not
   :root) and after the theme rules so they win over a theme's light tint;
   each theme then gets its own dark tint and lighter text accent. Images
   (question diagrams) keep their white card on purpose: they're drawn for
   paper. */
/* html paints its own background from :root's (light) --bg, and iPad Safari
   shows it in the rubber-band overscroll and around the safe areas — so it
   needs the dark shade too, not just body. */
html[data-mode="dark"] { color-scheme: dark; background: #11131b; }
html[data-mode="dark"] body {
  --bg: #11131b;
  --surface: #1b1e2a;
  --surface-2: #252939;
  --ink: #eceef7;
  --ink-soft: #a9aec4;
  --border: #2f3447;
  --border-strong: #3d435a;
  --primary-tint: #2a2e55;
  --accent-ink: #b4b8ff;
  --good-ink: #5fd39b; --good-bg: #163426;
  --bad-ink: #ff8a80;  --bad-bg: #3b1d1d;
  --warn-ink: #ffc766; --warn-bg: #3a2c10;
  /* Shadows barely read on dark, so cards show a 1px border instead. */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 6px 18px rgba(0, 0, 0, 0.45);
  --shadow-3: 0 16px 40px rgba(0, 0, 0, 0.55);
  --card-border: var(--border);
  --disabled-bg: #3d4260;
  --ring: var(--accent-ink);
}
html[data-mode="dark"] body[data-theme="theme-sunset"] { --primary-tint: #3d2a22; --accent-ink: #ffb08f; }
html[data-mode="dark"] body[data-theme="theme-forest"] { --primary-tint: #1f3a2b; --accent-ink: #86dcae; }
html[data-mode="dark"] body[data-theme="theme-berry"] { --primary-tint: #35254a; --accent-ink: #d4a6f2; }

/* ---------- Cosmetic fonts ----------
   Each sets --font-body, so headings, buttons, keypad and inputs all follow
   the bought font. Classic is the :root default (--font-stack-classic). */
body[data-font="font-rounded"] { --font-body: var(--font-stack-bubbly); --font-display: var(--font-stack-bubbly); }
body[data-font="font-mono"] { --font-body: var(--font-stack-robot); --font-display: var(--font-stack-robot); }

* { box-sizing: border-box; }

/* Author rules below set `display` on buttons/containers that are also
   toggled via the `hidden` attribute (e.g. .primary-btn, .mcq-grid) — an
   author `display` declaration always beats the UA stylesheet's
   `[hidden]{display:none}`, so pin it explicitly here. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  -webkit-tap-highlight-color: transparent;
}

body {
  display: flex;
  flex-direction: column;
  /* 100vh on mobile Safari (notably iPad) is the layout viewport, which is
     taller than what's actually visible whenever the browser's own address
     bar/toolbar is showing — that gap pushed the fixed footer (and its
     Start Session button) below the real screen with no way to scroll to
     it, since #app-scroll (not body) owns all the scrolling here. 100dvh
     tracks the actual visible viewport instead; 100vh stays first as a
     fallback for browsers that don't understand dvh (an unsupported unit
     makes the whole declaration invalid, so it's simply ignored). */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

button {
  font-family: inherit;
  cursor: pointer;
}
/* Fields follow the bought font too (browsers don't inherit it by default). */
input, textarea, select { font-family: inherit; }

/* Numbers that change in place (timer, points, keypad, scores, tables)
   keep a fixed width so they don't jiggle. */
.numeric-display, .key, .session-hud, .header-points, .summary-row .value,
.streak-widget-main, .pb-value, .mastery-label, .quest-count, .cal-cell,
.shop-balance, .stat-num, .pill { font-variant-numeric: tabular-nums; }

/* Keyboard focus on a laptop (touch never shows it). */
:focus-visible { outline: 3px solid var(--accent-ink); outline-offset: 2px; }

/* App shell: fixed header + fixed footer, with only the middle content
   region (#app) scrolling — this guarantees the Home button and each
   screen's primary nav button(s) stay on screen even when content
   (badges, mastery bars, session history, ...) grows long. */
.app-header {
  flex: 0 0 auto;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding-top: env(safe-area-inset-top, 0px);
}

/* Three-column grid (not flex) so the center "brand" block stays truly
   centered on the window, regardless of how wide the nav (left) or badges/
   points/settings (right) groups are — two equal 1fr tracks balance the
   auto-sized center column instead of it drifting off-center like it
   would between two unequal flex siblings. */
.header-top {
  display: grid;
  /* minmax(0, ...) rather than a bare 1fr/auto: a grid track's automatic
     minimum is its content's min-content width, which let the nav and brand
     force the whole page wider than a phone's screen (see the phone rules at
     the end of this file). Flooring the tracks at 0 lets them shrink and the
     title ellipsis actually do its job. */
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
}

/* Icon+word nav (replaces the old breadcrumb) — a fixed, always-visible
   way to reach Home/Progress/Shop from anywhere. Sits in the top bar
   alongside the title/avatar. */
.main-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-self: start;
}

/* Title + avatar stacked as one centered unit in the middle grid column. */
.header-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  justify-self: center;
  min-width: 0;
}

.header-title {
  max-width: 100%;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-heavy);
  color: var(--accent-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-self: end;
  /* Stay inside the grid column (#153): the badge strip below scrolls
     sideways, so it shrinks instead of sliding under the avatar. */
  min-width: 0;
  max-width: 100%;
}

.header-points {
  flex-shrink: 0;
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  color: var(--accent-ink);
}

.settings-btn {
  /* An icon tile, and a full 44pt tap target (#153). */
  width: var(--tap-min);
  height: var(--tap-min);
  border: none;
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 1.25rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.settings-btn:active { background: var(--border-strong); }
.nav-btn {
  border: none;
  background: var(--surface-2);
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--ink);
  padding: 0 12px;
  min-height: var(--tap-min);
  white-space: nowrap;
  /* Icon and label are separate spans so phones can stack them (see the
     phone rules at the end of this file); side by side, this is the same
     "icon + word" button it has always been. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
.nav-btn:active { background: var(--border-strong); }
/* Where you are: the soft theme tint with a ring, like a selected chip. */
.nav-btn.active {
  background: var(--primary-tint);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 2px var(--ring);
}

/* Badges sit inline in the header-top row (next to the avatar) rather than
   their own row, to keep the header compact; the strip scrolls horizontally
   if there are more badges than fit. The group carries its own trailing
   margin so there's a clear gap before the points value, distinct from the
   smaller gap the label sits at relative to the icons themselves. */
.header-badges-group {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-right: 12px;
  min-width: 0;
}
.header-badges-label {
  flex-shrink: 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.header-badges {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  max-width: 30vw;
  flex-shrink: 1;
}
.header-badge-icon {
  /* Icon tile, medium. */
  flex-shrink: 0;
  font-size: 1.1rem;
  background: var(--primary-tint);
  border-radius: 50%;
  width: var(--icon-md);
  height: var(--icon-md);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* #app is a non-scrolling positioning context sized to fill the space
   between header and footer. #app-scroll is the actual scroll container,
   full width so its native scrollbar rides the true window edge rather than
   looking inset next to centered content. #app-inner then caps line length
   for readability. The up/down indicators are absolutely positioned children
   of #app (not of #app-scroll), so they overlay the content and stay put
   regardless of #app-scroll's scroll offset. */
#app {
  flex: 1 1 auto;
  position: relative;
  overflow: hidden;
  width: 100%;
}

#app-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  width: 100%;
}

#app-inner {
  max-width: 640px;
  margin: 0 auto;
  padding: 20px 16px 24px;
}

.scroll-indicator {
  position: absolute;
  right: 14px;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  cursor: pointer;
  font-size: 2rem;
  color: var(--primary);
  background: var(--surface);
  border: none;
  box-shadow: var(--shadow-2);
  z-index: 5;
}
.scroll-indicator:active { background: var(--surface-2); transform: scale(0.94); }
.scroll-indicator-top { top: 10px; }
.scroll-indicator-bottom { bottom: 10px; }

.app-footer {
  flex: 0 0 auto;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
}

.footer-inner {
  max-width: 640px;
  margin: 0 auto;
}

.footer-group { display: none; text-align: center; }
.footer-group.active { display: block; }
.footer-group .primary-btn {
  display: inline-block;
  width: fit-content;
  min-width: 220px;
  margin-top: 0;
}
.footer-group .link-btn {
  margin-top: 0;
}
.footer-group > * + * {
  margin-top: 10px;
}

.screen { display: none; }
.screen.active { display: block; }

.section-heading {
  font-size: 1.05rem;
  margin: 24px 0 10px;
  color: var(--ink);
}
.section-subheading {
  font-size: 0.95rem;
  margin: 22px 0 8px;
  color: var(--ink-soft);
}

/* Roadmap #99: on narrow screens (iPad portrait, phones) the columns stack
   with what to do first — focus card, session length and topics — then the
   streak/next-session/personal-bests widgets, then the clock, weather, joke
   and word of the day. On wide screens they become three real columns (see
   the 900px rule near the end). */
.start-layout {
  display: flex;
  flex-direction: column;
}
.start-main { order: 1; min-width: 0; margin-top: 0; }
.start-side-right { order: 2; margin-top: 28px; }
.start-sidebar { order: 3; margin-top: 28px; }

/* ======================================================================
   Shared components (Roadmap #153). Existing class names are restyled to
   these looks; new screens (#143-#148) use the component classes
   directly. Only visual properties live here: each existing selector keeps
   its own layout rule (display, width, margins) so no layout changes.
   ====================================================================== */

/* Card: a white surface floating on a soft shadow. Dark mode draws the
   1px border via --card-border instead. */
.card,
.home-card,
.summary-card,
.summary-takeaways,
.summary-wrong,
.question-card,
.backup-reminder,
.home-streak-widget,
.next-session-widget,
.weather-widget,
.joke-of-day,
.word-of-day,
.adventure-card,
.trophy,
.badge-card,
.shop-item,
.powerup-card,
.session-history-row,
.wc-row,
.fixed-row,
.changelog-entry,
.suggestion-form,
.suggestion-row,
.import-card,
.cal-cell {
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}
.card { padding: var(--space-5); }
.card-title,
.home-card-title {
  font-size: var(--fs-md);
  font-weight: var(--fw-heavy);
  color: var(--ink);
}
.card-title { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-2); }

/* Hero card: at most one per screen, in the theme colour. Both gradient
   ends pass 4.5:1 with white; --accent may only glow behind, never under,
   the text. */
.card-hero,
.focus-card {
  background: var(--hero-bg);
  color: var(--on-primary);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-2);
}
.card-hero { padding: var(--space-6); }
.focus-card { padding: 20px; }
.focus-phase { font-size: 1.1rem; font-weight: 700; margin: 0 0 6px; }
.focus-tone { margin: 0; opacity: 0.92; font-size: 0.95rem; }
.focus-card .strength-summary {
  color: var(--on-primary);
}
.focus-card .strength-summary strong { color: var(--on-primary); }

/* Choice / chip: a soft grey button that turns the theme tint with a ring
   when selected. Solid chips (.chip.selected) fill with the theme colour. */
.choice-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.choice-btn {
  min-height: 56px;
  border: 2px solid transparent;
  background: var(--surface-2);
  border-radius: var(--radius-md);
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  color: var(--ink);
  padding: 10px;
  box-shadow: 0 var(--key-depth) 0 var(--border-strong);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.choice-btn:active:not(:disabled) {
  transform: translateY(var(--key-depth));
  box-shadow: 0 0 0 var(--border-strong);
  background: var(--border-strong);
}
.choice-btn.selected,
.choice-btn.selected:active:not(:disabled) {
  border-color: var(--ring);
  background: var(--primary-tint);
  color: var(--accent-ink);
}
.choice-btn.chip.selected,
.choice-btn.chip.selected:active:not(:disabled) {
  background: var(--primary);
  color: var(--on-primary);
  border-color: var(--primary);
  box-shadow: 0 var(--key-depth) 0 var(--primary-edge);
}

/* Buttons. Every kind is at least --btn-h (56px) unless a screen sets a
   smaller (still >=44px) size, and presses down on :active without hover.
   The pressed fill also changes, so the press still shows with Reduce
   Motion on (no edge, no movement). */
.btn-primary,
.btn-secondary,
.btn-danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0 var(--space-5);
}
.btn-primary,
.btn-secondary,
.btn-danger,
.primary-btn,
.secondary-btn,
.danger-btn {
  min-height: var(--btn-h);
  border-radius: var(--radius-md);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out), background-color var(--dur-fast);
}

/* Primary: theme fill, white text, darker edge. */
.btn-primary,
.primary-btn {
  background: var(--primary);
  color: var(--on-primary);
  border: none;
  box-shadow: 0 var(--press-depth) 0 var(--primary-edge);
}
.btn-primary:active:not(:disabled),
.primary-btn:active:not(:disabled) {
  background: var(--primary-edge);
  transform: translateY(var(--press-depth));
  box-shadow: 0 0 0 var(--primary-edge);
}

/* Secondary: white with a grey rim and edge, brand-coloured text. */
.btn-secondary,
.secondary-btn {
  background: var(--surface);
  color: var(--accent-ink);
  border: 2px solid var(--border-strong);
  box-shadow: 0 var(--press-depth) 0 var(--border-strong);
}
.btn-secondary:active:not(:disabled),
.secondary-btn:active:not(:disabled) {
  background: var(--surface-2);
  transform: translateY(var(--press-depth));
  box-shadow: 0 0 0 var(--border-strong);
}

/* Soft danger (Exit, Delete): calm pink, never a scary solid red. */
.btn-danger,
.danger-btn,
.hud-exit-btn {
  background: var(--bad-bg);
  color: var(--bad-ink);
  border: 2px solid transparent;
  box-shadow: 0 var(--press-depth) 0 var(--border-strong);
}
.btn-danger:active:not(:disabled),
.danger-btn:active:not(:disabled),
.hud-exit-btn:active {
  border-color: var(--bad);
  transform: translateY(var(--press-depth));
  box-shadow: 0 0 0 var(--border-strong);
}

/* Disabled: flat grey, no edge, no press. */
.btn-primary:disabled,
.btn-secondary:disabled,
.btn-danger:disabled,
.primary-btn:disabled,
.secondary-btn:disabled,
.danger-btn:disabled {
  background: var(--disabled-bg);
  color: var(--on-primary);
  border-color: transparent;
  box-shadow: none;
  cursor: default;
}
html[data-mode="dark"] .btn-secondary:disabled,
html[data-mode="dark"] .secondary-btn:disabled { color: var(--ink-soft); }

/* Existing buttons' layout, unchanged. */
.primary-btn {
  display: block;
  width: 100%;
  margin-top: 18px;
}
.secondary-btn {
  display: block;
  width: 100%;
  margin-top: 12px;
}

/* Link button: text only, but a full tap target. */
.btn-link,
.link-btn {
  min-height: var(--tap-min);
  background: none;
  border: none;
  color: var(--ink-soft);
  font-size: 0.95rem;
  text-decoration: underline;
  padding: 8px;
}
.btn-link:active,
.link-btn:active { color: var(--ink); }
.link-btn {
  display: block;
  width: 100%;
  margin-top: 16px;
}

/* Keypad key and game tile (#144): white keys on a grey tray, with a
   short grey edge that presses down. */
.key,
.game-tile {
  min-height: 56px;
  min-width: 56px;
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-md);
  color: var(--ink);
  box-shadow: 0 var(--key-depth) 0 var(--border-strong);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.key:active:not(:disabled),
.game-tile:active:not(:disabled) {
  background: var(--surface-2);
  transform: translateY(var(--key-depth));
  box-shadow: 0 0 0 var(--border-strong);
}
.tile-tray {
  background: var(--surface-2);
  border-radius: var(--radius-lg);
  padding: var(--space-2);
}

/* Pill: HUD items, timers, counts. A tappable pill must be >= --tap-min. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 36px;
  padding: 0 var(--space-3);
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--ink);
}
/* Last 10 seconds of a timer (#143). */
.pill-warn { background: var(--warn-bg); color: var(--warn-ink); }

/* Stat: a big number over a small label (score, streak, personal best). */
.stat { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); }
.stat-num {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: var(--fw-heavy);
  line-height: var(--lh-tight);
}
.stat-label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.card-hero .stat-label { color: var(--on-primary); }

/* Ranked list: top-5 tables, quests, history. */
.ranked-list { list-style: none; margin: 0; padding: 0; }
.ranked-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
}
.ranked-row + .ranked-row { border-top: 1px solid var(--border); }
.ranked-row.is-highlight { background: var(--primary-tint); border-top-color: transparent; }
.ranked-rank {
  min-width: 1.5em;
  font-weight: var(--fw-heavy);
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}
.ranked-row.is-highlight .ranked-rank { color: var(--accent-ink); }

/* Progress track: theme fill, or green (--good) where it means "done". */
.track {
  height: 12px;
  background: var(--surface-2);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.track-fill { height: 100%; background: var(--primary); border-radius: var(--radius-pill); }
.track-fill.is-done { background: var(--good); }

/* Icon tile: an emoji in a soft theme-tinted square (add .is-round for a
   circle), at three fixed sizes. */
.icon-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--icon-md);
  height: var(--icon-md);
  font-size: calc(var(--icon-md) * 0.6);
  line-height: 1;
  background: var(--primary-tint);
  border-radius: var(--radius-sm);
}
.icon-tile.is-round { border-radius: 50%; }
.icon-tile-sm { width: var(--icon-sm); height: var(--icon-sm); font-size: calc(var(--icon-sm) * 0.65); border-radius: 6px; }
.icon-tile-lg { width: var(--icon-lg); height: var(--icon-lg); font-size: calc(var(--icon-lg) * 0.6); }

/* Banner / celebration ("New best!"): a gold-edged card that pops in. */
.banner-celebrate {
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--gold);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  padding: var(--space-4) var(--space-5);
  text-align: center;
  animation: banner-in var(--dur-slow) var(--ease-spring) both;
}
@keyframes banner-in {
  from { opacity: 0; transform: scale(0.9); }
  to { opacity: 1; transform: none; }
}

/* Question screen */
.session-hud {
  position: relative;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 14px;
}

/* Streak-started celebration. The banner itself now rides in the centred
   verdict card (see .verdict-streak further down) rather than floating above
   the HUD, which on a tablet is often scrolled off the top of a long
   question. The pulse on the HUD badge stays as a secondary cue for when the
   HUD *is* in view. */
.streak-pulse { animation: streak-badge-pulse 0.6s ease-out; }
@keyframes streak-badge-pulse {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); box-shadow: 0 0 0 4px var(--warn-bg); }
  100% { transform: scale(1); }
}
.session-hud { align-items: center; }
/* Every HUD item is a Pill (see Shared components). */
.session-hud span {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 var(--space-3);
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
}
/* Soft-danger pill; 44pt tall so it's a real tap target (#153). */
.hud-exit-btn {
  margin-left: auto;
  min-height: var(--tap-min);
  border-radius: var(--radius-pill);
  padding: 0 var(--space-4);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.question-card {
  position: relative;
  padding: 22px;
  transition: border-color var(--dur-base) ease, box-shadow var(--dur-base) ease;
}

/* Once an answer is checked the whole card takes the verdict's colour, so
   right/wrong reads from peripheral vision on a big screen even if the
   feedback text itself is further down the card. */
.question-card.answered-correct {
  border-color: var(--good);
  box-shadow: 0 0 0 3px var(--good-bg);
}
.question-card.answered-incorrect {
  border-color: var(--bad);
  box-shadow: 0 0 0 3px var(--bad-bg);
}

.question-prompt {
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1.4;
  margin: 0 0 20px;
  text-align: center;
}

.question-diagram {
  text-align: center;
  margin: 0 0 20px;
}

.question-blocked {
  text-align: center;
  padding: 20px 10px;
}
.question-blocked-title {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--bad-ink);
  margin: 0 0 10px;
}
.question-blocked-detail {
  font-size: 0.9rem;
  color: var(--ink-soft);
  line-height: 1.5;
  margin: 0 0 18px;
}
.question-diagram img {
  max-width: 100%;
  max-height: 40vh;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: #fff; /* COLOUR EXCEPTION (#153): diagram paper, white in both modes */
}

/* Coordinate grids (js/coordinates.js) are inline SVG rather than an image,
   so they take their colours from the theme variables and stay crisp at any
   size. Every rule below is scoped to .coord-grid — the SVG has no styling
   of its own. */
.question-diagram-svg {
  display: flex;
  justify-content: center;
}
.coord-grid {
  width: 100%;
  max-width: 340px;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 6px;
}
.coord-grid .cg-grid {
  stroke: var(--border);
  stroke-width: 1;
}
.coord-grid .cg-axis {
  stroke: var(--ink-soft);
  stroke-width: 2;
}
.coord-grid .cg-tick {
  fill: var(--ink-soft);
  font-size: 10px;
}
.coord-grid .cg-axis-name {
  fill: var(--ink-soft);
  font-size: 11px;
  font-style: italic;
}
.coord-grid .cg-segment {
  stroke: var(--primary);
  stroke-width: 2.5;
  stroke-linecap: round;
}
.coord-grid .cg-dashed {
  stroke-dasharray: 5 4;
}
.coord-grid .cg-point {
  fill: var(--primary);
}
.coord-grid .cg-point-label {
  fill: var(--accent-ink);
  font-size: 13px;
  font-weight: 700;
}

/* Roadmap #121: shape pictures for rectangle, L-shape and cut-corner area
   questions (js/diagrams.js). Same frame as the coordinate grids. The shape
   is tinted, never the removed piece, so nothing hints at the method. */
.shape-diagram {
  width: 100%;
  max-width: 340px;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 6px;
}
.shape-diagram .sd-shape {
  fill: var(--primary-tint);
  stroke: var(--primary);
  stroke-width: 2.5;
  stroke-linejoin: round;
}
.shape-diagram .sd-label {
  fill: var(--ink);
  font-size: 12px;
  font-weight: 700;
}
.shape-diagram .sd-dim {
  stroke: var(--ink-soft);
  stroke-width: 1.5;
}
.shape-diagram .sd-caption {
  fill: var(--ink-soft);
  font-size: 11px;
  font-style: italic;
}

/* Roadmap #123: "Type it like this: 7/5" under the prompt — quieter than
   the prompt, but still readable at arm's length. */
.question-answer-hint {
  margin: -10px 0 18px;
  text-align: center;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink-soft);
}

/* Roadmap #133: the one-time tip under the pad, its pointer aimed up at the
   r key (the left-hand key of the two-key row, so a quarter of the way
   across). Ink-on-surface colours so it reads in every theme and mode. */
.remainder-tip {
  position: relative;
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  background: var(--ink);
  color: var(--surface);
}
.remainder-tip::before {
  content: '';
  position: absolute;
  top: -10px;
  left: calc(25% - 10px);
  border: 10px solid transparent;
  border-top: 0;
  border-bottom-color: var(--ink);
}
.remainder-tip-text {
  flex: 1;
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.4;
}
.remainder-tip-btn {
  min-height: 44px;
  min-width: 88px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 1rem;
  font-weight: 800;
}

/* Roadmap #97: "Right number!" above the display. Friendly, not a verdict:
   no red, ink on the chip colour so it reads in every theme and mode. */
.right-form-msg {
  margin: 0 0 10px;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  border-left: 6px solid var(--primary);
  background: var(--primary-tint);
  color: var(--ink);
  font-size: 1.05rem;
  line-height: 1.4;
}
.right-form-msg strong { font-weight: 800; }

.answer-area { margin-bottom: 16px; }

.numeric-display {
  min-height: 52px;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 1.6rem;
  font-weight: 700;
  text-align: center;
  padding: 10px;
  margin-bottom: 14px;
  background: var(--bg);
  letter-spacing: 1px;
}

.keypad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  /* The Key component's grey tray. */
  background: var(--surface-2);
  border-radius: var(--radius-lg);
  padding: var(--space-2);
}
.key-back { font-size: 1.4rem; }
/* Roadmap #98: the r and / row under the 12-key pad — two wide keys. */
.keypad-extra {
  grid-template-columns: repeat(2, 1fr);
  margin-top: 8px;
}

/* After Check, the answer controls stop being controls and become a record
   of what was entered. Collapsing the 12-key pad (roughly 250px of a tablet
   screen) is what lets the question, the verdict and the explanation sit on
   screen together instead of the verdict being pushed below the fold. */
#answer-numeric.answer-checked .keypad { display: none; }
#answer-numeric.answer-checked .numeric-display,
#answer-text.answer-checked .text-input {
  margin-bottom: 0;
}
.answer-area.answer-checked.answer-checked-correct .numeric-display,
.answer-area.answer-checked.answer-checked-correct .text-input {
  border-color: var(--good);
  background: var(--good-bg);
  color: var(--good-ink);
}
.answer-area.answer-checked.answer-checked-incorrect .numeric-display,
.answer-area.answer-checked.answer-checked-incorrect .text-input {
  border-color: var(--bad);
  background: var(--bad-bg);
  color: var(--bad-ink);
}

.text-input {
  width: 100%;
  min-height: 56px;
  font-size: 1.3rem;
  text-align: center;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px;
}

.mcq-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.mcq-grid .choice-btn { min-height: 64px; font-size: 1.2rem; }

/* Multiple choice keeps its buttons on screen after checking (they're far
   more compact than a keypad), marked up so the right answer and the one
   that was picked are both obvious at a glance. */
.mcq-grid .choice-btn.choice-correct {
  border-color: var(--good);
  background: var(--good-bg);
  color: var(--good-ink);
  font-weight: 800;
}
.mcq-grid .choice-btn.choice-correct::after { content: ' \2713'; }
.mcq-grid .choice-btn.choice-wrong {
  border-color: var(--bad);
  background: var(--bad-bg);
  color: var(--bad-ink);
  text-decoration: line-through;
}
.mcq-grid .choice-btn.choice-wrong::after { content: ' \2715'; text-decoration: none; }
.mcq-grid .choice-btn[disabled] { opacity: 1; cursor: default; }

/* Check-answer/Next-question share one fixed slot (see index.html) so the
   button never moves between answering and advancing. */
.action-slot { position: relative; }
.retry-btn { min-height: 56px; }

.feedback-inline {
  margin-top: 18px;
  border-radius: var(--radius-lg);
  padding: 20px;
  /* A thick coloured edge so the panel's verdict is legible from a glance
     at the side of the screen, not only by reading the heading. */
  border-left: 8px solid transparent;
}
.feedback-inline.correct { background: var(--good-bg); border-left-color: var(--good); }
.feedback-inline.incorrect { background: var(--bad-bg); border-left-color: var(--bad); }

.feedback-result {
  font-size: 1.35rem;
  font-weight: 800;
  margin: 0 0 10px;
  line-height: 1.3;
}
.feedback-inline.correct .feedback-result { color: var(--good-ink); }
.feedback-inline.incorrect .feedback-result { color: var(--bad-ink); }

.feedback-explanation {
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
  white-space: pre-line;
}

/* Roadmap #124: a wrong answer's explanation, one numbered step at a time. */
.feedback-steps {
  margin: 0;
  padding-left: 1.6em;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ink);
}
.feedback-steps li + li { margin-top: 6px; }
.feedback-steps li { animation: step-in 0.25s ease-out both; }
@keyframes step-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.next-step-btn { min-height: 56px; }

/* Roadmap #122: the bar model under a ratio explanation. The share the
   question asked for is filled in the theme colour. */
.feedback-diagram { margin-top: 16px; }
.bar-model {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto;
}
.bar-model .bm-box {
  fill: var(--surface);
  stroke: var(--ink-soft);
  stroke-width: 1.5;
}
.bar-model .bm-box.bm-on {
  fill: var(--primary);
  stroke: var(--primary-edge);
}
.bar-model .bm-num,
.bar-model .bm-total,
.bar-model .bm-label {
  fill: var(--ink);
  font-size: 13px;
  font-weight: 700;
}
.bar-model .bm-num.bm-on { fill: var(--on-primary); }
.bar-model .bm-label.bm-on,
.bar-model .bm-total.bm-on { fill: var(--accent-ink); font-weight: 800; }
.bar-model .bm-summary {
  fill: var(--ink-soft);
  font-size: 12px;
  font-weight: 600;
}

/* Tablet and up (an iPad in portrait is 768px): this is the primary device,
   and it has the room to make the verdict genuinely hard to miss. The
   matching sizes for the centred verdict card sit further down, after the
   base rules they override. */
@media (min-width: 700px) {
  .feedback-inline { padding: 24px 26px; }
  .feedback-result { font-size: 1.6rem; }
  .feedback-explanation { font-size: 1.1rem; }
}

/* ---------- Centred answer verdict (tablet-first feedback) ----------
   The inline feedback panel above is the permanent record of an answer, but
   it sits at the bottom of the question card — on an iPad a long prompt, a
   diagram and the keypad can push it entirely off screen, so the one thing
   the child most needs to see was the thing most likely to be missed. This
   overlay is a child of #app (which doesn't scroll), so it always appears in
   the middle of the content area, and it carries the correct-answer burst
   and the streak celebration with it. */
.verdict-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Never intercept a tap — Next sits underneath and a child mid-flow
     shouldn't have to wait out an animation to reach it. */
  pointer-events: none;
  z-index: 7;
}

.verdict-card {
  position: relative;
  min-width: 260px;
  max-width: min(440px, 86vw);
  padding: 26px 32px;
  border-radius: var(--radius-lg);
  text-align: center;
  background: var(--surface);
  border: 3px solid var(--border);
  box-shadow: var(--shadow-3);
  animation: verdict-pop 0.32s cubic-bezier(0.2, 1.3, 0.5, 1) both;
}
.verdict-overlay.correct .verdict-card {
  border-color: var(--good);
  background: var(--good-bg);
}
.verdict-overlay.incorrect .verdict-card {
  border-color: var(--bad);
  background: var(--bad-bg);
}
/* Fades out on its own once the timer in js/ui.js hits, rather than
   vanishing on a frame. */
.verdict-overlay.leaving .verdict-card {
  animation: verdict-fade 0.35s ease-in forwards;
}

@keyframes verdict-pop {
  0% { opacity: 0; transform: scale(0.72); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes verdict-fade {
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(0.94); }
}

/* The burst particles fly behind the wording (see .correct-burst below), so
   the verdict itself stays readable through the animation. */
.verdict-icon,
.verdict-headline,
.verdict-detail,
.verdict-streak {
  position: relative;
  z-index: 1;
}

.verdict-icon {
  font-size: 3.6rem;
  line-height: 1;
  font-weight: 900;
}
.verdict-overlay.correct .verdict-icon { color: var(--good-ink); }
.verdict-overlay.incorrect .verdict-icon { color: var(--bad-ink); }

.verdict-headline {
  margin: 10px 0 0;
  font-size: 1.7rem;
  font-weight: 900;
  letter-spacing: -0.01em;
}
.verdict-overlay.correct .verdict-headline { color: var(--good-ink); }
.verdict-overlay.incorrect .verdict-headline { color: var(--bad-ink); }

.verdict-detail {
  margin: 8px 0 0;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.35;
}
.verdict-detail:empty { display: none; }

/* The streak banner, previously a toast above the HUD. */
.verdict-streak {
  margin: 14px 0 0;
  display: inline-block;
  background: var(--warn-bg);
  color: var(--warn-ink);
  border: 2px solid var(--warn);
  font-weight: 800;
  font-size: 1rem;
  padding: 8px 18px;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-2);
  white-space: nowrap;
  animation: verdict-streak-in 0.5s cubic-bezier(0.2, 1.4, 0.5, 1) both;
}
@keyframes verdict-streak-in {
  0% { opacity: 0; transform: translateY(12px) scale(0.7); }
  70% { opacity: 1; transform: translateY(0) scale(1.08); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

/* The celebratory particle burst on a correct answer, now anchored to the
   centre of the verdict card instead of the top of the question card. */
.correct-burst {
  position: absolute;
  /* Launch from the foot of the card so the particles rise past the wording
     rather than sitting on top of it at the moment it's being read. */
  top: 86%;
  left: 50%;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 0;
}
.burst-particle {
  position: absolute;
  left: 0;
  top: 0;
  font-size: 1.9rem;
  opacity: 0;
  animation: burst-fly 1s ease-out forwards;
}
@keyframes burst-fly {
  0% { opacity: 1; transform: translate(-50%, 0) scale(0.6); }
  30% { opacity: 1; transform: translate(calc(-50% + var(--dx) * 0.45), -70px) scale(1.15); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), -170px) scale(1); }
}

/* Tablet and up — the other half of the breakpoint above. */
@media (min-width: 700px) {
  .verdict-card { min-width: 340px; padding: 34px 44px; }
  .verdict-icon { font-size: 4.6rem; }
  .verdict-headline { font-size: 2.1rem; }
  .verdict-detail { font-size: 1.3rem; }
  .verdict-streak { font-size: 1.1rem; }
}

/* Roadmap #100: the wrong-answer card is compact — about half the icon and
   headline — so it covers less of the worked explanation underneath. It
   still carries the answer, since on a long question the explanation panel
   can be below the fold when the card appears. Declared after the tablet
   sizes above so it wins at every width. */
.verdict-overlay.incorrect .verdict-card {
  min-width: 0;
  padding: 12px 22px;
  border-radius: var(--radius-lg);
}
.verdict-overlay.incorrect .verdict-icon { font-size: 1.9rem; }
.verdict-overlay.incorrect .verdict-headline { margin-top: 4px; font-size: 1.05rem; }
.verdict-overlay.incorrect .verdict-detail { margin-top: 4px; font-size: 1rem; }

/* Roadmap #117: the wrong-answer card is a slim banner pinned to the top of
   the content area, lined up with the content column, instead of a card in
   the middle of the screen where it covered the marked answer buttons.
   scrollFeedbackIntoView (ui.js) keeps the answer record below it. */
.verdict-overlay.incorrect {
  align-items: flex-start;
  padding: 8px 16px 0;
}
.verdict-overlay.incorrect .verdict-card {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  column-gap: 12px;
  row-gap: 2px;
  width: 100%;
  max-width: 608px;
  text-align: left;
  padding: 10px 18px;
  animation-name: verdict-drop;
}
.verdict-overlay.incorrect.leaving .verdict-card { animation-name: verdict-rise; }
.verdict-overlay.incorrect .verdict-headline,
.verdict-overlay.incorrect .verdict-detail { margin: 0; }
@keyframes verdict-drop {
  0% { opacity: 0; transform: translateY(-16px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes verdict-rise {
  0% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-12px); }
}

/* Motion is decoration here; the colour, the icon and the words carry the
   verdict on their own, so drop the movement but keep the card. */
@media (prefers-reduced-motion: reduce) {
  .verdict-card,
  .verdict-streak,
  .badge-card-reveal { animation: none; }
  .verdict-overlay.leaving .verdict-card { opacity: 0; animation: none; }
  /* The #117 banner's own slide, which is more specific than the above. */
  .verdict-overlay.incorrect .verdict-card { animation: none; }
  .verdict-overlay.incorrect.leaving .verdict-card { opacity: 0; animation: none; }
  .feedback-steps li { animation: none; }
  .correct-burst { display: none; }
  .streak-pulse { animation: none; }
  .combo-flame,
  .boss-beast,
  .boss-beast.hit,
  .boss-beast.roar,
  .boss-beast.destroyed .beast-half,
  .new-records,
  .chest-reward,
  .chest-icon { animation: none !important; }
  .boss-beast.destroyed { opacity: 0.15; }
}

/* Summary screen */
.summary-card {
  padding: 20px;
}
.summary-row {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 1.02rem;
}
.summary-row:last-child { border-bottom: none; }
/* Roadmap #111: the parts of the points breakdown sit indented under it. */
.summary-row-part .label { padding-left: 14px; }
.summary-row-total .label { color: var(--ink); font-weight: 700; }
.summary-row .label { color: var(--ink-soft); }
.summary-row .value { font-weight: 700; }

/* ---------- Home streak/points widget (right sidebar) ---------- */
.home-streak-widget {
  padding: 14px;
  text-align: center;
}
.streak-widget-main { font-size: 1.5rem; font-weight: 800; color: var(--ink); }
.streak-widget-label { font-size: 0.82rem; color: var(--ink-soft); margin-top: 2px; }
.streak-widget-points { font-size: 0.82rem; color: var(--accent-ink); font-weight: 700; margin-top: 8px; }

/* ---------- Next session recommendation widget (Roadmap #85) ---------- */
.next-session-widget {
  padding: 14px;
  margin-top: 14px;
}
.next-session-title { font-size: 0.95rem; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.next-session-body { font-size: 0.86rem; color: var(--ink-soft); line-height: 1.45; margin: 0; }
.next-session-length { font-size: var(--fs-xs); color: var(--accent-ink); font-weight: 700; margin: 8px 0 0; }

/* ---------- Daily mystery chest line (Roadmap #91) ---------- */
.streak-widget-chest {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--accent-ink);
  line-height: 1.35;
}
.streak-widget-chest-done { color: var(--ink-soft); font-weight: 600; }
.streak-widget-shield { font-size: 0.82rem; font-weight: 700; color: var(--good-ink); margin-top: 6px; }

/* ---------- Personal bests widget (Roadmap #89) ---------- */
.pb-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.84rem;
}
.pb-row:last-child { border-bottom: none; padding-bottom: 0; }
.pb-label { color: var(--ink-soft); }
.pb-value { font-weight: 800; color: var(--ink); white-space: nowrap; }

/* ---------- Combo meter in the session HUD (Roadmap #88) ----------
   The flame steps up a size at each multiplier: x1, x2 (3 in a row), x3
   (6 in a row). */
/* Reset the pill look .session-hud span gives every span in the HUD. */
.session-hud .combo-flame {
  display: inline-block;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  min-height: 0;
  box-shadow: none;
  margin-right: 2px;
  transition: transform 0.25s ease;
  transform-origin: 50% 80%;
}
#hud-streak[data-combo="2"] .combo-flame { transform: scale(1.25); margin-right: 4px; }
#hud-streak[data-combo="3"] .combo-flame { transform: scale(1.5); margin-right: 6px; animation: combo-flicker 0.9s ease-in-out infinite; }
#hud-streak:empty { display: none; }
.combo-mult { color: var(--warn-ink); margin-left: 2px; }
#hud-streak[data-combo="3"] { border-color: var(--warn); }
@keyframes combo-flicker {
  0%, 100% { transform: scale(1.5) rotate(-4deg); }
  50% { transform: scale(1.62) rotate(4deg); }
}

/* ---------- Boss challenge (Roadmap #92, #94, #95) ----------
   COLOUR EXCEPTION GROUP (#153): the boss arena is a dark purple/red arena
   in both colour modes, so it keeps its own colours. They're named once as
   --boss-* tokens here; the beast drawing (.beast-*) is art and keeps its
   literal colours. Nothing outside this section uses them. */
#screen-question {
  --boss-deep: #1b0f2a;
  --boss-surface: #26163a;
  --boss-raised: #2d1a45;
  --boss-inset: #36224f;
  --boss-line: #5b3b84;
  --boss-ink: #f6efff;
  --boss-ink-soft: #cbb9e8;
  --boss-accent-ink: #c3cbff;
  --boss-red: #ff4d6d;
  --boss-flame: #ff8a5c;
  --boss-glow: #ff9f43;
  --boss-gold: #ffb347;
  --boss-plot: #9aa8ff;
  --boss-spent: #5b4a70;
}
/* The whole question screen changes look for the challenge: a dark arena
   whatever the light/dark setting. Re-pointing the theme variables inside
   the card (rather than restyling each child) keeps the keypad, choices,
   text box and coordinate grids readable on the dark background. */
#screen-question.boss-mode .session-hud {
  --warn-ink: #ffc766; /* the combo "x3" on the dark arena HUD */
  background: var(--boss-deep);
  padding: 8px 12px;
  border-radius: var(--radius-pill);
}
#screen-question.boss-mode .session-hud span {
  background: var(--boss-raised);
  border-color: var(--boss-line);
  color: var(--boss-ink);
}
.question-card.boss-question {
  --bg: var(--boss-deep);
  --surface: var(--boss-surface);
  --ink: var(--boss-ink);
  --ink-soft: var(--boss-ink-soft);
  --border: var(--boss-line);
  --good-bg: rgba(47, 196, 138, 0.25);
  --bad-bg: rgba(255, 77, 109, 0.22);
  --accent-ink: var(--boss-accent-ink);
  /* #153 tokens the new components read, set to the same arena shades so
     keys, choices and verdict text stay readable on the dark card. */
  --surface-2: var(--boss-inset);
  --border-strong: var(--boss-line);
  --card-border: var(--boss-line);
  --primary-tint: var(--boss-inset);
  --good-ink: #7be0b0;
  --bad-ink: #ff9aa9;
  --warn-ink: #ffc766;
  background: radial-gradient(circle at 50% 0%, #4a1c3c 0%, #231433 55%, #170c24 100%);
  color: var(--ink);
  border: 2px solid var(--boss-red);
  box-shadow: 0 0 0 4px rgba(255, 77, 109, 0.18), 0 10px 30px rgba(20, 5, 30, 0.45);
}
.question-card.boss-question .question-prompt { font-size: 1.5rem; }
.question-card.boss-question .coord-grid .cg-segment { stroke: var(--boss-plot); }
.question-card.boss-question .coord-grid .cg-point { fill: var(--boss-plot); }
.question-card.boss-question .shape-diagram .sd-shape { stroke: var(--boss-plot); }

.boss-banner {
  margin: -6px 0 18px;
  text-align: center;
  color: var(--boss-ink);
}
.boss-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.boss-title {
  font-weight: 900;
  font-size: 1.3rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--boss-flame);
  text-shadow: 0 0 12px rgba(255, 77, 109, 0.6);
}
.boss-round { font-weight: 700; font-size: 0.9rem; color: var(--boss-ink-soft); }

.boss-stage {
  display: flex;
  justify-content: center;
  padding: 6px 0 2px;
}
.boss-beast {
  width: min(62vw, 230px);
  height: auto;
  overflow: visible;
  animation: beast-breathe 2.2s ease-in-out infinite;
  transform-origin: 50% 90%;
}
@keyframes beast-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.035, 0.975); }
}

.beast-body { fill: #b3243f; stroke: #5e0f22; stroke-width: 4; }
.beast-belly { fill: #d9546a; }
.beast-limb { fill: #8f1a33; stroke: #5e0f22; stroke-width: 3; stroke-linejoin: round; }
.beast-horn { fill: #f2e6c9; stroke: #6d5a3a; stroke-width: 3; stroke-linejoin: round; }
.beast-brow { stroke: #2a0710; stroke-width: 8; stroke-linecap: round; }
.beast-eye { fill: #ffe14d; stroke: #2a0710; stroke-width: 3; }
.beast-pupil { fill: #2a0710; }
.beast-x-eye { stroke: #2a0710; stroke-width: 6; stroke-linecap: round; }
.beast-mouth { fill: #3a0714; stroke: #2a0710; stroke-width: 3; stroke-linejoin: round; }
.beast-tooth { fill: #fffaf0; }
.beast-crack { fill: none; stroke: #2a0710; stroke-width: 3.5; stroke-linejoin: round; }

/* Damage stages: .dmg-show-N appears and .dmg-hide-N goes once the beast
   has taken N hits (data-damage is set by ui.js). */
.boss-beast .dmg-show-1,
.boss-beast .dmg-show-2 { display: none; }
.boss-beast[data-damage="1"] .dmg-show-1,
.boss-beast[data-damage="2"] .dmg-show-1,
.boss-beast[data-damage="3"] .dmg-show-1,
.boss-beast[data-damage="2"] .dmg-show-2,
.boss-beast[data-damage="3"] .dmg-show-2 { display: inline; }
.boss-beast[data-damage="1"] .dmg-hide-1,
.boss-beast[data-damage="2"] .dmg-hide-1,
.boss-beast[data-damage="3"] .dmg-hide-1,
.boss-beast[data-damage="2"] .dmg-hide-2,
.boss-beast[data-damage="3"] .dmg-hide-2 { display: none; }
/* It looks a little more battered with every hit. */
.boss-beast[data-damage="1"] .beast-body { fill: #a0223b; }
.boss-beast[data-damage="2"] .beast-body,
.boss-beast[data-damage="3"] .beast-body { fill: #8a2038; }

/* A hit: the beast flashes white and reels. A miss: it roars (grows and
   shakes) — no damage to the child's score either way. */
.boss-beast.hit { animation: beast-hit 0.6s ease-out; }
@keyframes beast-hit {
  0% { transform: translateX(0); filter: brightness(1); }
  15% { transform: translateX(-12px) rotate(-6deg); filter: brightness(3); }
  35% { transform: translateX(10px) rotate(5deg); filter: brightness(1.8); }
  60% { transform: translateX(-6px) rotate(-2deg); filter: brightness(1.2); }
  100% { transform: translateX(0); filter: brightness(1); }
}
.boss-beast.roar { animation: beast-roar 0.7s ease-out; }
@keyframes beast-roar {
  0%, 100% { transform: scale(1); }
  30% { transform: scale(1.14); }
  45% { transform: scale(1.1) rotate(-3deg); }
  60% { transform: scale(1.12) rotate(3deg); }
}

/* Destroyed: the beast splits along its crack and both halves fly apart. */
.boss-beast.destroyed { animation: none; }
.beast-half { transform-box: fill-box; transform-origin: 50% 100%; }
.boss-beast.destroyed .beast-half-l { animation: beast-split-l 1.4s cubic-bezier(0.3, 0.6, 0.4, 1) 0.35s forwards; }
.boss-beast.destroyed .beast-half-r { animation: beast-split-r 1.4s cubic-bezier(0.3, 0.6, 0.4, 1) 0.35s forwards; }
@keyframes beast-split-l {
  0% { transform: translate(0, 0) rotate(0); opacity: 1; }
  25% { transform: translate(-6px, -4px) rotate(-4deg); opacity: 1; }
  100% { transform: translate(-70px, 60px) rotate(-38deg); opacity: 0; }
}
@keyframes beast-split-r {
  0% { transform: translate(0, 0) rotate(0); opacity: 1; }
  25% { transform: translate(6px, -4px) rotate(4deg); opacity: 1; }
  100% { transform: translate(70px, 60px) rotate(38deg); opacity: 0; }
}

.boss-health {
  margin: 10px auto 0;
  max-width: 320px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.2);
  overflow: hidden;
}
.boss-health-fill {
  height: 100%;
  width: 100%;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--boss-red), var(--boss-glow));
  transition: width 1.1s cubic-bezier(0.3, 0.8, 0.3, 1);
}
.boss-pips { display: flex; justify-content: center; gap: 10px; margin-top: 10px; }
.boss-pip {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--boss-ink-soft);
}
.boss-pip.hit { background: var(--boss-gold); border-color: var(--boss-gold); box-shadow: 0 0 8px var(--boss-glow); }
.boss-pip.miss { background: var(--boss-spent); border-color: var(--boss-spent); }
.boss-status { margin: 10px 0 0; font-size: 0.95rem; font-weight: 700; line-height: 1.4; color: var(--boss-ink); }
/* ---------- End of the boss arena colour exception group ---------- */

/* ---------- Summary: NEW RECORD banner (#89) and chest (#91) ----------
   The NEW RECORD banner is the Banner / celebration component now: ink on
   a card with a gold edge (it used to be white on light green, under 3:1). */
.new-records {
  margin-bottom: 16px;
  padding: 14px 16px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--gold);
  box-shadow: var(--shadow-2);
  text-align: center;
  animation: record-flash 0.9s ease-out 2;
}
.new-records-title { margin: 0 0 6px; font-size: 1.5rem; font-weight: 900; letter-spacing: 0.06em; color: var(--good-ink); }
.new-records-list { list-style: none; margin: 0; padding: 0; font-size: 0.98rem; line-height: 1.6; }
@keyframes record-flash {
  0% { transform: scale(0.85); opacity: 0; }
  40% { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(1); }
}
.chest-reward {
  margin-bottom: 16px;
  padding: 16px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 2px solid var(--gold);
  text-align: center;
  animation: badge-pop 0.5s ease-out;
}
.chest-icon { font-size: 2.4rem; animation: chest-open 0.7s cubic-bezier(0.2, 1.4, 0.5, 1) both; }
@keyframes chest-open {
  0% { transform: scale(0.5) rotate(-12deg); }
  60% { transform: scale(1.2) rotate(6deg); }
  100% { transform: scale(1) rotate(0); }
}
.chest-title { margin: 4px 0 0; font-size: 0.85rem; font-weight: 700; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.05em; }
.chest-prize { margin: 6px 0 0; font-size: 1.35rem; font-weight: 900; color: var(--ink); }
.chest-note { margin: 4px 0 0; font-size: 0.85rem; color: var(--ink-soft); }

.mastery-bars { display: flex; flex-direction: column; gap: 12px; }
.mastery-row .mastery-label {
  display: flex;
  justify-content: space-between;
  font-size: 0.92rem;
  font-weight: 600;
  margin-bottom: 4px;
}
.mastery-row-bottom {
  display: flex;
  align-items: center;
  gap: 10px;
}
.mastery-track {
  flex: 1;
  height: 14px;
  background: var(--surface-2);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.mastery-fill {
  height: 100%;
  background: var(--primary);
  border-radius: var(--radius-pill);
}
.sparkline {
  flex-shrink: 0;
  color: var(--primary);
}
/* Roadmap #129: a topic's best medal beside its %, and the next target. */
.mastery-medal { margin-right: 6px; }
.mastery-target {
  margin-top: 4px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--ink-soft);
}

.badges-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.badge-card {
  padding: 12px;
  text-align: center;
}
.badge-card.locked { opacity: 0.5; }
.badge-icon { font-size: 1.8rem; }
.badge-label { font-weight: 700; font-size: 0.88rem; margin-top: 4px; }
.badge-desc { font-size: var(--fs-xs); color: var(--ink-soft); margin-top: 2px; }

.new-badges {
  margin-bottom: 16px;
  background: var(--warn-bg);
  border: 2px solid var(--gold);
  border-radius: var(--radius-lg);
  padding: 16px;
  animation: badge-pop 0.4s ease-out;
}
@keyframes badge-pop {
  0% { transform: scale(0.85); opacity: 0; }
  70% { transform: scale(1.03); opacity: 1; }
  100% { transform: scale(1); }
}
.new-badges-title {
  font-weight: 700;
  color: var(--warn-ink);
  margin: 0 0 10px;
  text-align: center;
  font-size: 1.05rem;
}
.new-badges-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  gap: 10px;
}
.badge-card-reveal {
  position: relative;
  overflow: visible;
  animation: badge-pop 0.5s ease-out backwards;
}
.badge-sparkle {
  position: absolute;
  top: -8px;
  right: -4px;
  font-size: 1.1rem;
  animation: sparkle-twinkle 1.4s ease-in-out infinite;
}
@keyframes sparkle-twinkle {
  0%, 100% { opacity: 0.3; transform: scale(0.8) rotate(0deg); }
  50% { opacity: 1; transform: scale(1.15) rotate(15deg); }
}

.session-history { display: flex; flex-direction: column; gap: 8px; }
/* ---------- Mistakes I fixed (Roadmap #105) ---------- */
.fixed-mistakes {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.fixed-row {
  border-left: 6px solid var(--good);
  padding: 10px 14px;
  font-size: 0.95rem;
  line-height: 1.4;
}
.fixed-kind { display: block; font-weight: 700; color: var(--ink); }
.fixed-dates { display: block; color: var(--ink-soft); }
.fixed-more { margin: 2px 0 0; font-size: 0.95rem; color: var(--ink-soft); }

/* One-line notes under the summary card: fixed mistakes (#105) and the
   streak shield (#112). */
.summary-notes {
  margin-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.summary-note {
  margin: 0;
  background: var(--good-bg);
  color: var(--ink);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
}

/* ---------- Best thing today / practise next time (Roadmap #110) ---------- */
.summary-takeaways {
  margin-top: 14px;
  padding: 16px 20px;
}
.takeaway + .takeaway {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.takeaway-title { margin: 0 0 4px; font-size: 1rem; font-weight: 800; color: var(--ink); }
.takeaway-body { margin: 0; font-size: 1rem; line-height: 1.45; color: var(--ink-soft); }

.session-history-row {
  padding: 10px 14px;
  font-size: 0.9rem;
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
}
.session-history-row .value { color: var(--ink); font-weight: 700; }

.empty-state {
  color: var(--ink-soft);
  font-size: 0.95rem;
  padding: 12px 0;
}

@media (min-width: 480px) {
  .choice-grid { grid-template-columns: repeat(4, 1fr); }
  #topic-choices { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Name input ---------- */
.name-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.name-label {
  font-size: 0.85rem;
  color: var(--ink-soft);
  font-weight: 600;
}
.name-input {
  flex: 1;
  min-height: var(--tap-min);
  border: none;
  border-bottom: 2px solid var(--border);
  background: none;
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  padding: 4px 2px;
}
.name-input:focus { outline: none; border-bottom-color: var(--primary); }

/* ---------- Strength summary (home page) ---------- */
.strength-summary {
  margin-top: 12px;
  font-size: 0.88rem;
  color: var(--ink-soft);
  line-height: 1.6;
}
.strength-summary strong { color: var(--ink); }

/* ---------- Date/time widget ----------
   Styled after the iPhone lock screen clock: dark card, small caps date on
   top, a big thin time underneath with tabular numerals so it doesn't jitter
   as the digits tick over. */
.datetime-widget {
  margin-bottom: 14px;
  background: var(--ink);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 16px 14px 18px;
  text-align: center;
}
.datetime-date {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--surface);
  opacity: 0.8;
}
.datetime-time {
  font-size: 2.6rem;
  font-weight: 200;
  color: var(--surface);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  margin-top: 2px;
}

/* ---------- Weather widget ---------- */
.weather-widget {
  padding: 14px;
  text-align: center;
}
.weather-icon { font-size: 2rem; line-height: 1; }
.weather-temp { font-size: 1.3rem; font-weight: 800; color: var(--ink); margin-top: 2px; }
.weather-label { font-size: 0.82rem; color: var(--ink-soft); margin-top: 2px; }
.weather-place { font-size: var(--fs-xs); color: var(--ink-soft); margin-top: 4px; font-weight: 600; }
.weather-empty { font-size: 0.82rem; color: var(--ink-soft); text-align: left; margin: 0; }
.weather-hint {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  margin: 6px 0 0;
  line-height: 1.4;
}

/* ---------- Settings: danger zone ---------- */
.danger-heading { color: var(--bad-ink); }
/* Light / Dark / Match device — three across at every width, unlike the
   2-then-4 column session-length grid it borrows its buttons from. */
.choice-grid.mode-choices {
  grid-template-columns: repeat(3, 1fr);
  margin-bottom: 8px;
}

.danger-hint {
  font-size: 0.82rem;
  color: var(--ink-soft);
  margin: 0 0 10px;
  line-height: 1.4;
}
.danger-btn {
  display: block;
  width: fit-content;
  min-width: 200px;
  margin: 0;
  padding: 10px 20px;
}

/* ---------- Joke / word of the day (sidebar panels) ----------
   These sit under the date and weather widgets, so they use the same card
   shell (surface, border, 12px radius, 14px padding) to make the sidebar
   read as one stack instead of two flat blocks tacked below two cards. The
   coloured left edge and the small heading row are what tell them apart:
   these two are the fun panels, not data. The accent is var(--primary), so
   it follows whichever cosmetic theme is on. */
.joke-of-day,
.word-of-day {
  margin-top: 14px;
  border-left: 4px solid var(--primary);
  padding: 12px 14px;
  color: var(--ink);
  line-height: 1.5;
}
.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.panel-icon { font-size: 1.15rem; line-height: 1; }
.panel-title {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* Roadmap #86: lets the child request a different joke/word without
   waiting for the day to change. Pushed to the far right of the heading
   row; sized as a real tap target, not just an icon glyph. */
.panel-refresh-btn {
  margin-left: auto;
  width: var(--tap-min);
  height: var(--tap-min);
  min-width: var(--tap-min);
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.panel-refresh-btn:active { background: var(--border); transform: scale(0.92); }
.joke-text {
  margin: 0;
  font-size: 0.9rem;
}
.word-term {
  margin: 0 0 4px;
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--accent-ink);
  line-height: 1.25;
}
.word-meaning {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

/* ---------- Custom session length ---------- */
.custom-length-panel {
  margin-top: 10px;
  display: flex;
  gap: 10px;
  align-items: center;
}
.custom-length-value {
  width: 80px;
  min-height: 48px;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 1.1rem;
  text-align: center;
  padding: 6px;
}
.custom-length-unit {
  display: flex;
  gap: 6px;
  flex: 1;
}
.unit-btn {
  flex: 1;
  min-height: 48px;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
}
.unit-btn.selected {
  border-color: var(--ring);
  background: var(--primary-tint);
  color: var(--accent-ink);
}

/* ---------- Topic weighting preview ---------- */
.topic-weighting-preview {
  margin-top: 10px;
  background: var(--primary-tint);
  border-radius: var(--radius-md);
  padding: 12px 14px;
}
.weighting-title {
  margin: 0 0 6px;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--accent-ink);
}
.weighting-row {
  display: flex;
  justify-content: space-between;
  font-size: 0.85rem;
  padding: 2px 0;
  color: var(--ink);
}
.start-warning {
  margin-top: 10px;
  background: var(--bad-bg);
  color: var(--bad-ink);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  font-size: 0.85rem;
  line-height: 1.4;
}

/* ---------- Header avatar ---------- */
.header-avatar {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.55rem;
  background: var(--surface-2);
  box-shadow: 0 0 0 2px var(--border);
  flex-shrink: 0;
  position: relative;
}
/* COLOUR EXCEPTION GROUP (#153): the avatar frames bought in the Shop keep
   their own special colours (gold, fire, diamond, rainbow), here and in the
   Shop's frame samples. */
.header-avatar[data-frame="frame-gold"] { box-shadow: 0 0 0 3px #d4a017; }
.header-avatar[data-frame="frame-fire"] { box-shadow: 0 0 0 3px #e8734a; }
.header-avatar[data-frame="frame-diamond"] { box-shadow: 0 0 0 3px #b9e0ff, 0 0 8px 1px #7fd0ff; }
.header-avatar[data-frame="frame-rainbow"] {
  box-shadow: none;
  background-image: conic-gradient(red, orange, yellow, green, blue, violet, red);
  background-origin: border-box;
  padding: 3px;
}
.header-avatar[data-frame="frame-rainbow"] .avatar-emoji {
  display: flex;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--surface);
  align-items: center;
  justify-content: center;
}

.avatar-accessory {
  position: absolute;
  top: -6px;
  right: -6px;
  font-size: 0.6em;
  line-height: 1;
}
.avatar-mood {
  position: absolute;
  bottom: -6px;
  left: -6px;
  font-size: 0.6em;
  line-height: 1;
}

/* A circle like the header avatar (#153 part 2): the bought avatar colour
   is its background, so it mustn't stretch into a full-width strip. */
.summary-avatar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  margin: 0 auto var(--space-2);
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 2.4rem;
}
.summary-avatar .avatar-accessory,
.summary-avatar .avatar-mood { font-size: 0.5em; }

/* ---------- Strength overview (simple strong/weak list) ---------- */
.strength-overview {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.strength-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}
.strength-topic { font-weight: 600; font-size: 0.92rem; }
.strength-detail {
  display: flex;
  align-items: center;
  gap: 8px;
}
.strength-pct {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink-soft);
}
.strength-tag {
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
}
.strength-strong { background: var(--good-bg); }
.strength-strong .strength-tag { background: var(--good-ink); color: var(--surface); }
.strength-medium { background: var(--primary-tint); }
.strength-medium .strength-tag { background: var(--primary); color: var(--on-primary); }
.strength-weak { background: var(--bad-bg); }
.strength-weak .strength-tag { background: var(--bad-ink); color: var(--surface); }

/* ---------- Import ---------- */
.import-panel { display: flex; flex-direction: column; }
.import-count {
  font-size: 0.88rem;
  color: var(--ink-soft);
  margin: 0 0 10px;
}
.upload-buttons-row {
  display: flex;
  gap: 10px;
}
.file-label {
  flex: 1;
  min-width: 0;
  text-align: center;
  cursor: pointer;
  margin-top: 0 !important;
}
.pdf-hint {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  margin: 6px 0 0;
  line-height: 1.4;
}
.import-errors {
  margin-top: 10px;
  background: var(--bad-bg);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: var(--fs-xs);
  color: var(--bad-ink);
}
.import-errors ul { margin: 6px 0 0; padding-left: 18px; }
.import-errors li { margin-bottom: 8px; }
.import-errors li:last-child { margin-bottom: 0; }
.import-error-hint {
  display: block;
  color: var(--ink-soft);
  font-size: var(--fs-xs);
  margin-top: 2px;
}

/* Read-only preview of what an import actually produced, so the questions
   can be visually spot-checked rather than trusted on a count alone. */
.import-preview { margin-top: 18px; }
.import-preview-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 10px;
}
.import-card {
  padding: 12px 14px;
}
.import-card-head {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.import-tag {
  font-size: var(--fs-xs);
  font-weight: 700;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-soft);
}
.import-tag-computed { background: var(--good-bg); color: var(--good-ink); }
.import-tag-guessed { background: var(--bad-bg); color: var(--bad-ink); }
.import-tag-recovered { background: var(--primary); color: var(--on-primary); }
.import-prompt {
  font-size: 0.92rem;
  font-weight: 600;
  margin: 0 0 8px;
  line-height: 1.4;
}
.import-diagram {
  display: block;
  max-width: 100%;
  max-height: 240px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: #fff; /* COLOUR EXCEPTION (#153): diagram paper */
  margin: 0 0 8px;
}
.import-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.import-choice {
  font-size: var(--fs-xs);
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-soft);
}
.import-choice-correct {
  background: var(--good-bg);
  color: var(--good-ink);
  font-weight: 700;
}
.import-answer {
  font-size: 0.85rem;
  margin: 0 0 8px;
}
.import-explanation {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.4;
  white-space: pre-line;
}

/* ---------- Shop ---------- */
.shop-balance {
  background: var(--hero-bg);
  color: var(--on-primary);
  border-radius: var(--radius-lg);
  padding: 16px 20px;
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 20px;
}
.shop-category { margin-bottom: 24px; }
.shop-category-title {
  font-size: 0.95rem;
  color: var(--ink-soft);
  margin: 0 0 10px;
}
.shop-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.shop-item {
  border-width: 2px;
  padding: 12px;
  text-align: center;
}
.shop-item.equipped { border-color: var(--ring); background: var(--primary-tint); }
.shop-item-swatch {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  margin: 0 auto 6px;
  border: 1px solid var(--border);
}
.shop-item-emoji { font-size: 1.8rem; }
.shop-item-label { font-weight: 700; font-size: 0.85rem; margin-top: 4px; }
.shop-item-font-sample { font-size: 1.1rem; margin-bottom: 4px; }
.shop-item-frame-sample {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  margin: 0 auto 6px;
  background: var(--surface-2);
}
.shop-item-frame-sample.frame-frame-gold { box-shadow: 0 0 0 3px #d4a017; }
.shop-item-frame-sample.frame-frame-fire { box-shadow: 0 0 0 3px #e8734a; }
.shop-item-frame-sample.frame-frame-rainbow { background-image: conic-gradient(red, orange, yellow, green, blue, violet, red); }
.shop-item-action {
  min-height: var(--tap-min);
  width: 100%;
  margin-top: 8px;
  border-radius: var(--radius-sm);
  border: none;
  font-size: var(--fs-xs);
  font-weight: 700;
  background: var(--primary);
  color: var(--on-primary);
}
.shop-item-action:disabled { background: var(--disabled-bg); }

/* ---------- Power-ups: the Streak Shield (Roadmap #112) ---------- */
.powerup-card {
  display: flex;
  align-items: center;
  gap: 14px;
  border-width: 2px;
  padding: 12px 14px;
}
.powerup-icon { font-size: 2.2rem; line-height: 1; }
.powerup-body { flex: 1; min-width: 0; }
.powerup-name { font-weight: 800; font-size: 1rem; color: var(--ink); }
.powerup-price { font-weight: 700; color: var(--accent-ink); margin-left: 4px; }
.powerup-desc { font-size: 0.9rem; color: var(--ink-soft); margin-top: 2px; }
.powerup-held { font-size: 0.9rem; font-weight: 700; color: var(--good-ink); margin-top: 4px; }
.powerup-buy-btn {
  min-height: 48px;
  min-width: 110px;
  padding: 0 14px;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-weight: 700;
  background: var(--primary);
  color: var(--on-primary);
}
.powerup-buy-btn:disabled { background: var(--disabled-bg); }
.shop-item-action.owned { background: var(--primary-tint); color: var(--accent-ink); }

.footer-link-row {
  display: flex;
  gap: 10px;
  justify-content: center;
}
.footer-link-row .link-btn { width: auto; }

/* ---------- Wider screens: use the extra horizontal space instead of
   leaving it empty either side of a narrow centered column ---------- */
@media (min-width: 900px) {
  #app-inner,
  .footer-inner {
    max-width: 1324px;
  }
  #topic-choices { grid-template-columns: repeat(4, 1fr); }
  .badges-grid { grid-template-columns: repeat(4, 1fr); }
  .shop-grid { grid-template-columns: repeat(4, 1fr); }
  .mastery-bars {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 14px 32px;
  }
  .session-history {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
  }
  /* Sidebar (fixed width, left) / main (centered) / streak+points widget
     (fixed width, right) — the two equal-width side columns are what
     actually center .start-main on the window: a plain two-column layout
     can only center main in the space *left over* next to the sidebar,
     which drifts off from true center. Mirroring the sidebar's width on
     the other side (now with real content instead of an empty balance
     column) keeps the two columns either side of main equal, so main
     sits truly centered regardless of viewport width. */
  .start-layout {
    display: grid;
    grid-template-columns: 260px 1fr 260px;
    align-items: start;
    gap: 36px;
  }
  /* Back to document order: grid auto-placement follows `order`, and the
     narrow-screen order above would push the left column onto a new row. */
  .start-sidebar,
  .start-main,
  .start-side-right { order: 0; }
  .start-sidebar {
    grid-column: 1;
    margin-top: 0;
  }
  .start-main {
    grid-column: 2;
    max-width: 700px;
    margin: 0 auto;
    width: 100%;
  }
  .start-side-right {
    grid-column: 3;
    margin-top: 0;
  }
}

/* ---------- Roadmap #132: the question screen on a wide landscape screen
   Two columns: the question, any picture and the feedback on the left; the
   answer controls with Check/Next (.question-controls) in a fixed column on
   the right, so the keypad isn't stretched across the whole tablet. The
   boss banner runs across both. The rows are only for lining things up:
   the controls span them all, and the last row soaks up any extra height
   so the left-hand items don't spread apart. Portrait, narrow windows and
   iPad Split View under 900px keep the single column. ---------- */
@media (min-width: 900px) and (orientation: landscape) {
  .question-card:not(.is-blocked) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 360px);
    grid-template-rows: auto auto auto auto auto 1fr;
    column-gap: 32px;
    align-items: start;
  }
  .question-card:not(.is-blocked) > * { grid-column: 1; }
  .question-card:not(.is-blocked) > .boss-banner { grid-column: 1 / -1; grid-row: 1; }
  .question-card:not(.is-blocked) > .question-controls { grid-column: 2; grid-row: 1 / -1; }
  .question-card.boss-question:not(.is-blocked) > .question-controls { grid-row: 2 / -1; }
}

/* ---------- Backup and restore (Roadmap #150) and reminder (#151) ---------- */
.backup-last {
  margin: 0 0 10px;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--ink);
}
.backup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.backup-actions .secondary-btn {
  flex: 1 1 200px;
  margin: 0;
}
.backup-restore-label {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.backup-status {
  margin: 10px 0 0;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--ink);
}
.backup-status.backup-status-warn { color: var(--bad-ink); }
.home-notice {
  margin: 12px 0 0;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: var(--good-bg);
  color: var(--good-ink);
  font-weight: 700;
}
/* Calm, not alarming: the ordinary card look, no red (#151 AC12). */
.backup-reminder {
  margin-top: 12px;
  padding: 14px 16px;
}
.backup-reminder-text {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--ink);
}
.backup-reminder-error {
  margin: 8px 0 0;
  font-size: 0.88rem;
  color: var(--ink-soft);
}
.backup-reminder-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  margin-top: 10px;
}
.backup-reminder-actions .secondary-btn {
  width: auto;
  flex: 0 1 auto;
  min-height: 48px;
  padding: 0 18px;
  margin: 0;
}
.backup-reminder-actions .link-btn {
  width: auto;
  min-height: 48px;
  margin: 0;
}

/* ---------- Settings > Changes (Roadmap #81) ---------- */
.changelog-hint {
  font-size: 0.85rem;
  color: var(--ink-soft);
  line-height: 1.5;
  margin: 0 0 12px;
}
.changelog-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.changelog-entry {
  padding: 12px 14px;
}
.changelog-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.changelog-when { font-weight: 700; font-size: 0.9rem; color: var(--ink); }
.changelog-items {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--accent-ink);
  background: var(--primary-tint);
  border-radius: var(--radius-pill);
  padding: 2px 8px;
}
.changelog-changes {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 0.88rem;
  color: var(--ink-soft);
  line-height: 1.55;
}
.changelog-changes li + li { margin-top: 5px; }

/* ---------- Suggestions screen (Roadmap #82) ---------- */
.suggestions-hint {
  font-size: 0.88rem;
  color: var(--ink-soft);
  line-height: 1.55;
  margin: 0 0 14px;
}
.suggestion-form {
  padding: 14px;
}
.suggestion-input {
  display: block;
  width: 100%;
  margin-top: 6px;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  font-family: inherit;
  font-size: 1rem;
  color: var(--ink);
  padding: 10px 12px;
  resize: vertical;
}
.suggestion-input:focus { outline: none; border-color: var(--primary); }
.suggestion-form-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}
.suggestion-submit-btn { width: auto; min-width: 200px; margin: 0; }
.suggestion-status { font-size: 0.85rem; font-weight: 600; color: var(--good-ink); }
.suggestion-status-warn { color: var(--bad-ink); }
.suggestion-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.suggestion-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
}
.suggestion-number {
  flex: 0 0 auto;
  min-width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--primary-tint);
  color: var(--accent-ink);
  font-weight: 800;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.suggestion-body { min-width: 0; }
.suggestion-text { margin: 0; font-size: 0.95rem; line-height: 1.5; }
.suggestion-when { margin: 4px 0 0; font-size: var(--fs-xs); color: var(--ink-soft); }
.suggestion-export { margin-top: 20px; }
.suggestion-export-text {
  display: block;
  width: 100%;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 0.85rem;
  color: var(--ink);
  padding: 10px 12px;
  resize: vertical;
}
.suggestion-export-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.suggestion-export-row .secondary-btn { width: auto; min-width: 160px; margin: 0; }
.suggestion-export-row .link-btn { width: auto; margin: 0; }

/* ---------- Phones: make the whole top bar fit the screen ----------

   The three-across desktop top bar (nav | brand | points+settings) needs
   roughly 420px of width before anything can shrink. On a phone that's wider
   than the screen itself, so the page was laid out wider than the viewport:
   the nav stacked one button per row (four rows, a 209px-tall header before
   the notch inset on top of that) and the right-hand group — the points and
   the settings gear — was pushed off the right edge entirely.

   Below 700px the bar becomes two rows instead: brand and points/settings
   share the top row, and the nav sits underneath as one row of equal-width
   buttons that always spans exactly the screen width. */
@media (max-width: 699px) {
  .header-top {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "brand actions"
      "nav   nav";
    gap: 8px 10px;
    padding: 8px 12px;
  }

  .header-brand {
    grid-area: brand;
    flex-direction: row;
    align-items: center;
    justify-self: start;
    gap: 10px;
  }
  .header-title { font-size: 1rem; }
  .header-avatar {
    width: 40px;
    height: 40px;
    font-size: 1.4rem;
  }

  .header-actions { grid-area: actions; }
  /* #153: no room for the word "Badges" beside the avatar on a phone; the
     button still says "Badges" to screen readers (aria-label). */
  .header-badges-label { display: none; }
  .header-badges { max-width: 22vw; }

  /* One row, four equal buttons, no wrapping — each button is whatever a
     quarter of the screen is, so adding a fifth later re-divides the row
     rather than growing the header. min-width:0 is what lets them shrink
     below their label width; the label then wraps inside the button. */
  .main-nav {
    grid-area: nav;
    flex-wrap: nowrap;
    justify-self: stretch;
    gap: 6px;
  }
  /* A quarter of a phone screen isn't wide enough for icon and word side by
     side once the word is as long as "Suggestions", so stack them into a
     tab-bar button instead — uniformly, rather than letting whichever label
     happens to be longest wrap on its own and look like a mistake. */
  .main-nav .nav-btn {
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    gap: 1px;
    padding: 6px 2px;
    font-size: var(--fs-xs);
    line-height: 1.25;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .main-nav .nav-icon { font-size: 1.05rem; line-height: 1.1; }
}

/* ---------- Suggestion relay (Settings + Suggestions screen) ---------- */
.sync-state {
  margin: 4px 0 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.sync-state-on { color: var(--good-ink); }

.suggestion-status-busy { color: var(--ink-soft); }

.suggestion-number-sent { background: var(--good-bg); color: var(--good-ink); }

.suggestion-when {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.suggestion-tag {
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-radius: var(--radius-pill);
  padding: 2px 8px;
  background: var(--primary-tint);
  color: var(--accent-ink);
}
.suggestion-tag-sent { background: var(--good-bg); color: var(--good-ink); }

/* ======================================================================
   Build 2 (Roadmap 127–128, 130–131, 134–142, 149)
   ====================================================================== */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* Shared Home card shell: the note (#139), recap (#141), reward goal
   (#138) and Fix my mistakes (#128) cards. */
.home-card {
  margin-top: 12px;
  padding: 14px 16px;
}
.start-main > .home-card:first-child { margin-top: 0; }
.home-card-title { margin: 0 0 6px; }
.home-card-btn { margin-top: 12px; min-height: 48px; }
.parent-note-card { border: 2px solid var(--primary); margin-bottom: 12px; }
.parent-note-text { margin: 0; font-size: 1.05rem; line-height: 1.45; color: var(--ink); white-space: pre-line; overflow-wrap: anywhere; }
.recap-card { margin-bottom: 12px; }
.recap-lines { margin: 0; padding-left: 0; list-style: none; display: flex; flex-direction: column; gap: 6px; color: var(--ink); line-height: 1.4; }

/* Check-up button (#149): white on the blue focus card. */
.focus-card-btn {
  display: block;
  width: 100%;
  min-height: 48px;
  margin-top: 14px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--on-primary);
  color: var(--primary-edge);
  font-size: 1rem;
  font-weight: 800;
}
.focus-card-btn:active { transform: scale(0.98); }

/* Daily goal ring (#130), inside the focus card. */
.goal-ring {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--on-primary-line);
}
.goal-ring-pic { position: relative; flex: 0 0 48px; width: 48px; height: 48px; }
.goal-ring-svg { width: 48px; height: 48px; display: block; }
.goal-ring-track { fill: none; stroke: var(--on-primary-line); stroke-width: 5; }
.goal-ring-fill { fill: none; stroke: var(--gold); stroke-width: 5; stroke-linecap: round; }
.goal-ring-star { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
.goal-ring-text { font-size: 0.98rem; font-weight: 700; color: var(--on-primary); line-height: 1.3; }
.goal-ring-label { display: block; font-size: var(--fs-xs); font-weight: 700; opacity: 0.85; text-transform: uppercase; letter-spacing: 0.03em; }
.goal-sparkle .goal-ring-pic { animation: goal-sparkle 1.5s ease-out 1; }
@keyframes goal-sparkle {
  0% { transform: scale(1); }
  30% { transform: scale(1.25) rotate(-8deg); filter: drop-shadow(0 0 10px var(--gold)); }
  60% { transform: scale(1.1) rotate(6deg); filter: drop-shadow(0 0 6px var(--gold)); }
  100% { transform: scale(1); }
}

/* Reward goal (#138). */
.reward-track { height: 14px; border-radius: var(--radius-pill); background: var(--surface-2); overflow: hidden; }
.reward-fill { height: 100%; background: var(--good); border-radius: var(--radius-pill); max-width: 100%; }
.reward-text { margin: 8px 0 0; font-size: 0.95rem; font-weight: 700; color: var(--ink); }
.reward-reached { border: 2px solid var(--good); }
.reward-celebrate { animation: badge-pop 0.6s ease-out 1; }

/* Fix my mistakes (#128). */
.fix-card-detail { margin: 0; font-size: 0.95rem; line-height: 1.4; color: var(--ink-soft); }
.fix-btn { margin-top: 10px; min-height: 48px; }

/* Weekly quests (#136). */
.quest-row { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 6px 0; }
.quest-row + .quest-row { border-top: 1px solid var(--border); }
.quest-icon { font-size: 1.3rem; flex: 0 0 auto; }
.quest-body { flex: 1 1 auto; min-width: 0; }
.quest-label { font-size: 0.86rem; font-weight: 700; color: var(--ink); line-height: 1.3; }
.quest-bar-row { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.quest-track { flex: 1 1 auto; height: 10px; border-radius: var(--radius-pill); background: var(--surface-2); overflow: hidden; }
.quest-fill { height: 100%; background: var(--primary); border-radius: var(--radius-pill); }
.quest-done .quest-fill { background: var(--good); }
.quest-count { font-size: var(--fs-xs); font-weight: 700; color: var(--ink-soft); white-space: nowrap; }
.quest-footer { margin: 8px 0 0; font-size: var(--fs-xs); font-weight: 700; color: var(--accent-ink); }

/* Adventure card (#137): the whole card is one button. */
.adventure-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 64px;
  margin-top: 14px;
  padding: 12px 14px;
  text-align: left;
  color: var(--ink);
  font: inherit;
}
.adventure-card:active { background: var(--surface-2); }
.adventure-avatar { flex: 0 0 auto; }
.adventure-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.adventure-title { font-size: 0.95rem; font-weight: 700; }
.adventure-where { font-size: 0.9rem; font-weight: 700; color: var(--accent-ink); }
.adventure-next { font-size: 0.82rem; color: var(--ink-soft); }

/* Map screen (#137). */
.back-btn {
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--accent-ink);
  font-size: 1rem;
  font-weight: 700;
}
.map-lap { margin: 0 0 12px; color: var(--ink-soft); font-weight: 700; }
.map-path { position: relative; }
.map-line { position: absolute; top: 0; left: 0; pointer-events: none; overflow: visible; }
.map-line polyline { fill: none; stroke: var(--border); stroke-width: 4; stroke-dasharray: 2 8; stroke-linecap: round; }
.map-area { position: relative; border-radius: var(--radius-lg); padding: 12px 12px 4px; margin-bottom: 10px; }
.map-area-0 { background: var(--primary-tint); }
.map-area-1 { background: var(--good-bg); }
.map-area-2 { background: var(--warn-bg); }
.map-area-3 { background: var(--surface-2); }
.map-area-name { margin: 0; font-weight: 800; color: var(--ink); position: relative; z-index: 1; }
.map-steps { position: relative; height: 230px; }
.map-step {
  position: absolute;
  width: 26px;
  height: 26px;
  margin-left: -13px;
  border-radius: 50%;
  border: 3px solid var(--primary);
  background: var(--surface);
  z-index: 1;
}
.map-step:nth-child(1) { top: 8px; }
.map-step:nth-child(2) { top: 52px; }
.map-step:nth-child(3) { top: 96px; }
.map-step:nth-child(4) { top: 140px; }
.map-step:nth-child(5) { top: 184px; }
.map-step-visited { background: var(--primary); }
.map-step-current { background: var(--gold); border-color: var(--warn-ink); }
.map-avatar { position: absolute; top: 0; left: 0; z-index: 2; pointer-events: none; }
.map-avatar-moving { transition: transform 1.1s ease-in-out; }

/* Summary: questions I got wrong (#127). */
.summary-wrong {
  margin-top: 14px;
  padding: 16px 20px;
}
.summary-wrong-title { margin: 0 0 8px; font-size: 1rem; font-weight: 800; color: var(--ink); }
.wrong-list { margin: 0; padding-left: 1.3em; display: flex; flex-direction: column; gap: 12px; }
.wrong-row { color: var(--ink); }
.wrong-prompt { margin: 0; font-size: 1rem; line-height: 1.4; overflow-wrap: anywhere; }
.wrong-diagram { margin-top: 8px; max-width: 280px; background: #fff; /* COLOUR EXCEPTION (#153): diagram paper */ border-radius: var(--radius-sm); }
.wrong-diagram svg { display: block; width: 100%; height: auto; max-width: 100%; }
.wrong-answer { margin: 6px 0 0; font-size: 1rem; }
.wrong-how { margin-top: 4px; }
.wrong-how summary {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  color: var(--accent-ink);
  font-weight: 700;
  cursor: pointer;
}
.wrong-how p { margin: 0 0 4px; color: var(--ink-soft); line-height: 1.45; white-space: pre-line; }
.retry-round-btn { margin-top: 14px; min-height: 52px; }

/* Summary: quests, map, goal and check-up lines. */
.summary-more { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.summary-line {
  margin: 0;
  background: var(--surface-2);
  color: var(--ink);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
}
.summary-map-btn { display: block; width: 100%; min-height: 48px; text-align: left; border: none; font: inherit; font-weight: 600; }
.summary-map-arrive { background: var(--good-bg); font-weight: 800; }
.summary-map-go { display: block; margin-top: 2px; font-size: 0.88rem; color: var(--accent-ink); font-weight: 700; }

/* Progress: my practice days (#140). */
.cal-caption { margin: 0 0 8px; font-weight: 700; color: var(--ink); }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(36px, 1fr)); gap: 6px; max-width: 420px; }
.cal-head { text-align: center; font-size: var(--fs-xs); font-weight: 700; color: var(--ink-soft); }
.cal-cell {
  aspect-ratio: 1;
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  font-size: 1.1rem;
  font-weight: 800;
}
.cal-ticked { background: var(--good-bg); color: var(--good-ink); border-color: var(--good); box-shadow: none; }
.cal-cell { box-shadow: none; border-color: var(--border); }
.cal-shield { background: var(--surface-2); }
.cal-future { opacity: 0.35; }
.cal-today { outline: 3px solid var(--primary); outline-offset: 1px; }

/* Progress: this week vs last week (#131). No red anywhere. */
.wc-headline { margin: 0 0 8px; font-weight: 700; color: var(--ink); }
.wc-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  margin-bottom: 6px;
}
.wc-topic { font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.wc-pcts { color: var(--ink-soft); font-weight: 600; white-space: nowrap; }
.wc-arrow { font-weight: 800; white-space: nowrap; }
.wc-up { color: var(--good-ink); }
.wc-steady, .wc-down { color: var(--ink-soft); }
.wc-partial { margin: 6px 0 0; font-size: 0.88rem; color: var(--ink-soft); }

/* Trophy cabinet (#134, hidden badges #135). */
.cabinet-btn { min-height: 56px; }
.cabinet-count { margin: 0 0 8px; font-weight: 700; color: var(--ink-soft); }
.shelf { margin-bottom: 8px; }
.shelf-topic { margin: 10px 0 6px; font-size: 0.88rem; font-weight: 700; color: var(--ink); }
.shelf-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
.trophy {
  padding: 12px;
  text-align: center;
  min-width: 0;
}
.trophy-icon { font-size: 2rem; line-height: 1.2; }
.trophy-name { font-weight: 700; font-size: 0.88rem; margin-top: 4px; color: var(--ink); }
.trophy-hint { font-size: var(--fs-xs); color: var(--ink-soft); margin-top: 2px; line-height: 1.35; }
.trophy-earned {
  border: 2px solid var(--gold);
  background: var(--warn-bg);
  box-shadow: var(--shadow-2);
}
.trophy-earned .trophy-name { color: var(--warn-ink); }
.trophy-earned .trophy-hint { color: var(--ink); }
/* A flat grey shape of the badge's own emoji. */
.trophy-locked:not(.trophy-secret) .trophy-icon { filter: grayscale(1) brightness(0); opacity: 0.28; }
html[data-mode="dark"] .trophy-locked:not(.trophy-secret) .trophy-icon { filter: grayscale(1) brightness(0) invert(1); opacity: 0.4; }
.trophy-secret .trophy-icon { opacity: 0.6; }
.trophy-tag {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: 800;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  padding: 1px 6px;
  background: var(--warn-ink);
  color: var(--surface);
  vertical-align: middle;
}

/* Header badges group is a button now (#134): at least 44pt to tap. */
button.header-badges-group {
  min-height: 44px;
  min-width: 44px;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: none;
  font: inherit;
  color: inherit;
}
button.header-badges-group:active { background: var(--surface-2); }

/* Settings: sounds (#142) and the grown-ups section (#138, #139). */
.settings-minor-heading { margin: 14px 0 4px; font-size: 0.95rem; color: var(--ink); }
.note-input { min-height: 88px; }
.note-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.note-count { font-size: 0.85rem; color: var(--ink-soft); margin-right: auto; }
.note-row .note-btn { width: auto; min-height: 48px; padding: 0 18px; margin: 0; }
.note-row .note-delete-btn { width: auto; min-height: 44px; margin: 0; }
.note-status { margin: 0; font-weight: 700; color: var(--ink); }
.reward-goal-status { margin: 4px 0 0; font-size: 0.9rem; color: var(--good-ink); }
.reward-goal-status.backup-status-warn { color: var(--bad-ink); }

@media (prefers-reduced-motion: reduce) {
  .goal-sparkle .goal-ring-pic,
  .reward-celebrate { animation: none; }
  .map-avatar-moving { transition: none; }
}

/* ---------- Motion (#153) ----------
   A screen fades in and rises 8px as it opens. Only transform and opacity
   animate, and `both` leaves no transform behind afterwards. */
.screen.active { animation: screen-in var(--dur-base) var(--ease-out) both; }
@keyframes screen-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* Reduce Motion: no slides, presses or pops. State is still shown by
   colour, icon and words. (The two blocks above stay as they are.) */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0.01ms; --dur-base: 0.01ms; --dur-slow: 0.01ms;
    --press-depth: 0px; --key-depth: 0px;
  }
  .screen.active,
  .banner-celebrate { animation: none; }
}

/* ======================================================================
   Build 3 (Roadmap #143-#148): games, Spot the mistake, Year 7. Built
   only from the tokens and shared components at the top of this file.
   ====================================================================== */

/* Home: the one Games card (#143-#147). */
.games-card { margin-top: var(--space-6); }
.games-card-text { margin: 0 0 var(--space-3); color: var(--ink-soft); font-size: var(--fs-sm); line-height: var(--lh-body); }
.games-open-btn { width: 100%; }

/* Games screen. */
.games-intro { margin: 0 0 var(--space-4); color: var(--ink-soft); font-size: var(--fs-sm); }
.games-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.game-tile-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  min-height: 170px;
  padding: var(--space-5);
  text-align: left;
  font: inherit;
  color: var(--ink);
  box-shadow: 0 var(--press-depth) 0 var(--border-strong), var(--shadow-1);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.game-tile-card:active { transform: translateY(var(--press-depth)); box-shadow: var(--shadow-1); background: var(--surface-2); }
.game-name { font-size: var(--fs-md); font-weight: var(--fw-heavy); }
.game-desc { font-size: var(--fs-sm); color: var(--ink-soft); line-height: var(--lh-body); }
.game-best { margin-top: auto; font-size: var(--fs-xs); font-weight: var(--fw-bold); color: var(--accent-ink); }

/* Shared bits of the game screens. */
.game-hud { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-3); }
.game-hud .hud-exit-btn { margin-left: auto; }
.game-line { margin: var(--space-3) 0; font-size: var(--fs-base); line-height: var(--lh-body); color: var(--ink); }
.game-stats { color: var(--ink-soft); font-weight: var(--fw-bold); font-size: var(--fs-sm); }
.game-msg { min-height: 1.5em; margin: var(--space-3) 0 0; font-weight: var(--fw-bold); color: var(--warn-ink); }
.game-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin-top: var(--space-4); }
.game-wide-btn { width: 100%; margin-top: var(--space-4); }
.card + .card, .card-hero + .card, .banner-celebrate + .card-hero { margin-top: var(--space-4); }
.ranked-main { flex: 1 1 auto; min-width: 0; }
.ranked-side { color: var(--ink-soft); font-size: var(--fs-sm); white-space: nowrap; }
.ranked-list .ranked-row { font-variant-numeric: tabular-nums; }

/* #143 Blitz. */
.blitz-countdown { text-align: center; padding: var(--space-10) 0; }
.blitz-countdown-label { margin: 0; font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--ink-soft); }
.blitz-countdown-num { font-size: 6rem; color: var(--accent-ink); }
.blitz-card { position: relative; }
.blitz-prompt { margin: 0 0 var(--space-4); text-align: center; font-size: var(--fs-xl); font-weight: var(--fw-heavy); font-variant-numeric: tabular-nums; }
.blitz-display { margin-bottom: var(--space-3); }
.blitz-go { min-height: 56px; }
.blitz-flash {
  position: absolute;
  top: var(--space-3);
  right: var(--space-4);
  font-size: var(--fs-xl);
  font-weight: var(--fw-heavy);
}
.blitz-flash.is-right { color: var(--good-ink); }
.blitz-flash.is-wrong { color: var(--bad-ink); }
.blitz-hero { text-align: center; padding: var(--space-6); }
.blitz-hero .stat-num { color: var(--on-primary); }
.blitz-tried { margin: var(--space-2) 0 0; font-weight: var(--fw-bold); color: var(--on-primary); }
.banner-celebrate { margin-bottom: var(--space-4); font-size: var(--fs-lg); font-weight: var(--fw-heavy); }

/* #144 Numbers Target. */
.numbers-target { text-align: center; padding: var(--space-5); }
.numbers-target .stat-num, .numbers-target .stat-label { color: var(--on-primary); }
.numbers-card { margin-top: var(--space-4); }
.numbers-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.numbers-tile { min-height: 64px; }
.numbers-ops { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); margin-top: var(--space-3); }
.numbers-op { font-size: var(--fs-xl); }
.game-tile.is-selected {
  background: var(--primary-tint);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 3px var(--ring), 0 var(--key-depth) 0 var(--primary-edge);
}
.numbers-tile.is-target { background: var(--good-bg); color: var(--good-ink); }
.numbers-steps { margin-top: var(--space-2); }
.numbers-controls { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.numbers-result-text { font-weight: var(--fw-bold); }
.numbers-levels { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.numbers-big { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* #145 Close Enough. */
.estimate-card { text-align: center; }
.estimate-ask { margin: 0; }
.estimate-calc { margin: var(--space-2) 0; font-size: var(--fs-xl); font-weight: var(--fw-heavy); font-variant-numeric: tabular-nums; }
.estimate-readout { margin: var(--space-2) 0 var(--space-4); color: var(--accent-ink); font-variant-numeric: tabular-nums; }
.estimate-slider-row { display: flex; align-items: center; gap: var(--space-3); }
.estimate-nudge { flex: 0 0 56px; font-size: var(--fs-xl); }
/* The whole strip takes touches: no page scroll or bounce while dragging. */
.estimate-slider {
  position: relative;
  flex: 1 1 auto;
  height: 56px;
  display: flex;
  align-items: center;
  touch-action: none;
  cursor: pointer;
}
.estimate-track { width: 100%; }
.estimate-thumb {
  position: absolute;
  top: 50%;
  width: 44px;
  height: 44px;
  margin-left: -22px;
  margin-top: -22px;
  border-radius: 50%;
  background: var(--surface);
  border: 4px solid var(--primary);
  box-shadow: var(--shadow-2);
}
.estimate-slider.is-locked { cursor: default; }
.estimate-marker {
  position: absolute;
  bottom: 100%;
  transform: translateX(-50%);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--good-bg);
  color: var(--good-ink);
  border: 2px solid var(--good);
  font-size: var(--fs-sm);
  font-weight: var(--fw-heavy);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.estimate-ticks { position: relative; height: 1.6em; margin: var(--space-1) 72px 0; }
.estimate-ticks span {
  position: absolute;
  transform: translateX(-50%);
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.estimate-reveal { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border); }
.estimate-reveal-title { justify-content: center; }
.estimate-hero { text-align: center; padding: var(--space-6); }
.estimate-hero .stat-num, .estimate-hero .stat-label { color: var(--on-primary); }

/* #147 Beat the Grown-Up. */
.guest-handover { text-align: center; padding: var(--space-8) var(--space-6); }
.guest-big { margin: 0; font-size: var(--fs-display); }
.guest-title { margin: var(--space-3) 0; font-size: var(--fs-xl); font-weight: var(--fw-heavy); }
.guest-sub { margin: 0; font-size: var(--fs-base); }
.guest-scores { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); text-align: center; }

/* #146 Spot the mistake. */
.spot-heading { margin: 0 0 var(--space-2); font-weight: var(--fw-heavy); font-size: var(--fs-md); color: var(--accent-ink); text-align: center; }
.spot-intro { margin: calc(-1 * var(--space-2)) 0 var(--space-4); text-align: center; color: var(--ink-soft); font-size: var(--fs-base); }
.spot-rows { display: flex; flex-direction: column; gap: var(--space-2); }
.spot-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  text-align: left;
  font-size: var(--fs-md);
  font-variant-numeric: tabular-nums;
}
.spot-num {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-md);
  height: var(--icon-md);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: var(--fw-heavy);
}
.spot-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.spot-row.spot-faulty { background: var(--bad-bg); border-color: var(--bad); color: var(--bad-ink); }
.spot-row.spot-fine { border-color: var(--border-strong); }
.spot-row[disabled] { opacity: 1; cursor: default; }
.spot-note { margin: var(--space-1) 0 0 var(--space-3); font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--ink-soft); }
.spot-note.spot-note-fix { color: var(--bad-ink); }
.spot-hint { margin: var(--space-3) 0 0; text-align: center; font-weight: var(--fw-bold); color: var(--warn-ink); }

/* #148 Year 7: the minus key, the "New" tag and the factor tree. */
.keypad-minus { grid-template-columns: 1fr; margin-top: var(--space-2); }
.new-tag {
  display: inline-block;
  margin-left: var(--space-1);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--warn-bg);
  color: var(--warn-ink);
  font-size: var(--fs-xs);
  font-weight: var(--fw-heavy);
  vertical-align: middle;
}
.choice-btn.chip.selected .new-tag { background: var(--surface); color: var(--accent-ink); }
.tree-diagram {
  width: 100%;
  max-width: 340px;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 6px;
}
.tree-diagram .ft-line { stroke: var(--ink-soft); stroke-width: 2; }
.tree-diagram .ft-prime { fill: var(--primary-tint); stroke: var(--primary); stroke-width: 2.5; }
.tree-diagram .ft-missing { fill: var(--warn-bg); stroke: var(--warn-ink); stroke-width: 2.5; stroke-dasharray: 6 4; }
.tree-diagram .ft-label { fill: var(--ink); font-size: 20px; font-weight: 800; }

@media (max-width: 599px) {
  .games-grid, .game-actions { grid-template-columns: minmax(0, 1fr); }
}
