:root {
  --bg: #0b0b0d;
  --panel: #1c1c1f;
  --panel-alt: #2a2a30;
  --ink: #f2f2f4;
  --muted: #9a9aa1;
  --border: #333338;
  --accent: #e4e4e7;
  --accent2: #d4d4d8;
  --accent3: #cfcfd4;
  --accent4: #ffffff;
  --good: #4ade80;
  --bad: #f87171;
  --radius: 20px;
}

* { box-sizing: border-box; }

a { color: var(--accent4); }

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, -apple-system, Verdana, sans-serif;
  /* Math/science doodles (hand-drawn SVG) layered over the gradient. */
  background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22420%22%20height%3D%22420%22%20viewBox%3D%220%200%20420%20420%22%20fill%3D%22none%22%20stroke%3D%22%23232329%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%20%3Cg%20transform%3D%22translate%2860%2060%29%20rotate%28-8%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%229%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2228%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E%26%23960%3B%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28180%2056%29%22%3E%20%3Cpath%20d%3D%22M-14%200%20L-9%2010%20L-2%20-12%20H14%22%2F%3E%3Cpath%20d%3D%22M-2%20-8%20H12%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28300%2060%29%20rotate%286%29%22%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%22-2%22%20r%3D%2210%22%2F%3E%3Cpath%20d%3D%22M-5%206%20Q0%202%205%206%22%2F%3E%20%3Cpath%20d%3D%22M-4%2010%20H4%20M-3%2014%20H3%22%2F%3E%3Cpath%20d%3D%22M-3%20-4%20L0%200%20L3%20-4%20M0%200%20V6%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28390%2070%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%227%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2222%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E%26%238734%3B%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%2880%20170%29%20rotate%284%29%22%3E%20%3Cpath%20d%3D%22M-12%2014%20L0%20-16%20L12%2014%20Z%22%2F%3E%3Cpath%20d%3D%22M4%2014%20V9%20H9%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28200%20150%29%22%3E%20%3Cpath%20d%3D%22M-18%208%20A18%2018%200%200%201%2018%208%20Z%22%2F%3E%20%3Cpath%20d%3D%22M-13%203%20L-11%205%20M-8%20-3%20L-6%20-1%20M0%20-6%20V-3%20M8%20-3%20L6%20-1%20M13%203%20L11%205%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28320%20160%29%20rotate%28-6%29%22%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%22-12%22%20r%3D%223%22%2F%3E%20%3Cpath%20d%3D%22M-2%20-9%20L-10%2014%20M2%20-9%20L10%2014%22%2F%3E%3Cpath%20d%3D%22M-7%206%20Q0%2010%207%206%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28390%20200%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%228%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2224%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E%26%23931%3B%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%2860%20300%29%22%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%223%22%2F%3E%20%3Cellipse%20cx%3D%220%22%20cy%3D%220%22%20rx%3D%2217%22%20ry%3D%227%22%2F%3E%20%3Cellipse%20cx%3D%220%22%20cy%3D%220%22%20rx%3D%2217%22%20ry%3D%227%22%20transform%3D%22rotate%2860%29%22%2F%3E%20%3Cellipse%20cx%3D%220%22%20cy%3D%220%22%20rx%3D%2217%22%20ry%3D%227%22%20transform%3D%22rotate%28-60%29%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28170%20260%29%20rotate%285%29%22%3E%20%3Cpath%20d%3D%22M-4%20-16%20H4%20M-3%20-16%20V-6%20L8%2010%20Q10%2014%206%2014%20H-6%20Q-10%2014%20-8%2010%20L3%20-6%20V-16%22%2F%3E%20%3Cpath%20d%3D%22M-4%206%20H4%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2210%22%20r%3D%221%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28280%20260%29%20rotate%2830%29%22%3E%20%3Cpath%20d%3D%22M-5%208%20Q-9%204%20-5%20-2%20Q0%20-10%200%20-16%20Q0%20-10%205%20-2%20Q9%204%205%208%22%2F%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%22-2%22%20r%3D%223%22%2F%3E%20%3Cpath%20d%3D%22M-5%208%20L-8%2013%20M5%208%20L8%2013%20M0%209%20V14%22%2F%3E%20%3Cpath%20d%3D%22M-5%208%20Q0%2011%205%208%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%2880%20390%29%20rotate%28-4%29%22%3E%20%3Crect%20x%3D%22-24%22%20y%3D%22-5%22%20width%3D%2248%22%20height%3D%2210%22%20rx%3D%221%22%2F%3E%20%3Cpath%20d%3D%22M-16%20-5%20V0%20M-8%20-5%20V-1%20M0%20-5%20V0%20M8%20-5%20V-1%20M16%20-5%20V0%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28200%20380%29%22%3E%20%3Cpath%20d%3D%22M-8%20-14%20Q-14%20-14%20-14%20-6%20Q-14%202%20-8%202%20L-8%2010%20H-2%20L-2%202%20M-8%20-14%20H-2%20V-6%22%2F%3E%20%3Cpath%20d%3D%22M8%20-14%20Q14%20-14%2014%20-6%20Q14%202%208%202%20L8%2010%20H2%20L2%202%20M8%20-14%20H2%20V-6%22%2F%3E%20%3Cpath%20d%3D%22M-8%20-14%20V-18%20M8%20-14%20V-18%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28310%20370%29%22%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%22-4%22%20r%3D%229%22%2F%3E%3Cpath%20d%3D%22M-4%205%20H4%20M-3%209%20H3%20M-2%2013%20H2%22%2F%3E%20%3Cpath%20d%3D%22M-3%20-6%20Q0%20-2%200%205%20M3%20-6%20Q0%20-2%200%205%22%2F%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28390%20320%29%20rotate%28-8%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%226%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2217%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3Ex%26%23178%3B%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28140%20320%29%20rotate%286%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%226%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2215%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E2%2B2%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28260%20100%29%20rotate%28-5%29%22%3E%20%3Ctext%20x%3D%220%22%20y%3D%226%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2217%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E%26%23247%3B%3C%2Ftext%3E%20%3Ctext%20x%3D%2220%22%20y%3D%226%22%20text-anchor%3D%22middle%22%20font-family%3D%22Trebuchet%20MS%2C%20sans-serif%22%20font-size%3D%2215%22%20fill%3D%22%23232329%22%20stroke%3D%22none%22%3E%26%23215%3B%3C%2Ftext%3E%20%3C%2Fg%3E%20%3Cg%20transform%3D%22translate%28340%20300%29%22%3E%20%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%228%22%2F%3E%3Cellipse%20cx%3D%220%22%20cy%3D%220%22%20rx%3D%2216%22%20ry%3D%225%22%20transform%3D%22rotate%28-18%29%22%2F%3E%20%3C%2Fg%3E%20%3C%2Fsvg%3E") repeat, linear-gradient(160deg, #0b0b0d, #131316 60%, #0b0b0d);
  background-size: 210px 210px, auto;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 16px 64px;
  /* Looser than the browser default (~1.2) — trialed on shapes.html first
     (see PROJECT-NOTES.md), then rolled out everywhere: dense line spacing
     read as "wall of text, skip it" to an 8-year-old even when the actual
     word count was fine. */
  line-height: 1.6;
}

