:root {
  color-scheme: dark;
  --bg: #0f1117;
  --panel: #171a23;
  --panel-2: #1e2230;
  --line: #2a2f3f;
  --line-strong: #4a5268;
  --text: #e6e9f2;
  --muted: #9aa3b8;
  --accent: #4f8cff;
  --accent-2: #6ea0ff;
  --accent-soft: rgba(79, 140, 255, .24);
  --accent-faint: rgba(79, 140, 255, .09);
  --good: #3ecf8e;
  --bad: #ff6b6b;
  --warn: #ffb454;
  --given: #e6e9f2;
  --user: #7fb3ff;
  --note: #9aa3b8;
  --radius: 10px;
  --bar: 52px;
  --keybar: 26px;
  --cell-min: 30px;
  --chrome: 245px; /* bar + keybar + controls + footer: the vertical budget handed back to the board */
  --cell: clamp(var(--cell-min), min(10.5vw, calc((100dvh - var(--chrome)) / 9)), 68px);
  --drawer-w: min(420px, 92vw);
}

/* ------------------------------------------------------------------ *
 * Themes. Each one is a complete palette, not an accent layer: the whole
 * surface ladder (grounds, panels, rules, text) is tinted around the theme
 * hue, with the brand colour used as the mid-tone anchor -- lightened for
 * text and accents, darkened for grounds, so contrast holds.
 *
 * Azure is the original default dark palette and is left exactly as :root
 * declares it. #950000 itself is only ~2:1 on a dark ground, so the dark
 * crimson lifts the same hue until it clears 3:1 as a rule and 4.5:1 under
 * white button text.
 *
 * Digit colours keep two independent signals in every palette: givens are
 * near-neutral, user digits are warm-and-light, and wrong digits are vivid
 * plus a background wash -- so "wrong" is never carried by hue alone, which
 * matters most in the red theme.
 * ------------------------------------------------------------------ */
:root[data-accent="azure"] {
  color-scheme: dark;
  --accent: #4f8cff;
  --accent-2: #6ea0ff;
  --accent-soft: rgba(79, 140, 255, .24);
  --accent-faint: rgba(79, 140, 255, .09);
  --user: #7fb3ff;
}

:root[data-accent="crimson"] {
  color-scheme: dark;
  --bg: hsl(357, 48%, 4.5%);
  --panel: hsl(357, 44%, 7.5%);
  --panel-2: hsl(357, 41%, 11.5%);
  --line: hsl(357, 36%, 19%);
  --line-strong: hsl(357, 30%, 39%);
  --text: hsl(357, 45%, 95%);
  --muted: hsl(357, 24%, 73%);
  --accent: hsl(357, 82%, 53%);
  --accent-2: hsl(357, 100%, 79%);
  --accent-soft: hsla(357, 92%, 56%, .30);
  --accent-faint: hsla(357, 92%, 56%, .12);
  --given: hsl(357, 35%, 94%);
  --user: hsl(28, 100%, 82%);
  --note: hsl(357, 22%, 62%);
  --bad: hsl(358, 100%, 66%);
}

:root[data-accent="amber"] {
  /* A light theme: amber on warm paper. Mode is fixed by the theme, not by the
     system preference -- the swatch says so. */
  color-scheme: light;
  --bg: hsl(36, 45%, 97%);
  --panel: hsl(36, 62%, 99%);
  --panel-2: hsl(36, 58%, 94%);
  --line: hsl(36, 42%, 87%);
  --line-strong: hsl(36, 26%, 58%);
  --text: hsl(32, 45%, 12%);
  --muted: hsl(34, 22%, 40%);
  --accent: hsl(32, 90%, 33%);
  --accent-2: hsl(30, 88%, 27%);
  --accent-soft: hsla(34, 90%, 40%, .18);
  --accent-faint: hsla(34, 90%, 40%, .07);
  --given: hsl(32, 30%, 12%);
  --user: hsl(28, 85%, 33%);
  --note: hsl(34, 18%, 46%);
  --bad: hsl(8, 80%, 44%);
}

