:root {
  color-scheme: dark;
  --bg: #090d18;
  --surface: rgba(18, 24, 42, 0.88);
  --surface-2: #151c30;
  --surface-3: #1c2540;
  --text: #f5f7ff;
  --muted: #94a0b8;
  --faint: #59647b;
  --line: rgba(158, 174, 210, 0.16);
  --grid: #71809e;
  --accent: #8b7cff;
  --accent-2: #4ee0bd;
  --danger: #ff6f91;
  --success: #35e0aa;
  --peer-bg: #283052;
  --axis-bg: #354176;
  --box-bg: #252c4c;
  --same-bg: #087c6a;
  --same-text: #ffffff;
  --same-number: #64ffd5;
  --selected-bg: #6c55e8;
  --pattern: #8b7cff;
  --link: #4ee0bd;
  --on-accent: #ffffff;
  --on-success: #041b15;
  --shadow: 0 30px 80px rgba(0, 0, 0, 0.3);
}

:root.light {
  color-scheme: light;
  --bg: #f4f2ed;
  --surface: rgba(255, 255, 255, 0.9);
  --surface-2: #f8f7f3;
  --surface-3: #eeece6;
  --text: #171925;
  --muted: #676d7c;
  --faint: #9296a0;
  --line: rgba(32, 38, 57, 0.13);
  --grid: #5d6474;
  --accent: #6755da;
  --accent-2: #0b9b7b;
  --danger: #d73e64;
  --success: #078565;
  --peer-bg: #e7e2fa;
  --axis-bg: #cec4f7;
  --box-bg: #ebe8f8;
  --same-bg: #53cbae;
  --same-text: #092b24;
  --same-number: #007c61;
  --selected-bg: #8b73eb;
  --pattern: #7563ee;
  --link: #079879;
  --on-accent: #ffffff;
  --on-success: #041b15;
  --shadow: 0 30px 80px rgba(45, 42, 35, 0.13);
}

* { box-sizing: border-box; }

/* --- Text: font and size ------------------------------------------------- */

/* Settings → Text writes data-font and --text-size onto <html> (settings.mjs
   applyText, and the boot script in index.html before first paint). The app
   sizes text in rem, so the root size scales every screen; the board does not
   use rem (see .value) and keeps its size. These live on html rather than in
   the :root block above, which is the colour set every palette must define. */
html {
  --text-size: 16px;
  /* One stack per option in Settings → Text (FONT_IDS in settings.mjs); the
     font cards there are set in these too. */
  --font-system: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-figtree: "Figtree", ui-sans-serif, system-ui, sans-serif;
  --font-lexend: "Lexend", ui-sans-serif, system-ui, sans-serif;
  --font-dm-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  --font-outfit: "Outfit", ui-sans-serif, system-ui, sans-serif;
  --font-space-grotesk: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --font-kalam: "Kalam", cursive;
  --font-dancing-script: "Dancing Script", cursive;
  --font-neuton: "SudokuLab Serif", Georgia, serif;
  --font-damion: "Damion", cursive;
  --font-niconne: "SudokuLab Script", cursive;
  --font-pattaya: "Pattaya", ui-sans-serif, sans-serif;
  --font-blenda: "Blenda Script", cursive;
  --font-ui: var(--font-system);
  /* Board cells and the number pad. Handwritten faces make 1/7 and 5/6 hard to
     tell apart at pencil-mark size, so under those the digits stay system. */
  --font-digits: var(--font-ui);
  font-size: var(--text-size);
  scroll-behavior: smooth;
}
:root[data-font="figtree"] { --font-ui: var(--font-figtree); }
:root[data-font="lexend"] { --font-ui: var(--font-lexend); }
:root[data-font="dm-sans"] { --font-ui: var(--font-dm-sans); }
:root[data-font="outfit"] { --font-ui: var(--font-outfit); }
:root[data-font="space-grotesk"] { --font-ui: var(--font-space-grotesk); }
:root[data-font="kalam"] { --font-ui: var(--font-kalam); }
:root[data-font="dancing-script"] { --font-ui: var(--font-dancing-script); }
:root[data-font="neuton"] { --font-ui: var(--font-neuton); }
:root[data-font="damion"] { --font-ui: var(--font-damion); }
:root[data-font="niconne"] { --font-ui: var(--font-niconne); }
:root[data-font="pattaya"] { --font-ui: var(--font-pattaya); }
:root[data-font="blenda"] { --font-ui: var(--font-blenda); }
:root[data-font="kalam"],
:root[data-font="dancing-script"],
:root[data-font="damion"],
:root[data-font="niconne"],
:root[data-font="pattaya"],
:root[data-font="blenda"] { --font-digits: var(--font-system); }
/* Single-weight faces: the 720–900 weights used across the app would otherwise
   be smeared into a faux bold. */
:root[data-font="damion"],
:root[data-font="niconne"],
:root[data-font="pattaya"],
:root[data-font="blenda"] { font-synthesis-weight: none; }

body {
  margin: 0;
  min-width: 320px;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  transition: background 180ms ease, color 180ms ease;
}
button, input, select, textarea { font: inherit; }
button { color: inherit; }
button:focus-visible, textarea:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 3px; }

.ambient { position: fixed; border-radius: 50%; filter: blur(100px); opacity: .15; pointer-events: none; z-index: -1; }
.ambient-a { width: 460px; height: 460px; background: var(--accent); top: -220px; left: 18%; }
.ambient-b { width: 380px; height: 380px; background: var(--accent-2); right: -180px; top: 42%; }

/* iOS-style navigation bar. It holds only controls: the way back on the left,
   actions on the right. Each screen's large title lives in the content below;
   when it scrolls under the bar, screens.mjs adds .is-scrolled, the bar gets a
   frosted background and a small copy of the title fades in at the centre. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: calc(56px + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) max(24px, calc((100% - 1280px) / 2)) 0;
  display: flex;
  align-items: center;
  gap: 12px;
  border-bottom: 1px solid transparent;
  transition: background 160ms ease, border-color 160ms ease;
}
.topbar.is-scrolled {
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-bottom-color: var(--line);
}
.topbar-title {
  position: absolute;
  top: env(safe-area-inset-top);
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  max-width: calc(100% - 240px);
  display: flex;
  align-items: center;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 1.06rem;
  font-weight: 720;
  letter-spacing: -.02em;
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease;
}
.topbar.is-scrolled .topbar-title { opacity: 1; }
.topbar #theme-toggle { margin-left: auto; }
.nav-back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: 44px;
  margin-left: -8px;
  padding: 0 10px 0 2px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--accent);
  font-size: 1.06rem;
  font-weight: 650;
  letter-spacing: -.01em;
  cursor: pointer;
}
.nav-back[hidden] { display: none; }
.nav-back:hover { color: color-mix(in srgb, var(--accent) 70%, var(--text)); }
/* The app icon itself, rounded the way iOS rounds it on the Home Screen. */
.brand-mark { display: block; width: 32px; height: 32px; border-radius: 22.5%; box-shadow: 0 1px 2px rgb(0 0 0 / .18); user-select: none; -webkit-user-drag: none; }
.icon-button { border: 1px solid var(--line); background: var(--surface); border-radius: 10px; width: 38px; height: 38px; cursor: pointer; flex: 0 0 auto; }

main { width: min(1280px, calc(100% - 48px)); margin: 0 auto; padding-bottom: 56px; }
.eyebrow { margin: 0 0 10px; color: var(--accent-2); font-size: .7rem; font-weight: 800; letter-spacing: .16em; text-transform: uppercase; }
h1 { margin: 0; font-size: clamp(2.6rem, 5.5vw, 5.4rem); line-height: .94; letter-spacing: -.065em; font-weight: 710; }
h1 em { color: var(--accent); font-family: Georgia, "Times New Roman", serif; font-weight: 400; }

.workspace { display: grid; grid-template-columns: minmax(430px, 1.04fr) minmax(340px, .96fr); background: var(--surface); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--shadow); backdrop-filter: blur(22px); overflow: hidden; }
.board-column { padding: 28px; border-right: 1px solid var(--line); min-width: 0; }
.board-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; }
.segmented { background: var(--surface-2); padding: 4px; border-radius: 10px; border: 1px solid var(--line); display: flex; }
.segmented button { border: 0; background: transparent; color: var(--muted); border-radius: 7px; padding: 7px 16px; cursor: pointer; font-size: .8rem; font-weight: 700; }
.segmented button.active { background: var(--surface-3); color: var(--text); box-shadow: 0 3px 10px rgba(0,0,0,.12); }
.history-controls { display: flex; gap: 4px; --toolbar-icon: 15px; }
.icon-toggle { display: inline-grid; place-items: center; border-radius: 7px; }
.icon-toggle svg { display: block; width: var(--toolbar-icon); height: var(--toolbar-icon); }
/* Undo and redo: a bold arrow, with its word beside it where there is room. */
.history-key { display: inline-flex; align-items: center; gap: 6px; }
.history-key svg { flex: none; }
.icon-toggle[aria-pressed="true"]:not(:disabled),
.icon-toggle[aria-expanded="false"]:not(:disabled) { color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent); }
.text-button, .quiet-button { border: 0; background: transparent; color: var(--muted); padding: 7px 9px; cursor: pointer; font-size: .78rem; }
.text-button:disabled { opacity: .35; cursor: default; }

/* Rows are fixed like the columns: left to size themselves, a row with no digits
   (pencil marks are absolutely placed) collapsed to a sliver. */
.sudoku-board { width: min(100%, 600px); aspect-ratio: 1; margin: 0 auto; display: grid; grid-template-columns: repeat(9, 1fr); grid-template-rows: repeat(9, minmax(0, 1fr)); border: 2px solid var(--grid); background: var(--grid); gap: 1px; user-select: none; }
.cell { position: relative; border: 0; background: var(--surface-2); min-width: 0; padding: 0; cursor: pointer; color: var(--text); overflow: hidden; }
/* Rows group the cells for assistive technology and add no box of their own. */
.board-row { display: contents; }
.cell.box-end-col { border-right: 2px solid var(--grid); }
.cell.box-end-row { border-bottom: 2px solid var(--grid); }
.cell.peer { background: var(--peer-bg); }
.cell.peer.axis-peer { background: var(--axis-bg); }
.cell.peer.box-peer { background: var(--box-bg); }
.cell.same-value { background: var(--same-bg); box-shadow: inset 0 0 0 3px var(--accent-2); }
.cell.same-value .value { color: var(--same-number); font-weight: 900; }
.cell.selected { background: var(--selected-bg); z-index: 2; }
.cell.selected::after { content: ""; position: absolute; inset: 5px; border: 3px solid var(--accent); box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 52%, transparent); pointer-events: none; }
.cell.selected .value { color: var(--on-accent); font-weight: 900; }
.cell.selected.same-value .value { color: var(--same-number); text-shadow: 0 1px 0 rgba(0,0,0,.2); }
/* Draw every internal 3×3 seam once, without changing a cell's dimensions or
   replacing value/same-number highlights that also use box shadows. */
.cell.block-divider-top::before, .cell.block-divider-left::before { content: ""; position: absolute; inset: 0; pointer-events: none; }
.cell.block-divider-top::before { background: linear-gradient(var(--grid), var(--grid)) top / 100% 3px no-repeat; }
.cell.block-divider-left::before { background: linear-gradient(var(--grid), var(--grid)) left / 3px 100% no-repeat; }
.cell.block-divider-top.block-divider-left::before { background: linear-gradient(var(--grid), var(--grid)) top / 100% 3px no-repeat, linear-gradient(var(--grid), var(--grid)) left / 3px 100% no-repeat; }
.cell.pattern-cell { background: color-mix(in srgb, var(--pattern) 22%, var(--surface-2)); }
.cell.link-cell { background: color-mix(in srgb, var(--link) 20%, var(--surface-2)); }
.cell.invalid { background: color-mix(in srgb, var(--danger) 18%, var(--surface-2)); }
/* Board digits are sized in px and by the board, never in rem: the text size
   setting scales the app around the board, not the grid itself. */
.value, .candidate, .number-pad button { font-family: var(--font-digits); font-variant-numeric: tabular-nums; }
.value { font-size: clamp(21.6px, 4.3vw, 40.8px); font-weight: 520; line-height: 1; }
/* Every placed digit is one colour; weight alone separates a given from a move,
   so the board is not asking to be read as a palette. */
.cell.given .value { font-weight: 760; }
.cell.given.same-value .value,
.cell:not(.given).same-value .value,
.cell.selected.same-value .value { color: var(--same-number); font-weight: 900; }
.candidates { position: absolute; inset: 5%; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); place-items: center; }
.candidate { font-size: clamp(8px, 1.15vw, 12.3px); color: var(--muted); font-weight: 650; line-height: 1; width: 100%; height: 100%; display: grid; place-items: center; border-radius: 50%; }
.candidate.pattern { color: var(--on-accent); background: var(--pattern); }
.candidate.linked { color: var(--on-success); background: var(--link); }
.candidate.remove { color: var(--on-accent); background: var(--danger); text-decoration: line-through; box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 22%, transparent); }
.candidate.proposed { color: var(--on-accent); background: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }
.candidate.selected-digit:not(:empty) { color: var(--same-text); background: var(--same-bg); font-weight: 900; }
.candidate.answer-correct { color: var(--on-success); background: var(--success); box-shadow: 0 0 0 2px color-mix(in srgb, var(--success) 30%, transparent); }
.candidate.answer-wrong { color: var(--on-accent); background: var(--danger); box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 30%, transparent); }

.number-pad { display: grid; grid-template-columns: repeat(9, 1fr); gap: 7px; margin: 16px auto 0; width: min(100%, 600px); }
.number-pad button { aspect-ratio: 1.08; border: 1px solid var(--line); background: var(--surface-2); border-radius: 9px; cursor: pointer; font-weight: 730; font-size: 16px; }
.number-pad button:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); }
.number-pad button.complete, .number-pad button:disabled { opacity: .28; filter: grayscale(1); cursor: not-allowed; transform: none; background: var(--surface-3); }
.board-actions { display: flex; gap: 8px; flex-wrap: wrap; }
/* Under the numbers: the actions, and beside them the pencil, undo and redo
   when the row has room for both (placeHistoryControls, app.mjs). Without it
   they go back up to the toolbar. The row is one line on purpose: an overflow
   is how the script tells that it does not fit. */
.board-footer { width: min(100%, 600px); margin: 13px auto 0; display: flex; align-items: center; gap: 8px; }
.board-footer .board-actions { flex: 1 1 auto; flex-wrap: nowrap; }
.board-footer .history-controls { margin-left: auto; }
/* Nothing to show while a move is explained: no empty row under the pad. */
.board-footer:not(:has(> .board-actions:not(.hidden), .history-controls > button:not(.hidden))) { display: none; }
.secondary-button, .primary-button, .danger-button { border-radius: 10px; border: 1px solid var(--line); min-height: 42px; padding: 10px 15px; cursor: pointer; font-weight: 750; font-size: .78rem; letter-spacing: .01em; }
.secondary-button { color: var(--text); background: var(--surface-2); }
.primary-button { background: var(--accent); color: var(--on-accent); border-color: transparent; }
.danger-button { background: var(--danger); color: var(--on-accent); border-color: transparent; }
.primary-button:hover, .danger-button:hover { filter: brightness(1.08); }
.board-status { width: min(100%, 600px); min-height: 1.2em; margin: 10px auto 0; color: var(--muted); font-size: .74rem; }
.mistake-counter { width: min(100%, 600px); margin: 0 auto 12px; display: flex; justify-content: space-between; align-items: center; padding: 9px 12px; border-radius: 9px; background: var(--surface-2); border: 1px solid var(--line); color: var(--muted); font-size: .72rem; }
.mistake-counter strong { color: var(--text); }.mistake-counter.danger strong { color: var(--danger); }
.game-feedback { width: min(100%, 600px); margin: 0 auto 12px; padding: 12px 14px; border-radius: 10px; color: var(--on-accent); background: var(--danger); border: 2px solid color-mix(in srgb, var(--danger) 70%, var(--on-accent)); font-size: .78rem; font-weight: 750; line-height: 1.45; box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 16%, transparent); }
.game-feedback.game-over { padding: 16px; font-size: .84rem; }

.coach-panel { padding: 34px; display: flex; flex-direction: column; min-width: 0; }
.coach-heading-row { display: flex; align-items: start; justify-content: space-between; }
.glossary-panel { margin-top: 12px; border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line)); border-radius: 11px; background: color-mix(in srgb, var(--accent) 10%, var(--surface-2)); }
.glossary-panel summary { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 13px; list-style: none; cursor: pointer; color: var(--text); font-size: .76rem; font-weight: 850; }
.glossary-panel summary::-webkit-details-marker { display: none; }
.glossary-panel summary span { display: flex; align-items: center; gap: 8px; }
.glossary-panel summary b { width: 20px; height: 20px; display: grid; place-items: center; border-radius: 50%; background: var(--accent); color: var(--on-accent); font-family: Georgia, serif; }
.glossary-panel summary em { color: var(--muted); font-style: normal; font-size: .63rem; }
.glossary-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; padding: 0 12px 12px; }
.glossary-grid article { padding: 10px; border-radius: 8px; background: var(--surface-3); border: 1px solid var(--line); }
.glossary-grid h3 { margin: 0 0 5px; color: var(--accent-2); font-size: .72rem; }
.glossary-grid p { margin: 0; color: var(--text); font-size: .68rem; line-height: 1.45; }
h2 { font-size: 1.7rem; letter-spacing: -.04em; margin: 0; }
.technique-filter { display: flex; flex-wrap: wrap; gap: 7px; margin: 24px 0; }
.technique-filter button { border: 1px solid var(--line); background: transparent; color: var(--muted); padding: 7px 10px; border-radius: 999px; cursor: pointer; font-size: .68rem; font-weight: 700; }
.technique-filter button.active { background: color-mix(in srgb, var(--accent) 15%, transparent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--text); }
.practice-controls { display: grid; gap: 10px; margin: 14px 0 18px; }
.practice-desc { grid-column: 1 / -1; margin: 2px 0 0; padding-left: 10px; border-left: 2px solid color-mix(in srgb, var(--accent) 55%, transparent); color: var(--muted); font-size: .76rem; line-height: 1.55; }
.practice-desc:empty { display: none; }
.explain-button { grid-column: 1 / -1; min-height: 40px; }
.explain-button[aria-pressed="true"] { border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: color-mix(in srgb, var(--accent) 14%, var(--surface-2)); }
.hint-card { flex: 1; min-height: 288px; padding: 25px; border: 1px solid var(--line); background: var(--surface-2); border-radius: 16px; position: relative; overflow: hidden; }
.hint-card::before { content: ""; position: absolute; inset: 0 auto auto 0; width: 3px; height: 100%; background: var(--accent); }
.hint-card.empty { display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
.hint-card.empty::before { display: none; }
.hint-empty-icon { width: 52px; height: 52px; border: 1px solid var(--line); display: grid; place-items: center; border-radius: 50%; color: var(--accent); font-size: 1.6rem; margin-bottom: 14px; }
.hint-card h3 { margin: 0 0 8px; font-size: 1.08rem; }
.hint-card p { color: var(--muted); line-height: 1.58; font-size: .82rem; max-width: 390px; margin: 0; }
.answer-feedback { display: flex; gap: 11px; align-items: start; margin: 0 0 18px; padding: 14px; border-radius: 11px; border: 2px solid; }
.answer-feedback.success { background: color-mix(in srgb, var(--success) 16%, var(--surface-3)); border-color: var(--success); }
.answer-feedback.error { background: color-mix(in srgb, var(--danger) 13%, var(--surface-3)); border-color: var(--danger); }
.answer-icon { flex: 0 0 27px; height: 27px; border-radius: 50%; display: grid; place-items: center; color: var(--on-accent); font-weight: 900; background: var(--danger); }
.answer-feedback.success .answer-icon { background: var(--success); color: var(--on-success); }
.answer-feedback strong { display: block; font-size: .86rem; margin: 2px 0 4px; }
.answer-feedback p { color: var(--text); font-size: .74rem; line-height: 1.45; }
.term-row { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 18px; }
.pattern-note { margin-top: 14px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-3); }
.pattern-note summary { list-style: none; padding: 9px 11px; color: var(--text); font-size: .74rem; font-weight: 750; cursor: pointer; }
.pattern-note summary::-webkit-details-marker { display: none; }
.pattern-note summary::after { content: " \203a"; color: var(--muted); }
.pattern-note[open] summary::after { content: " \2039"; }
.pattern-note p { margin: 0 11px 11px; color: var(--muted); font-size: .78rem; line-height: 1.6; }
.term-help { position: relative; }
.term-help summary { list-style: none; display: flex; align-items: center; gap: 6px; padding: 6px 8px; color: var(--text); background: var(--surface-3); border: 1px solid var(--line); border-radius: 8px; cursor: pointer; font-size: .68rem; font-weight: 750; text-transform: capitalize; }
.term-help summary::-webkit-details-marker { display: none; }
.term-help summary span { width: 16px; height: 16px; display: grid; place-items: center; border-radius: 50%; color: var(--on-accent); background: var(--accent); font-size: .58rem; }
.term-help[open] { width: 100%; }
.term-help[open] summary { width: max-content; border-color: var(--accent); }
.term-help p { margin-top: 8px; padding: 10px 11px; color: var(--text); background: color-mix(in srgb, var(--accent) 12%, var(--surface-3)); border-left: 3px solid var(--accent); border-radius: 7px; }
.hint-kicker { color: var(--accent-2); font-size: .65rem; text-transform: uppercase; font-weight: 800; letter-spacing: .13em; margin-bottom: 12px; }
.hint-title { font-size: 1.35rem !important; letter-spacing: -.025em; }
.hint-meta { display: flex; gap: 8px; flex-wrap: wrap; margin: 18px 0; }
.hint-meta span { background: var(--surface-3); border: 1px solid var(--line); padding: 6px 8px; border-radius: 7px; font-size: .68rem; color: var(--muted); }
.elimination-list { margin: 15px 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 6px; }
.elimination-list li { color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 25%, transparent); border-radius: 7px; padding: 6px 8px; font-size: .72rem; font-weight: 750; }
.coach-actions { display: grid; margin-top: 12px; gap: 8px; }
.coach-actions .primary-button { min-height: 50px; display: flex; justify-content: space-between; align-items: center; padding-inline: 18px; }
.coach-actions .answer-success { justify-content: center; background: var(--success); color: var(--on-success); border-color: var(--success); box-shadow: 0 0 0 5px color-mix(in srgb, var(--success) 22%, transparent); font-size: .84rem; }
.coach-actions .answer-error { justify-content: center; background: var(--danger); color: var(--on-accent); border-color: var(--danger); box-shadow: 0 0 0 5px color-mix(in srgb, var(--danger) 20%, transparent); font-size: .84rem; }
.hidden { display: none !important; }
.legend { display: flex; gap: 17px; flex-wrap: wrap; margin-top: 18px; color: var(--muted); font-size: .66rem; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 8px; height: 8px; border-radius: 50%; }
.pattern-dot { background: var(--pattern); }.link-dot { background: var(--link); }.remove-dot { background: var(--danger); }


