/* ==========================================================================
   Sudoku — stylesheet
   Original design. Every colour, size and font choice lives in the token block
   below, so the whole platform can be rebranded by editing :root.
   ========================================================================== */

/* ---------- Tokens ---------- */
:root {
  /* Warm paper + ink, with an indigo accent. */
  --bg:            #f7f6f3;
  --bg-sunk:       #efede8;
  --surface:       #ffffff;
  --surface-2:     #faf9f7;
  --ink:           #23201d;
  --ink-2:         #57514b;
  --ink-3:         #8b837b;
  --line:          #ddd8d0;
  --cage-line:     #9c9488;
  --cage-ink:      #6b6357;
  --line-strong:   #2c2825;

  --accent:        #4f46c8;
  --accent-soft:   #e9e8fb;
  --accent-mid:    #c8c5f2;
  --accent-ink:    #ffffff;

  --good:          #1c7d5a;
  --good-soft:     #dff2ea;
  --bad:           #c0392f;
  --bad-soft:      #fbe6e3;
  --warn:          #a86612;

  --focus:         #4f46c8;

  --radius:        10px;
  --radius-sm:     7px;
  --radius-lg:     16px;
  --unit:          4px;

  /* ---- Type ------------------------------------------------------------
     Sudoku was popularised in Japan, so the faces come from that tradition
     rather than from the generic "elegant serif" reflex.

     The signature is the board itself: the clues you are GIVEN are set in a
     mincho, the way a puzzle arrives printed on paper; the digits YOU write are
     set in a kyokasho face, which is what a Japanese school textbook uses and
     reads as pen on paper. Given versus entered therefore differs by the shape
     of the letterform, not only by weight and colour — which is also why the
     distinction survives for a colour-blind player.

     `--font-code` stays monospaced on purpose: the sync code is transcribed by
     hand from one device to another, and there ambiguity between 8/B or 0/O
     costs a real failure. Atmosphere does not get a vote on that one. */
  --font-display: 'Shippori Mincho', 'Hiragino Mincho ProN', 'Yu Mincho', Georgia, serif;
  --font-ui:      'Zen Kaku Gothic New', 'Hiragino Sans', 'Yu Gothic UI', system-ui, sans-serif;
  --font-entry:   'Klee One', 'Yu Kyokasho', 'Bradley Hand', cursive;
  --font-num:     'Shippori Mincho', 'Hiragino Mincho ProN', Georgia, serif;
  --font-code:    ui-monospace, 'SF Mono', 'Cascadia Mono', Consolas, monospace;
  --font: var(--font-ui);

  --shadow-sm: 0 1px 2px rgba(35, 32, 29, .07);
  --shadow:    0 4px 14px rgba(35, 32, 29, .10);
  --shadow-lg: 0 18px 50px rgba(35, 32, 29, .22);

  --board-max: 540px;
  /* Vertical space the board must leave for everything else. 0 by default so the
     height cap is inert on phones, where the board is width-bound and scrolling
     is expected; the wide two-column layout sets a real value. */
  --board-chrome: 0px;
}

/* Dark theme: explicit opt-in, plus automatic when the OS asks for it. */
:root[data-theme="dark"] {
  --bg:            #171513;
  --bg-sunk:       #100f0e;
  --surface:       #221f1c;
  --surface-2:     #2a2622;
  --ink:           #f2efe9;
  --ink-2:         #bdb5ab;
  --ink-3:         #8b8279;
  --line:          #3a352f;
  --cage-line:     #7d766c;
  --cage-ink:      #b3aca1;
  --line-strong:   #f2efe9;

  --accent:        #8b83f0;
  --accent-soft:   #2e2a4d;
  --accent-mid:    #4a4487;
  --accent-ink:    #171513;

  --good:          #5fcfa4;
  --good-soft:     #1c3a30;
  --bad:           #f08b80;
  --bad-soft:      #45211d;
  --warn:          #e0a44a;

  --focus:         #8b83f0;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
  --shadow:    0 4px 14px rgba(0, 0, 0, .55);
  --shadow-lg: 0 18px 50px rgba(0, 0, 0, .7);
}