:root[data-accent="teal"] {
  /* A light theme: teal on cool paper. */
  color-scheme: light;
  --bg: hsl(174, 40%, 97%);
  --panel: hsl(174, 58%, 99%);
  --panel-2: hsl(174, 48%, 94%);
  --line: hsl(174, 36%, 86%);
  --line-strong: hsl(174, 24%, 56%);
  --text: hsl(180, 35%, 11%);
  --muted: hsl(177, 16%, 38%);
  --accent: hsl(176, 82%, 26%);
  --accent-2: hsl(178, 76%, 21%);
  --accent-soft: hsla(176, 70%, 35%, .18);
  --accent-faint: hsla(176, 70%, 35%, .07);
  --given: hsl(180, 25%, 11%);
  --user: hsl(182, 78%, 27%);
  --note: hsl(177, 14%, 44%);
  --bad: hsl(358, 78%, 42%);
}

/* Only for an un-themed first paint (storage blocked, or before the inline
   script runs): follow the system preference with the neutral palette. Once a
   theme is chosen the theme itself fixes the mode, so this stops applying. */
@media (prefers-color-scheme: light) {
  :root:not([data-accent]) {
    color-scheme: light;
    --bg: #f4f6fb;
    --panel: #ffffff;
    --panel-2: #f0f3fa;
    --line: #d8deea;
    --line-strong: #9aa3b8;
    --text: #1a1f2e;
    --muted: #5b6478;
    --user: #2f6fd0;
    --note: #7a8499;
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overscroll-behavior: none;
}

body {
  background: var(--bg);
  color: var(--text);
  font: 15px/1.45 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: hidden; /* single pane: the page itself never scrolls */
}

.app {
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ---------------- top bar ---------------- */
.bar {
  flex: 0 0 auto;
  height: var(--bar);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 12px;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.bar-title { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.timer { font-size: 18px; font-variant-numeric: tabular-nums; letter-spacing: .02em; white-space: nowrap; }
.bar-chips { display: flex; gap: 6px; }
.chip {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}
.chip b { color: var(--text); }

.icon-btn {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--text);
  cursor: pointer;
}
.icon-btn:hover { border-color: var(--accent); }

/* ---------------- stage ---------------- */
.stage {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 10px 12px 14px;
  overflow: hidden;
}

.board {
  display: grid;
  grid-template-columns: repeat(9, var(--cell));
  grid-auto-rows: var(--cell);
  border: 2px solid var(--line-strong);
  border-radius: 6px;
  overflow: hidden;
  touch-action: manipulation;
  flex: 0 0 auto;
}
.cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  cursor: pointer;
  user-select: none;
  font-size: calc(var(--cell) * 0.52);
  font-variant-numeric: tabular-nums;
  transition: background .07s linear;
}
.cell:nth-child(9n) { border-right: none; }
.cell:nth-child(n+73) { border-bottom: none; }
.cell[data-col="2"], .cell[data-col="5"] { border-right: 2px solid var(--line-strong); }
.cell[data-row="2"], .cell[data-row="5"] { border-bottom: 2px solid var(--line-strong); }
.cell.given { color: var(--given); font-weight: 600; }
.cell.user { color: var(--user); }
.cell.selected { background: var(--accent-soft); }
.cell.peer { background: var(--accent-faint); }
.cell.same-digit { background: rgba(62, 207, 142, .16); }
/* Wrong digits get a wash as well as a colour, so "wrong" is never carried by
   hue alone - which matters once the accent itself is red. */
.cell.wrong { color: var(--bad); background: rgba(255, 107, 107, .14); }
.cell.hint { outline: 2px solid var(--good); outline-offset: -2px; }
.cell.just-placed { animation: pop .16s ease-out; }
@keyframes pop { from { transform: scale(.86); } to { transform: scale(1); } }

.notes {
  position: absolute;
  inset: 7%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  font-size: calc(var(--cell) * 0.22);
  color: var(--note);
  line-height: 1;
  pointer-events: none;
}
.notes span { display: flex; align-items: center; justify-content: center; }

/* ---------------- controls ---------------- */
.controls {
  flex: 0 0 auto;
  display: flex;
  gap: 10px;
  align-items: stretch;
  justify-content: center;
  width: min(calc(var(--cell) * 9 + 4px), 100%);
}
.numpad { display: grid; grid-template-columns: repeat(9, 1fr); gap: 5px; flex: 1 1 auto; touch-action: manipulation; }
.numpad button {
  padding: calc(var(--cell) * 0.16) 0;
  font-size: calc(var(--cell) * 0.36);
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.numpad button:hover { border-color: var(--accent); }
.numpad button.depleted { opacity: .35; }

.tool-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; flex: 0 0 auto; touch-action: manipulation; }
.tool-buttons button { font-size: 12px; padding: 6px 10px; }

button {
  font: inherit;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  padding: 7px 12px;
  cursor: pointer;
}
button:hover { border-color: var(--accent); }
button:disabled { opacity: .45; cursor: not-allowed; }
button.sm { padding: 3px 9px; font-size: 12px; }
button.ghost { background: transparent; }
button.wide { width: 100%; }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
/* Follows the theme accent. This was hardcoded to --warn (orange), so the Hint
   button stayed orange on every theme. */
button.accent { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
button.toggle[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #fff; }
button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
button.danger-ghost { background: transparent; border-color: var(--bad); color: var(--bad); }

.hint-box {
  flex: 0 0 auto;
  max-width: min(640px, 100%);
  padding: 7px 11px;
  border-left: 3px solid var(--good);
  background: var(--panel-2);
  border-radius: 6px;
  font-size: 12.5px;
  max-height: 4.6em;
  overflow: auto;
}

/* ---------------- shortcut strip ---------------- */
.keybar {
  flex: 0 0 auto;
  min-height: var(--keybar);
  display: flex;
  flex-wrap: wrap;
  gap: 2px 14px;
  justify-content: center;
  align-items: center;
  padding: 4px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
  font-size: 11px;
  color: var(--muted);
}
.kb { white-space: nowrap; }

/* The underlined glyph is the keyboard mnemonic. */
.key {
  color: var(--accent-2);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1.5px;
}
button .key { color: inherit; text-decoration: underline; }

/* ---------------- footer ---------------- */
.foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  background: var(--panel);
  padding: 7px 18px;
  display: flex;
  justify-content: center;
}

/* Spread the items across whatever width is available, and keep each label off
   its value - inline span + b used to collide into "DifficultyEasy". */
.puzzle-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  column-gap: 18px;
  row-gap: 4px;
  width: 100%;
  font-size: 11.5px;
  color: var(--muted);
}
.puzzle-meta .kv {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  white-space: nowrap;
}
.puzzle-meta .kv > span { color: var(--muted); }
.puzzle-meta .kv b { color: var(--text); font-weight: 600; }
.puzzle-meta code { font-size: 11px; }

/* ---------------- drawer ---------------- */
.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  z-index: 40;
  opacity: 0;
  transition: opacity .18s ease;
}
.backdrop.show { opacity: 1; }

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--drawer-w);
  background: var(--panel);
  border-left: 1px solid var(--line);
  box-shadow: -14px 0 40px rgba(0, 0, 0, .35);
  z-index: 41;
  transform: translateX(100%);
  transition: transform .2s ease;
  display: flex;
  flex-direction: column;
}
.drawer.open { transform: translateX(0); }