dialog { width: min(560px, calc(100% - 32px)); color: var(--text); background: var(--surface-2); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow); padding: 0; }
dialog::backdrop { background: color-mix(in srgb, var(--bg) 82%, transparent); backdrop-filter: blur(5px); }
dialog form { padding: 25px; }
.dialog-heading { display: flex; justify-content: space-between; align-items: start; }
dialog > form > p { color: var(--muted); font-size: .8rem; line-height: 1.5; }
dialog textarea { width: 100%; resize: vertical; border: 1px solid var(--line); background: var(--bg); color: var(--text); padding: 14px; border-radius: 10px; line-height: 1.45; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* Import dialog: the field's visible label, its help text tied to it, and a
   rejected grid's field marked by more than colour (the message says why). */
#paste-dialog .dialog-heading { align-items: center; gap: 12px; }
#paste-dialog .field-label { display: block; margin-top: 16px; font-weight: 700; font-size: .95rem; }
#paste-dialog .field-label + p { margin-top: 4px; }
dialog textarea[aria-invalid="true"] { border-color: var(--danger); border-width: 2px; padding: 13px; }
/* On a short screen (a phone held sideways, large text) nine rows would be taller
   than the screen; the field scrolls inside instead, so its label, help, message
   and actions stay next to it. */
@media (max-height: 500px) { #paste-dialog textarea { max-height: 50vh; max-height: 50dvh; } }
.dialog-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 13px; }
.form-error { color: var(--danger) !important; min-height: 1.2em; }

@media (max-width: 900px) {
  .workspace { grid-template-columns: 1fr; }
  .board-column { border-right: 0; border-bottom: 1px solid var(--line); }
}
@media (max-width: 560px) {
  main { width: min(100% - 24px, 1280px); }
  .topbar { padding-left: 12px; padding-right: 12px; }
  .workspace { border-radius: 16px; }
  .board-column, .coach-panel { padding: 16px; }
  .cell.selected::after { inset: 4px; border-width: 2px; }
  .number-pad { gap: 3px; }.number-pad button { border-radius: 5px; font-size: 13px; }
  .history-controls .history-label { display: none; }
  .history-controls { --toolbar-icon: 18px; }
  .glossary-grid { grid-template-columns: 1fr; }
}

/* A generated challenge's one-line summary: its mode, and on a Mixed board
   what finishing needs after the target, read before the first digit. */
.hint-card p.challenge-summary {
  margin: 0.35rem 0 0.5rem;
  color: var(--text);
  font-size: 0.82rem;
  font-weight: 700;
  line-height: 1.4;
}
.challenge-failure-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.challenge-failure-actions button { min-height: 44px; padding-inline: 16px; }

/* What a generated pattern-required board guarantees about the chosen technique. */
.drill-guarantee {
  margin: 0.5rem 0 0;
  padding: 0.5rem 0.65rem;
  border-left: 3px solid var(--accent);
  border-radius: 0 6px 6px 0;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  font-size: 0.85rem;
  line-height: 1.45;
  opacity: 0.92;
}


/* --- Touch devices ------------------------------------------------------- */

/* Safari waits ~300ms on every tap to see whether it is a double-tap zoom.
   On a Sudoku grid that reads as lag on every single cell. */
.cell, .number-pad button, button, select {
  touch-action: manipulation;
}

/* Installed full-screen on iPad there is no browser chrome, so the page has to
   respect the safe area itself. */
body {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  padding-bottom: env(safe-area-inset-bottom);
}

@media (pointer: coarse) {
  /* Apple's minimum comfortable target is 44px; the desktop pad is smaller. */
  .number-pad button { aspect-ratio: 1; min-height: 46px; font-size: 18.4px; }
  .board-actions button, .board-toolbar button { min-height: 44px; }
  /* iOS zooms into any field under 16px when it is focused, which a 14 px
     text size would otherwise trigger. */
  select { min-height: 44px; font-size: max(16px, 1rem); }
  textarea { font-size: max(16px, 1rem); }
  /* Long-pressing a cell otherwise pops up iOS text selection and callouts. */
  .sudoku-board, .number-pad { -webkit-touch-callout: none; }
}

/* ==========================================================================
   Screens
   Every colour below comes from the palette variables so a new palette needs
   no edits here.
   ========================================================================== */

.screen { display: block; }
.screen[hidden] { display: none; }

.back-button {
  flex: 0 0 auto;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text);
  border-radius: 10px;
  min-height: 38px;
  padding: 0 13px;
  cursor: pointer;
  font-size: .78rem;
  font-weight: 750;
}
.back-button[hidden] { display: none; }

.screen-bar {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 0 22px;
  margin-bottom: 4px;
}
/* The screen's large title: big, left-aligned, part of the content. */
.large-title { font-size: 2.5rem; line-height: 1.08; letter-spacing: -.045em; font-weight: 740; }
.screen-note { color: var(--muted); font-size: .75rem; line-height: 1.6; margin: 22px 0 0; }

/* --- Menu rows (the levels on Play) ------------------------------------- */

.home-menu { display: grid; gap: 10px; padding-bottom: 40px; }
.home-row {
  display: grid;
  gap: 4px;
  text-align: left;
  min-height: 72px;
  padding: 15px 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
}
.home-row:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
.home-row-label { font-size: 1.05rem; font-weight: 780; letter-spacing: -.02em; }
.home-row-desc { color: var(--muted); font-size: .78rem; line-height: 1.5; }

/* --- Stats --------------------------------------------------------------- */

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 10px; }
.stat-tile {
  display: grid;
  gap: 5px;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface-2);
}
.stat-value { font-size: 1.85rem; font-weight: 760; letter-spacing: -.04em; color: var(--accent-2); line-height: 1; }
.stat-label { font-size: .82rem; font-weight: 750; }
.stat-note { color: var(--muted); font-size: .7rem; line-height: 1.45; }

/* --- Options ------------------------------------------------------------- */

.option-section { border: 1px solid var(--line); border-radius: 16px; background: var(--surface-2); margin-bottom: 14px; overflow: hidden; }
.option-header { padding: 13px 18px; background: var(--surface-3); border-bottom: 1px solid var(--line); }
.option-header h3 { margin: 0; font-size: .74rem; font-weight: 820; letter-spacing: .13em; text-transform: uppercase; color: var(--accent-2); }
.option-body { padding: 16px 18px; }
.option-list { display: grid; gap: 2px; padding: 6px 8px; }

.option-row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 60px;
  padding: 10px;
  border-radius: 11px;
  cursor: pointer;
}
.option-row:hover { background: var(--surface-3); }
.option-text { display: grid; gap: 3px; flex: 1; min-width: 0; }
.option-label { font-size: .86rem; font-weight: 720; }
.option-note { color: var(--muted); font-size: .72rem; line-height: 1.45; }

/* The checkbox stays in the accessibility tree and drives the drawn track. */
.option-switch { position: absolute; opacity: 0; width: 44px; height: 44px; margin: 0; cursor: pointer; }
.switch-track {
  flex: 0 0 auto;
  width: 50px;
  height: 30px;
  border-radius: 999px;
  background: var(--surface-3);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  padding: 3px;
  transition: background 140ms ease, border-color 140ms ease;
}
.switch-thumb { width: 22px; height: 22px; border-radius: 50%; background: var(--muted); transition: transform 140ms ease, background 140ms ease; }
.option-switch:checked ~ .switch-track { background: color-mix(in srgb, var(--accent) 60%, var(--surface-3)); border-color: var(--accent); }
.option-switch:checked ~ .switch-track .switch-thumb { transform: translateX(20px); background: var(--text); }
.option-switch:focus-visible ~ .switch-track { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 3px; }

.mode-switch { width: max-content; margin-bottom: 16px; }
.mode-switch button { min-height: 44px; padding-inline: 22px; }

.palette-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.palette-card {
  display: grid;
  gap: 9px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface-3);
  cursor: pointer;
  color: var(--text);
  min-height: 78px;
}
.palette-card.active { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
.palette-swatches { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; }
.palette-swatches i { display: block; height: 26px; border-radius: 6px; border: 1px solid var(--line); }
.palette-name { font-size: .74rem; font-weight: 750; text-transform: capitalize; text-align: left; }

/* Text: font cards and the size slider on the left, a live preview on the
   right once the section is wide enough for both. */
.option-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.link-button { min-height: 44px; margin: -12px -10px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: var(--muted); font-size: .76rem; font-weight: 700; text-align: right; cursor: pointer; }
.link-button:hover:not(:disabled) { color: var(--accent); }
.link-button:disabled { opacity: .45; cursor: default; }
.text-options { container-type: inline-size; }
.text-layout { display: grid; gap: 18px; }
@container (min-width: 860px) {
  .text-layout { grid-template-columns: minmax(0, 1fr) minmax(280px, 340px); gap: 24px; align-items: start; }
}
.text-controls { display: grid; gap: 14px; min-width: 0; }
.text-field-head { display: grid; gap: 3px; }
.font-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 8px; }
.font-card {
  display: grid;
  align-content: center;
  justify-items: start;
  gap: 4px;
  min-height: 76px;
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface-3);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.font-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
.font-card.active { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
/* The sample stays one size so the cards compare faces, not sizes; the name
   follows the text size and wraps when it has to. */
.font-sample { font-size: 24px; line-height: 1.15; font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.font-name { max-width: 100%; font-size: .8125rem; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.font-note { margin: -4px 0 0; }
.text-size { display: grid; gap: 8px; }
.text-size-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; column-gap: 12px; }
.text-size-value { color: var(--muted); font-size: .78rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.text-size-row { display: flex; align-items: center; gap: 10px; }
.size-step { flex: 0 0 auto; display: grid; place-items: center; min-width: 44px; min-height: 44px; padding: 0; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-3); color: var(--text); font-size: .8rem; font-weight: 750; line-height: 1; cursor: pointer; }
.size-step-large { font-size: 1.3rem; }
.size-step:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.size-step:disabled { opacity: .35; cursor: default; }
/* The track fills with the accent up to the thumb; --fill is set by renderText. */
.size-range { --fill: 16.67%; flex: 1 1 auto; min-width: 0; height: 44px; margin: 0; background: transparent; cursor: pointer; -webkit-appearance: none; appearance: none; }
.size-range::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; background: linear-gradient(to right, var(--accent) var(--fill), var(--surface-3) var(--fill)); border: 1px solid var(--line); }
.size-range::-moz-range-track { height: 6px; border-radius: 999px; background: var(--surface-3); border: 1px solid var(--line); }
.size-range::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--accent); }
.size-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 22px; height: 22px; margin-top: -9px; border-radius: 50%; border: 2px solid var(--surface-2); background: var(--accent); box-shadow: 0 1px 4px rgba(0, 0, 0, .25); }
.size-range::-moz-range-thumb { width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--surface-2); background: var(--accent); box-shadow: 0 1px 4px rgba(0, 0, 0, .25); }
.size-range:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 3px; border-radius: 8px; }

/* The preview is set like the app: in the chosen font at the root size, and
   everything inside it in em so it follows. */
.text-preview { margin: 0; display: grid; gap: 8px; min-width: 0; font-family: var(--font-ui); font-size: var(--text-size); }
.text-preview-card { padding: 1.1em 1.15em 1.2em; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); }
.text-preview-card p { margin: 0; }
.preview-kicker { color: var(--accent); font-size: .86em; font-weight: 800; letter-spacing: .08em; font-variant-caps: all-small-caps; }
.preview-title { margin-top: .2em !important; font-size: 1.35em; font-weight: 760; letter-spacing: -.02em; line-height: 1.2; }
.preview-body { margin-top: .45em !important; color: var(--muted); font-size: .86em; line-height: 1.55; }
.preview-cells { display: flex; margin: 1em 0; width: max-content; border: 2px solid var(--grid); background: var(--grid); gap: 1px; font-family: var(--font-digits); font-variant-numeric: tabular-nums; }
.preview-cell { display: grid; place-items: center; width: 2.6em; height: 2.6em; background: var(--surface-2); color: var(--text); font-size: 1em; line-height: 1; }
.preview-given { font-size: 1.45em; width: 1.79em; height: 1.79em; font-weight: 760; }
.preview-selected { position: relative; font-size: 1.45em; width: 1.79em; height: 1.79em; font-weight: 900; background: var(--selected-bg); color: var(--on-accent); }
.preview-notes { grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); padding: .2em; place-items: center; }
.preview-notes i { font-style: normal; font-size: .58em; font-weight: 650; color: var(--muted); }
.preview-button { display: inline-flex; align-items: center; min-height: 2.6em; padding: .6em 1em; font-size: .8em; cursor: default; }
.text-preview figcaption { margin: 0; }

/* --- Help ---------------------------------------------------------------- */

.help-pages { border: 1px solid var(--line); border-radius: 16px; background: var(--surface-2); padding: 24px; min-height: 260px; }
.help-page h3 { margin: 0 0 12px; font-size: 1.2rem; letter-spacing: -.025em; }
.help-page p { color: var(--muted); font-size: .86rem; line-height: 1.65; margin: 0 0 12px; }
.help-page strong { color: var(--text); }
.help-page em { color: var(--accent-2); font-style: normal; font-weight: 700; }
/* Seven techniques will not fit a help page, so this one scrolls inside itself
   rather than pushing the pager off the bottom of the screen. */
.pattern-library { max-height: 56vh; overflow-y: auto; padding-right: 6px; }
.pattern-entry + .pattern-entry { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--line); }
.pattern-entry h4 { margin: 0 0 8px; font-size: .95rem; color: var(--accent-2); }
.pattern-entry p { color: var(--muted); font-size: .82rem; line-height: 1.65; margin: 0 0 10px; }
.help-nav { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-top: 16px; }
.help-nav button { min-height: 44px; }
.help-nav button:disabled { opacity: .35; cursor: default; }
.help-dots { display: flex; gap: 8px; flex: 0 0 auto; }
.help-dot { width: 22px; height: 22px; padding: 0; border: 0; background: transparent; cursor: pointer; display: grid; place-items: center; }
.help-dot::before { content: ""; width: 12px; height: 12px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface-3); }
.help-dot.active::before { background: var(--accent); border-color: var(--accent); }

/* --- About --------------------------------------------------------------- */

.about-card { border: 1px solid var(--line); border-radius: 16px; background: var(--surface-2); padding: 24px; }
.about-card h3 { margin: 0 0 12px; font-size: 1.3rem; letter-spacing: -.03em; }
.about-card p { color: var(--muted); font-size: .86rem; line-height: 1.65; margin: 0 0 12px; }
.about-build { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem !important; }
.about-privacy { border-left: 3px solid var(--accent-2); padding: 11px 13px; border-radius: 0 9px 9px 0; background: color-mix(in srgb, var(--accent-2) 10%, transparent); margin-bottom: 0 !important; }
.about-privacy strong { color: var(--text); }
.about-links { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 16px; }
.about-links button { min-height: 44px; }
.legal-copy { max-width: 760px; margin: 0 auto; }
.legal-copy h2 { color: var(--text); font-size: max(1.12rem, calc(var(--text-size, 16px) * 1.12)); margin: 1.5em 0 .55em; }
.legal-copy h2:first-child { margin-top: 0; }
.legal-copy p { font-size: max(.92rem, var(--text-size, 16px)); line-height: 1.65; overflow-wrap: anywhere; }
.legal-copy button { min-height: 44px; margin: 0 8px 8px 0; }
.notice-entry { border-top: 1px solid var(--line); padding: 12px 0; }
.notice-entry summary { cursor: pointer; color: var(--text); font-weight: 700; line-height: 1.5; padding: 8px 2px; }
.notice-entry summary:focus-visible, .legal-copy button:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.notice-entry pre { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 55vh; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em; line-height: 1.6; padding: 14px; background: var(--surface-3); border-radius: 8px; }
.notice-source { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .75rem !important; }
.public-policy { max-width: 800px; margin: 0 auto; padding: 32px max(16px, env(safe-area-inset-left)) 48px; }
.public-policy h1 { margin: 0 0 22px; }
.legal-return { max-width: 760px; margin: 16px auto 30px; }
.legal-return button { min-height: 44px; }

/* --- Selection pulse ------------------------------------------------------ */

@keyframes cell-pulse {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 70%, transparent); }
  to { box-shadow: 0 0 0 14px color-mix(in srgb, var(--accent) 0%, transparent); }
}
.cell.selected.pulse { animation: cell-pulse 420ms ease-out 1; }
@media (prefers-reduced-motion: reduce) { .cell.selected.pulse { animation: none; } }

.number-pad button { position: relative; }

/* --- Coach: a drawer in landscape, a sheet anywhere else ------------------- */

/* The coach never takes a column beside the board, at any width, so opening
   or closing it never moves or resizes the board. Collapsed, it is gone; the
   toolbar's Hint (or Drill) button brings it back. */
.workspace.coach-collapsed { grid-template-columns: minmax(0, 1fr); }
.workspace.coach-collapsed .coach-panel { display: none; }
.workspace.coach-collapsed .board-column { border-right: 0; border-bottom: 0; }
#screen-game .workspace { grid-template-columns: minmax(0, 1fr); position: relative; }
#screen-game .board-column { border-right: 0; border-bottom: 0; }

.coach-open {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 12px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
  color: var(--text);
  font-size: .78rem;
  font-weight: 760;
  cursor: pointer;
  flex: 0 0 auto;
}
.coach-open svg { color: color-mix(in srgb, var(--accent) 60%, var(--text)); }
.coach-open[aria-expanded="true"] { background: color-mix(in srgb, var(--accent) 28%, var(--surface-2)); border-color: var(--accent); }
.sheet-close {
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--muted);
  cursor: pointer;
}
.sheet-close:hover { color: var(--text); }
.coach-heading-row { align-items: center; gap: 12px; }
.coach-heading-row .eyebrow { margin-bottom: 4px; }

.hint-filter { margin: 18px 0 0; border-top: 1px solid var(--line); padding-top: 14px; }
.hint-filter summary { cursor: pointer; color: var(--muted); font-size: .76rem; font-weight: 700; list-style-position: inside; min-height: 32px; }
.hint-filter summary span { color: var(--text); margin-left: 4px; }
.hint-filter .technique-filter { margin: 10px 0 0; }
.coach-panel .glossary-panel { margin-top: 14px; }

/* The sheet: wherever the board is stacked (portrait, phones), the coach slides
   up over the lower part of the screen. The query is the landscape layout's,
   negated, so exactly one of the two applies. */