@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --bg:            #171513;
    --bg-sunk:       #100f0e;
    --surface:       #221f1c;
    --surface-2:     #2a2622;
    --ink:           #f2efe9;
    --ink-2:         #bdb5ab;
    --ink-3:         #8b8279;
    --line:          #3a352f;
    --cage-line:     #7d766c;
    --cage-ink:      #b3aca1;
    --line-strong:   #f2efe9;
    --accent:        #8b83f0;
    --accent-soft:   #2e2a4d;
    --accent-mid:    #4a4487;
    --accent-ink:    #171513;
    --good:          #5fcfa4;
    --good-soft:     #1c3a30;
    --bad:           #f08b80;
    --bad-soft:      #45211d;
    --warn:          #e0a44a;
    --focus:         #8b83f0;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
    --shadow:    0 4px 14px rgba(0, 0, 0, .55);
    --shadow-lg: 0 18px 50px rgba(0, 0, 0, .7);
  }
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* The UA rule for [hidden] is `display: none` at UA origin, so ANY author rule
   setting display on the same element silently beats it. That shipped: .veil set
   `display: grid`, so the pause overlay stayed painted over the board forever —
   blurring it and swallowing every real click, while the code believed it was
   hidden. This one !important is the standard immunisation and covers every
   current and future toggled element. Do not remove it. */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; }

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

button { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 10px 16px;
  border-radius: 0 0 var(--radius) 0;
  z-index: 100;
}
.skip-link:focus { left: 0; }

/* ---------- Shell ---------- */
.app {
  max-width: 620px;
  margin: 0 auto;
  padding: calc(var(--unit) * 5) calc(var(--unit) * 4) calc(var(--unit) * 12);
  /* A height budget, so the board can be told to fit inside what is left after
     the chrome. Without it the single column measured 1125px on a 1280x720
     laptop — 405px of overflow, with New game and Restart below the fold. */
  min-height: 100dvh;
}

/* ---------- Header ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: calc(var(--unit) * 4);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
}
.brand-mark { width: 26px; height: 26px; color: var(--accent); flex: none; }
.brand-name {
  font-family: var(--font-display);
  font-size: 25px;
  font-weight: 500;
  /* Opened up rather than tightened: the mincho's vertical stress needs air, and
     the extra tracking is where the page gets its calm. */
  letter-spacing: .1em;
}

.topbar-actions { display: flex; align-items: center; gap: 4px; }

/* ---------- Buttons ---------- */
.btn {
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--radius-sm);
  padding: 8px 13px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s, transform .06s;
}
.btn:active { transform: translateY(1px); }

.btn-ghost { color: var(--ink-2); }
.btn-ghost:hover { background: var(--bg-sunk); color: var(--ink); }

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-sm);
}
.btn-primary:hover { filter: brightness(1.08); }

.btn-outline {
  border-color: var(--line);
  color: var(--ink);
  background: var(--surface);
}
.btn-outline:hover { border-color: var(--ink-3); }

.btn-danger { color: var(--bad); }
.btn-danger:hover { background: var(--bad-soft); }

.btn-wide { flex: 1; padding: 13px 18px; font-size: 15px; }

.btn-icon {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  color: var(--ink-2);
  padding: 0;
}
.btn-icon:hover { background: var(--bg-sunk); color: var(--ink); }

/* Theme glyph: sun in light mode, moon in dark. */
.theme-icon {
  width: 17px; height: 17px;
  border-radius: 50%;
  border: 2px solid currentColor;
  position: relative;
  display: block;
}
.theme-icon::after {
  content: "";
  position: absolute;
  inset: -2px -2px auto auto;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--bg);
  transform: translate(3px, -3px) scale(0);
  transition: transform .2s;
}
:root[data-theme="dark"] .theme-icon::after { transform: translate(3px, -3px) scale(1); }
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] .theme-icon::after { transform: translate(3px, -3px) scale(1); }
}