.drawer-head {
  flex: 0 0 auto;
  height: var(--bar);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px;
  border-bottom: 1px solid var(--line);
}
.tabs { display: flex; gap: 4px; padding: 10px 10px 0; flex: 0 0 auto; }
.tab { flex: 1; background: transparent; font-size: 12px; padding: 6px 4px; }
.tab.active { background: var(--panel-2); border-color: var(--accent); }
.drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 12px;
  -webkit-overflow-scrolling: touch;
}

.field { display: block; margin-bottom: 10px; }
.field > span { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.field em { font-style: normal; opacity: .7; }
.field input[type="text"], .field select {
  width: 100%;
  padding: 8px 10px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 8px;
  font: inherit;
}
.field.checkbox { display: flex; align-items: center; gap: 8px; }
.field.checkbox > span { margin: 0; }
.tier-hint { font-size: 12px; color: var(--muted); margin: -4px 0 10px; }
.fine { font-size: 11.5px; color: var(--muted); }
.gen-status { margin-top: 10px; font-size: 12px; color: var(--warn); }
.gen-status.ok { color: var(--good); }
.gen-status.bad { color: var(--bad); }
.play-actions { display: flex; flex-direction: column; gap: 8px; }
hr { border: none; border-top: 1px solid var(--line); margin: 16px 0; }
h3 { margin: 0 0 8px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }

.subtabs { display: flex; gap: 4px; margin-bottom: 10px; }
.subtab { flex: 1; font-size: 11.5px; padding: 5px 4px; background: transparent; }
.subtab.active { background: var(--panel-2); border-color: var(--accent); }

.puzzle-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.puzzle-item {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 10px;
  background: var(--panel-2);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 8px;
  align-items: center;
}
.puzzle-item.current { border-color: var(--accent); }
.puzzle-item .title { font-size: 13px; font-weight: 600; }
.puzzle-item .sub { font-size: 11px; color: var(--muted); grid-column: 1 / -1; }
.puzzle-item .row { display: flex; gap: 5px; align-items: center; }
.puzzle-item button { font-size: 11px; padding: 4px 7px; }
.badge {
  font-size: 10px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--line);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-right: 4px;
}
.badge.easy { color: var(--good); border-color: var(--good); }
.badge.medium { color: var(--accent-2); border-color: var(--accent-2); }
.badge.hard { color: var(--warn); border-color: var(--warn); }
.badge.expert { color: #d98cff; border-color: #d98cff; }
.badge.extreme { color: var(--bad); border-color: var(--bad); }
.badge.unverified { color: var(--muted); border-color: var(--line-strong); }
.empty { color: var(--muted); font-size: 13px; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 8px; }
.stat { background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 9px; }
.stat .label { font-size: 11px; color: var(--muted); }
.stat .value { font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
.stat .sub { font-size: 10.5px; color: var(--muted); }

.tier-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.tier-table th, .tier-table td { text-align: left; padding: 5px 4px; border-bottom: 1px solid var(--line); }
.tier-table th { color: var(--muted); font-weight: 500; }

.tech-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.tech-list li { font-size: 11px; background: var(--panel-2); border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px; }

/* ---------------- theme picker ---------------- */
.swatches { display: flex; flex-wrap: wrap; gap: 8px; }
/* Settings sections: the theme picker is grouped by mode, with the sun/moon
   icon carrying the category instead of the words. */
.set-group { margin: 0 0 4px; }
.set-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
}
.set-title .ico { flex: 0 0 auto; opacity: .9; }

.swatch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: inherit;
  font-size: 13px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  border-radius: 999px;
  padding: 6px 12px 6px 8px;
  cursor: pointer;
}
.swatch:hover { border-color: var(--accent); }
.swatch[aria-checked="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
/* Two-tone chip: the theme's accent on its own ground, so the swatch previews
   both the colour and whether the theme is light or dark. */
.swatch .dot {
  width: 20px;
  height: 20px;
  border-radius: 6px;
  background: var(--dot-bg);
  border: 1px solid var(--line-strong);
  flex: 0 0 auto;
  position: relative;
  overflow: hidden;
}
.swatch .dot::after {
  content: '';
  position: absolute;
  inset: 4px 4px auto 4px;
  height: 5px;
  border-radius: 3px;
  background: var(--dot);
}

.data-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* The upload control is a <label> wrapping a hidden input, so it needs the same
   treatment the real <button>s get - otherwise it renders as bare text. */
.file-btn {
  display: inline-block;
  font: inherit;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  padding: 7px 12px;
  cursor: pointer;
}
.file-btn:hover { border-color: var(--accent); }
.file-btn.ghost { background: transparent; }
.merge-choice { display: flex; gap: 12px; margin: 10px 0; font-size: 12px; color: var(--muted); flex-wrap: wrap; }
.import-report { font-size: 12px; margin-top: 8px; white-space: pre-wrap; color: var(--muted); }
.danger-zone { border: 1px solid var(--bad); border-radius: 8px; padding: 10px 12px; }
.danger-zone h3 { margin-top: 0; color: var(--bad); }

/* ---------------- update offer ---------------- */
.update-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 62; /* above the toast, so an update notice is never covered by one */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--panel-2);
  border-bottom: 2px solid var(--accent);
  color: var(--text);
  font-size: 13px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
  animation: drop-in .18s ease-out;
}
/* display:flex beats the UA's [hidden] { display: none }, so say it outright. */
.update-bar[hidden] { display: none; }
@keyframes drop-in {
  from { transform: translateY(-100%); }
  to { transform: translateY(0); }
}
.update-bar .update-msg { color: var(--muted); }