@media not all and (min-aspect-ratio: 11/10), not all and (min-width: 700px) {
  .workspace .coach-panel {
    position: fixed;
    z-index: 45;
    left: 0;
    right: 0;
    bottom: 0;
    /* Tall enough to read a hint, short enough that the board above it — about
       one screen-width tall on a phone — stays in view while it is read. */
    max-height: max(280px, calc(100vh - 100vw - 76px));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 10px 18px calc(20px + env(safe-area-inset-bottom));
    background: var(--surface-2);
    border-top: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -20px 50px rgba(0, 0, 0, .35);
    animation: sheet-up .22s cubic-bezier(.2, .9, .3, 1);
  }
  @supports (height: 100dvh) { .workspace .coach-panel { max-height: max(280px, calc(100dvh - 100vw - 76px)); } }
  .workspace .coach-panel .hint-card { min-height: 0; flex: none; padding: 18px; }
  /* The grab handle: a sheet reads as one at a glance. */
  .coach-panel::before { content: ""; display: block; width: 40px; height: 5px; margin: 0 auto 10px; border-radius: 3px; background: var(--line); }
  .coach-panel .coach-heading-row h2 { font-size: 1.35rem; }
  .board-column { border-bottom: 0; }
}
@keyframes sheet-up { from { transform: translateY(40%); opacity: 0; } }
@keyframes drawer-in { from { transform: translateX(-18px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .coach-panel { animation: none; } }

/* --- Narrow screens ------------------------------------------------------- */

@media (max-width: 900px) {
  .screen-bar { padding-top: 4px; }
  .stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

@media (max-width: 560px) {
  .large-title { font-size: 2.1rem; }
  .home-row { min-height: 68px; padding: 13px 15px; }
  .screen-bar { padding: 2px 0 18px; margin-bottom: 0; }
  .option-body { padding: 13px; }
  .option-list { padding: 5px; }
  .option-row { gap: 10px; padding: 9px; }
  .help-pages, .about-card { padding: 18px; }
  .palette-grid { grid-template-columns: 1fr 1fr; }
  .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat-value { font-size: 1.5rem; }
  /* At 360px a long word must wrap rather than widen the page. */
  h1, h2, h3, p, .home-row-desc, .option-note, .stat-note { overflow-wrap: anywhere; }
  .about-links button, .help-nav button { flex: 1 1 auto; }
  /* Five 44px dots and two buttons do not fit one phone row (it overflowed even
     at the default text size): the dots take their own row above the buttons. */
  .help-nav { flex-wrap: wrap; row-gap: 8px; }
  .help-dots { order: -1; flex: 1 1 100%; justify-content: center; }
  .help-nav button { white-space: nowrap; }
}

/* Measured in Chromium at 360 and 768 with touch emulation: these were the
   controls still landing under Apple's 44px minimum. */
@media (pointer: coarse) {
  .back-button, .home-row, .option-row, .palette-card { min-height: 44px; }
  .icon-button { width: 44px; height: 44px; }
  .quiet-button, .text-button, .secondary-button { min-height: 44px; min-width: 44px; }
  .help-dot { width: 44px; height: 44px; flex: 0 0 auto; }
  .help-dot::before { width: 14px; height: 14px; }
  /* Coach, pencil, undo and redo were the smallest targets on the board. The
     glyphs double and the labels drop, so all four read as icons of one size. */
  .history-controls { --toolbar-icon: 30px; gap: 8px; }
  .history-controls .text-button { min-width: 52px; min-height: 52px; padding: 6px; justify-content: center; }
  .history-controls .history-label { display: none; }
}

/* At phone width nine buttons across cannot each be 44px wide, so the pad wraps
   to two rows rather than shipping targets too small to hit. */
@media (max-width: 430px) {
  .number-pad { grid-template-columns: repeat(5, 1fr); gap: 6px; }
  .number-pad button { aspect-ratio: auto; min-height: 50px; font-size: 17.6px; }
  .board-column, .coach-panel { padding: 12px; }
  .board-actions { gap: 6px; }
  .board-actions button { flex: 1 1 auto; }
}

/* On a phone the game screen starts at the board. */
@media (max-width: 900px) {
  #screen-game .workspace { margin-top: 18px; }
}

/* --- Refused duplicate ----------------------------------------------------- */

/* A number the selected cell cannot take is masked on the pad: dimmed like a
   finished digit, and struck through so the two read differently. */
.number-pad button.blocked { position: relative; overflow: hidden; }
.number-pad button.blocked::after {
  content: "";
  position: absolute;
  left: 18%;
  right: 18%;
  top: 50%;
  border-top: 2px solid currentColor;
  transform: rotate(-35deg);
  pointer-events: none;
}

/* The cell that already holds the number the player just tried to enter. It
   has to win over the row/column/box shading it usually sits in. */
.sudoku-board .cell.clash { background: color-mix(in srgb, var(--danger) 30%, var(--surface-2)); box-shadow: inset 0 0 0 3px var(--danger); z-index: 1; animation: clash-nudge 320ms ease-out 1; }
.sudoku-board .cell.clash .value { color: var(--text); }
@keyframes clash-nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-3px); }
  75% { transform: translateX(3px); }
}
@media (prefers-reduced-motion: reduce) { .sudoku-board .cell.clash { animation: none; } }

/* A wrong number is refused rather than placed. The cell it was tried in stays
   selected but trades the accent frame for a red one until the next action,
   and gives the clash's shake once, on the refusal itself. */
.sudoku-board .cell.wrong { background: color-mix(in srgb, var(--danger) 24%, var(--selected-bg)); }
.sudoku-board .cell.selected.wrong::after { border-color: var(--danger); box-shadow: 0 0 14px color-mix(in srgb, var(--danger) 55%, transparent); }
.sudoku-board .cell.wrong:not(.selected) { box-shadow: inset 0 0 0 3px var(--danger); z-index: 1; }
.sudoku-board .cell.wrong.nudge { animation: clash-nudge 320ms ease-out 1; }
@media (prefers-reduced-motion: reduce) { .sudoku-board .cell.wrong.nudge { animation: none; } }

/* Drill loader and reveal. While a drill builds (a Full Sudoku journey can take
   a few seconds) every tile flips in a diagonal wave, --wave being row + column.
   When the drill lands each cell flips once to show its face. The build runs in
   a worker, so the page is free to animate the whole time. */
.sudoku-board {
  --flip-back: color-mix(in srgb, var(--accent) 34%, var(--surface-2));
  --flip-field: color-mix(in srgb, var(--grid) 22%, var(--surface-2));
  /* Speed of the effect, in one place. The reveal is one flip per cell, each
     starting --flip-stagger after the diagonal before it; the loader repeats
     a flip every --loop-duration. Slow enough to watch a tile turn over. */
  --flip-duration: 1.5s;
  --flip-stagger: 45ms;
  --loop-duration: 2.4s;
  --loop-stagger: 90ms;
  --flip-ease: cubic-bezier(.45, 0, .55, 1);
}
/* Mid-flip a tile is edge-on and the grid colour behind it shows. At full
   strength that reads as bright bars, so the field darkens while tiles turn and
   the grid lines come back as the reveal settles. */
.sudoku-board.is-loading { pointer-events: none; background: var(--flip-field); }
.sudoku-board.flip-in { animation: board-lines-in calc(var(--flip-duration) + 16 * var(--flip-stagger)) ease-in backwards; }
.sudoku-board.is-loading .cell > * { visibility: hidden; }
.sudoku-board.is-loading .cell {
  animation: cell-flip-loop var(--loop-duration) var(--flip-ease) infinite;
  animation-delay: calc(var(--wave, 0) * var(--loop-stagger));
}
.sudoku-board.flip-in .cell {
  animation: cell-flip-in var(--flip-duration) var(--flip-ease) backwards;
  animation-delay: calc(var(--wave, 0) * var(--flip-stagger));
}
/* The face (digits and marks) appears only once the cell has turned past edge-on,
   so nothing is ever seen mirrored. --flip-ease is symmetric, so edge-on is
   exactly halfway through, which is where cell-face-in switches. */
.sudoku-board.flip-in .cell > * {
  animation: cell-face-in var(--flip-duration) linear backwards;
  animation-delay: calc(var(--wave, 0) * var(--flip-stagger));
}
@keyframes cell-flip-loop {
  0%, 15% { transform: perspective(600px) rotateY(0deg); }
  50%, 65% { transform: perspective(600px) rotateY(180deg); background-color: var(--flip-back); }
  100% { transform: perspective(600px) rotateY(360deg); }
}
@keyframes cell-flip-in {
  0% { transform: perspective(600px) rotateY(-180deg); background-color: var(--flip-back); }
  45% { background-color: var(--flip-back); }
  100% { transform: perspective(600px) rotateY(0deg); }
}
@keyframes cell-face-in {
  0%, 49% { opacity: 0; }
  50%, 100% { opacity: 1; }
}
@keyframes board-lines-in {
  0%, 55% { background-color: var(--flip-field); }
  100% { background-color: var(--grid); }
}
@keyframes cell-loading-tint {
  0%, 100% { background-color: var(--surface-2); }
  50% { background-color: var(--flip-back); }
}
/* A Play deal under way: the board in front of the player stays, its numbers
   dimmed, with a slow colour ripple to say the new one is coming. It stays
   playable; nothing on it changes until the new board lands. */
.sudoku-board.is-dealing { position: relative; }
.sudoku-board.is-dealing .cell > * { opacity: .45; transition: opacity .2s; }
.sudoku-board.is-dealing .cell {
  animation: cell-loading-tint var(--loop-duration) ease-in-out infinite;
  animation-delay: calc(var(--wave, 0) * var(--loop-stagger));
}
/* Said on the board itself: playing, the status line is kept for screen
   readers only. Taps pass through to the cells. */
.sudoku-board.is-dealing::after {
  content: attr(data-dealing);
  position: absolute; left: 50%; top: 50%; z-index: 4;
  transform: translate(-50%, -50%);
  max-width: calc(100% - 24px); padding: 10px 18px; border-radius: 999px;
  background: var(--surface); color: var(--text); border: 1px solid var(--line);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .35);
  font-size: .9rem; font-weight: 700; text-align: center; white-space: nowrap;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .sudoku-board.flip-in, .sudoku-board.flip-in .cell, .sudoku-board.flip-in .cell > * { animation: none; }
  /* Still say "working", as a colour ripple rather than movement. */
  .sudoku-board.is-loading .cell { animation-name: cell-loading-tint; }
}

/* ==========================================================================
   Play-only view
   With the coach put away the board is the whole screen. The numbers always sit
   in one row directly under the board, in either tablet orientation; the board
   takes every pixel left after that row, sized from the width and height that
   are actually there rather than a fixed cap.
   ========================================================================== */

/* With the coach put away (or, on a phone, lying over the board as a sheet)
   the chrome around the board slims down and the board takes the room. */
#screen-game.play-only .workspace { margin-top: 10px; }
main:has(> #screen-game.play-only:not([hidden])) { width: min(1280px, calc(100% - 24px)); padding-bottom: 12px; }
body:has(#screen-game.play-only:not([hidden])) .topbar {
  width: min(1280px, calc(100% - 24px));
  height: calc(60px + env(safe-area-inset-top));
}

#screen-game.play-only .board-column {
  --topbar-h: 60px;
  --col-pad: 12px;
  --sheet-h: 0px;
  /* Pad buttons are a little wider than tall, so the row costs less height. */
  --pad-gap: 7px;
  --pad-aspect: 1.2;
  /* Board + number row = board × (1 + 1 / (9 × aspect)), plus the gap. */
  --pad-ratio: 1.1;
  /* Everything between the top of the viewport and the bottom of the board
     column that is not board: top bar, card margin and border, and padding.
     --sheet-h is room kept for anything docked under the board; the coach
     sheet lies over the board, so nothing is docked today. */
  --play-h: calc(100vh - var(--topbar-h) - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 24px - 2 * var(--col-pad) - var(--sheet-h));
  /* Stacked: toolbar above, action buttons below the numbers. */
  --stack-fixed: 116px;
  /* The mistakes row above the board, while it is showing. */
  --counter-h: 0px;
  /* --coord-w / --coord-g: room for the board coordinates, when they are on;
     --bt-h: the board view panel, while it is open under the board
     (board-tools.mjs). All three are nothing unless those are showing. */
  --board-size: max(240px, min(100cqi - 2 * var(--col-pad) - var(--coord-w, 0px), (var(--play-h) - var(--stack-fixed) - var(--counter-h) - var(--coord-g, 0px) - var(--bt-h, 0px)) / var(--pad-ratio)));
  container-type: inline-size;
  padding: var(--col-pad);
}
@supports (height: 100dvh) {
  /* dvh follows Safari's toolbar as it slides in and out; vh would not. */
  #screen-game.play-only .board-column {
    --play-h: calc(100dvh - var(--topbar-h) - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 24px - 2 * var(--col-pad) - var(--sheet-h));
  }
}

/* Every piece of the board column is exactly as wide as the board, so the
   number row, toolbar and buttons line up with its edges. */
#screen-game.play-only .sudoku-board,
#screen-game.play-only .board-toolbar,
#screen-game.play-only .number-pad,
#screen-game.play-only .board-footer,
#screen-game.play-only .board-status,
#screen-game.play-only .mistake-counter,
#screen-game.play-only .game-feedback { width: var(--board-size); margin-inline: auto; }
#screen-game.play-only .board-toolbar { flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
/* Playing, the status line under the board is not shown: the board takes its
   room. It stays in the page as the live region, so a screen reader still
   hears every status change. */
#screen-game.play-only .board-column > .board-status {
  position: absolute; width: 1px; height: 1px; min-height: 0; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* Stacked, the mistakes row costs board height, so the board gives it room. */
#screen-game.play-only .board-column:has(> .mistake-counter:not(.hidden)) { --counter-h: 50px; }

/* A refused move is a passing notice. Stacked, it floats over the top of the
   board instead of opening a row that would shove the board down the screen;
   taps pass through it to the cells, and the next action clears it. */
#screen-game.play-only .board-column { position: relative; --toolbar-h: 34px; }
#screen-game.play-only .game-feedback {
  position: absolute;
  z-index: 5;
  top: calc(var(--col-pad) + var(--toolbar-h) + 22px);
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--board-size) - 24px);
  margin: 0;
  pointer-events: none;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .35), 0 0 0 4px color-mix(in srgb, var(--danger) 16%, transparent);
}

/* Digits and pencil marks scale with the board, not the window, so a bigger
   board reads as bigger rather than as the same digits with more padding. */
#screen-game.play-only .sudoku-board { container-type: inline-size; }
#screen-game.play-only .value { font-size: max(19.2px, 100cqi / 9 * .58); }
#screen-game.play-only .candidate { font-size: max(8px, 100cqi / 9 * .21); }

/* One row of nine, the same width as the board, keys growing with it. */
#screen-game.play-only .number-pad { grid-template-columns: repeat(9, 1fr); gap: var(--pad-gap); margin-top: 12px; }
#screen-game.play-only .number-pad button {
  aspect-ratio: var(--pad-aspect);
  min-height: 0;
  border-radius: 10px;
  font-size: max(16px, var(--board-size) / 9 * .42);
}
/* placeHistoryControls (app.mjs) measures the row with every label on one line
   (.measuring). Once placed, a label too long for its key at a large text size
   wraps instead of spilling out of it. */
#screen-game.play-only .board-actions button { line-height: 1.15; text-wrap: balance; }
#screen-game.play-only .board-footer.measuring .board-actions button { white-space: nowrap; }

@media (pointer: coarse) {
  #screen-game.play-only .board-column { --stack-fixed: 136px; --toolbar-h: 52px; }
}
@media (max-width: 560px) {
  body:has(#screen-game.play-only:not([hidden])) .topbar { height: calc(56px + env(safe-area-inset-top)); }
  #screen-game.play-only .board-column { --topbar-h: 56px; --col-pad: 10px; }
  /* A little tighter under the numbers, so a large phone (414–430) still has
     the board tools beside the actions rather than up in the toolbar. */
  #screen-game.play-only .board-footer .history-controls { gap: 2px; }
  #screen-game.play-only .board-footer .board-actions button { padding-inline: 12px; }
}
@media (max-width: 430px) {
  /* Nine keys across a phone would each be under Apple's 44px minimum, so on a
     phone — not a tablet — the row wraps to two. */
  #screen-game.play-only .board-column { --stack-fixed: 306px; --pad-ratio: 1; }
  #screen-game.play-only .number-pad { grid-template-columns: repeat(5, 1fr); }
  #screen-game.play-only .number-pad button { aspect-ratio: auto; min-height: 50px; }
}

/* Landscape (iPad on its side, laptop, desktop): height is the scarce side, so
   nothing stays above or below the board. Everything else — the toolbar, the
   number pad as a 3 × 3 keypad, the actions and the status line — shares one
   column to the left of it, and the board takes the full height that leaves.
   One column rather than a margin on each side: the width a second margin
   would waste goes to the board instead. The coach is a drawer over that
   column, so the board stays put whether it is open or not. */
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  #screen-game.play-only .board-column {
    --side: clamp(232px, 24vw, 330px);
    --side-gap: clamp(16px, 2.4vw, 36px);
    --board-size: max(260px, min(100cqi - 2 * var(--col-pad) - var(--side) - var(--side-gap) - var(--coord-w, 0px), var(--play-h) - 4px - var(--coord-g, 0px)));
    display: grid;
    grid-template-columns: var(--side) auto;
    grid-template-rows: repeat(4, auto) 1fr repeat(3, auto);
    grid-template-areas:
      "tools board"
      "feedback board"
      "mistakes board"
      "lesson board"
      ". board"
      "pad board"
      "actions board"
      "status board";
    column-gap: var(--side-gap);
    justify-content: center;
    align-items: start;
  }
  #screen-game.play-only .sudoku-board { grid-area: board; align-self: start; margin: 0; }
  #screen-game.play-only .board-toolbar { grid-area: tools; }
  #screen-game.play-only .game-feedback { grid-area: feedback; }
  #screen-game.play-only .mistake-counter { grid-area: mistakes; }
  #screen-game.play-only .lesson-panel { grid-area: lesson; }
  #screen-game.play-only .number-pad { grid-area: pad; }
  #screen-game.play-only .board-footer { grid-area: actions; }
  #screen-game.play-only .board-status { grid-area: status; }

  #screen-game.play-only .board-toolbar,
  #screen-game.play-only .number-pad,
  #screen-game.play-only .board-footer,
  #screen-game.play-only .board-status,
  #screen-game.play-only .mistake-counter,
  #screen-game.play-only .game-feedback,
  #screen-game.play-only .lesson-panel { width: 100%; margin-inline: 0; }

  /* Top of the column: the way back with Hint and the theme on one row. */
  #screen-game.play-only .board-toolbar { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 8px; margin: 0 0 12px; }
  #screen-game.play-only .board-toolbar .play-menu { order: 0; }
  #screen-game.play-only .board-toolbar .coach-open { order: 1; margin-left: auto; }
  #screen-game.play-only .board-toolbar .play-theme { order: 2; }
  #screen-game.play-only .board-toolbar .segmented { order: 3; flex: 1 1 100%; }
  #screen-game.play-only .board-toolbar .segmented button { flex: 1; }

  /* The keypad: three by three, square keys as wide as the column allows. */
  #screen-game.play-only .number-pad { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 12px 0 0; }
  #screen-game.play-only .number-pad button { aspect-ratio: 1.15; font-size: max(17.6px, var(--side) / 3 * .38); }

  /* Under the keypad: the board tools spread across one row, the actions two
     by two under them, then the status line. The column is too narrow for the
     tools beside the actions, so they stack. */
  #screen-game.play-only .board-footer { flex-direction: column; align-items: stretch; gap: 8px; margin: 10px 0 0; }
  /* The pencil, undo and redo are keys in their own right here: a row under
     the keypad on the same columns, gap and key face, so all twelve read as
     one pad. Icons only, sized like the digits; the words are in the labels. */
  #screen-game.play-only .board-footer .history-controls {
    order: -1; margin: 0;
    display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px;
    --toolbar-icon: clamp(24px, var(--side) / 3 * .32, 38px);
  }
  #screen-game.play-only .board-footer .history-controls .text-button {
    min-width: 0; min-height: 0; aspect-ratio: 1.6; padding: 0;
    display: grid; place-items: center;
    border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); color: var(--text);
  }
  #screen-game.play-only .board-footer .history-controls .text-button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); }
  #screen-game.play-only .board-footer .history-controls .text-button:disabled { opacity: .28; filter: grayscale(1); background: var(--surface-3); }
  #screen-game.play-only .board-footer .history-controls .icon-toggle[aria-pressed="true"]:not(:disabled) { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: color-mix(in srgb, var(--accent) 16%, var(--surface-2)); }
  #screen-game.play-only .board-footer .history-controls .history-label { display: none; }
  #screen-game.play-only .board-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  #screen-game.play-only .board-actions button { padding-inline: 8px; }
  #screen-game.play-only .game-feedback,
  #screen-game.play-only .mistake-counter { margin: 0 0 10px; }
  /* Landscape has room in the column, so the notice sits there. */
  #screen-game.play-only .game-feedback { position: static; transform: none; width: 100%; pointer-events: auto; }
  #screen-game.play-only .lesson-panel { margin: 0 0 10px; }

  /* The coach: a drawer over the controls column, from under the toolbar to
     the foot of the board, so the toolbar's Hint button stays in reach to put
     it away. Its box is measured from the column (placeCoachDrawer, app.mjs). */
  #screen-game .workspace .coach-panel {
    position: absolute;
    z-index: 30;
    left: var(--drawer-left, 12px);
    top: var(--drawer-top, 12px);
    width: var(--drawer-width, 300px);
    height: var(--drawer-height, calc(100% - 24px));
    padding: 16px 16px 18px;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--surface-2);
    border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
    border-radius: 16px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .35);
    animation: drawer-in .2s cubic-bezier(.2, .9, .3, 1);
  }
  #screen-game .workspace .coach-panel .coach-heading-row h2 { font-size: 1.35rem; }
  #screen-game .workspace .coach-panel .hint-card { min-height: 0; flex: none; padding: 16px; }
  #screen-game .workspace .coach-panel .glossary-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) { #screen-game .workspace .coach-panel { animation: none; } }