/* ---------- Killer cages ----------
   Each cell draws the dashed outline only on the sides where its cage ends.
   Because the outline is inset by the same amount in every cell, neighbouring
   cells in one cage leave those sides blank and the dashes join into a single
   continuous boundary around the cage. */
.cage-sum { display: none; }

.board.is-killer .cell::before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1.5px dashed transparent;
  border-radius: 3px;
  pointer-events: none;
}
.board.is-killer .cell.cage-t::before { border-top-color: var(--cage-line); }
.board.is-killer .cell.cage-r::before { border-right-color: var(--cage-line); }
.board.is-killer .cell.cage-b::before { border-bottom-color: var(--cage-line); }
.board.is-killer .cell.cage-l::before { border-left-color: var(--cage-line); }

.board.is-killer .cage-sum {
  display: block;
  position: absolute;
  top: 2px;
  left: 4px;
  font-size: clamp(8px, 2.1vw, 11px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--cage-ink);
  pointer-events: none;
}

/* Nudge the digit down so it never collides with the cage total above it. */
.board.is-killer .cell-value { transform: translateY(2px); }

/* Killer grids can take a second to generate; make that visible. */
.board.is-dealing { opacity: .55; transition: opacity .1s; }

/* ---------- Game-type switch ---------- */
.variants { margin-bottom: calc(var(--unit) * 2); }

.variant-switch {
  display: inline-flex;
  gap: 2px;
  background: var(--bg-sunk);
  border-radius: var(--radius);
  padding: 3px;
}