.toast {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  background: var(--panel-2);
  border: 1px solid var(--accent);
  color: var(--text);
  padding: 8px 16px;
  border-radius: 999px;
  font-size: 13px;
  box-shadow: 0 8px 30px rgba(0, 0, 0, .4);
  z-index: 60;
  max-width: 92vw;
}
.toast.bad { border-color: var(--bad); }
.toast.good { border-color: var(--good); }

/* narrow phones: tighten the bar so the timer never wraps and the chips keep a
   real margin off the edge (they were landing flush at 389/390) */
@media (max-width: 430px) {
  .bar { gap: 5px; padding: 0 10px; }
  .bar-title { gap: 5px; }
  .bar-chips { gap: 4px; margin-left: auto; min-width: 0; }
  .chip { padding: 2px 5px; font-size: 10.5px; white-space: nowrap; }
  .timer { font-size: 15px; }
  #pauseBtn { padding: 3px 7px; font-size: 11px; }
}

/* short viewports: hand more height back to the board */
@media (max-height: 620px) {
  :root { --chrome: 200px; }
  .puzzle-meta { font-size: 10px; gap: 2px 10px; }
  .hint-box { max-height: 3.2em; font-size: 11.5px; }
}
@media (max-height: 500px) {
  :root { --chrome: 170px; }
  .keybar { display: none; }
}