/* --- Explain on the board -------------------------------------------------- */

/* A walkthrough plays on the board itself. Everything it is not talking about
   steps back, so the handful of candidates in the current sentence are the
   only things at full strength. */
.sudoku-board.lesson { position: relative; }
.sudoku-board.lesson .cell { cursor: default; }
.sudoku-board.lesson .candidate { opacity: .32; }
.sudoku-board.lesson .candidate[class*="lesson-"] { opacity: 1; }
.cell.lesson-band-a { background: color-mix(in srgb, var(--pattern) 15%, var(--surface-2)); }
.cell.lesson-band-b { background: color-mix(in srgb, var(--link) 14%, var(--surface-2)); }
.cell.lesson-cell-key { box-shadow: inset 0 0 0 2px var(--muted); }
.cell.lesson-cell-remove { background: color-mix(in srgb, var(--danger) 16%, var(--surface-2)); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--danger) 70%, transparent); }
/* The cell a step lands on, and the cells a step rules out (hatched: "not here"). */
.cell.lesson-cell-a { box-shadow: inset 0 0 0 2.5px var(--pattern); }
.cell.lesson-cell-off { box-shadow: inset 0 0 0 1.5px var(--faint); background-image: repeating-linear-gradient(135deg, transparent 0 5px, color-mix(in srgb, var(--faint) 40%, transparent) 5px 6px); }
/* One vocabulary for the board and the sentence beside it: outlined is "look
   here", the two fills are the two cases, struck is what a case rules out. */
.candidate.lesson-key, .lx-key { color: var(--text); box-shadow: inset 0 0 0 1.5px var(--muted); }
.candidate.lesson-key { font-weight: 850; box-shadow: inset 0 0 0 1.5px var(--text); }
.candidate.lesson-a, .lx-a { color: var(--on-accent); background: var(--pattern); }
.candidate.lesson-b, .lx-b { color: var(--on-success); background: var(--link); }
.candidate.lesson-off, .lx-off { color: var(--muted); text-decoration: line-through; box-shadow: inset 0 0 0 1px var(--faint); }
.candidate.lesson-remove, .lx-remove { color: var(--on-accent); background: var(--danger); }
.candidate.lesson-remove { text-decoration: line-through; box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 22%, transparent); }
/* A placement a walkthrough has proved: written in, in the colour of the case
   that is now certain. */
.value.lesson-place { color: var(--pattern); font-weight: 820; }
.cell.lesson-placed { box-shadow: inset 0 0 0 2.5px var(--pattern); }
/* A drill's answer pad. The caption names the cell the keys work on and sits
   above them in every layout; it comes last in the markup so the keys stay
   children 1 to 9. A key whose candidate is marked is pressed, in the accent of
   the marked candidate on the board: a mark, not a verdict. */
.number-pad .pad-caption {
  grid-column: 1 / -1;
  order: -1;
  margin: 0 0 4px;
  color: var(--muted);
  font-size: .84rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
}
.number-pad button.picked { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.number-pad button.picked:hover { color: var(--on-accent); }
:root.tactile #screen-game.play-only .number-pad button.picked:not(:disabled) {
  color: var(--on-accent);
  text-shadow: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 80%, #fff), var(--accent));
}
.hint-card p.pad-reach { margin-top: 8px; font-size: .78rem; }

/* A placement drill's answer: proposed in the cell, never entered on the grid. */
.value.proposed-value { color: var(--accent); font-weight: 760; }
.cell.proposed { box-shadow: inset 0 0 0 2.5px var(--accent); }
.cell.proposed.answer-correct { box-shadow: inset 0 0 0 2.5px var(--success); }
.cell.proposed.answer-correct .value { color: var(--success); }
.cell.proposed.answer-wrong { box-shadow: inset 0 0 0 2.5px var(--danger); }
.cell.proposed.answer-wrong .value { color: var(--danger); }
/* A candidate-only board says it is a picture of the pattern, not a Sudoku. */
.hint-card p.teaching-note { margin: 8px 0 0; padding-left: 8px; border-left: 3px solid var(--faint); color: var(--muted); font-size: .8rem; line-height: 1.45; }
.lesson-links { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.lesson-links svg { display: block; width: 100%; height: 100%; overflow: visible; }
.lesson-links line { stroke-width: 2.5px; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.lesson-links .ll-key { stroke: var(--muted); }
.lesson-links .ll-a { stroke: var(--pattern); }
.lesson-links .ll-b { stroke: var(--link); }
.lesson-links .ll-remove { stroke: var(--danger); stroke-dasharray: 5 5; }
.lesson-links .ll-off { stroke: var(--muted); }
/* A weak link: dotted, so a chain's strong (solid) and weak links never look alike. */
.lesson-links .ll-weak { stroke-dasharray: 1 5; }

.lesson-panel { width: min(100%, 600px); margin: 14px auto 0; padding: 16px 18px 18px; border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line)); border-radius: 14px; background: var(--surface-2); scroll-margin-bottom: 12px; }
.lesson-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.lesson-head .hint-kicker { margin: 0; }
.lesson-panel h3 { margin: 0 0 8px; font-size: 1.14rem; letter-spacing: -.02em; }
/* Room for the longest step, so Back and Next do not move under the pointer. */
.lesson-panel p { margin: 0; min-height: 6.5em; color: var(--text); font-size: .86rem; line-height: 1.7; }
.lesson-nav { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 12px; margin-top: 14px; }
.lesson-nav button { min-height: 42px; padding-inline: 16px; }
.lesson-nav button:disabled { opacity: .4; cursor: default; }
.lesson-dots { display: flex; justify-content: center; gap: 7px; }
.lesson-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); opacity: .5; }
.lesson-dots i.done { background: var(--accent); opacity: .5; }
.lesson-dots i.on { background: var(--accent); opacity: 1; transform: scale(1.35); }
/* In a narrow column (the side of the landscape tactile board is 140-220px)
   Back · dots · Next cannot share a row: the buttons wrapped and Next was
   pushed out past the panel, under the board. There the step dots go on top
   and the two buttons stack full width, Next first. */
.lesson-panel { container-type: inline-size; }
@container (max-width: 260px) {
  .lesson-head { align-items: flex-start; }
  #lesson-close { flex: none; white-space: nowrap; }
  .lesson-nav { grid-template-columns: minmax(0, 1fr); grid-template-areas: "dots" "next" "back"; gap: 8px; }
  .lesson-dots { grid-area: dots; padding-block: 2px 4px; }
  #lesson-next { grid-area: next; }
  #lesson-prev { grid-area: back; }
  .lesson-nav button { width: 100%; padding-inline: 10px; text-wrap: balance; }
}
.lx { padding: 1px 5px; border-radius: 5px; font-size: .92em; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
.lx-digit { color: var(--text); font-weight: 900; }
@media (max-width: 560px) {
  .lesson-panel { padding: 14px; }
  .lesson-panel p { min-height: 8.5em; }
}
.hint-card p.lesson-kept { margin-top: 10px; }

/* --- Game: the header lives inside the card -------------------------------- */

/* On the game screen the page header and the tabs are put away: the way back
   and the theme switch join the board's own toolbar, so nothing stands between
   the top of the screen and the board. */
body:has(#screen-game:not([hidden])) .topbar { display: none; }
#screen-game { padding-top: env(safe-area-inset-top); }
#screen-game .workspace { margin-top: 20px; }
#screen-game.play-only .workspace { margin-top: 10px; }
#screen-game.play-only .board-column { --topbar-h: 0px; }
/* The board column sizes the board from 100cqi. A container cannot measure
   against itself, so the panel is the container; without it the column fell
   back to the window's width, and on a window wider than the 1280px panel the
   side columns were pushed out of the panel and clipped. */
#screen-game.play-only .workspace { container-type: inline-size; }

.board-toolbar { flex-wrap: wrap; gap: 8px; }
/* Hint and the theme keep to the right; the board tools join them there only
   on a screen too narrow to have them under the numbers. */
.board-toolbar .coach-open { margin-left: auto; }
.board-toolbar .history-controls { margin-left: auto; }
.board-toolbar .history-controls + .coach-open { margin-left: 0; }
.play-menu { display: inline-flex; align-items: center; gap: 6px; }
.play-theme { background: var(--surface-2); color: var(--text); }


@media (max-width: 900px) {
  /* The coach is a sheet over the board here, opened from the toolbar. */
  #screen-game .workspace { margin-top: 10px; }
}
@media (max-width: 560px) {
  /* Menu, four board controls and the theme switch share one row on a phone,
     so the menu button keeps its arrow and drops its word. */
  .play-menu-label { display: none; }
  .play-menu { justify-content: center; min-width: 44px; padding: 0; font-size: 1.25rem; }
  .board-toolbar { gap: 4px; }
}

/* --- Play edge to edge on a tablet or phone -------------------------------- */

/* On a touch screen (or any narrow window) the card around Play is only a
   frame taking pixels from the board: an outer margin, a border, rounded
   corners and a wide padding on every side. Here Play is the page itself, flush
   to the screen edges apart from the device's own safe areas, and the board is
   sized against the whole height that leaves. */
@media (pointer: coarse), (max-width: 900px) {
  main:has(> #screen-game:not([hidden])),
  main:has(> #screen-game.play-only:not([hidden])) { width: 100%; }
  main:has(> #screen-game.play-only:not([hidden])) { padding-bottom: 0; }
  #screen-game .workspace,
  #screen-game.play-only .workspace { margin-top: 0; border: 0; border-radius: 0; box-shadow: none; background: transparent; backdrop-filter: none; }
  #screen-game .board-column { padding: 8px; }
  #screen-game.play-only .board-column {
    --col-pad: 6px;
    /* No card margin or border left to subtract: only the padding, the coach
       bar a narrow screen keeps at the bottom, and the safe areas. */
    --play-h: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 2 * var(--col-pad) - var(--sheet-h));
  }
  @supports (height: 100dvh) {
    #screen-game.play-only .board-column {
      --play-h: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 2 * var(--col-pad) - var(--sheet-h));
    }
  }
}

/* --- Solved ---------------------------------------------------------------- */

.win-overlay { position: fixed; inset: 0; z-index: 30; display: grid; place-items: center; padding: 20px; background: color-mix(in srgb, var(--bg) 72%, transparent); backdrop-filter: blur(4px); animation: win-fade .22s ease-out; }
.win-card { width: min(420px, 100%); padding: 30px 28px 26px; text-align: center; color: var(--text); background: var(--surface-2); border: 1px solid color-mix(in srgb, var(--success) 55%, var(--line)); border-radius: 20px; box-shadow: var(--shadow), 0 0 0 6px color-mix(in srgb, var(--success) 14%, transparent); animation: win-pop .34s cubic-bezier(.2, 1.3, .4, 1); }
.win-mark { width: 62px; height: 62px; margin: 0 auto 16px; display: grid; place-items: center; border-radius: 50%; color: var(--on-success); background: var(--success); font-size: 1.9rem; font-weight: 900; box-shadow: 0 0 0 8px color-mix(in srgb, var(--success) 20%, transparent); }
.win-card .eyebrow { margin-bottom: 6px; }
.win-card h2 { font-size: 1.9rem; }
.win-card p:not(.eyebrow) { margin: 12px 0 0; color: var(--muted); font-size: .9rem; line-height: 1.6; }
.win-actions { display: grid; gap: 8px; margin-top: 22px; }
.win-actions button { min-height: 46px; }
@keyframes win-fade { from { opacity: 0; } }
@keyframes win-pop { from { opacity: 0; transform: translateY(10px) scale(.94); } }
@media (prefers-reduced-motion: reduce) { .win-overlay, .win-card { animation: none; } }

/* --- Levels (on the Play tab) ---------------------------------------------- */

.level-menu { display: grid; gap: 10px; padding-bottom: 0; }
.level-row .home-row-label { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.level-pips { display: inline-flex; gap: 5px; }
.level-pips i { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--accent); }
.level-pips i.on { background: var(--accent); }
.win-actions .quiet-button { min-height: 40px; }

/* ==========================================================================
   Tabs: Play, Learn, Stats, Settings
   A bottom bar on a phone, a rail down the left from tablet width up — the
   same four destinations either way, as iOS and Android lay them out. The
   bar is put away while a board is played: the board needs the room.
   ========================================================================== */

body { --tabbar-h: 64px; --rail-w: 88px; }

.tabbar {
  position: fixed;
  z-index: 40;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding: 6px 8px env(safe-area-inset-bottom);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: start;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-top: 1px solid var(--line);
}
.tabbar-mark { display: none; }
.tab {
  min-height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--muted);
  text-decoration: none;
  font-size: .7rem;
  font-weight: 650;
  letter-spacing: .01em;
  border-radius: 14px;
}
.tab-icon { width: 56px; height: 30px; display: grid; place-items: center; border-radius: 15px; transition: background 160ms ease; }
.tab:hover { color: var(--text); }
.tab.active { color: var(--text); font-weight: 760; }
.tab.active .tab-icon { background: color-mix(in srgb, var(--accent) 26%, transparent); color: color-mix(in srgb, var(--accent) 55%, var(--text)); }
.tab:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: -3px; }

/* Room for the bar under the content, so the last row can scroll above it. */
main { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 32px); }

@media (min-width: 768px) {
  .tabbar {
    top: 0;
    right: auto;
    width: calc(var(--rail-w) + env(safe-area-inset-left));
    height: auto;
    padding: calc(env(safe-area-inset-top) + 18px) 4px 18px calc(4px + env(safe-area-inset-left));
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    border-top: 0;
    border-right: 1px solid var(--line);
    background: color-mix(in srgb, var(--surface-2) 70%, var(--bg));
  }
  .tabbar-mark { display: block; width: 85%; height: auto; aspect-ratio: 1; margin: 2px 0 20px; box-shadow: 0 2px 6px rgb(0 0 0 / .2); }
  .tab { width: 76px; min-height: 64px; font-size: .72rem; }
  body { padding-left: calc(var(--rail-w) + env(safe-area-inset-left)); }
  main { padding-bottom: 56px; }
}

/* The game is full screen: no bar, no rail, no room kept for them. */
:root[data-screen-active="game"] .tabbar { display: none; }
:root[data-screen-active="game"] body { padding-left: 0; }
:root[data-screen-active="game"] main { padding-bottom: 56px; }
@media (pointer: coarse), (max-width: 900px) {
  :root[data-screen-active="game"] main { padding-bottom: 0; }
}

/* --- Tab screens ------------------------------------------------------------ */

.screen-bar h1 { margin: 0; }
.screen-lead { max-width: 600px; margin: 8px 0 0; color: var(--muted); font-size: .92rem; line-height: 1.55; }
.section-label { margin: 0 0 10px; color: var(--muted); font-size: .72rem; font-weight: 780; letter-spacing: .1em; text-transform: uppercase; }

.continue-card {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 84px;
  padding: 16px 18px;
  text-align: left;
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--line));
  border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 13%, var(--surface-2));
  cursor: pointer;
}
.continue-card.hidden { display: none; }
.continue-card:hover { border-color: var(--accent); }
.continue-text { display: grid; gap: 3px; min-width: 0; }
.continue-kicker { color: color-mix(in srgb, var(--accent) 60%, var(--text)); font-size: .68rem; font-weight: 820; letter-spacing: .15em; text-transform: uppercase; }
.continue-title { font-size: 1.12rem; font-weight: 760; letter-spacing: -.02em; }
.continue-desc { color: var(--muted); font-size: .8rem; line-height: 1.45; }
.continue-go { flex: 0 0 auto; width: 48px; height: 48px; display: grid; place-items: center; border-radius: 50%; color: var(--on-accent); background: var(--selected-bg); }
.learn-continue { margin-bottom: 18px; border-color: color-mix(in srgb, var(--accent-2) 50%, var(--line)); background: color-mix(in srgb, var(--accent-2) 10%, var(--surface-2)); }
.learn-continue:hover { border-color: var(--accent-2); }
.learn-continue .continue-kicker { color: var(--accent-2); }
.learn-continue .continue-go { color: var(--accent-2); background: transparent; }

/* Play: Continue, then the four levels and the way to bring a puzzle in. */
.hub-grid { display: grid; gap: 22px; align-items: start; }
.play-continue { min-height: 96px; }
.import-row {
  width: 100%;
  min-height: 56px;
  margin-top: 12px;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  color: var(--text);
  font-size: .9rem;
  font-weight: 650;
  border: 1px dashed color-mix(in srgb, var(--muted) 50%, transparent);
  border-radius: 14px;
  background: transparent;
  cursor: pointer;
}
.import-row:hover { border-color: var(--accent); }
.import-note { color: var(--muted); font-weight: 500; }
@media (min-width: 900px) {
  .hub-grid { grid-template-columns: minmax(300px, 380px) minmax(0, 1fr); gap: 28px; }
  .play-continue { min-height: 220px; flex-direction: column; align-items: flex-start; justify-content: space-between; padding: 22px; }
  .play-continue .continue-title { font-size: 1.5rem; }
  .play-continue .continue-go { align-self: flex-end; width: 56px; height: 56px; }
  .hub-grid .level-menu { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hub-grid .level-row { min-height: 128px; align-content: start; }
}
/* With no game to continue, the levels take the whole width. */
.hub-grid:has(> .play-continue.hidden) { grid-template-columns: minmax(0, 1fr); }

/* Learn: the path, then one row per technique. */
.learn-progress { display: grid; gap: 7px; margin: 4px 0 16px; }
.progress-segments { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; }
.progress-segments i { height: 6px; border-radius: 3px; background: var(--peer-bg); }
.progress-segments i.part { background: color-mix(in srgb, var(--accent-2) 45%, var(--peer-bg)); }
.progress-segments i.done { background: var(--accent-2); }
.learn-count { color: var(--muted); font-size: .76rem; }
.technique-list { display: grid; border: 1px solid var(--line); border-radius: 16px; background: var(--surface-2); overflow: hidden; }
.technique-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 66px;
  padding: 10px 16px;
  text-align: left;
  color: var(--text);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  cursor: pointer;
}
.technique-row:last-child { border-bottom: 0; }
/* The list's three groups: the foundations, the advanced patterns, the expert ones. */
.technique-group { margin: 0; padding: 12px 16px 6px; color: var(--faint); font-size: .68rem; font-weight: 780; letter-spacing: .08em; text-transform: uppercase; border-bottom: 1px solid var(--line); }
.technique-row:hover { background: var(--surface-3); }
.technique-badge { flex: 0 0 auto; width: 28px; height: 28px; display: grid; place-items: center; border-radius: 50%; font-size: .74rem; font-weight: 820; color: var(--muted); background: var(--surface-3); box-sizing: border-box; }
.technique-badge.started { color: var(--accent-2); background: transparent; border: 2px solid var(--accent-2); }
.technique-badge.done { color: var(--on-success); background: var(--accent-2); }
.technique-text { flex: 1; min-width: 0; display: grid; gap: 2px; }
.technique-name { font-size: .98rem; font-weight: 740; letter-spacing: -.01em; }
.technique-family { margin-left: 6px; color: var(--faint); font-size: .7rem; font-weight: 560; letter-spacing: 0; }
.technique-status { color: var(--muted); font-size: .76rem; }
.step-bars { flex: 0 0 auto; display: flex; gap: 4px; }
.step-bar { width: 14px; height: 6px; border-radius: 3px; background: var(--peer-bg); box-sizing: border-box; }
.step-bar.part { background: color-mix(in srgb, var(--accent-2) 45%, var(--peer-bg)); }
.step-bar.done { background: var(--accent-2); }
.step-bar.none { background: transparent; border: 1px dashed var(--line); }

/* Learn: the suggested next step. Its own card, set apart from the challenge
   in progress below it, in the Continue card's colours. */