.variant-btn {
  border: 0;
  background: transparent;
  color: var(--ink-3);
  border-radius: var(--radius-sm);
  padding: 6px 16px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.variant-btn:hover { color: var(--ink); }
.variant-btn[aria-selected="true"] {
  background: var(--bg);
  color: var(--accent);
  box-shadow: 0 1px 2px rgb(0 0 0 / .08);
}

/* ---------- Difficulty tabs ---------- */
.difficulty { margin-bottom: calc(var(--unit) * 3); }

.difficulty-track {
  display: flex;
  gap: 2px;
  background: var(--bg-sunk);
  border-radius: var(--radius);
  padding: 3px;
  overflow-x: auto;
  scrollbar-width: none;
}
.difficulty-track::-webkit-scrollbar { display: none; }

.diff-tab {
  font-family: var(--font-ui);
  letter-spacing: .05em;
  flex: 1 0 auto;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.diff-tab:hover { color: var(--ink); }
.diff-tab[aria-selected="true"] {
  background: var(--surface);
  color: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* ---------- Status row ---------- */
.status {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: calc(var(--unit) * 3);
}

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 7px 10px 8px;
  min-width: 0;
}
.stat-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.stat-value {
  display: block;
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Tabular figures keep the timer from jittering as its digits change width — the
   original reason these values were monospaced. The mincho carries the atmosphere;
   tnum keeps the second hand from shoving the layout sideways once a second. */
#stat-time, #stat-mistakes, #stat-progress {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Mistakes turn red on the final life. */
.stat-value.is-danger { color: var(--bad); }

/* The generator returned its nearest miss rather than a grid inside this
   level's technique band; hover for what it actually requires. */
.stat-value.is-approx {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  cursor: help;
}

.stat-timer { position: relative; }
.btn-pause {
  position: absolute;
  top: 6px; right: 6px;
  width: 22px; height: 22px;
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 0;
  border-radius: 5px;
  color: var(--ink-3);
}
.btn-pause:hover { background: var(--bg-sunk); color: var(--ink); }
.btn-pause::before {
  content: "";
  display: block;
  margin: 0 auto;
  width: 8px; height: 10px;
  border-left: 3px solid currentColor;
  border-right: 3px solid currentColor;
}
.btn-pause.is-paused::before {
  width: 0; height: 0;
  border: 6px solid transparent;
  border-left: 9px solid currentColor;
  border-right: 0;
  margin-left: 8px;
}

/* ---------- Board ---------- */
.board-wrap {
  position: relative;
  max-width: var(--board-max);
  margin: 0 auto calc(var(--unit) * 3);
}

.board {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  /* The outer frame and the thick 3x3 rules are drawn with cell borders. */
  border: 2.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface);
  aspect-ratio: 1;
  touch-action: manipulation;
  user-select: none;
  /* Square AND never taller than the room the viewport actually has. Capping
     both axes keeps aspect-ratio: 1 intact while letting a short laptop shrink
     the board instead of pushing the controls off-screen. --board-chrome is the
     vertical space everything else needs; it is per-breakpoint. */
  max-height: calc(100dvh - var(--board-chrome));
  max-width:  calc(100dvh - var(--board-chrome));
  margin: 0 auto;
}
.board:focus { outline: none; }
.board:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

.cell {
  position: relative;
  border: 0;
  background: var(--surface);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  min-width: 0;
  font-family: var(--font-num);
  /* Scales with the board so the grid works at any width. */
  font-size: clamp(17px, 5.1vw, 27px);
  font-weight: 500;
  color: var(--accent);
  transition: background .1s;
}
.cell:nth-child(9n) { border-right: 0; }
.cell:nth-child(n + 73) { border-bottom: 0; }
/* Thick rules after columns 3 and 6 */
.cell:nth-child(9n + 3), .cell:nth-child(9n + 6) {
  border-right: 2.5px solid var(--line-strong);
}
/* Thick rules after rows 3 and 6 */
.cell:nth-child(n + 19):nth-child(-n + 27),
.cell:nth-child(n + 46):nth-child(-n + 54) {
  border-bottom: 2.5px solid var(--line-strong);
}

/* Givens read as printed ink; entries are in the accent colour. */
/* The signature. A clue arrives printed on the page, so it is set in the mincho
   at full weight; the digits the player writes are set in the kyokasho face, which
   reads as pen on paper. The two states now differ by LETTERFORM, so the board
   stays readable without relying on weight or colour alone. */
/* The paint tip sits under the key legend and must stay quieter than it. */
.hotkeys-tip { margin-top: calc(var(--unit) * 1.5); color: var(--ink-3); }

/* ---------- Painting notes across a drag ---------- */

/* Live preview of the cells a stroke is about to write. The stroke has not
   committed yet, so this reads as intent rather than as a mark. */
.cell.is-stroke {
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 2px var(--accent-mid);
}

/* The digit a drag will paint. A ring rather than a fill, so the key still reads
   as itself and the armed state does not compete with :hover or is-done. */
.key.is-armed {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--accent-mid);
}

/* Claim the gesture ONLY while a drag could paint something, so a swipe that
   starts on the board still scrolls the page the rest of the time. */
.board.can-paint { touch-action: none; }
/* Text selection would otherwise fight the drag and highlight the digits. */
.board.can-paint .cell { user-select: none; }

.cell.is-given {
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 700;
  cursor: default;
}
.cell:not(.is-given) .cell-value {
  font-family: var(--font-entry);
  font-weight: 600;
}

.cell.is-peer      { background: var(--bg-sunk); }
.cell.is-same      { background: var(--accent-soft); }
.cell.is-selected  { background: var(--accent-mid); }
.cell.is-conflict  { background: var(--bad-soft); }
.cell.is-wrong     { background: var(--bad-soft); color: var(--bad); }
.cell.is-hint      { animation: hintPulse 1.5s ease-out; }
/* Selection wins over the softer highlights. */
.cell.is-selected.is-same,
.cell.is-selected.is-peer { background: var(--accent-mid); }

@keyframes hintPulse {
  0%, 55%  { background: var(--good-soft); }
  100%     { background: var(--surface); }
}

.cell.just-placed { animation: pop .18s ease-out; }
@keyframes pop {
  0%   { transform: scale(.7); }
  70%  { transform: scale(1.1); }
  100% { transform: scale(1); }
}

/* Pencil marks: a 3x3 micro-grid inside the cell. */
.notes {
  position: absolute;
  inset: 2px;
  display: grid;
  grid-template: repeat(3, 1fr) / repeat(3, 1fr);
  font-size: clamp(7px, 1.85vw, 10px);
  font-weight: 600;
  color: var(--ink-3);
  line-height: 1;
  pointer-events: none;
}
.notes span { display: grid; place-items: center; }

/* ---------- Pause veil ---------- */
.veil {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(5px);
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  z-index: 5;
}
.veil-inner { text-align: center; }
.veil-title {
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 500;
  letter-spacing: .22em;
  /* Optical correction: heavy tracking pushes the visual centre right, so pull
     the trailing space back to keep the word centred over the board. */
  text-indent: .22em;
  color: var(--ink);
}

/* ---------- Hint bar ---------- */
.hint-bar {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--good-soft);
  border: 1px solid color-mix(in srgb, var(--good) 35%, transparent);
  border-radius: var(--radius);
  padding: 10px 34px 11px 12px;
  margin: 0 auto calc(var(--unit) * 3);
  max-width: var(--board-max);
  animation: slideIn .22s ease-out;
}
@keyframes slideIn {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}
.hint-tag {
  flex: none;
  background: var(--good);
  color: var(--surface);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 5px;
  margin-top: 1px;
}
:root[data-theme="dark"] .hint-tag { color: var(--bg-sunk); }
.hint-text { margin: 0; font-size: 13.5px; color: var(--ink); }
.hint-close {
  position: absolute;
  top: 6px; right: 8px;
  border: 0; background: transparent;
  font-size: 19px; line-height: 1;
  color: var(--ink-3);
  cursor: pointer;
  padding: 2px 5px;
  border-radius: 5px;
}
.hint-close:hover { color: var(--ink); background: rgba(0, 0, 0, .06); }