header {
  text-align: center;
  margin-bottom: 24px;
}

header h1 {
  font-size: clamp(2rem, 6vw, 3.2rem);
  margin: 0 0 8px;
  color: var(--accent4);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
}

header p {
  font-size: 1.1rem;
  margin: 0;
  opacity: 0.8;
}

nav {
  display: flex;
  gap: 12px;
  margin: 16px 0 32px;
  flex-wrap: wrap;
  justify-content: center;
}

nav a {
  text-decoration: none;
  font-weight: bold;
  font-size: 1.1rem;
  padding: 10px 20px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--ink);
  box-shadow: 0 4px 0 rgba(0,0,0,0.4);
  transition: transform 0.1s ease;
}

nav a:hover, nav a:focus-visible {
  transform: translateY(-2px);
}

nav a.active {
  background: var(--accent4);
  border-color: var(--accent4);
  color: var(--bg);
}

main {
  width: 100%;
  max-width: 720px;
}

main.wide {
  max-width: min(1200px, 95vw);
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
}

.cards + .section-title {
  margin-top: 40px;
}

.section-title {
  text-align: center;
  font-size: 1.4rem;
  margin: 0 0 20px;
  color: var(--accent4);
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 20px;
  text-align: center;
  text-decoration: none;
  color: var(--ink);
  box-shadow: 0 6px 0 rgba(0,0,0,0.4);
  transition: transform 0.15s ease;
}

.card:hover, .card:focus-visible {
  transform: translateY(-4px) rotate(-1deg);
}

.card .emoji {
  font-size: 3rem;
  display: block;
  margin-bottom: 8px;
}

.card h2 {
  margin: 0 0 6px;
}

.card p {
  margin: 0;
  opacity: 0.75;
  font-size: 0.95rem;
}

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px 24px;
  box-shadow: 0 6px 0 rgba(0,0,0,0.4);
  margin-bottom: 24px;
}

.op-picker {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 20px;
}

.op-picker button {
  font-size: 1.4rem;
  padding: 10px 18px;
  border-radius: 14px;
  border: 3px solid transparent;
  background: var(--panel-alt);
  color: var(--ink);
  cursor: pointer;
  font-family: inherit;
}

.op-picker button.active {
  border-color: var(--accent4);
  background: #303036;
}

.op-picker.disabled button {
  opacity: 0.5;
  pointer-events: none;
}