.guide-card {
  display: grid;
  gap: 14px;
  margin-bottom: 14px;
  padding: 18px;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--line));
  border-radius: 18px;
  background: color-mix(in srgb, var(--accent) 13%, var(--surface-2));
}
.guide-card.complete { border-color: color-mix(in srgb, var(--accent-2) 50%, var(--line)); background: color-mix(in srgb, var(--accent-2) 10%, var(--surface-2)); }
.guide-card.complete .continue-kicker { color: var(--accent-2); }
.guide-text { display: grid; gap: 4px; min-width: 0; }
.guide-text p, .guide-title { margin: 0; }
.guide-title { font-size: 1.25rem; font-weight: 760; letter-spacing: -.02em; overflow-wrap: anywhere; }
.guide-step { color: var(--text); font-size: .8rem; font-weight: 700; }
.guide-desc { max-width: 640px; color: var(--muted); font-size: .84rem; line-height: 1.5; }
.guide-actions { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.guide-action { width: 100%; min-height: 48px; padding-inline: 20px; font-size: .86rem; white-space: normal; }
@media (min-width: 900px) {
  .guide-card { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 18px; padding: 20px 22px; }
  .guide-actions { max-width: 320px; justify-content: flex-end; }
  .guide-action { width: auto; }
}

/* Learn: finding a technique. A search box, then two independent groups of
   radio chips; the count under them is the only part a screen reader hears. */
.learn-finder { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; margin: 0 0 14px; }
.finder-label { display: block; margin: 0 0 6px; padding: 0; color: var(--muted); font-size: .72rem; font-weight: 780; letter-spacing: .1em; text-transform: uppercase; }
.search-field { display: flex; align-items: center; min-height: 48px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface-2); }
.search-field:focus-within { border-color: var(--accent); outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 2px; }
.search-icon { flex: 0 0 auto; margin-left: 14px; color: var(--muted); }
.search-input { flex: 1; width: 100%; min-width: 0; min-height: 46px; padding: 10px 12px; border: 0; background: transparent; color: var(--text); font-size: max(16px, .95rem); outline: none; }
.search-input::placeholder { color: var(--faint); opacity: 1; }
.search-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.search-clear { flex: 0 0 auto; min-width: 44px; min-height: 44px; margin-right: 2px; padding: 0 12px; border: 0; border-radius: 12px; background: transparent; color: var(--accent); font-size: .82rem; font-weight: 750; cursor: pointer; }
.search-clear:hover { background: var(--surface-3); }
.search-clear[hidden] { display: none; }
.filter-group { min-width: 0; margin: 0; padding: 0; border: 0; }
.filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.filter-chip { position: relative; display: inline-flex; max-width: 100%; }
.filter-chip input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.filter-chip span { display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); color: var(--muted); font-size: .82rem; font-weight: 700; }
.filter-chip:hover span { border-color: var(--accent); color: var(--text); }
.filter-chip input:checked + span { color: var(--on-accent); background: var(--accent); border-color: transparent; }
.filter-chip input:focus-visible + span { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 3px; }
.learn-results-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 2px 12px; min-height: 44px; }
.learn-results { margin: 0; color: var(--muted); font-size: .8rem; }
.learn-clear { min-height: 44px; color: var(--accent); font-size: .82rem; font-weight: 750; }
.learn-clear[hidden] { display: none; }
@media (min-width: 900px) {
  .learn-finder { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; }
  .learn-search, .learn-results-row { grid-column: 1 / -1; }
}
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Each stage is its own section under a heading; the rule between two
   sections is the one the last row of a group used to draw. */
.technique-section { display: grid; }
.technique-section + .technique-section { border-top: 1px solid var(--line); }
.technique-list[hidden] { display: none; }
.learn-empty { display: grid; justify-items: start; gap: 8px; padding: 22px 18px; border: 1px dashed var(--line); border-radius: 16px; }
.learn-empty[hidden] { display: none; }
.learn-empty-title { margin: 0; font-weight: 760; }
.learn-empty-desc { margin: 0; color: var(--muted); font-size: .86rem; line-height: 1.5; }

/* A technique: what it is, then its three steps. */
.technique-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.technique-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 6px 14px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--surface-2);
}
.technique-step.current { border-color: color-mix(in srgb, var(--accent-2) 55%, var(--line)); background: color-mix(in srgb, var(--accent-2) 8%, var(--surface-2)); }
.technique-step.none { background: transparent; border-style: dashed; }
.step-mark { width: 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; font-size: .82rem; font-weight: 820; color: var(--muted); background: var(--surface-3); box-sizing: border-box; }
.technique-step.done .step-mark { color: var(--on-success); background: var(--accent-2); }
.technique-step.current .step-mark { color: var(--accent-2); background: transparent; border: 2px solid var(--accent-2); }
.technique-step.none .step-mark { background: transparent; border: 1px dashed var(--line); }
.step-body h3 { margin: 4px 0 4px; font-size: 1rem; letter-spacing: -.01em; }
.step-body p { margin: 0; color: var(--muted); font-size: .84rem; line-height: 1.5; }
.step-body .step-status { margin-top: 6px; color: var(--text); font-size: .8rem; }
.step-actions { grid-column: 2; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.step-actions button { min-height: 44px; padding-inline: 16px; }
/* A Challenge step's Focused / Mixed choice: two radio cards, the chosen one
   in the accent colour, each with its one-line promise under its name. */
.challenge-modes { display: grid; gap: 8px; margin: 10px 0 0; padding: 0; border: 0; min-width: 0; }
.challenge-modes-legend { padding: 0; margin: 0 0 2px; color: var(--muted); font-size: .72rem; font-weight: 760; letter-spacing: .06em; text-transform: uppercase; }
.challenge-mode-option { position: relative; display: flex; align-items: flex-start; gap: 10px; min-height: 44px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); cursor: pointer; }
.challenge-mode-option:hover { border-color: var(--accent); }
.challenge-mode-option:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.challenge-mode-option:has(input:focus-visible) { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 2px; }
.challenge-mode-option input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--accent); }
.challenge-mode-text { display: grid; gap: 2px; min-width: 0; }
.challenge-mode-name { color: var(--text); font-size: .86rem; font-weight: 760; }
.step-body .challenge-mode-desc, .challenge-mode-desc { color: var(--muted); font-size: .8rem; line-height: 1.4; }
.step-body .challenge-mode-single { margin-top: 10px; color: var(--muted); font-size: .8rem; }
.challenge-mode-tag { display: inline-block; margin-right: 6px; padding: 1px 8px; border-radius: 999px; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--text); font-size: .72rem; font-weight: 760; }
@media (min-width: 560px) and (max-width: 999px) {
  .challenge-modes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .challenge-modes-legend { grid-column: 1 / -1; }
}
.technique-notes { margin-top: 26px; }
.technique-notes p { max-width: 720px; margin: 0 0 10px; color: var(--muted); font-size: .88rem; line-height: 1.6; }
@media (min-width: 1000px) {
  .technique-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .technique-step { grid-template-columns: minmax(0, 1fr); align-content: start; }
  .step-actions { grid-column: 1; margin-top: auto; padding-top: 10px; }
}

/* Settings: the rows that lead to Help and About. */
.link-list { display: grid; padding-top: 4px; padding-bottom: 4px; }
.link-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 56px;
  padding: 8px 0;
  text-align: left;
  color: var(--text);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  cursor: pointer;
}
.link-row:last-child { border-bottom: 0; }
.link-row > span { display: grid; gap: 3px; }
.link-row svg { color: var(--muted); flex: 0 0 auto; }
#theme-toggle, .play-theme { display: grid; place-items: center; color: var(--text); }

/* Settings → Backup and transfer (backup-ui.mjs). Every control keeps a
   48 × 48 CSS px target, and the buttons wrap onto their own rows rather than
   shrink when the text is large or the screen narrow. */
.data-options { display: grid; gap: 12px; }
.data-options > p { margin: 0; }
.data-status { display: flex; gap: 10px; align-items: flex-start; font-size: .8rem; font-weight: 650; line-height: 1.45; overflow-wrap: anywhere; }
.data-status::before { content: ""; flex: 0 0 auto; width: .6em; height: .6em; margin-top: .45em; border-radius: 50%; background: var(--accent-2); }
.data-status[data-state="saving"]::before { background: var(--muted); }
.data-status[data-state="failed"]::before, .data-status[data-state="session"]::before { background: var(--danger); }
.data-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.data-action { flex: 1 1 12em; min-width: 48px; min-height: 48px; font-size: .8rem; white-space: normal; overflow-wrap: anywhere; }
.data-action:disabled { opacity: .5; cursor: default; }
.data-undo { justify-self: start; flex: none; }
.data-undo-when { font-weight: 600; color: var(--muted); }
.data-undo-when:empty { display: none; }
.data-message { min-height: 0; font-size: .76rem; line-height: 1.45; overflow-wrap: anywhere; }
/* Kept in the accessibility tree while empty, so what is said in it later is
   announced; the negative margin takes back the grid gap. */
.data-message:empty { margin-top: -12px; }
.data-message[data-kind="problem"] { color: var(--danger); font-weight: 700; }

/* The restore preview: what is on the device now, and what replaces it. */
#restore-dialog { max-height: calc(100dvh - 24px); overflow: auto; }
#restore-dialog .dialog-heading { align-items: center; gap: 12px; }
#restore-dialog h2 { margin: 0; font-size: 1.15rem; overflow-wrap: anywhere; }
#restore-close { min-width: 48px; min-height: 48px; width: auto; height: auto; }
.restore-rows { display: grid; gap: 8px; margin: 12px 0; padding: 0; list-style: none; }
.restore-row { display: grid; gap: 3px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); font-size: .76rem; line-height: 1.4; overflow-wrap: anywhere; }
.restore-row.changes { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
.restore-label { font-weight: 750; font-size: .82rem; }
.restore-same { font-weight: 600; color: var(--muted); }
.restore-now { color: var(--muted); }
.restore-tag { display: inline-block; min-width: 6.5em; font-size: .68rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.restore-next .restore-tag { color: var(--accent-2); }
#restore-dialog .restore-safety { margin: 0; }
.restore-actions { flex-wrap: wrap; gap: 10px; }
.restore-actions button { flex: 1 1 10em; min-width: 48px; min-height: 48px; font-size: .82rem; white-space: normal; }
.restore-actions button:disabled { opacity: .6; cursor: default; }
@media (max-width: 480px) {
  #restore-dialog form { padding: 18px; }
  /* Narrow: the Now / Backup tag sits above its value instead of beside it. */
  .restore-tag { display: block; min-width: 0; }
}

/* ==========================================================================
   Tactile board (design 1c)
   After a wooden-tile set: numbers sit on raised tiles, empty squares are
   wells sunk into a tray, and every key has a lip and presses down. Built on
   each palette's own colours, so it works in every palette and in light mode.
   On whenever <html> has the `tactile` class (Settings → Board → Tactile board).
   ========================================================================== */

:root.tactile {
  /* Every colour comes from the palette's own variables — accent, accent-2,
     the surfaces, the grid frame and the peer shades — so each palette keeps
     the character its swatches promise. */
  /* A raised face: the palette's top surface, warmed with its accent. */
  --tac-face-base: color-mix(in srgb, var(--surface-3) 72%, var(--text) 28%);
  --tac-face-top: color-mix(in srgb, var(--accent) 18%, var(--tac-face-base));
  --tac-face-bottom: color-mix(in srgb, var(--accent) 14%, color-mix(in srgb, var(--surface-3) 86%, var(--text) 14%));
  --tac-mine-top: color-mix(in srgb, var(--accent) 88%, #ffffff);
  --tac-mine-bottom: color-mix(in srgb, var(--accent) 78%, var(--surface-3));
  --tac-mine-text: var(--on-accent);
  /* Wells and tray: the palette's background, with its accent under it. */
  --tac-well: color-mix(in srgb, var(--accent) 7%, color-mix(in srgb, var(--bg) 80%, #000000));
  --tac-tray-top: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
  --tac-tray-bottom: color-mix(in srgb, var(--accent) 5%, var(--bg));
  --tac-tray-edge: color-mix(in srgb, var(--grid) 55%, transparent);
  --tac-sheen: color-mix(in srgb, var(--text) 18%, transparent);
  /* The lip is a darker shade of the accent, not plain grey. */
  --tac-lip: color-mix(in srgb, var(--accent) 22%, rgba(0, 0, 0, .62));
  --tac-drop: rgba(0, 0, 0, .4);
  --tac-sunk: rgba(0, 0, 0, .65);
  --tac-glow: color-mix(in srgb, var(--accent) 60%, transparent);
  --tac-match: var(--accent-2);
  --tac-lift: inset 0 1px 0 var(--tac-sheen);
}
:root.tactile.light {
  --tac-face-top: color-mix(in srgb, var(--accent) 5%, color-mix(in srgb, var(--surface-2) 75%, #ffffff));
  --tac-face-bottom: color-mix(in srgb, var(--accent) 9%, var(--surface-2));
  --tac-mine-top: color-mix(in srgb, var(--accent) 86%, #ffffff);
  --tac-mine-bottom: var(--accent);
  --tac-well: color-mix(in srgb, var(--accent) 12%, color-mix(in srgb, var(--bg) 86%, #000000));
  --tac-tray-top: color-mix(in srgb, var(--accent) 10%, var(--surface-3));
  --tac-tray-bottom: color-mix(in srgb, var(--accent) 8%, color-mix(in srgb, var(--bg) 92%, #000000));
  --tac-tray-edge: color-mix(in srgb, var(--grid) 45%, transparent);
  --tac-sheen: rgba(255, 255, 255, .8);
  --tac-lip: color-mix(in srgb, color-mix(in srgb, var(--accent) 45%, var(--grid)) 70%, transparent);
  --tac-drop: color-mix(in srgb, var(--accent) 18%, rgba(0, 0, 0, .14));
  --tac-sunk: rgba(0, 0, 0, .26);
}

/* High Contrast is about legibility, not mood: plain light-on-black tiles
   (black-on-white in light mode), with the accent kept for what is yours. */
:root.tactile:not(.light)[data-palette="contrast"] {
  --tac-face-top: #3d3d3d;
  --tac-face-bottom: #2b2b2b;
  --tac-well: #000000;
  --tac-tray-top: #161616;
  --tac-tray-bottom: #0a0a0a;
  --tac-tray-edge: var(--line);
  --tac-lip: rgba(0, 0, 0, .9);
}
:root.tactile.light[data-palette="contrast"] {
  --tac-face-top: #ffffff;
  --tac-face-bottom: #f4f4f4;
  --tac-well: #d6d6d6;
  --tac-tray-top: #ececec;
  --tac-tray-bottom: #e2e2e2;
  --tac-tray-edge: var(--line);
  --tac-lip: rgba(0, 0, 0, .5);
}

/* --- The board: a tray, nine boxes of wells and tiles -------------------- */

/* Registered as a length so it is worked out once, on the board, and handed
   down to the cells as pixels. Unregistered, --board-size (which is built
   from 100cqi) was re-read inside each cell, where cqi measures the board
   itself rather than the column, and the cells' corners came out a third of
   the size the design meant (and of the keys'). */
@property --t-size { syntax: "<length>"; inherits: true; initial-value: 0px; }

:root.tactile #screen-game.play-only .sudoku-board {
  --t-size: var(--board-size, min(100vw - 32px, 600px));
  --t-cell-gap: calc(var(--t-size) * .009);
  --t-box-gap: calc(var(--t-size) * .016);
  --t-radius: calc(var(--t-size) * .017);
  grid-template-columns: repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr));
  gap: var(--t-cell-gap);
  padding: calc(var(--t-size) * .026);
  border: 0;
  border-radius: calc(var(--t-size) * .042);
  background: linear-gradient(180deg, var(--tac-tray-top), var(--tac-tray-bottom));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--text) 6%, transparent), 0 0 0 1.5px var(--tac-tray-edge), 0 12px 30px var(--tac-drop);
}
:root.tactile #screen-game.play-only .sudoku-board.is-loading { background: var(--flip-field); }

:root.tactile #screen-game.play-only .cell {
  --ring: 0 0 0 0 transparent;
  grid-row: var(--gr);
  grid-column: var(--gc);
  border: 0;
  border-radius: var(--t-radius);
  overflow: visible;
  background: var(--tac-well);
  color: var(--muted);
  box-shadow: inset 0 2px 5px var(--tac-sunk), var(--ring);
  transition: background-color .12s, box-shadow .12s;
}
/* The 3×3 boxes are told apart by the gutter, not by drawn lines. */
:root.tactile #screen-game.play-only .cell.block-divider-top::before,
:root.tactile #screen-game.play-only .cell.block-divider-left::before { content: none; }
/* The row/column band. The palettes' --axis-bg is tuned for the flat board;
   mixed into a dark well it landed within a shade or two of the well itself,
   so in most palettes the band all but vanished, and it skipped the tiles
   entirely. Here it is built from the accent instead — the one colour every
   palette makes sure stands out — as a lit well with a thin accent rim, and
   the tiles in the line pick up a tint of the same colour. The box tint stays
   quieter than the band so the cross reads first. */
:root.tactile {
  --tac-band: color-mix(in srgb, var(--accent) 28%, color-mix(in srgb, var(--axis-bg) 70%, var(--tac-well)));
  --tac-band-rim: color-mix(in srgb, var(--accent) 60%, transparent);
  --tac-band-face: 20%;
}
:root.tactile.light {
  --tac-band: color-mix(in srgb, var(--accent) 26%, color-mix(in srgb, var(--tac-well) 70%, #ffffff));
  --tac-band-rim: color-mix(in srgb, var(--accent) 55%, transparent);
  --tac-band-face: 16%;
}
:root.tactile #screen-game.play-only .cell.peer:not(.filled) { background: color-mix(in srgb, var(--peer-bg) 70%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.peer.box-peer:not(.filled) { background: color-mix(in srgb, var(--box-bg) 55%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.peer.axis-peer:not(.filled) {
  background: var(--tac-band);
  box-shadow: inset 0 2px 5px var(--tac-sunk), inset 0 0 0 1.5px var(--tac-band-rim), var(--ring);
}
:root.tactile #screen-game.play-only .cell.peer.axis-peer.filled {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) var(--tac-band-face), var(--tac-face-top)), color-mix(in srgb, var(--accent) var(--tac-band-face), var(--tac-face-bottom)));
}

/* A number is a tile standing proud of the tray. The player's own numbers
   use the same wooden face as the given ones, so a correct entry blends in.
   The ring comes first in the shadow list: shadows paint first-on-top, so
   listed after the lip it was hidden along the tile's bottom edge. */
:root.tactile #screen-game.play-only .cell.filled {
  background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom));
  color: var(--text);
  transform: translateY(-1px);
  box-shadow: var(--ring), var(--tac-lift), 0 3px 0 var(--tac-lip), 0 5px 8px var(--tac-drop);
}
:root.tactile #screen-game.play-only .cell.filled .value { font-weight: 650; text-shadow: 0 1px 0 rgba(0, 0, 0, .35); }
:root.tactile.light #screen-game.play-only .cell.filled .value { text-shadow: 0 1px 0 rgba(255, 255, 255, .6); }

/* Selection and matches are light around a tile, not a fill. */
:root.tactile #screen-game.play-only .cell.selected { --ring: 0 0 0 2px var(--accent), 0 0 16px var(--accent); }
:root.tactile #screen-game.play-only .cell.selected:not(.filled) { background: color-mix(in srgb, var(--selected-bg) 42%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.selected::after { content: none; }
:root.tactile #screen-game.play-only .cell.same-value { --ring: 0 0 0 2px var(--tac-match), 0 0 12px color-mix(in srgb, var(--tac-match) 55%, transparent); }
:root.tactile #screen-game.play-only .cell.same-value.selected { --ring: 0 0 0 2px var(--accent), 0 0 16px var(--accent); }
:root.tactile #screen-game.play-only .cell.selected .value,
:root.tactile #screen-game.play-only .cell.same-value .value { color: inherit; font-weight: 650; }
:root.tactile #screen-game.play-only .cell.selected.same-value .value { color: inherit; text-shadow: 0 1px 0 rgba(0, 0, 0, .35); }

/* Mistakes: a red ring and red digit, on a tile or a well alike. */
:root.tactile #screen-game.play-only .cell.invalid,
:root.tactile #screen-game.play-only .sudoku-board .cell.clash,
:root.tactile #screen-game.play-only .sudoku-board .cell.wrong { --ring: 0 0 0 2px var(--danger), 0 0 12px color-mix(in srgb, var(--danger) 55%, transparent); z-index: 1; }
:root.tactile #screen-game.play-only .sudoku-board .cell.clash,
:root.tactile #screen-game.play-only .sudoku-board .cell.wrong:not(.selected) { box-shadow: inset 0 2px 5px var(--tac-sunk), var(--ring); }
:root.tactile #screen-game.play-only .sudoku-board .cell.filled.clash,
:root.tactile #screen-game.play-only .sudoku-board .cell.filled.wrong:not(.selected) { box-shadow: var(--ring), var(--tac-lift), 0 3px 0 var(--tac-lip), 0 5px 8px var(--tac-drop); }
:root.tactile #screen-game.play-only .cell.invalid:not(.given) .value,
:root.tactile #screen-game.play-only .sudoku-board .cell.wrong .value { color: color-mix(in srgb, var(--danger) 70%, #ffffff); }
:root.tactile #screen-game.play-only .sudoku-board .cell.wrong.filled:not(.given) { background: linear-gradient(180deg, color-mix(in srgb, var(--danger) 45%, var(--surface-3)), color-mix(in srgb, var(--danger) 28%, var(--surface-3))); }
:root.tactile #screen-game.play-only .sudoku-board .cell.wrong.filled .value { color: #ffffff; }

/* The coach's colours stay readable on wells and tiles. */
:root.tactile #screen-game.play-only .cell.pattern-cell:not(.filled) { background: color-mix(in srgb, var(--pattern) 30%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.link-cell:not(.filled) { background: color-mix(in srgb, var(--link) 26%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.lesson-band-a:not(.filled) { background: color-mix(in srgb, var(--pattern) 20%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.lesson-band-b:not(.filled) { background: color-mix(in srgb, var(--link) 18%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.filled:is(.pattern-cell, .lesson-band-a) { background: linear-gradient(180deg, color-mix(in srgb, var(--pattern) 30%, var(--tac-face-top)), color-mix(in srgb, var(--pattern) 30%, var(--tac-face-bottom))); }
:root.tactile #screen-game.play-only .cell.filled:is(.link-cell, .lesson-band-b) { background: linear-gradient(180deg, color-mix(in srgb, var(--link) 28%, var(--tac-face-top)), color-mix(in srgb, var(--link) 28%, var(--tac-face-bottom))); }
:root.tactile #screen-game.play-only .cell.lesson-cell-key { --ring: 0 0 0 2px var(--muted); }
:root.tactile #screen-game.play-only .cell.lesson-cell-remove { --ring: 0 0 0 2px color-mix(in srgb, var(--danger) 80%, transparent); background: color-mix(in srgb, var(--danger) 18%, var(--tac-well)); }
:root.tactile #screen-game.play-only .cell.lesson-cell-a { --ring: 0 0 0 2px var(--pattern), 0 0 12px color-mix(in srgb, var(--pattern) 50%, transparent); }
:root.tactile #screen-game.play-only .cell.lesson-cell-off { --ring: 0 0 0 1.5px var(--faint); background-image: repeating-linear-gradient(135deg, transparent 0 5px, color-mix(in srgb, var(--faint) 45%, transparent) 5px 6px); }
:root.tactile #screen-game.play-only .cell.lesson-placed { --ring: 0 0 0 2px var(--pattern), 0 0 12px color-mix(in srgb, var(--pattern) 50%, transparent); }
:root.tactile #screen-game.play-only .cell.proposed { --ring: 0 0 0 2px var(--accent); }
:root.tactile #screen-game.play-only .cell.proposed.answer-correct { --ring: 0 0 0 2px var(--success); }
:root.tactile #screen-game.play-only .cell.proposed.answer-wrong { --ring: 0 0 0 2px var(--danger); }
:root.tactile #screen-game.play-only .cell .value.proposed-value { color: var(--accent); font-weight: 760; text-shadow: none; }
:root.tactile #screen-game.play-only .cell.proposed.answer-correct .value.proposed-value { color: var(--success); }
:root.tactile #screen-game.play-only .cell.proposed.answer-wrong .value.proposed-value { color: var(--danger); }
:root.tactile #screen-game.play-only .cell .value.lesson-place { color: var(--pattern); font-weight: 820; text-shadow: none; }

/* --- Keys: a lip underneath, and they go down when pressed -------------- */

:root.tactile #screen-game.play-only .number-pad button,
:root.tactile #screen-game.play-only .board-actions button {
  border: 0;
  color: var(--text);
  background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom));
  box-shadow: var(--tac-lift), 0 4px 0 var(--tac-lip), 0 7px 12px var(--tac-drop);
  transition: transform .06s, box-shadow .06s, opacity .15s;
}
:root.tactile #screen-game.play-only .number-pad button {
  font-weight: 650;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .35);
  margin-bottom: 4px;
}
:root.tactile.light #screen-game.play-only .number-pad button { text-shadow: 0 1px 0 rgba(255, 255, 255, .6); }
:root.tactile #screen-game.play-only .number-pad button:hover:not(:disabled),
:root.tactile #screen-game.play-only .board-actions button:hover { border-color: transparent; color: var(--text); transform: none; }
:root.tactile #screen-game.play-only .number-pad button:active:not(:disabled),
:root.tactile #screen-game.play-only .board-actions button:active {
  transform: translateY(3px);
  box-shadow: var(--tac-lift), 0 1px 0 var(--tac-lip);
}
:root.tactile #screen-game.play-only .number-pad button.current:not(:disabled) {
  box-shadow: var(--tac-lift), 0 4px 0 var(--tac-lip), 0 7px 12px var(--tac-drop), 0 0 0 2px var(--accent), 0 0 14px var(--accent);
}
/* A spent or ruled-out key is sunk flat into the tray. */
:root.tactile #screen-game.play-only .number-pad button.complete,
:root.tactile #screen-game.play-only .number-pad button:disabled {
  background: var(--tac-well);
  box-shadow: inset 0 2px 5px var(--tac-sunk);
  color: var(--faint);
  text-shadow: none;
  filter: none;
  opacity: .7;
  transform: translateY(3px);
}
:root.tactile #screen-game.play-only .board-actions button { border-radius: 12px; margin-bottom: 4px; }