/* ---------- Tools ---------- */
.tools {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 7px;
  max-width: var(--board-max);
  margin: 0 auto calc(var(--unit) * 3);
}

.tool {
  position: relative;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 9px 4px 7px;
  display: grid;
  justify-items: center;
  gap: 3px;
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .06s;
}
.tool:hover:not(:disabled) { border-color: var(--accent); background: var(--surface-2); }
.tool:active:not(:disabled) { transform: translateY(1px); }
.tool:disabled { opacity: .42; cursor: not-allowed; }

.tool-glyph { font-size: 17px; line-height: 1.1; color: var(--ink-2); }
.tool-label { font-size: 11px; font-weight: 600; color: var(--ink-2); }

.tool[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
}
.tool[aria-pressed="true"] .tool-glyph,
.tool[aria-pressed="true"] .tool-label { color: var(--accent-ink); }

.tool-badge {
  position: absolute;
  top: -6px; right: -4px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 10px;
  font-weight: 700;
  min-width: 18px;
  padding: 1px 5px;
  border-radius: 9px;
  border: 2px solid var(--bg);
  font-family: var(--font-ui);
}
.tool-badge.is-zero { background: var(--ink-3); }
.tool[aria-pressed="true"] .tool-badge {
  background: var(--surface);
  color: var(--accent);
}

/* ---------- Number pad ---------- */
.pad {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 5px;
  max-width: var(--board-max);
  margin: 0 auto calc(var(--unit) * 4);
}

.key {
  position: relative;
  aspect-ratio: 1 / 1.22;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius);
  font-family: var(--font-entry);
  font-size: clamp(18px, 4.4vw, 25px);
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background .12s, border-color .12s, transform .06s;
}
.key:hover:not(:disabled) { background: var(--accent-soft); border-color: var(--accent); }
.key:active:not(:disabled) { transform: translateY(1px); }
.key:disabled { opacity: .32; cursor: not-allowed; }