/* ------------------------------------------------------------------ *
 * Touch numpad. Portrait touch devices get a 3x3 phone-style pad: the
 * digits are what gets tapped hundreds of times a puzzle, and the 9-wide
 * row gives each one ~30px on a phone. DOM order 1..9 with three columns
 * is already the phone layout (1 top-left), so no reordering is needed.
 *
 * The budget is the hard part. Measured with real font metrics, a portrait
 * phone spends 52px on the bar, 24px of stage padding, ~37px on the tools
 * row and a footer that wraps anywhere from 53px (iPad) to 115px (280px
 * wide) -- so --chrome is budgeted against the *smallest* pad, which keeps
 * the board from ever overpaying, and the pad then absorbs whatever the
 * footer did not take. That is why .controls/.numpad are flexible instead
 * of fixed: footer variance lands on the pad, not on clipped board.
 * --cell-min drops to 24px so short phones still fit.
 *
 * Landscape touch keeps the row: at ~380px of height a 3-row pad and the
 * board cannot coexist, and the width already gives the row big targets.
 * ------------------------------------------------------------------ */
@media (pointer: coarse) and (orientation: portrait) {
  :root {
    --pad-min: 40px;
    --pad-max: 72px;
    /* bar 52 + stage padding 24 + board gap 10 + controls gap 8 + tools 37
       + 3 pad rows at their minimum 120 + slack for a ~120px wrapped footer */
    --chrome: 372px;
    --cell-min: 24px;
  }

  /* No keyboard on a touch device, so the shortcut strip is dead weight. */
  .keybar { display: none; }

  .controls {
    flex-direction: column;
    width: 100%;
    gap: 8px;
    flex: 1 1 auto;
    min-height: 0;
  }

  .numpad {
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: minmax(var(--pad-min), var(--pad-max));
    align-content: center;
    gap: 6px;
    flex: 1 1 auto;
    min-height: 0;
  }
  .numpad button {
    padding: 0;
    font-size: clamp(22px, 7vw, 32px);
    border-radius: 10px;
    user-select: none;
    -webkit-user-select: none;
  }

  /* One row of four under the pad, instead of a 2x2 block beside a row. */
  .tool-buttons { grid-template-columns: repeat(4, 1fr); flex: 0 0 auto; }
  .tool-buttons button { padding: 9px 6px; }

  /* The menu takes the whole screen: a 420px sheet with a sliver of board
     still showing behind it reads as a desktop popover on a phone. The
     safe-area insets matter here because the viewport is viewport-fit=cover,
     so a full-bleed sheet would otherwise tuck the close button under the
     notch and the bottom of the form under the home indicator. */
  .drawer {
    width: 100%;
    border-left: none;
    box-shadow: none;
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  .drawer-head {
    height: calc(var(--bar) + env(safe-area-inset-top, 0px));
    padding-top: env(safe-area-inset-top, 0px);
  }
  .drawer-body { padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }

  /* The update bar is fixed to the very top, so it needs the notch too. */
  .update-bar { padding-top: calc(8px + env(safe-area-inset-top, 0px)); }
}

/* A finger leaves :hover stuck on the last thing it tapped, so on touch the
   hover accent is dropped and press feedback comes from :active instead. */
@media (hover: none) {
  .numpad button:hover { border-color: var(--line); }
  .numpad button:active {
    border-color: var(--accent);
    background: var(--accent-soft);
  }
}