/* The row of keys is laid out on the board's own columns: the same tray
   padding, cell gap and box gutters, so each key is exactly as wide as the
   cells above it and sits under its column. Corners and digits match the
   tiles too. Keys 4 and 7 step over the gutters. */
:root.tactile #screen-game.play-only .number-pad {
  --t-size: var(--board-size, min(100vw - 32px, 600px));
  --t-cell-gap: calc(var(--t-size) * .009);
  --t-box-gap: calc(var(--t-size) * .016);
  grid-template-columns: repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr));
  column-gap: var(--t-cell-gap);
  padding-inline: calc(var(--t-size) * .026);
}
:root.tactile #screen-game.play-only .number-pad button:nth-child(4) { grid-column: 5; }
:root.tactile #screen-game.play-only .number-pad button:nth-child(7) { grid-column: 9; }
:root.tactile #screen-game.play-only .number-pad button {
  border-radius: calc(var(--t-size) * .017);
  /* The board's digits are 100cqi / 9 * .58, where 100cqi is the board's
     width inside its tray padding. */
  font-size: max(19.2px, var(--t-size) * (1 - 2 * .026) / 9 * .58);
}
:root.tactile #screen-game.play-only .board-actions button[aria-pressed="true"] {
  background: linear-gradient(180deg, var(--tac-mine-top), var(--tac-mine-bottom));
  color: var(--tac-mine-text);
}

/* --- Round keys: back, theme, and the board tools ----------------------- */

/* The board tools — Notes, the pattern, Undo, Redo and Hint — are round keys
   with their name underneath. The round face is the icon itself. */
:root.tactile #screen-game.play-only .history-controls {
  --round: 52px;
  display: flex;
  justify-content: space-around;
  align-items: flex-start;
  gap: 4px;
}
:root.tactile #screen-game.play-only .history-controls > button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  min-width: 0;
  min-height: 0;
  height: auto;
  aspect-ratio: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: var(--muted);
  font-size: .74rem;
  font-weight: 600;
  cursor: pointer;
}
:root.tactile #screen-game.play-only .history-controls > button.hidden { display: none; }
:root.tactile #screen-game.play-only .history-controls > button > svg {
  flex: none;
  box-sizing: border-box;
  width: var(--round);
  height: var(--round);
  padding: calc(var(--round) * .29);
  border-radius: 50%;
  color: var(--text);
  background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom));
  box-shadow: var(--tac-lift), 0 3px 0 var(--tac-lip), 0 6px 12px var(--tac-drop);
  transition: transform .06s, box-shadow .06s;
}
:root.tactile #screen-game.play-only .history-controls > button:active:not(:disabled) > svg {
  transform: translateY(2px);
  box-shadow: var(--tac-lift), 0 1px 0 var(--tac-lip);
}
:root.tactile #screen-game.play-only .history-controls > button:hover:not(:disabled) { color: var(--text); background: transparent; transform: none; }
:root.tactile #screen-game.play-only .history-controls > button:disabled { opacity: .4; filter: none; background: transparent; }
:root.tactile #screen-game.play-only .history-controls > button[aria-pressed="true"]:not(:disabled) { color: var(--text); background: transparent; }
:root.tactile #screen-game.play-only .history-controls > button[aria-pressed="true"]:not(:disabled) > svg {
  color: var(--tac-mine-text);
  background: linear-gradient(180deg, var(--tac-mine-top), var(--tac-mine-bottom));
  box-shadow: var(--tac-lift), 0 3px 0 var(--tac-lip), 0 6px 12px var(--tac-drop), 0 0 16px var(--tac-glow);
}
:root.tactile #screen-game.play-only .history-controls .history-label { display: block; white-space: nowrap; }
:root:not(.tactile) .tactile-only { display: none; }

/* Hint is one of the round keys, its bulb in the accent colour. */
:root.tactile #screen-game.play-only .history-controls > .coach-open > svg { color: color-mix(in srgb, var(--accent) 55%, var(--text)); }
:root.tactile #screen-game.play-only .history-controls > .coach-open[aria-expanded="true"] > svg {
  color: var(--tac-mine-text);
  background: linear-gradient(180deg, var(--tac-mine-top), var(--tac-mine-bottom));
  box-shadow: var(--tac-lift), 0 3px 0 var(--tac-lip), 0 0 18px var(--tac-glow);
}

/* Back and the theme switch are round keys too. */
:root.tactile #screen-game.play-only .board-toolbar .play-menu,
:root.tactile #screen-game.play-only .board-toolbar .play-theme {
  width: 46px;
  height: 46px;
  min-height: 0;
  padding: 0;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  color: var(--text);
  background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom));
  box-shadow: var(--tac-lift), 0 3px 0 var(--tac-lip), 0 6px 12px var(--tac-drop);
  transition: transform .06s, box-shadow .06s;
}
:root.tactile #screen-game.play-only .board-toolbar .play-menu:active,
:root.tactile #screen-game.play-only .board-toolbar .play-theme:active { transform: translateY(2px); box-shadow: var(--tac-lift), 0 1px 0 var(--tac-lip); }
:root.tactile #screen-game.play-only .board-toolbar .play-menu > span[aria-hidden] { font-size: 1.6rem; line-height: 1; margin-top: -3px; }
:root.tactile #screen-game.play-only .board-toolbar .play-menu .play-menu-label { display: none; }

/* Level and mistakes: plain text between the two round keys. */
:root.tactile #screen-game.play-only .mistake-counter {
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  font-size: .74rem;
  white-space: nowrap;
}
:root.tactile #screen-game.play-only .mistake-counter.hidden { display: none; }
:root.tactile #screen-game.play-only .mistake-counter strong { font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* --- Upright (phone, tablet held upright) -------------------------------- */

/* One grid, so the header can hold its three pieces on one row however the
   toolbar is built: back · level and mistakes · theme, then the board, the
   round tools, one row of nine keys, and New puzzle / Fill candidates. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .board-column {
    /* Above and below the board: header 58, tools 92, keys 72, actions 56. */
    --stack-fixed: 278px;
    --counter-h: 0px;
    --pad-ratio: 1;
    --t-key-h: clamp(50px, var(--board-size) / 9 * 1.5, 80px);
    display: grid;
    grid-template-columns: calc(var(--board-size) * .25) calc(var(--board-size) * .5) calc(var(--board-size) * .25);
    grid-template-rows: repeat(4, auto) minmax(0, 1fr) repeat(3, auto);
    grid-template-areas:
      "back status theme"
      "seg seg seg"
      "board board board"
      "lesson lesson lesson"
      ". . ."
      "tools tools tools"
      "pad pad pad"
      "actions actions actions";
    justify-content: center;
    /* As tall as the screen allows, so the tools and keys sit down by the
       thumb and any spare height opens up between them and the board. */
    min-height: calc(var(--play-h) + 2 * var(--col-pad));
  }
  :root.tactile #screen-game.play-only .board-toolbar,
  :root.tactile #screen-game.play-only .board-footer { display: contents; }
  :root.tactile #screen-game.play-only .play-menu { grid-area: back; justify-self: start; margin-bottom: 12px; }
  :root.tactile #screen-game.play-only .play-theme { grid-area: theme; justify-self: end; margin-bottom: 12px; }
  :root.tactile #screen-game.play-only .mistake-counter { grid-area: status; width: auto; margin: 0 0 12px; }
  :root.tactile #screen-game.play-only .board-toolbar .segmented { grid-area: seg; margin-bottom: 12px; }
  :root.tactile #screen-game.play-only .sudoku-board { grid-area: board; }
  :root.tactile #screen-game.play-only .lesson-panel { grid-area: lesson; width: var(--board-size); margin: 14px auto 0; }
  :root.tactile #screen-game.play-only .history-controls { grid-area: tools; width: var(--board-size); margin: 16px auto 0; padding: 0 8px; }
  :root.tactile #screen-game.play-only .number-pad { grid-area: pad; margin-top: 16px; }
  /* As wide as a cell; taller than one, so the keys stay a thumb's size. */
  :root.tactile #screen-game.play-only .number-pad button {
    aspect-ratio: auto;
    height: var(--t-key-h);
    min-height: 0;
  }
  :root.tactile #screen-game.play-only .board-actions {
    grid-area: actions;
    width: var(--board-size);
    margin: 12px auto 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  :root.tactile #screen-game.play-only .board-actions.hidden { display: none; }
  :root.tactile #screen-game.play-only .board-actions button { min-height: 40px; padding-block: 8px; }
  /* A notice floats over the top of the board, under the header. */
  :root.tactile #screen-game.play-only .game-feedback { top: calc(var(--col-pad) + 70px); }
}
/* A short phone (iPhone SE and the like): the tools lose their names first. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  @media (max-height: 700px) {
    :root.tactile #screen-game.play-only .board-column { --stack-fixed: 246px; }
    :root.tactile #screen-game.play-only .history-controls { --round: 46px; }
    :root.tactile #screen-game.play-only .history-controls .history-label,
    :root.tactile #screen-game.play-only .history-controls #coach-open-label { display: none; }
  }
}

/* A drill's answer pad on a phone. Nine keys across a phone are about 34px
   wide, under the 44px a thumb needs, and on a drill the pad is how a
   candidate is marked without hitting an 8px digit on the board. So a drill's
   keys wrap to two rows, as the flat pad's always do, and the board gives up
   the height the second row and the caption take. */
@media (max-width: 430px) {
  :root.tactile #screen-game.play-only .board-column:has(> .number-pad.answer-pad:not(.hidden)) { --stack-fixed: 350px; }
  :root.tactile #screen-game.play-only .number-pad.answer-pad {
    width: var(--board-size);
    justify-self: center;
    padding-inline: 0;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
  }
  :root.tactile #screen-game.play-only .number-pad.answer-pad button:nth-child(n) { grid-column: auto; }
  :root.tactile #screen-game.play-only .number-pad.answer-pad button { height: auto; min-height: 50px; }
}

/* --- On its side (tablet landscape, desktop) ------------------------------ */

/* Three columns, as in the design: the way back, mistakes and the board
   actions on the left; the board with its row of nine keys in the middle; the
   theme, Hint and the board tools down the right. */
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .board-column {
    --side: clamp(140px, 15vw, 220px);
    /* The two sides share the same room as before, unevenly: the right holds
       only round icon buttons, the left the drill text and the walkthrough,
       which were cramped at an even split. */
    --side-r: clamp(84px, 8vw, 120px);
    --side-l: calc(2 * var(--side) - var(--side-r));
    --side-gap: clamp(16px, 2.6vw, 40px);
    /* The keys under the board are about a ninth of it tall, plus their gap. */
    --board-size: max(260px, min(100cqi - 2 * var(--col-pad) - var(--side-l) - var(--side-r) - 2 * var(--side-gap) - var(--coord-w, 0px), (var(--play-h) - 30px - var(--coord-g, 0px)) / 1.14));
    grid-template-columns: var(--side-l) var(--board-size) var(--side-r);
    grid-template-rows: repeat(5, auto) 1fr auto auto;
    grid-template-areas:
      "back board theme"
      "status board tools"
      "seg board tools"
      "feedback board tools"
      "lesson board tools"
      ". board tools"
      "actions board tools"
      ". pad .";
  }
  :root.tactile #screen-game.play-only .board-toolbar,
  :root.tactile #screen-game.play-only .board-footer { display: contents; }

  :root.tactile #screen-game.play-only .board-toolbar .play-menu {
    grid-area: back;
    justify-self: start;
    width: auto;
    height: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    font-size: 1.4rem;
    font-weight: 600;
  }
  :root.tactile #screen-game.play-only .board-toolbar .play-menu:active { transform: none; box-shadow: none; }
  :root.tactile #screen-game.play-only .board-toolbar .play-menu .play-menu-label { display: inline; }
  :root.tactile #screen-game.play-only .board-toolbar .play-theme { grid-area: theme; justify-self: end; }
  :root.tactile #screen-game.play-only .board-toolbar .segmented { grid-area: seg; margin: 0 0 12px; }
  :root.tactile #screen-game.play-only .game-feedback { grid-area: feedback; }
  :root.tactile #screen-game.play-only .lesson-panel { grid-area: lesson; padding: 14px; }

  :root.tactile #screen-game.play-only .mistake-counter {
    grid-area: status;
    width: 100%;
    margin: 32px 0 12px;
    align-items: flex-start;
    gap: 6px;
    font-size: .82rem;
  }
  :root.tactile #screen-game.play-only .mistake-counter strong { font-size: clamp(1.05rem, 1.7vw, 1.6rem); line-height: 1.15; }

  :root.tactile #screen-game.play-only .board-actions {
    grid-area: actions;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  :root.tactile #screen-game.play-only .board-actions.hidden { display: none; }
  :root.tactile #screen-game.play-only .board-actions button { min-height: 46px; }

  /* Down the right: Hint near the top, Notes / Undo / Redo at the foot. */
  :root.tactile #screen-game.play-only .history-controls {
    --round: clamp(52px, 5.6vw, 68px);
    grid-area: tools;
    order: 0;
    width: 100%;
    margin: 28px 0 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-start;
    gap: 18px;
    align-self: stretch;
  }
  :root.tactile #screen-game.play-only .history-controls > .coach-open { order: -1; margin-bottom: auto; }
  :root.tactile #screen-game.play-only .history-controls > button { flex: none; font-size: .86rem; gap: 9px; }
  :root.tactile #screen-game.play-only .history-controls .history-label { display: block; }

  :root.tactile #screen-game.play-only .sudoku-board { grid-area: board; }
  :root.tactile #screen-game.play-only .number-pad {
    grid-area: pad;
    width: var(--board-size);
    margin: 18px 0 0;
  }
  /* Each key is a cell-sized tile. */
  :root.tactile #screen-game.play-only .number-pad button {
    aspect-ratio: 1;
    height: auto;
  }
}

/* --- Board view: coordinates, digit focus, cell inspector ------------------ */

/* board-tools.mjs. Viewing tools only: none of this recolours what the coach,
   a walkthrough or a checked answer has drawn, and with every tool off the
   board is exactly as before (--coord-g, --coord-w and --bt-h are then 0 in
   the --board-size formulas above). */

/* Coordinates: r1–r9 down the left, c1–c9 across the top, outside the cells.
   The board gives up the labels' room: it drops by their height and narrows
   by their width. Stacked, the board and everything lined up with it stay
   centred, and the labels hang into the margin that leaves; on its side the
   gap beside the board widens instead. */
:root.board-coords-on #screen-game.play-only .board-column {
  --coord-g: clamp(16px, 4.6vmin, 22px);
  --coord-w: var(--coord-g);
}
:root.board-coords-on #screen-game.play-only .sudoku-board { position: relative; margin-top: var(--coord-g); }
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.board-coords-on #screen-game.play-only .board-column { column-gap: calc(var(--side-gap) + var(--coord-g)); }
  /* Two gaps widen on the tactile board, one each side of it. */
  :root.board-coords-on.tactile #screen-game.play-only .board-column { --coord-w: calc(2 * var(--coord-g)); }
}
.board-coords { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.coord-cols, .coord-rows {
  /* The flat board's 2px frame lies outside the box the labels are placed
     in; the tactile tray has none. */
  --coord-edge: 2px;
  position: absolute;
  display: grid;
  color: var(--muted);
  font-family: var(--font-digits);
  font-variant-numeric: tabular-nums;
  /* The board is the labels' container (100cqi is its width), so they grow
     with its cells, as its digits do. */
  font-size: clamp(9.5px, 100cqi / 9 * .25, 14px);
  font-weight: 760;
  line-height: 1;
}
.coord-cols { left: 0; right: 0; bottom: calc(100% + var(--coord-edge) + 2px); height: calc(var(--coord-g) - var(--coord-edge) - 2px); grid-template-columns: repeat(9, minmax(0, 1fr)); column-gap: 1px; }
.coord-rows { top: 0; bottom: 0; right: calc(100% + var(--coord-edge) + 2px); width: calc(var(--coord-g) - var(--coord-edge) - 2px); grid-template-rows: repeat(9, minmax(0, 1fr)); row-gap: 1px; }
.coord { display: flex; align-items: center; justify-content: center; min-width: 0; min-height: 0; border-radius: 5px; white-space: nowrap; }
.coord-row { justify-content: flex-end; padding-right: 1px; }
.coord i { font-style: normal; font-weight: 600; font-size: .78em; opacity: .7; margin-right: .5px; }
/* The selected cell's row and column: its reference read off the two edges. */
.coord.on { color: var(--on-accent); background: var(--accent); }
.coord-row.on { justify-content: center; padding-right: 0; }
.coord.on i { opacity: .85; }
:root.tactile #screen-game.play-only .coord-cols {
  --coord-edge: 0px;
  padding-inline: calc(var(--t-size) * .026);
  grid-template-columns: repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr));
  column-gap: var(--t-cell-gap);
}
:root.tactile #screen-game.play-only .coord-rows {
  --coord-edge: 0px;
  padding-block: calc(var(--t-size) * .026);
  grid-template-rows: repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr)) var(--t-box-gap) repeat(3, minmax(0, 1fr));
  row-gap: var(--t-cell-gap);
}
:root.tactile #screen-game.play-only .coord-col { grid-column: var(--t); grid-row: 1; }
:root.tactile #screen-game.play-only .coord-row { grid-row: var(--t); grid-column: 1; }