/* Remaining-count pip under each digit. */
.key-left {
  position: absolute;
  bottom: 3px;
  font-size: 9.5px;
  font-weight: 700;
  color: var(--ink-3);
  letter-spacing: .02em;
}
.key.is-done { color: var(--ink-3); }

/* In notes mode the pad shifts to signal the different action. */
.pad.notes-mode .key { color: var(--ink-2); border-style: dashed; }

/* ---------- Footer ---------- */
.footer {
  display: flex;
  gap: 8px;
  max-width: var(--board-max);
  margin: 0 auto;
}

.hotkeys {
  max-width: var(--board-max);
  margin: calc(var(--unit) * 5) auto 0;
  font-size: 11.5px;
  color: var(--ink-3);
  text-align: center;
  line-height: 1.9;
}
.hotkeys strong { color: var(--ink-2); }
kbd {
  font-family: var(--font-code);
  font-size: 10.5px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 4px;
  color: var(--ink-2);
}

/* ---------- Modals ---------- */
.modal {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 16, .55);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 50;
  animation: fadeIn .16s ease-out;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.modal[hidden] { display: none; }
/* The confirm dialog is the only modal raised while another modal is ALREADY open
   ("Delete synced data", from inside Stats). With one shared z-index the winner
   was DOM order, and #modal-confirm is declared before #modal-stats and
   #modal-settings — so it was painted and hit-tested underneath them. A sample
   grid over the confirm card found 1521/1521 points landing outside it, and the
   Delete button's centre resolved to a cell of the stats table. */
#modal-confirm { z-index: 55; }