.picker-hint {
  text-align: center;
  margin: 0 0 10px;
  opacity: 0.65;
  font-size: 0.9rem;
}

.start-panel {
  text-align: center;
  margin-bottom: 20px;
}

.start-panel p {
  font-size: 1.2rem;
  margin: 0 0 14px;
}

.start-panel button {
  font-size: 1.3rem;
  padding: 12px 28px;
  border-radius: 14px;
  border: none;
  background: var(--accent4);
  color: var(--bg);
  cursor: pointer;
  font-family: inherit;
}

.problem {
  text-align: center;
}

.problem .expr {
  font-size: clamp(2.5rem, 10vw, 4rem);
  font-weight: bold;
  margin: 10px 0 20px;
}

.problem input[type="number"] {
  font-size: 2rem;
  width: 140px;
  text-align: center;
  padding: 8px;
  border-radius: 12px;
  border: 3px solid var(--accent4);
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
}

.problem button {
  font-size: 1.3rem;
  padding: 10px 24px;
  margin-left: 10px;
  border-radius: 14px;
  border: none;
  background: var(--accent4);
  color: var(--bg);
  cursor: pointer;
  font-family: inherit;
}

.feedback {
  min-height: 2.4rem;
  font-size: 1.5rem;
  font-weight: bold;
  margin-top: 16px;
}

.feedback.good { color: var(--good); }
.feedback.bad { color: var(--bad); }

.stats {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin-top: 20px;
  font-size: 1.1rem;
}

.stats b {
  display: block;
  font-size: 1.6rem;
}

.grid12 {
  display: grid;
  /* 21 columns: the real 1-20 grid, plus a fixed-width 21st column,
     flush against the 20th, for the 30x30 cell — see
     renderGrid()/renderJoke30x30()
     in multiplication-table.js for why every cell is explicitly placed
     rather than auto-flowed. Capped at max-width so the columns can't
     grow arbitrarily large on a wide screen — they did, once, when the
     page's main was widened for more panel breathing room, and the
     whole table got tall enough to need vertical scrolling instead. */
  grid-template-columns: repeat(20, minmax(26px, 1fr)) 34px;
  max-width: 700px;
  margin: 16px auto 0;
  gap: 2px;
  font-size: 0.8rem;
  overflow-x: auto;
  padding-bottom: 6px;
}

.grid12 div {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: var(--panel-alt);
  /* White text with a black outline reads against every one of the
     rainbow background colors (see multiplication-table.js) without
     needing a per-number light/dark text lookup. */
  color: #ffffff;
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}

.grid12 div.dim {
  /* Selection is shown by darkening everything EXCEPT the selected
     row/column, not by outlining the selected cells — a ring around the
     marked cells had the same problem as plain-color rings always do:
     no single ring color contrasts against all ten-plus rainbow
     backgrounds at once (see git history). Dimming has no such problem —
     it darkens every cell's own color by the same amount, so the
     selected row/column simply reads as "the bright ones" against a
     uniformly muted rest of the grid, whatever colors are involved. */
  filter: brightness(0.35);
}

.grid12 div.joke-cell {
  /* Elton's "30 times table" joke row: sits below the regular 1-20 grid,
     so it's not one of the rainbow-colored gnomon cells above (see
     NUMBER_COLORS in multiplication-table.js) — flat yellow instead, and
     half the height so it visually reads as a bonus strip, not another
     real row. */
  aspect-ratio: 2 / 1;
  background: #E6C40C;
  color: #1a1a1a;
  text-shadow: none;
  cursor: pointer;
}

.grid12.quiz-grid {
  /* Quiz mode has no joke row, so no 21st bonus column either — without
     this override the empty reserved column shoves the quiz grid 34px
     off-center compared to the Explore grid above it. */
  grid-template-columns: repeat(20, minmax(26px, 1fr));
}

.quiz-cell-input {
  /* Fills its square exactly; the translucent dark backing keeps the
     white text readable on every rainbow cell color, including the
     white teen/Ten cells. */
  width: 100%;
  height: 100%;
  border: none;
  border-radius: 0;
  padding: 0;
  text-align: center;
  font: inherit;
  color: #fff;
  background: rgba(0, 0, 0, 0.45);
  outline: 2px solid #fff;
  outline-offset: -2px;
}

.grid12 div.quiz-reveal {
  /* The square you got wrong, showing the correct number during the
     pause before the table resets. */
  outline: 3px solid #fff;
  outline-offset: -3px;
  font-weight: 700;
}

.grid12 div.joke-cell-extra {
  /* The 30x30 "diagonal" cell for the joke row — a real grid cell in its
     own dedicated 21st column (see .grid12 above). First cut had a gap
     on its left so it read as hanging off rather than a natural
     continuation; Elton asked for it connected to the 30x20 cell
     instead, flush against it like the rest of the row. */
}

.number-picker {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 16px;
}