/* Digit focus. The chosen digit's numbers and candidates stand out and the
   rest step back. Anything the coach, a walkthrough, the player's marks or a
   checked answer coloured is left exactly as drawn — the :where()s keep these
   rules weaker than every one of those — and walkthroughs suspend it. */
.sudoku-board[data-focus-digit] .candidate:where(:not(:empty, .pattern, .linked, .remove, .proposed, .answer-correct, .answer-wrong, .selected-digit, [class*="lesson-"])) { opacity: .24; }
:is(
  .sudoku-board[data-focus-digit="1"] .candidate[data-digit="1"],
  .sudoku-board[data-focus-digit="2"] .candidate[data-digit="2"],
  .sudoku-board[data-focus-digit="3"] .candidate[data-digit="3"],
  .sudoku-board[data-focus-digit="4"] .candidate[data-digit="4"],
  .sudoku-board[data-focus-digit="5"] .candidate[data-digit="5"],
  .sudoku-board[data-focus-digit="6"] .candidate[data-digit="6"],
  .sudoku-board[data-focus-digit="7"] .candidate[data-digit="7"],
  .sudoku-board[data-focus-digit="8"] .candidate[data-digit="8"],
  .sudoku-board[data-focus-digit="9"] .candidate[data-digit="9"]
):where(:not(:empty, .pattern, .linked, .remove, .proposed, .answer-correct, .answer-wrong, .selected-digit, [class*="lesson-"])),
.bt-mark.bt-focus-hit:where(:not([class*="bt-r-"])) {
  opacity: 1;
  color: var(--text);
  font-weight: 900;
  background: color-mix(in srgb, var(--text) 14%, transparent);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--text) 78%, transparent);
}
/* Placed numbers: the chosen digit is ringed; a player's own is underlined as
   well, so givens and entries stay apart on the tactile board too, where they
   share a face. Other numbers fade. */
.sudoku-board[data-focus-digit] .cell:where(:not(.focus-value, .selected, .same-value, .wrong, .invalid, .clash, .proposed, .pattern-cell, .link-cell, [class*="lesson-"])) .value { opacity: .38; }
.cell:where(.focus-value) { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--text) 72%, transparent); }
:root.tactile #screen-game.play-only .cell:where(.focus-value) { --ring: 0 0 0 2px color-mix(in srgb, var(--text) 72%, transparent); }
.cell.focus-entered .value { text-decoration: underline; text-decoration-thickness: .07em; text-underline-offset: .12em; }
/* A cell picked for the inspector during a walkthrough. */
.sudoku-board .cell.inspected { outline: 2px dashed var(--text); outline-offset: -5px; }

/* The board view toggle, among the board tools; the badge is the digit in focus. */
.board-view-toggle { position: relative; }
.board-view-badge {
  position: absolute; top: -3px; right: -3px; z-index: 1;
  display: grid; place-items: center; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: var(--accent); color: var(--on-accent);
  font: 800 11px/1 var(--font-digits); pointer-events: none;
}
.board-view-badge[hidden] { display: none; }
.board-view-toggle.focusing:not([aria-pressed="true"]) { color: var(--accent); }
.board-view-toggle.paused .board-view-badge { opacity: .5; }
:root.tactile #screen-game.play-only .history-controls > .board-view-toggle .board-view-badge { top: -2px; right: calc(50% - var(--round) / 2 - 4px); }

/* Stacked, the toolbar and the row under the numbers keep the width the
   board would have without these tools: they are sized to the board, and on
   a flat board a narrower toolbar wraps onto a second row and takes yet more
   of the board's height. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.board-coords-on #screen-game.play-only :is(.board-toolbar, .board-footer),
  #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > :is(.board-toolbar, .board-footer) {
    width: min(600px, max(var(--board-size), 100cqi - 2 * var(--col-pad)));
  }
}

/* On a narrow flat board, where the board tools are up in the toolbar, the
   toggle stays in the row under the numbers, beside New puzzle / Fill
   candidates — and keeps that row up during a walkthrough. */
#screen-game .board-footer:has(> .board-view-toggle:not(.hidden)) { display: flex; }
.board-footer > .board-view-toggle {
  flex: none; min-width: 48px; min-height: 44px; margin-left: auto; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); color: var(--text);
  --toolbar-icon: 18px;
}
/* placeHistoryControls (app.mjs) measures the row under the numbers to see
   whether the board tools fit there; the toggle is not one of them, so it is
   left out of that measurement and they land where they always have. */
.board-footer.measuring .board-view-toggle { display: none; }
/* A flat board held upright with the board tools under the numbers: the
   toggle waits up in the toolbar, beside Hint, as a key like the theme's. */
.board-toolbar > .board-view-toggle {
  flex: 0 0 auto; display: grid; place-items: center; width: 38px; height: 38px; min-height: 38px; padding: 0; margin-left: auto;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2); color: var(--text);
  --toolbar-icon: 17px;
}
.board-toolbar > .board-view-toggle + .coach-open { margin-left: 0; }
.board-toolbar > .board-view-toggle .history-label { display: none; }
:is(.board-toolbar, .board-footer) > .board-view-toggle[aria-pressed="true"] { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: color-mix(in srgb, var(--accent) 16%, var(--surface-2)); }
:is(.board-toolbar, .board-footer) > .board-view-toggle:hover { border-color: var(--accent); }
/* Beside a flat board, the board tools are a row of keys under the numbers;
   with the toggle there are four, so they keep a thumb's height (44px) even
   in a tablet's narrower column. */
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root:not(.tactile) #screen-game.play-only .board-footer .history-controls:has(> .board-view-toggle) .text-button { aspect-ratio: auto; height: max(44px, (var(--side) - 24px) / 4 / 1.6); }
}

/* The panel: under the board when stacked, in the controls column beside it
   on its side. Its height under the board is measured (--bt-h) and the board
   gives it up, so the numbers below never move off screen. */
.board-tools {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  width: min(100%, 600px);
  margin: 10px auto 0;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface-2);
  color: var(--text);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .12);
}
.board-tools[hidden] { display: none; }
/* Laid out by its own width (below), whichever column it is in. */
.board-tools { container-type: inline-size; }
/* As wide as the board would be without it, never narrower: its height must
   not depend on the board it takes room from, or the two would chase each
   other down (a narrower panel wraps taller and shrinks the board again). */
#screen-game.play-only .board-tools { width: min(600px, max(var(--board-size), 100cqi - 2 * var(--col-pad))); }
.bt-inspector { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "cell info actions"; gap: 10px; align-items: start; }
.bt-cell { grid-area: cell; width: 74px; height: 74px; display: grid; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.bt-cell-candidates { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(3, minmax(0, 1fr)); gap: 1px; padding: 3px; }
/* The inspector's candidates, coloured exactly as the board colours them (the
   .candidate rules near the top of this file and the walkthrough's tones). */
.bt-mark { display: grid; place-items: center; min-width: 0; min-height: 0; border-radius: 50%; color: var(--text); font-family: var(--font-digits); font-variant-numeric: tabular-nums; font-size: 18px; font-weight: 760; line-height: 1; }
.bt-mark.bt-r-pattern, .bt-mark.bt-r-lesson-a { color: var(--on-accent); background: var(--pattern); }
.bt-mark.bt-r-linked, .bt-mark.bt-r-lesson-b { color: var(--on-success); background: var(--link); }
.bt-mark.bt-r-proposed { color: var(--on-accent); background: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }
.bt-mark.bt-r-remove, .bt-mark.bt-r-lesson-remove { color: var(--on-accent); background: var(--danger); text-decoration: line-through; }
.bt-mark.bt-r-answer-correct { color: var(--on-success); background: var(--success); }
.bt-mark.bt-r-answer-wrong { color: var(--on-accent); background: var(--danger); }
.bt-mark.bt-r-lesson-key { font-weight: 850; box-shadow: inset 0 0 0 1.5px var(--text); }
.bt-mark.bt-r-lesson-off { color: var(--muted); text-decoration: line-through; box-shadow: inset 0 0 0 1px var(--faint); }
.bt-mark.bt-focus-other { opacity: .3; }
.bt-cell-value { place-items: center; }
.bt-value { font-family: var(--font-digits); font-size: 40px; font-weight: 520; line-height: 1; }
.bt-given .bt-value { font-weight: 760; }
.bt-entered .bt-value { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 4px; }
.bt-proposed .bt-value { color: var(--accent); font-weight: 760; }
.bt-checked-right .bt-value { color: var(--success); }
.bt-checked-wrong .bt-value { color: var(--danger); }
.bt-lesson .bt-value { color: var(--pattern); font-weight: 820; }
.bt-cell-value.bt-focus-hit { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--text) 72%, transparent); }
.bt-info { grid-area: info; min-width: 0; }
.bt-info p, .bt-roles { margin: 0; }
.bt-ref { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 8px; }
.bt-ref strong { font-family: var(--font-digits); font-size: 1.1rem; font-weight: 800; }
.bt-ref span { color: var(--muted); font-size: .72rem; }
.bt-state { margin-top: 3px !important; font-size: .82rem; line-height: 1.35; }
.bt-roles { padding: 0; list-style: none; color: var(--muted); font-size: .74rem; line-height: 1.35; }
.bt-head-actions { grid-area: actions; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.bt-close { display: grid; place-items: center; width: var(--board-target); height: var(--board-target); padding: 0; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--text); cursor: pointer; }
.bt-coords { display: grid; place-items: center; width: var(--board-target); height: var(--board-target); padding: 0; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--muted); cursor: pointer; }
.bt-coords[aria-pressed="true"] { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 60%, transparent); background: color-mix(in srgb, var(--accent) 14%, var(--surface)); }
.bt-close:hover, .bt-coords:hover { border-color: var(--accent); }
.bt-focus { min-width: 0; margin: 0; padding: 0; border: 0; }
.bt-legend { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 2px 10px; width: 100%; margin: 0 0 6px; padding: 0; color: var(--muted); font-size: .7rem; font-weight: 760; }
.bt-legend > span:first-child { letter-spacing: .06em; text-transform: uppercase; }
.bt-summary { font-weight: 600; }
.bt-digits { display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(9, minmax(0, 1fr)); gap: 4px; }
.bt-digit { position: relative; display: grid; place-items: center; min-width: 0; min-height: var(--board-target); border: 1px solid var(--line); border-radius: 9px; background: var(--surface); color: var(--text); font-family: var(--font-digits); font-size: 16px; font-weight: 760; cursor: pointer; }
.bt-digit-all { font-family: inherit; font-size: .74rem; }
.bt-digit input { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); margin: 0; opacity: 0; cursor: pointer; }
.bt-digit:hover { border-color: var(--accent); }
.bt-digit.on { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.bt-digit:has(input:focus-visible) { outline: 3px solid var(--text); outline-offset: 2px; }
.bt-note { margin: 0; color: var(--muted); font-size: .76rem; line-height: 1.4; }
.bt-note[hidden] { display: none; }
.bt-fill { min-height: var(--board-target); padding: 6px 10px; color: var(--accent); font-weight: 760; }
.bt-fill[hidden] { display: none; }
.board-tools button:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 60%, transparent); outline-offset: 2px; }
:root.tactile #screen-game.play-only .board-tools { border-color: var(--tac-tray-edge); background: linear-gradient(180deg, var(--tac-tray-top), var(--tac-tray-bottom)); }
:root.tactile #screen-game.play-only .bt-cell { background: var(--tac-well); box-shadow: inset 0 2px 5px var(--tac-sunk); border-color: transparent; }
:root.tactile #screen-game.play-only .bt-digit:not(.on) { border-color: transparent; background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom)); box-shadow: var(--tac-lift), 0 2px 0 var(--tac-lip); }

/* A short screen held upright (an iPhone SE): the same panel, tighter, so the
   board keeps its size and the number pad stays on screen. */
@media (max-height: 740px) and (not ((min-aspect-ratio: 11/10) and (min-width: 700px))) {
  .board-tools { gap: 6px; padding: 8px; }
  .bt-inspector { gap: 8px; align-items: center; }
  .bt-cell { width: 50px; height: 50px; }
  .bt-mark { font-size: 13px; }
  .bt-value { font-size: 28px; }
  .bt-ref strong { font-size: .95rem; }
  .bt-ref span { display: none; }
  .bt-state { font-size: .76rem; }
  .bt-roles { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; }
  .bt-head-actions { flex-direction: row; gap: 4px; }
  .bt-legend { margin-bottom: 4px; }
  .bt-digit { min-height: var(--board-target); }
}

/* The same tightening wherever the board has shrunk as far as it goes and
   the page still runs over (board-tools.mjs adds .bt-tight): the largest text
   on a phone, with a drill's two rows of answer keys. */
.board-tools.bt-tight { gap: 6px; padding: 8px; }
.bt-tight .bt-inspector { gap: 8px; align-items: center; }
.bt-tight .bt-cell { width: 56px; height: 56px; }
.bt-tight .bt-mark { font-size: 14px; }
.bt-tight .bt-value { font-size: 30px; }
.bt-tight .bt-ref span { display: none; }
.bt-tight .bt-state { font-size: .76rem; }
.bt-tight .bt-roles { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; }
.bt-tight .bt-head-actions { flex-direction: row; gap: 4px; }
.bt-tight .bt-legend { margin-bottom: 4px; }
.bt-tight .bt-digit { min-height: var(--board-target); }

/* Upright, tactile: in the spare row between the board and its tools. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  /* Centred on the board's tracks even when wider than them. */
  :root.tactile #screen-game.play-only .board-tools { grid-row: 5; grid-column: 1 / -1; align-self: start; justify-self: start; position: relative; left: 50%; translate: -50% 0; margin: 12px 0 0; }
}
@media (max-height: 740px) and (not ((min-aspect-ratio: 11/10) and (min-width: 700px))) {
  :root.tactile #screen-game.play-only .board-tools { margin-top: 6px; }
}
/* Upright, tactile, with the panel open: the board gives up the panel's
   height, but the rows of keys under it do not shrink with it. They keep the
   size they have with the panel shut (--board-free: the same sum as
   --board-size, without --bt-h), so at a large text size, where the board has
   to give up the most, the round tools, the number keys and New puzzle / Fill
   candidates stay a thumb's size and their labels stay apart. The keys are
   then a little wider than the board's columns until the panel shuts. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) {
    --board-free: max(240px, min(100cqi - 2 * var(--col-pad) - var(--coord-w, 0px), (var(--play-h) - var(--stack-fixed) - var(--counter-h) - var(--coord-g, 0px)) / var(--pad-ratio)));
    grid-template-columns: calc(var(--board-free) * .25) calc(var(--board-free) * .5) calc(var(--board-free) * .25);
  }
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .history-controls,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .board-actions { width: var(--board-free); }
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > .number-pad { --t-size: var(--board-free); width: var(--board-free); }
}

/* During a walkthrough (.bt-in-lesson) the focus is paused and its digits are
   put away: the panel is the inspector alone. Held upright it sits between
   the board and the explanation, so a tapped cell is read next to the step
   that names it — the flat board's column already has it there (the panel
   follows the board in the page); the tactile grid is given a row for it. */
.bt-focus[hidden] { display: none; }
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools.bt-in-lesson:not([hidden])) {
    grid-template-rows: repeat(5, auto) minmax(0, 1fr) repeat(3, auto);
    grid-template-areas:
      "back status theme"
      "seg seg seg"
      "board board board"
      "inspect inspect inspect"
      "lesson lesson lesson"
      ". . ."
      "tools tools tools"
      "pad pad pad"
      "actions actions actions";
  }
  :root.tactile #screen-game.play-only .board-tools.bt-in-lesson { grid-area: inspect; }
}
/* On its side: in the controls column's spare row, as wide as the column. */
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  #screen-game.play-only .board-tools { grid-row: 5; grid-column: 1; align-self: start; width: 100%; margin: 0 0 10px; }
  :root.tactile #screen-game.play-only .board-tools { grid-row: 6; grid-column: 1; }
}
/* A narrow column (a tablet's controls column): the details under the cell,
   and the digits in two rows of five. */
@container (max-width: 300px) {
  .bt-inspector { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "cell actions" "info info"; }
  .bt-ref span { display: none; }
}
/* Beside a flat board the keypad shares the column with the open panel, so
   its keys are a little shorter meanwhile (still well over 44px). */