.modal-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 24px;
  width: 100%;
  max-width: 400px;
  box-shadow: var(--shadow-lg);
  animation: rise .2s ease-out;
  max-height: 88vh;
  overflow-y: auto;
}
.modal-wide { max-width: 520px; }
@keyframes rise {
  from { opacity: 0; transform: translateY(14px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

.modal-title {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: 23px;
  font-weight: 500;
  letter-spacing: .06em;
}
.modal-body { margin: 0 0 16px; color: var(--ink-2); font-size: 14px; }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

.modal-celebrate { text-align: center; }
.trophy {
  font-size: 44px;
  color: var(--warn);
  line-height: 1;
  margin-bottom: 6px;
  animation: spin .7s ease-out;
}
@keyframes spin {
  from { transform: scale(0) rotate(-90deg); }
  to   { transform: none; }
}
.modal-celebrate .modal-actions { justify-content: center; }

.result-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 16px 0 0;
}
.result-grid > div {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 6px;
}
.result-grid dt {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.result-grid dd {
  margin: 2px 0 0;
  font-family: var(--font-num);
  font-size: 16px;
  font-weight: 700;
}

/* Level picker inside the new-game modal */
.level-list { display: grid; gap: 6px; }
.level-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 11px 14px;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.level-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
.level-btn-name { font-weight: 700; font-size: 15px; }
.level-btn-meta { font-size: 11.5px; color: var(--ink-3); font-family: var(--font-num); }

.checkline {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  color: var(--ink-2);
  cursor: pointer;
}
.checkline input { width: 16px; height: 16px; accent-color: var(--accent); }

/* Stats table */
.table-scroll { overflow-x: auto; }
.stats-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.stats-table th, .stats-table td {
  padding: 8px 9px;
  text-align: right;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.stats-table th:first-child, .stats-table td:first-child { text-align: left; }
.stats-table thead th {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.stats-table td { font-family: var(--font-num); }
.stats-table td:first-child { font-family: var(--font); font-weight: 600; }
.stats-table tbody tr:last-child td { border-bottom: 0; }
.stats-empty { color: var(--ink-3); font-style: italic; }

/* Settings list */
.settings-list { display: grid; gap: 2px; }
.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 11px 2px;
  border-bottom: 1px solid var(--line);
}
.setting-row:last-child { border-bottom: 0; }
.setting-text { min-width: 0; }
.setting-name { font-size: 14px; font-weight: 600; }
.setting-desc { font-size: 11.5px; color: var(--ink-3); margin-top: 1px; }

/* Accessible toggle switch */
.switch { flex: none; position: relative; width: 42px; height: 24px; }
.switch input {
  position: absolute; inset: 0;
  opacity: 0; margin: 0; cursor: pointer; z-index: 1;
}
.switch-track {
  position: absolute; inset: 0;
  background: var(--line);
  border-radius: 999px;
  transition: background .18s;
  pointer-events: none;
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 18px; height: 18px;
  background: var(--surface);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
  transition: transform .18s;
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(18px); }
.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.seg { display: inline-flex; gap: 2px; background: var(--bg-sunk); border-radius: 8px; padding: 2px; }
.seg button {
  border: 0; background: transparent; color: var(--ink-3);
  font-size: 12px; font-weight: 600;
  padding: 5px 10px; border-radius: 6px; cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-sm); }

/* ---------- Toast ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  background: var(--line-strong);
  color: var(--bg);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 13.5px;
  font-weight: 600;
  box-shadow: var(--shadow);
  /* Above the confirm dialog (55) so a message is never hidden behind one. */
  z-index: 70;
  animation: toastIn .2s ease-out;
  max-width: calc(100vw - 40px);
  text-align: center;
  /* Purely informational and fixed over the controls: on the single-column layout
     it overlaps "New game", and without this every tap in that band hit the toast
     instead of the button for the 1.9s it was up. It must never take a pointer. */
  pointer-events: none;
}
.toast[hidden] { display: none; }
@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to   { opacity: 1; transform: translateX(-50%); }
}

/* ---------- Responsive ---------- */
@media (max-width: 560px) {
  .app { padding: calc(var(--unit) * 3) calc(var(--unit) * 2.5) calc(var(--unit) * 10); }
  .brand-name { font-size: 18px; }
  .topbar-actions .btn-ghost { padding: 7px 9px; font-size: 13px; }
  .status { grid-template-columns: repeat(2, 1fr); }
  .pad { gap: 4px; }
  .tools { gap: 5px; }
  .tool-label { font-size: 10px; }
  .hotkeys { display: none; }   /* no physical keyboard to speak of */
}

@media (max-width: 380px) {
  .topbar-actions .btn-ghost { display: none; }  /* keep the theme toggle only */
  .tools { grid-template-columns: repeat(5, 1fr); }
  .tool { padding: 8px 2px 6px; }
}

/* Landscape phones: put the pad beside the board to avoid vertical scrolling. */
@media (max-height: 560px) and (min-width: 640px) and (orientation: landscape) {
  .app { max-width: 940px; display: grid; grid-template-columns: minmax(0, 1fr) 260px;
         grid-template-areas: "head head" "board side" "board side";
         gap: 0 20px; align-items: start; }
  .topbar { grid-area: head; }
  .board-wrap { grid-area: board; }
  .difficulty, .status, .tools, .pad, .footer, .hint-bar { grid-column: 2; }
  .board-wrap { --board-max: 420px; }
}

/* Laptops and desktops: two columns, same idea as the landscape-phone rule above.
   A single column needs roughly 945px of height even with the board shrunk, so
   every laptop scrolled to reach the keypad — while ~660px of horizontal space sat
   empty on either side. The board keeps the left column, everything else stacks in
   a fixed-width side column.
   The breakpoint is 780px, not a "desktop" width: a non-maximised 900x700 window
   fell back to one column and overflowed by 383px with 25 controls off-screen.
   Anything wide enough for a board beside a 272px control column takes two. */
@media (min-width: 780px) {
  :root { --board-chrome: 250px; }

  .app {
    max-width: 1080px;
    display: grid;
    align-content: start;
    grid-template-columns: minmax(0, 1fr) minmax(272px, 340px);
    grid-template-areas:
      "topbar   topbar"
      "variants variants"
      "diff     diff"
      "board    status"
      "board    hint"
      "board    tools"
      "board    pad"
      "board    footer";
    column-gap: calc(var(--unit) * 7);
    padding-bottom: calc(var(--unit) * 6);
  }

  .topbar     { grid-area: topbar; }
  .variants   { grid-area: variants; }
  .difficulty { grid-area: diff; }
  .board-wrap { grid-area: board; margin-bottom: 0; }
  .status     { grid-area: status; }
  .hint-bar   { grid-area: hint; }
  .tools      { grid-area: tools; }
  .pad        { grid-area: pad; }
  .footer     { grid-area: footer; }

  /* The side column sets its own width, so the board's cap no longer applies. */
  .status, .hint-bar, .tools, .pad, .footer { max-width: none; }
  /* These carry `margin: 0 auto` from the single-column layout. On a grid item
     auto inline margins override `stretch` and shrink it to fit-content — which
     collapsed the board wrapper to 164px (nine cells at min-content) inside a
     680px column. Neutralise them here and give the board a definite width. */
  .status, .hint-bar, .tools, .pad, .footer { margin-inline: 0; }
  .board-wrap { width: min(var(--board-max), 100%); margin-inline: auto; }
  /* One compact row of four: two rows cost 119px of the side column's height
     budget, which is the difference between fitting a 720px laptop and not. */
  .status { grid-template-columns: repeat(4, 1fr); }
  .stat { padding: 7px 6px; }
  .stat-label { font-size: 9px; letter-spacing: .06em; }
  .stat-value { font-size: 15px; }
  /* Tighter vertical rhythm in the side column than the single-column layout. */
  .status, .tools { margin-bottom: calc(var(--unit) * 2); }
  .pad { margin-bottom: calc(var(--unit) * 2); }
  /* Nine keys read better as the same 3x3 the board is built from. Constrained
     to a definite width because keys are aspect-ratio'd: at the column's full
     340px they became 134px tall each and three rows overflowed the viewport
     by 217px. Square keys at 240px total keep the whole column inside 720px. */
  .pad { grid-template-columns: repeat(3, 1fr); width: 240px; margin-inline: auto; }
  .pad .key { aspect-ratio: 1; font-size: 22px; }
}

/* ---------- Motion and print ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

@media print {
  .topbar-actions, .tools, .pad, .footer, .hotkeys, .difficulty, .status { display: none; }
  .board { border-color: #000; }
  .cell { border-color: #999; color: #000; }
}

/* Group heading in the New game dialog, separating Classic from Killer. */
.level-group {
  margin: calc(var(--unit) * 3) 0 calc(var(--unit) * 1.5);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.level-group:first-child { margin-top: 0; }

/* ---------- Cross-device sync panel ---------- */
.sync {
  margin-top: calc(var(--unit) * 5);
  padding-top: calc(var(--unit) * 4);
  border-top: 1px solid var(--line);
}

.sync-title {
  margin: 0 0 calc(var(--unit) * 1.5);
  font-size: 15px;
  font-weight: 700;
}

.sync-note {
  margin: 0 0 calc(var(--unit) * 3);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
}

.sync-code {
  margin: 0 0 calc(var(--unit) * 1.5);
  padding: 10px 14px;
  background: var(--bg-sunk);
  border-radius: var(--radius-sm);
  font-family: var(--font-code);
  font-size: clamp(16px, 4.6vw, 21px);
  font-weight: 600;
  letter-spacing: .1em;
  text-align: center;
  cursor: pointer;
  user-select: all;
}

.sync-meta {
  margin: 0 0 calc(var(--unit) * 3);
  font-size: 12px;
  color: var(--ink-3);
}

.sync-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--unit);
  margin-bottom: calc(var(--unit) * 2);
}

.sync-input {
  flex: 1 1 160px;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  padding: 9px 12px;
  font-family: var(--font-code);
  font-size: 14px;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.sync-input:focus-visible { border-color: var(--accent); }

.sync-error {
  margin: calc(var(--unit) * 1.5) 0 0;
  font-size: 12.5px;
  color: var(--bad);
}