.number-picker button {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--panel-alt);
  color: var(--ink);
  font-size: 1.1rem;
  font-family: inherit;
  cursor: pointer;
}

.number-picker button.active {
  background: var(--accent4);
  color: var(--bg);
}

.number-picker button.clear-btn {
  width: auto;
  border-radius: 999px;
  padding: 0 18px;
}

.fact {
  margin-bottom: 30px;
}

.fact h3 {
  margin: 0 0 8px;
  color: var(--accent4);
}

.fact p {
  margin: 0;
}

.fact p + p {
  margin-top: 12px;
}

.pi-glyph {
  font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
  font-style: italic;
}

.pi-digits, .equation {
  font-family: "Courier New", monospace;
  font-size: 0.95rem;
  letter-spacing: 1px;
  background: var(--panel-alt);
  border-radius: 10px;
  padding: 10px 12px;
  line-height: 1.6;
}

.pi-digits {
  /* Bigger than the shared .equation size above — Elton asked for the
     digits themselves to read larger now that there are 200 of them
     instead of 100 (see pi.html/pi.js). Sized (measured with Playwright,
     not guessed) so a full row fills ~96% of the box's content width at
     the panel's max size without wrapping — first at 20/row (4 blocks of
     5) that was 2.7rem; Elton then asked for 25/row (5 blocks of 5)
     instead and a bit smaller, which at the same 96%-fill target comes
     out to 2.1rem (a longer row needs a smaller font to still fit). */
  font-size: 2.1rem;
  line-height: 1.5;
  /* If anything ever renders wider than the box (e.g. a stale-cache
     version mix, see the .pi-grid note below), it scrolls inside the box
     instead of bleeding invisibly past both screen edges. */
  overflow-x: auto;
}

/* The reference is a grid of 5-digit <span> blocks (see pi.html), not
   text with hard <br>s: on phones a hard-broken 25-digit row wrapped
   as 2+2+1 blocks, leaving every third visual line a lone dangling
   block. A grid instead drops evenly to 4 columns, then 2, below —
   rows stay uniform at every width.

   Scoped to .pi-grid (a co-class that ships in the SAME pi.html as the
   span markup), NOT bare .pi-digits: the service worker revalidates
   pi.html and style.css independently, so right after a deploy a phone
   can hold new CSS + old HTML for one load — grid rules applied to the
   old <br>-based text turned each 25-digit run into one huge anonymous
   grid item and sprayed digits past both screen edges (seen live on
   Rosie's phone). With the co-class, a version mix just renders in the
   other version's plain-block layout until the next reload. */
.pi-digits.pi-grid {
  display: grid;
  grid-template-columns: repeat(5, max-content);
  justify-content: center;
  justify-content: safe center; /* older browsers keep plain center */
  column-gap: 0.55em;
}

.pi-digits .pi-lead {
  /* The "3." line above the digit blocks. */
  grid-column: 1 / -1;
}

@media (max-width: 719px) {
  /* Breakpoint measured: 5 columns at 2.1rem genuinely need ~720px of
     viewport — at 700px they quietly overhung the box padding by a few
     px on each side (invisible, but real). */
  .pi-digits.pi-grid {
    grid-template-columns: repeat(4, max-content);
    /* Fluid: 20 digits + gaps must fit the viewport minus page/panel/box
       chrome (~130px). Divisor measured with Playwright, same method as
       the 2.1rem above. */
    font-size: min(2.1rem, calc((100vw - 130px) / 14.5));
  }
}

@media (max-width: 460px) {
  .pi-digits.pi-grid {
    grid-template-columns: repeat(2, max-content);
    /* Half the digits per row, so the font gets room to grow back. */
    font-size: min(2.1rem, calc((100vw - 130px) / 7.2));
  }
}

.equation {
  display: inline-block;
  font-size: 1.1rem;
}

.prime-check input {
  font-size: 1.5rem;
  width: 220px;
  max-width: 100%;
  text-align: center;
  padding: 8px;
  border-radius: 12px;
  border: 3px solid var(--accent2);
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
}

.learn-more {
  display: inline-block;
  margin-top: 14px;
  font-weight: bold;
}

#nameForm input, #passwordForm input {
  font-size: 1.3rem;
  padding: 10px 14px;
  border-radius: 12px;
  border: 3px solid var(--accent4);
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
  /* An <input> defaults to a ~20-character intrinsic width, which at this
     font size comes out around 302px — wider than the panel's 240px
     content box on a 320px phone. Because body is a centred flex column,
     an over-wide child doesn't just overflow to the right, it bleeds off
     BOTH edges, and the left bleed can't be scrolled to at all. Cap it
     instead; border-box so the padding and 3px border count inside the
     cap rather than pushing back past it. */
  max-width: 100%;
  box-sizing: border-box;
}