@media (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root:not(.tactile) #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .number-pad button { aspect-ratio: 1.6; }
}
@container (max-width: 540px) {
  .bt-digits { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* --- On its side, short: a phone turned landscape (both designs) ---------------- */

/* Held on its side a phone is 320–430px tall, so nothing can sit above or
   below the board: the tablet's landscape grid (round tools stacked down the
   right, keys under the board) needed about 590px, and the upright stack
   about 500px, and either way the number keys landed under the screen. Here
   the board takes the full height in the middle, and the controls go either
   side of it:

     left   the way back and the theme, level and mistakes, the notices, the
            board view panel, and New puzzle / Fill candidates at the foot;
     right  the board tools (Notes, Undo, Redo, View, Hint) as round keys in
            rows of three, and the number keys as a 3 × 3 keypad under them,
            down by the right thumb.

   Every key keeps a thumb's size (48px round tools, 50px number keys); the
   board gives up width instead. The coach is a drawer over the left column,
   as on a tablet, so the keypad and Hint stay in reach while it is open.
   board-tools.mjs and app.mjs know this query as SHORT_LANDSCAPE. */
/* The board's size, worked out once on the column. 100cqi there is the
   panel's width; a piece inside the column reading the same sum would take
   100cqi from the column itself (it is a container too) and come out smaller
   than the board's own track. Registered as a length, the property inherits
   as the number of pixels the column worked out instead of as the sum. */
@property --short-board {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) {
  /* Edge to edge, whatever the pointer: no card, no margin, no border. */
  :root:is(.tactile, :not(.tactile)) main:has(> #screen-game.play-only:not([hidden])) { width: 100%; padding-bottom: 0; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .workspace { margin-top: 0; border: 0; border-radius: 0; box-shadow: none; background: transparent; backdrop-filter: none; }

  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column {
    --col-pad: 6px;
    /* The game screen drops the body's left padding (the rail's room), and
       with it the left safe area; a notch on the left is kept clear here. The
       right one is the body's own padding, the top and bottom are in --play-h. */
    --inset-l: env(safe-area-inset-left, 0px);
    --play-h: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 2 * var(--col-pad));
    /* Number keys, and the gap between them; the round tools above them. */
    --ck: 50px;
    --ck-gap: 6px;
    --side-r: calc(3 * var(--ck) + 2 * var(--ck-gap));
    --side-gap: 12px;
    /* The left column never narrows past this: the panel and the coach's
       drawer are read there. The board takes what is left, up to the height. */
    --side-l-min: 180px;
    --side-room: calc(100cqi - 2 * var(--col-pad) - var(--inset-l) - var(--side-r) - 2 * var(--side-gap) - var(--coord-w, 0px));
    --short-board: max(180px, min(var(--play-h) - var(--coord-g, 0px), var(--side-room) - var(--side-l-min)));
    --board-size: var(--short-board);
    --side-l: clamp(var(--side-l-min), var(--side-room) - var(--board-size), 300px);
    display: grid;
    grid-template-columns: var(--side-l) var(--board-size) var(--side-r);
    /* back · entry mode · mistakes · notice · inspector (walkthrough) ·
       walkthrough · spare · actions. The board and both right-hand pieces span
       every row, the spare one included, so only the left column sizes them. */
    grid-template-rows: repeat(6, auto) minmax(0, 1fr) auto;
    grid-template-areas: none;
    column-gap: var(--side-gap);
    row-gap: 0;
    justify-content: center;
    align-items: start;
    /* Exactly the screen's height, so the tools sit at the top and the keypad
       and the actions at the foot whatever the board's size, and the spare row
       is sized from the room left rather than from the board view panel that
       lies over it. */
    height: calc(var(--play-h) + 2 * var(--col-pad));
    min-height: 0;
    padding: var(--col-pad) var(--col-pad) var(--col-pad) calc(var(--col-pad) + var(--inset-l));
  }
  @supports (height: 100dvh) {
    :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column {
      --play-h: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 2 * var(--col-pad));
    }
  }
  /* The narrowest phones (568–620px wide) give the column a little less. */
  @media (max-width: 620px) {
    :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column { --side-l-min: 150px; }
  }
  /* The coordinates hang into the gaps either side of the board. */
  :root.board-coords-on #screen-game.play-only .board-column {
    --coord-w: calc(2 * var(--coord-g));
    column-gap: calc(var(--side-gap) + var(--coord-g));
  }
  /* The panel open no longer costs the board anything (it lies in the left
     column), so the upright layout's narrower tracks do not apply. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools:not([hidden])),
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools.bt-in-lesson:not([hidden])) {
    grid-template-columns: var(--side-l) var(--board-size) var(--side-r);
    grid-template-rows: repeat(6, auto) minmax(0, 1fr) auto;
    grid-template-areas: none;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar,
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-footer { display: contents; }

  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .sudoku-board { grid-area: 1 / 2 / -1 / 3; align-self: center; }

  /* Left: back and the theme share the top row, round keys at either end. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar :is(.play-menu, .play-theme) {
    width: 48px;
    height: 48px;
    min-height: 0;
    margin: 0;
    padding: 0;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 50%;
    color: var(--text);
    background: linear-gradient(180deg, var(--tac-face-top), var(--tac-face-bottom));
    box-shadow: var(--tac-lift), 0 3px 0 var(--tac-lip), 0 6px 12px var(--tac-drop);
    font-size: 1rem;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar :is(.play-menu, .play-theme):active { transform: translateY(2px); box-shadow: var(--tac-lift), 0 1px 0 var(--tac-lip); }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar .play-menu { grid-area: 1 / 1 / 2 / 2; justify-self: start; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar .play-menu .play-menu-label { display: none; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar .play-theme { grid-area: 1 / 1 / 2 / 2; justify-self: end; }

  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .mistake-counter {
    grid-area: 3 / 1 / 4 / 2;
    width: 100%;
    margin: 10px 0 0;
    align-items: flex-start;
    gap: 2px;
    font-size: .76rem;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .mistake-counter strong { font-size: 1.05rem; line-height: 1.2; }
  /* Value / Mark elimination: the words wrap rather than run out of the column. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar .segmented { grid-area: 2 / 1 / 3 / 2; width: 100%; margin: 10px 0 0; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar .segmented button { flex: 1 1 0; min-width: 0; min-height: 44px; padding: 6px 8px; line-height: 1.15; text-wrap: balance; }
  /* A refused move's notice sits in the column, not over the board. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .game-feedback {
    grid-area: 4 / 1 / 5 / 2;
    position: static;
    top: auto;
    left: auto;
    transform: none;
    width: 100%;
    margin: 10px 0 0;
    pointer-events: auto;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .lesson-panel { grid-area: 6 / 1 / 7 / 2; width: 100%; margin: 10px 0 0; padding: 12px; }

  /* New puzzle and Fill candidates at the foot: side by side while their
     words fit, one above the other at a large text size. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-actions {
    grid-area: 8 / 1 / 9 / 2;
    width: 100%;
    margin: 10px 0 0;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-actions.hidden { display: none; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-actions button { flex: 1 1 0; min-width: min-content; min-height: 48px; margin-bottom: 4px; padding: 6px 10px; }

  /* Right: the board tools, three to a row, in the order they are in the page
     (so Tab follows them as they read), with their names under them. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls {
    --round: 48px;
    grid-area: 1 / 3 / -1 / 4;
    align-self: start;
    width: 100%;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-flow: row;
    column-gap: var(--ck-gap);
    row-gap: 8px;
    justify-items: stretch;
    align-items: start;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls > button { position: relative; order: 0; margin: 0; gap: 4px; font-size: .7rem; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls > .coach-open { order: 0; margin: 0; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls .history-label,
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls #coach-open-label { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

  /* The keypad: three by three under the tools, at the foot of the column. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad,
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > .number-pad {
    grid-area: 1 / 3 / -1 / 4;
    align-self: end;
    width: 100%;
    margin: 0;
    padding: 0;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--ck-gap);
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad button,
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad.answer-pad button {
    grid-column: auto;
    aspect-ratio: auto;
    height: var(--ck);
    min-height: 0;
    border-radius: 12px;
    font-size: calc(var(--ck) * .48);
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad button:nth-child(n) { grid-column: auto; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad .pad-caption { font-size: .76rem; margin-bottom: 2px; }

  /* The width the upright layout gives these while the panel is open. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) :is(.history-controls, .board-actions) { width: 100%; }

  /* The board view panel lies over the left column under the way back (and
     the entry mode, in a challenge), so opening it moves nothing. It is as
     tall as its content: New puzzle / Fill candidates stay in reach under it
     unless it needs their room too — the note offering to fill the
     candidates, which is that same button — and it scrolls inside itself
     past the foot of the screen. In a walkthrough it is the inspector alone,
     and takes its own row above the explanation instead, so the step it is
     read against stays in view. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-tools {
    grid-area: 3 / 1 / 9 / 2;
    position: relative;
    z-index: 6;
    left: auto;
    translate: none;
    align-self: start;
    justify-self: stretch;
    width: auto;
    max-height: calc(100% - 10px);
    overflow-y: auto;
    overscroll-behavior: contain;
    margin: 10px 0 0;
    gap: 6px;
    padding: 8px;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-tools.bt-in-lesson { grid-area: 5 / 1 / 6 / 2; max-height: none; overflow: visible; }
  /* Under the panel the mistakes would show at its rounded corners. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools:not([hidden], .bt-in-lesson)) > .mistake-counter { visibility: hidden; }
  /* Tighter inside, as on a short upright phone; the close and coordinates
     buttons and the digits keep a thumb's height. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-inspector { gap: 6px 8px; align-items: center; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-cell { width: 52px; height: 52px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-mark { font-size: 13px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-value { font-size: 28px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-ref strong { font-size: .95rem; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-ref span { display: none; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-state { font-size: .76rem; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-roles { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-head-actions { flex-direction: row; gap: 4px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only :is(.bt-close, .bt-coords) { width: var(--board-target); height: var(--board-target); }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-legend { margin-bottom: 4px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .bt-digit { min-height: var(--board-target); }

  /* The coach: a drawer over the left column (placeCoachDrawer, app.mjs, sets
     its box), not the sheet a phone otherwise slides up over the board. */
  :root:is(.tactile, :not(.tactile)) #screen-game .workspace .coach-panel {
    position: absolute;
    z-index: 30;
    left: var(--drawer-left, 12px);
    top: var(--drawer-top, 12px);
    right: auto;
    bottom: auto;
    width: var(--drawer-width, 300px);
    height: var(--drawer-height, calc(100% - 24px));
    max-height: none;
    padding: 14px 14px 16px;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--surface-2);
    border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--line));
    border-radius: 16px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .35);
    animation: drawer-in .2s cubic-bezier(.2, .9, .3, 1);
  }
  :root:is(.tactile, :not(.tactile)) #screen-game .workspace .coach-panel::before { content: none; }
  :root:is(.tactile, :not(.tactile)) #screen-game .workspace .coach-panel .hint-card { min-height: 0; flex: none; padding: 14px; }
  :root:is(.tactile, :not(.tactile)) #screen-game .workspace .coach-panel .coach-heading-row h2 { font-size: 1.2rem; }

  /* Shorter still (a 320px-tall phone): the tools keep their names for
     screen readers only, and the rows close up. */
  @media (max-height: 359px) {
    :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls .history-label,
    :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls #coach-open-label {
      position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
      overflow: hidden; clip-path: inset(50%); white-space: nowrap;
    }
  }
}
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) and (prefers-reduced-motion: reduce) {
  :root:is(.tactile, :not(.tactile)) #screen-game .workspace .coach-panel { animation: none; }
}

/* A number entered from the pad flies to its cell (flyDigit in app.mjs). */
.app-update { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin: 16px 0; padding: 14px 16px; border: 1px solid var(--accent); border-radius: 14px; background: var(--surface-2); }
.app-update[hidden] { display: none; }
.app-update p { margin: 0; flex: 1 1 240px; }
.app-update button { min-height: 48px; }
.app-startup { margin: 24px; color: var(--text); }

.flying-digit {
  position: fixed;
  z-index: 1000;
  display: grid;
  place-items: center;
  pointer-events: none;
  color: var(--accent);
  font-weight: 760;
  line-height: 1;
  transform-origin: center;
  will-change: transform;
  text-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 35%, transparent);
}
.cell.landing .value { visibility: hidden; }
.cell.landed .value { display: inline-block; animation: digit-land 286ms cubic-bezier(.2,.8,.3,1.4); }
@keyframes digit-land {
  from { transform: scale(1.35); }
  to { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) { .cell.landed .value { animation: none; } }

/* Storage refused: a quiet note pinned over the page, put away with ×. */
.storage-notice {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;
  width: min(calc(100% - 32px), 520px);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px 8px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: .78rem;
  line-height: 1.35;
}
.storage-notice[hidden] { display: none; }
.storage-notice p { margin: 0; flex: 1; }
.storage-notice-close {
  flex: none;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 1.1rem;
  cursor: pointer;
}
.storage-notice-close:hover, .storage-notice-close:focus-visible { color: var(--text); background: var(--line); }

/* The coach refusing to reason from pencil marks that rule out a real answer. */
.hint-card.warning::before { background: var(--danger); }
.hint-card .restore-marks { margin-top: 14px; }

/* Unassisted practice: the two ways to practise on a technique's page, the
   question panel on the board, and the mixed quiz's setup screen. Built from
   the same tokens and shapes as the challenge-mode choice and the guide card. */
.practice-group { flex: 1 1 100%; display: grid; gap: 6px; min-width: 0; }
.practice-group + .practice-group { margin-top: 4px; }
.practice-group-label { color: var(--muted); font-size: .72rem; font-weight: 780; letter-spacing: .06em; text-transform: uppercase; }
.practice-group-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.practice-group-buttons button { min-height: 44px; padding-inline: 16px; white-space: normal; }
.assess-summary { display: grid; gap: 4px; margin-top: 10px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
.assess-summary.ready { border-color: color-mix(in srgb, var(--accent-2) 60%, var(--line)); background: color-mix(in srgb, var(--accent-2) 10%, var(--surface)); }
.step-body .assess-ready { color: var(--text); font-size: .82rem; font-weight: 760; }
.assess-summary.ready .assess-ready { color: var(--accent-2); }
.step-body .assess-desc, .step-body .assess-counts { font-size: .78rem; line-height: 1.45; }
.step-body .assess-counts { color: var(--text); }

/* The question panel. */
.assess-card .hint-title { overflow-wrap: anywhere; }
.assess-picker { min-width: 0; margin: 4px 0 14px; padding: 0; border: 0; }
.assess-picker legend { padding: 0; margin: 0 0 8px; color: var(--muted); font-size: .72rem; font-weight: 780; letter-spacing: .06em; text-transform: uppercase; }
.assess-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 150px), 1fr)); gap: 8px; }
.assess-option { position: relative; display: grid; gap: 2px; min-height: 48px; padding: 8px 10px 8px 36px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); cursor: pointer; }
.assess-option:hover { border-color: var(--accent); }
.assess-option input { position: absolute; left: 11px; top: 12px; width: 18px; height: 18px; margin: 0; accent-color: var(--accent); }
.assess-option:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
.assess-option:has(input:focus-visible) { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 2px; }
.assess-option:has(input:disabled) { cursor: default; opacity: .7; }
.assess-option-name { color: var(--text); font-size: .84rem; font-weight: 720; overflow-wrap: anywhere; }
.assess-option-type { color: var(--muted); font-size: .7rem; }
.hint-card .assess-rules { margin-top: 10px; font-size: .76rem; }
.hint-card .assess-note { margin-top: 6px; font-size: .74rem; }
.hint-meta .assess-flag { color: var(--text); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); background: color-mix(in srgb, var(--accent) 12%, var(--surface-3)); font-weight: 700; }
.assess-card .answer-feedback { margin: 16px 0 0; }
.answer-feedback.info { background: var(--surface-3); border-color: var(--line); }
.answer-feedback.info .answer-icon { background: var(--surface-2); color: var(--text); }
.assess-reveal { margin-top: 14px; padding: 12px 14px; border-left: 3px solid var(--accent-2); border-radius: 0 10px 10px 0; background: color-mix(in srgb, var(--accent-2) 9%, transparent); }
.assess-reveal .hint-kicker { margin-bottom: 6px; }
.hint-card .assess-progress { margin-top: 14px; color: var(--text); font-size: .78rem; }
.assess-progress span { display: block; color: var(--muted); font-size: .68rem; font-weight: 780; letter-spacing: .06em; text-transform: uppercase; }
.assess-results, .assess-readiness { display: grid; gap: 6px; margin: 12px 0 0; padding: 0; list-style: none; }
.assess-results li, .assess-readiness li { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 10px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-3); font-size: .78rem; }
.assess-result-name { color: var(--text); font-weight: 700; }
.assess-result-outcome { color: var(--muted); }
.assess-result-outcome.independent { color: var(--accent-2); font-weight: 750; }
.assess-readiness li span:last-child { color: var(--muted); }
.hint-card .assess-subhead { margin-top: 16px; color: var(--muted); font-size: .7rem; font-weight: 780; letter-spacing: .06em; text-transform: uppercase; }

/* Mixed practice: the entry on Learn and the setup screen. */
.mixed-entry { background: var(--surface-2); border-color: var(--line); }
.mixed-entry .guide-actions button { min-height: 48px; padding-inline: 18px; }
.mixed-form { display: grid; gap: 16px; }
.mixed-set { min-width: 0; margin: 0; padding: 0; border: 0; }
.mixed-default { margin: 0 0 8px; color: var(--muted); font-size: .84rem; line-height: 1.5; }
.mixed-quick { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-bottom: 8px; }
.mixed-quick .text-button { min-height: 44px; color: var(--accent); font-size: .82rem; font-weight: 750; }
.mixed-groups { display: grid; gap: 14px; }
.mixed-group { min-width: 0; margin: 0; padding: 0; border: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 190px), 1fr)); gap: 8px; }
.mixed-group legend { padding: 0; margin: 0 0 8px; color: var(--muted); font-size: .72rem; font-weight: 780; letter-spacing: .1em; text-transform: uppercase; }
.mixed-option { position: relative; display: grid; gap: 2px; min-height: 48px; padding: 9px 12px 9px 40px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-2); cursor: pointer; }
.mixed-option:hover { border-color: var(--accent); }
.mixed-option input { position: absolute; left: 13px; top: 14px; width: 18px; height: 18px; margin: 0; accent-color: var(--accent); }
.mixed-option:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); }
.mixed-option:has(input:focus-visible) { outline: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 2px; }
.mixed-option-name { color: var(--text); font-size: .86rem; font-weight: 720; overflow-wrap: anywhere; }
.mixed-option-tag { color: var(--accent-2); font-size: .7rem; font-weight: 700; }
.mixed-option-tag:empty { display: none; }
.mixed-count { margin: 0; color: var(--text); font-size: .84rem; font-weight: 700; }
.mixed-rules ul { margin: 0; padding-left: 1.1em; display: grid; gap: 6px; color: var(--muted); font-size: .84rem; line-height: 1.5; }
.mixed-replace, .mixed-error { margin: 0; font-size: .84rem; line-height: 1.5; }
.mixed-replace { color: var(--text); }
.mixed-error { color: var(--danger); font-weight: 700; }
.mixed-replace[hidden], .mixed-error[hidden] { display: none; }
#mixed-start { min-height: 50px; justify-self: start; padding-inline: 22px; white-space: normal; }
#mixed-resume[hidden] { display: none; }
@media (max-width: 520px) { #mixed-start { justify-self: stretch; } }

/* Shared board-control hit regions. Layout uses these real boxes, never
   pseudo-element hit slop, so adjacent choices remain separate. */
#screen-game { --board-target: 48px; }
:root:is(.tactile, :not(.tactile)) #screen-game.play-only :is(.board-toolbar button, .history-controls > button, .board-view-toggle, .coach-open, .board-actions button) {
  min-width: var(--board-target);
  min-height: var(--board-target);
}
#screen-game.play-only .board-toolbar > .board-view-toggle { width: var(--board-target); height: var(--board-target); }
#screen-game.play-only .number-pad button { min-width: var(--board-target); min-height: var(--board-target); }
/* Ten native radio labels: auto-fit also handles the narrow landscape panel.
   Fixed minimum tracks make container width, not a phone breakpoint, decisive. */
.bt-digits { grid-template-columns: repeat(auto-fit, minmax(var(--board-target), 1fr)); gap: 6px; }
.bt-digit { min-width: var(--board-target); min-height: var(--board-target); }
@container (min-width: 270px) and (max-width: 540px) {
  .bt-digits { grid-template-columns: repeat(5, minmax(var(--board-target), 1fr)); }
}
/* Upright controls keep the column's available width even when View gives
   up board height. Five digits then four, in the same reading/Tab order. */
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  #screen-game.play-only .board-column { --stack-fixed: 320px; --pad-ratio: 1; }
  #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > .number-pad {
    width: min(600px, calc(100cqi - 2 * var(--col-pad)));
    grid-template-columns: repeat(5, minmax(var(--board-target), 1fr));
    gap: 6px; padding-inline: 0;
  }
  #screen-game.play-only .number-pad button,
  :root.tactile #screen-game.play-only .number-pad button { aspect-ratio: auto; height: max(var(--board-target), 2.5rem); }
  :root.tactile #screen-game.play-only .number-pad button:nth-child(n) { grid-column: auto; }
  :root.tactile #screen-game.play-only .history-controls,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .history-controls {
    width: min(600px, calc(100cqi - 2 * var(--col-pad))); padding-inline: 0; --round: 48px;
  }
}
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) {
  /* Restore the three-column compact geometry after upright overrides. */
  #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > .number-pad {
    width: 100%; grid-template-columns: repeat(3, minmax(var(--board-target), 1fr));
  }
  :root.tactile #screen-game.play-only .history-controls,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .history-controls { width: 100%; }
  #screen-game.play-only .number-pad button { height: max(50px, 2.5rem); }
  #screen-game.play-only .history-controls { flex-direction: row; }
  #screen-game.play-only .history-controls > button { display: grid; justify-items: center; padding: 0; }
  #screen-game.play-only .history-controls > button svg { width: 48px; height: 48px; padding: 14px; }
  :root:not(.tactile) #screen-game.play-only :is(.play-menu, .play-theme) { background: var(--surface-2); box-shadow: none; }
  :root:not(.tactile) #screen-game.play-only .history-controls > button { background: var(--surface-2); border-radius: 9px; }
}

:root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-actions button { min-height: var(--board-target); }
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root:not(.tactile) #screen-game.play-only .board-footer { flex-wrap: wrap; }
  :root:not(.tactile) #screen-game.play-only .board-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); flex: 1 1 100%; }
}
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) {
  /* Keep the user's font size. Icon names stay accessible; removing their
     visible row leaves room for three digit rows and a practice caption. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls :is(.history-label, #coach-open-label) {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .history-controls > button { height: 48px; }
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad button { height: 50px; font-size: max(24px, 1rem); }
  :root:not(.tactile) #screen-game.play-only .board-actions { display: flex; flex: none; }
}
/* Reserve the summary row even for All: choosing a digit changes its text,
   without changing panel height and moving the next target during a tap. */
.bt-summary { flex: 0 0 100%; min-height: 1.35em; }
.bt-digit { font-size: max(16px, 1rem); }
.bt-digit-all { font-size: .74rem; }
#screen-game.play-only .number-pad button { font-size: max(1rem, 16px, var(--board-size) / 9 * .42); }
:root.tactile #screen-game.play-only .number-pad button { font-size: max(1rem, 19.2px, var(--t-size) * (1 - 2 * .026) / 9 * .58); }
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) {
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .number-pad button { font-size: max(24px, 1rem); }
}
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .history-controls { justify-self: center; margin-inline: 0; }
}
@media not all and (min-aspect-ratio: 11/10) and (min-width: 700px) {
  :root.tactile #screen-game.play-only .board-column { --stack-fixed: 320px; }
}
/* Upright tablets can fit nine targets on the 600px control strip. */
@media (min-width: 561px) and (max-aspect-ratio: 10/11) {
  :root.tactile #screen-game.play-only .board-column { --stack-fixed: 278px; }
  #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .number-pad,
  :root.tactile #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) > .number-pad {
    grid-template-columns: repeat(9, minmax(var(--board-target), 1fr));
  }
}
@media (min-aspect-ratio: 13/10) and (max-height: 500px) and (min-width: 560px) {
  /* The older flat View rule uses aspect-ratio: 1.6. A 50px minimum height
     would then force 80px-wide buttons out of their 50px grid tracks. */
  :root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-column:has(> .board-tools:not([hidden])) .number-pad button {
    aspect-ratio: auto; width: 100%;
  }
}
:root:is(.tactile, :not(.tactile)) #screen-game.play-only .board-toolbar :is(.play-menu, .play-theme, [data-mode]) {
  min-width: var(--board-target); min-height: var(--board-target);
}


@media (min-aspect-ratio: 11/10) and (min-width: 700px) and (min-height: 501px) {
  /* Give the flexible spare row a stable viewport baseline. Without it the
     indefinite grid could shrink between touch release and the compatibility
     click, moving actions away from the finger. View can still grow the page. */
  :root.tactile #screen-game.play-only .board-column { min-height: calc(var(--play-h) + 2 * var(--col-pad)); }
}