#nameForm button, #passwordForm button {
  font-size: 1.2rem;
  padding: 10px 20px;
  margin-left: 10px;
  border-radius: 14px;
  border: none;
  background: var(--accent4);
  color: var(--bg);
  cursor: pointer;
  font-family: inherit;
}

.player-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 10px 18px;
  margin-bottom: 16px;
  font-size: 1rem;
}

.player-bar a {
  color: var(--accent4);
  font-weight: bold;
}

/* Some tables can't fit a narrow phone no matter what: a column's
   min-content is set by its longest unbreakable word plus its header, and
   si-units' "Metric Prefixes" (Prefix/Symbol/Means/Example) needs 352px
   against a 240px panel at 320px wide. width: 100% doesn't help — a table
   won't shrink below min-content. Rather than shrink the type for an
   8-year-old, let the table scroll inside its own box, same reasoning as
   the .pi-digits note above: bleeding past both screen edges hides the
   left half where nothing can scroll to it. */
.table-scroll {
  overflow-x: auto;
}

.hiscore-table {
  width: 100%;
  border-collapse: collapse;
}

.hiscore-table th, .hiscore-table td {
  text-align: left;
  padding: 8px 10px;
}

.hiscore-table tr:nth-child(odd) td {
  background: var(--panel-alt);
  border-radius: 8px;
}

.hiscore-table td:first-child {
  font-weight: bold;
  width: 2.2em;
}

/* si-units.html's step tables reuse .hiscore-table for the styling, but their
   first column holds a whole quantity ("1 minute"), not a rank number — the
   2.2em rank width above wrapped every single row to two lines on a phone.
   Scoped to the co-class so the actual hiscore boards keep their narrow rank
   column. */
.steps-table td:first-child {
  width: auto;
  white-space: nowrap;
}

.op-tags {
  margin-left: 8px;
  font-size: 0.85rem;
  opacity: 0.65;
  white-space: nowrap;
}

.hiscore-empty {
  opacity: 0.7;
  text-align: center;
  padding: 12px;
}

.offline-note {
  text-align: center;
  opacity: 0.75;
  font-size: 0.9rem;
  margin: 0 0 12px;
}

.prime-result {
  margin-top: 12px;
  font-size: 1.3rem;
  font-weight: bold;
  min-height: 1.6rem;
}

.translator textarea {
  width: 100%;
  font-size: 1.2rem;
  padding: 10px 12px;
  border-radius: 12px;
  border: 3px solid var(--accent4);
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
  resize: vertical;
}

.code-output {
  font-family: "Courier New", monospace;
  font-size: 1.1rem;
  letter-spacing: 1px;
  background: var(--panel-alt);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 12px 0;
  min-height: 2.6rem;
  line-height: 1.6;
  word-break: break-all;
  white-space: pre-wrap;
}

.code-output.bad {
  color: var(--bad);
  font-family: inherit;
  word-break: normal;
}

/* Secret Agent Missions (elton.html + missions.js) */

/* .picker-hint dims the whole prompt and opacity doesn't un-cascade to a
   child, so the prompt carries no opacity of its own and the question line
   dims itself instead. */
#missionPrompt {
  opacity: 1;
}

.mission-ask {
  display: block;
  opacity: 0.65;
}

.mission-gloss {
  display: block;
  margin-top: 4px;
  font-size: 1.3rem;
  color: var(--ink);
}

.mission-key {
  background: var(--panel-alt);
  border-radius: 10px;
  padding: 8px 14px;
  margin: 0 0 18px;
  font-size: 0.9rem;
}

.mission-key summary {
  cursor: pointer;
  opacity: 0.75;
}

.mission-key p {
  margin: 10px 0 4px;
  line-height: 1.7;
}

/* The four options for a "decode the order" mission are emoji only — showing
   the Swedish word on the button would let him match strings without reading
   the order. */
.mission-emoji-btn {
  font-size: 2.4rem;
  line-height: 1;
  padding: 12px 20px;
}

/* Options are disabled during the feedback pause so a fast second tap can't
   answer the next question by accident. Keep them legible while frozen. */
#missionOptions button:disabled {
  cursor: default;
  opacity: 0.55;
}

.copy-btn {
  font-size: 1.1rem;
  padding: 8px 20px;
  border-radius: 14px;
  border: none;
  background: var(--accent4);
  color: var(--bg);
  cursor: pointer;
  font-family: inherit;
}

code {
  font-family: "Courier New", monospace;
  background: var(--panel-alt);
  border-radius: 6px;
  padding: 2px 6px;
}

.ptable-wrap {
  overflow-x: auto;
  padding-bottom: 6px;
}

.ptable {
  display: grid;
  grid-template-columns: repeat(18, minmax(34px, 1fr));
  grid-template-rows: repeat(7, minmax(34px, 1fr)) 22px repeat(2, minmax(34px, 1fr));
  gap: 3px;
  min-width: 700px;
}

.ptable .elem {
  position: relative;
  border-radius: 6px;
  background: var(--panel-alt);
  border: 2px solid transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px;
}

.ptable .elem .num {
  position: absolute;
  top: 2px;
  left: 3px;
  font-size: 0.5rem;
  opacity: 0.6;
}

.ptable .elem .sym {
  font-weight: bold;
  font-size: 0.9rem;
}

.ptable .elem .cat-emoji {
  position: absolute;
  bottom: 0;
  right: 1px;
  font-size: 0.5rem;
}

.ptable .elem:hover, .ptable .elem:focus-visible {
  border-color: var(--accent3);
}

.ptable .elem.selected {
  background: var(--accent3);
  border-color: var(--accent4);
}

.ptable .elem.selected .num,
.ptable .elem.selected .sym {
  color: var(--bg);
}

.ptable .elem.placeholder {
  cursor: default;
  opacity: 0.5;
  font-size: 0.65rem;
  text-align: center;
}

.ptable-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  justify-content: center;
  margin-top: 16px;
  font-size: 0.8rem;
  opacity: 0.75;
}

.elem-detail {
  margin-top: 20px;
  min-height: 3rem;
  display: flex;
  align-items: center;
  gap: 18px;
}

.elem-detail-hint {
  margin: 0 auto;
  opacity: 0.7;
  text-align: center;
}

.elem-detail.active {
  background: var(--panel-alt);
  border-radius: 14px;
  padding: 16px 20px;
}

.elem-big {
  font-size: 3rem;
  font-weight: bold;
  flex-shrink: 0;
}

.elem-info h3 {
  margin: 0 0 4px;
}

.elem-info p {
  margin: 0;
}

.elem-info p + p {
  margin-top: 6px;
}

.elem-number {
  opacity: 0.6;
  font-weight: normal;
  font-size: 0.9rem;
}

.bohr-block {
  margin-top: 16px;
}

.bohr-host {
  display: flex;
  justify-content: center;
}

.bohr-svg {
  width: min(220px, 60vw);
  height: auto;
}

.bohr-ring {
  fill: none;
  stroke: var(--muted);
  stroke-width: 1.5;
  opacity: 0.6;
}

.bohr-electron {
  fill: #eaf0ff;
  stroke: #7c8db5;
  stroke-width: 1;
}

.bohr-caption {
  text-align: center;
  opacity: 0.75;
  font-size: 0.85rem;
  margin: 8px 0 0;
}

.mode-panel {
  display: none;
}

.mode-panel.active {
  display: block;
}

.circle-diagram {
  display: flex;
  justify-content: center;
  margin: 16px 0;
}

.circle-diagram-svg {
  width: min(220px, 70vw);
  height: auto;
}

.diagram-label {
  fill: var(--ink);
  font-size: 18px;
  font-weight: bold;
  font-family: inherit;
}

/* Same shape as .circle-diagram/.circle-diagram-svg above, just a generic
   name for shapes.html's square/triangle/pentagon/etc diagrams instead of
   one specific to circles. */
.shape-diagram {
  display: flex;
  justify-content: center;
  margin: 0;
}

.shape-diagram-svg {
  width: min(200px, 65vw);
  height: auto;
}

/* Text beside its diagram (text on the left, shape on the right) instead
   of stacked above/below — wraps to stacked automatically on narrow
   screens since there's no room for two columns. */
.shape-row {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 12px;
}

.shape-row .shape-text {
  flex: 1 1 220px;
}

.shape-row .shape-diagram {
  flex: 0 0 auto;
  margin: 0 auto;
}

.clock-host {
  display: flex;
  justify-content: center;
  margin: 16px 0;
}

.clock-svg {
  width: min(300px, 80vw);
  height: auto;
}

.clock-face {
  fill: var(--panel-alt);
  stroke: var(--accent4);
  stroke-width: 3;
}

.clock-tick {
  stroke: var(--muted);
  stroke-width: 2;
}

.clock-number {
  fill: var(--ink);
  font-size: 16px;
  font-weight: bold;
  font-family: inherit;
  text-anchor: middle;
  dominant-baseline: central;
  pointer-events: none;
}

.clock-hand {
  stroke: var(--ink);
  stroke-linecap: round;
}

.clock-hand-hour {
  stroke-width: 6;
}

.clock-hand-minute {
  stroke-width: 4;
}

.clock-center {
  fill: var(--accent4);
}

.clock-hit {
  fill: transparent;
  cursor: pointer;
}

.clock-hit:hover, .clock-hit:focus-visible {
  fill: rgba(255, 255, 255, 0.08);
}

.clock-hit.picked {
  fill: rgba(255, 255, 255, 0.18);
}

.clock-hint {
  text-align: center;
  opacity: 0.7;
  font-size: 0.9rem;
  margin: 0 0 14px;
}

.clock-swedish {
  text-align: center;
  font-style: italic;
  opacity: 0.8;
  margin: 4px 0 0;
}

.time-picker {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.time-picker select, .time-picker input {
  font-size: 1.3rem;
  padding: 8px 10px;
  border-radius: 12px;
  border: 3px solid var(--accent2);
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
}

.time-picker input {
  width: 160px;
  max-width: 100%;
  text-align: center;
}

/* Pi Quiz second-chance lock: input still focused (so the on-screen
   keyboard stays up), just ignoring keystrokes for a moment. */
.time-picker input.locked {
  border-color: var(--bad);
  opacity: 0.6;
}

.time-colon {
  font-size: 1.3rem;
  font-weight: bold;
}

.time-picker button {
  font-size: 1.2rem;
  padding: 10px 22px;
  border-radius: 14px;
  border: none;
  background: var(--accent4);
  color: var(--bg);
  cursor: pointer;
  font-family: inherit;
}

.pie-host {
  display: flex;
  justify-content: center;
  margin: 16px 0;
}

.pie-svg {
  width: min(300px, 80vw);
  height: auto;
}

/* Unfilled slices are the grey --panel-alt. This was briefly --bg, to stop
   the grey wedges competing with the white filled ones for "the shaded
   one" — but the fix that actually landed was renaming the page's wording
   from "shaded" to "filled" (see PROJECT-NOTES.md), so the grey is back by
   request. Must stay an OPAQUE fill, not fill:none, whatever the colour —
   clicks inside an empty slice have to hit-test in "Show the Fraction". */
.pie-slice {
  fill: var(--panel-alt);
  stroke: var(--border);
  stroke-width: 2;
}

.pie-slice.filled {
  fill: var(--accent4);
}

.pie-slice.interactive {
  cursor: pointer;
}

.pie-slice.interactive:hover {
  opacity: 0.85;
}

.sudoku-grid {
  display: grid;
  gap: 4px;
  max-width: 320px;
  margin: 20px auto;
  padding: 4px;
  background: var(--border);
  border-radius: 12px;
}

/* 9x9 has more than twice the cells of 4x4 in the same rough footprint,
   so it gets its own (larger) max-width and (smaller) per-cell font —
   otherwise cells shrink below a comfortable tap target on a tablet. */
.sudoku-grid.size-9 {
  max-width: 500px;
}

.sudoku-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.8rem;
  font-weight: bold;
  border-radius: 8px;
  background: var(--panel-alt);
  color: var(--ink);
  cursor: pointer;
  border: 2px solid transparent;
}

.sudoku-grid.size-9 .sudoku-cell {
  font-size: 1.3rem;
  border-radius: 6px;
}

.sudoku-cell.given {
  color: var(--muted);
  cursor: default;
}

.sudoku-cell.selected {
  border-color: var(--accent4);
}

.sudoku-cell.wrong {
  color: var(--bad);
}

.ttt-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  max-width: 320px;
  margin: 16px auto 20px;
}

.ttt-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.6rem;
  border-radius: 14px;
  background: var(--panel-alt);
  cursor: pointer;
  border: 3px solid transparent;
}

.ttt-cell.selected {
  border-color: var(--accent4);
}

.ttt-grid.over .ttt-cell {
  cursor: default;
}

/* 2048's tile-color ramp is a second deliberate exception to the site's
   monochrome rule (see TILE_COLORS in 2048.js and PROJECT-NOTES.md) — the
   colors are set inline per-tile in JS, this just lays out the board. */
.board2048-wrap {
  /* Board + D-pad side by side; on narrow phones the D-pad wraps to
     below the board instead of squeezing it. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 16px 0 20px;
}

.grid2048 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  width: min(340px, 100%);
  padding: 8px;
  background: var(--panel-alt);
  border-radius: 12px;
  touch-action: none;
  cursor: pointer;
}

.dpad2048 {
  display: grid;
  grid-template-areas:
    '.    up    .'
    'left .     right'
    '.    down  .';
  gap: 6px;
}

.dpad2048 button {
  width: 54px;
  height: 54px;
  border: none;
  border-radius: 12px;
  background: var(--panel-alt);
  color: var(--ink);
  font-size: 1.3rem;
  font-family: inherit;
  cursor: pointer;
}

.dpad2048 button:active {
  background: #303036;
}

.dpad2048 .dpad-up { grid-area: up; }
.dpad2048 .dpad-left { grid-area: left; }
.dpad2048 .dpad-right { grid-area: right; }
.dpad2048 .dpad-down { grid-area: down; }

.tile2048 {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.6rem;
  font-weight: bold;
  border-radius: 8px;
  background: var(--border);
}

.memory-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  max-width: 400px;
  margin: 20px auto;
}

.memory-card {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  font-weight: bold;
  border-radius: 10px;
  background: var(--panel-alt);
  color: var(--ink);
  cursor: pointer;
  border: 2px solid transparent;
}

.memory-card.flipped {
  background: var(--accent4);
  color: var(--bg);
}

.memory-card.matched {
  /* background/color set inline per-pair in memory.js (pairColor()) */
  cursor: default;
  opacity: 0.85;
}

/* Nordic countries: the one deliberate exception to the site's monochrome
   rule — the flags ARE color (that's the whole teaching point, they're
   almost impossible to tell apart in greyscale), see PROJECT-NOTES.md. */
.flag-grid {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 20px;
}

.flag-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  background: var(--panel-alt);
  border: 3px solid transparent;
  border-radius: 14px;
  padding: 10px 14px;
  cursor: pointer;
  font-family: inherit;
  color: var(--ink);
}

.flag-card:hover, .flag-card:focus-visible {
  border-color: var(--accent3);
}

.flag-card.active {
  border-color: var(--accent4);
}

.flag-card.static {
  cursor: default;
}

.flag-card span {
  font-weight: bold;
  font-size: 0.9rem;
}

.flag-svg {
  width: 70px;
  height: auto;
  border-radius: 4px;
  display: block;
}

.flag-svg.large {
  width: 120px;
  flex-shrink: 0;
}

.bit-row {
  display: flex;
  gap: 6px;
  justify-content: center;
  flex-wrap: wrap;
  margin: 16px 0;
}

.bit {
  width: 44px;
  height: 60px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: 10px;
  border: 3px solid transparent;
  background: var(--panel-alt);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
}

.bit:disabled {
  cursor: default;
}

/* Gap before the 5th bit splits the byte into its two nibbles — one hex
   digit each, see the Binary & Hex facts panel. */
.bit.nibble-start {
  margin-left: 14px;
}

.bit .place-value {
  font-size: 0.65rem;
  opacity: 0.6;
}

.bit .bit-value {
  font-size: 1.4rem;
  font-weight: bold;
}

.bit.on {
  background: var(--accent4);
  border-color: var(--accent4);
  color: var(--bg);
}

/* Coin purse badge (coins.js) — pinned to the top-right corner of every
   page. Chrome borrowed from `nav a` so it reads as part of the site
   furniture; the 🪙 emoji is the only color, no new palette entries (see
   the monochrome note in PROJECT-NOTES.md). */
.coin-badge {
  position: fixed;
  top: 10px;
  right: 10px;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: 0 4px 0 rgba(0,0,0,0.4);
  font-size: 1.1rem;
  line-height: 1;
  /* The badge sits over the page, so a stray tap/drag on it shouldn't
     select the number or block a tap meant for what's underneath. */
  user-select: none;
  pointer-events: none;
}

.coin-badge b {
  font-size: 1.3rem;
}

.coin-badge-label {
  font-size: 1rem;
  color: var(--muted);
}

/* Pulse when a coin lands — the "+1" alone is small and easy to miss with
   your eyes on the middle of the screen. */
.coin-badge.bumped {
  animation: coin-bump 0.6s ease;
}

@keyframes coin-bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}

.coin-gain {
  position: absolute;
  top: 100%;
  right: 14px;
  font-weight: bold;
  color: var(--good);
  animation: coin-gain-float 1.1s ease-out forwards;
}

@keyframes coin-gain-float {
  0%   { opacity: 0; transform: translateY(-6px); }
  25%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(14px); }
}

/* The badge is fixed, so it doesn't reserve space in the flow, and the
   header's <h1> is centred and full-bleed — nothing keeps the title out of
   the top-right corner on its own. Two defences, split at the same 900px
   the rest of the sheet uses:

   Below that, the title is close to the full width of the screen whatever
   it says, so no amount of horizontal room helps — give the badge its own
   row above everything instead.

   Above it, reserve the badge's width plus its 10px inset on BOTH sides
   so the title stays centred and simply wraps a line earlier rather than
   running underneath. A max-width media query was the first attempt and it
   was wrong: the title is capped at 3.2rem but the badge keeps sliding
   right with the viewport, so each long title has its own overlap band
   (big-numbers ~901-925px, elton ~1100-1184px — iPad landscape sits in
   that second one). Any fixed breakpoint just moves the band to wherever
   the next long title happens to land.

   The reservation must be >= the badge, not merely close to it: the ink is
   bounded by the padded content box, so equality is what makes an overlap
   impossible at every width at once. 180px covers the badge at its widest
   realistic size, 168.5px with a four-digit count ("moneys: 9999") — the
   word made it more than twice the 82px the coin symbol needed. A
   five-digit purse would need this raised again; nothing on the site
   enforces that, so measure it if the number ever gets there. */
@media (max-width: 900px) {
  body {
    padding-top: 62px;
  }
}

@media (min-width: 901px) {
  header h1 {
    padding-inline: 180px;
  }
}
