:root {
  --bg: #12161c;
  --bg-soft: #1b212a;
  --surface-hi: #26303e;
  --frame: #f3f5f7;
  --frame-edge: #cbd2da;
  --text: #eef2f6;
  --text-body: #c6d0dc;
  --text-dim: #93a1b1;
  --accent: #4da3ff;
  --accent-soft: #223449;
  --on-accent: #08121d;
  --line: #262e39;
  --line-strong: #333d4a;
  --line-bright: #4a5768;
  --danger: #ffb3b8;
  --danger-line: #7c3b41;
  --gold: #ffd35c;
  --board-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  --wash: rgba(255, 255, 255, 0.06);
  --radius: 14px;
  --gutter: 6px;
  --cell: 56px;
  --slot: rgba(0, 0, 0, 0.14);
}

/* Appearance only moves the surfaces around the board. The board frame stays
   light in every dark theme because the tile colours are tuned against it;
   Light is the one theme that has to restate it, since a near-white frame on a
   near-white page would stop reading as a separate surface.

   The three original themes were all the same blue-grey at different
   brightnesses, so they read as one theme with a dimmer switch. Each of these
   moves the hue, not just the lightness. */

/* :root carries the dark palette so the first paint is correct before JS runs.
   It is repeated here because each preview swatch re-scopes these variables on
   itself to show the theme it offers - without an explicit rule, the Dark
   swatch would inherit whatever theme is currently applied and preview it. */
[data-appearance="dark"] {
  --bg: #12161c;
  --bg-soft: #1b212a;
  --surface-hi: #26303e;
  --frame: #f3f5f7;
  --frame-edge: #cbd2da;
  --text: #eef2f6;
  --text-body: #c6d0dc;
  --text-dim: #93a1b1;
  --accent: #4da3ff;
  --accent-soft: #223449;
  --on-accent: #08121d;
  --line: #262e39;
  --line-strong: #333d4a;
  --line-bright: #4a5768;
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="midnight"] {
  --bg: #000000;
  --bg-soft: #0c0c0e;
  --surface-hi: #17171b;
  --text: #f5f5f7;
  --text-body: #c9c9cf;
  --text-dim: #8a8a93;
  --accent: #7fb8ff;
  --accent-soft: #16243a;
  --on-accent: #05080d;
  --line: #1c1c21;
  --line-strong: #2a2a31;
  --line-bright: #3d3d46;
  --board-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="slate"] {
  --bg: #222831;
  --bg-soft: #2d3540;
  --surface-hi: #3a4450;
  --text: #f4f7fa;
  --text-body: #cdd7e1;
  --text-dim: #a8b6c4;
  --accent: #5cc9d6;
  --accent-soft: #1d4650;
  --on-accent: #04171b;
  --line: #3a4552;
  --line-strong: #4a5665;
  --line-bright: #5e6b7b;
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="forest"] {
  --bg: #0d1a13;
  --bg-soft: #15271c;
  --surface-hi: #1e3527;
  --text: #eef7f1;
  --text-body: #c2d8ca;
  --text-dim: #8fab99;
  --accent: #4fd18b;
  --accent-soft: #17392a;
  --on-accent: #05130c;
  --line: #1d3325;
  --line-strong: #2a4633;
  --line-bright: #3a5c44;
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="plum"] {
  --bg: #170f1f;
  --bg-soft: #22182e;
  --surface-hi: #2e2140;
  --text: #f5eefa;
  --text-body: #d3c5e0;
  --text-dim: #a695b5;
  --accent: #c98bfc;
  --accent-soft: #33204a;
  --on-accent: #12061b;
  --line: #2b1e3a;
  --line-strong: #3b2a4f;
  --line-bright: #4e3a66;
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="amber"] {
  --bg: #1b1611;
  --bg-soft: #271f18;
  --surface-hi: #342a20;
  --text: #faf3ea;
  --text-body: #ddccb8;
  --text-dim: #b3a08a;
  --accent: #f0a860;
  --accent-soft: #40301d;
  --on-accent: #1a0f03;
  --line: #33291f;
  --line-strong: #45382a;
  --line-bright: #5c4b38;
  --wash: rgba(255, 255, 255, 0.06);
}

[data-appearance="light"] {
  --bg: #e8ecf1;
  --bg-soft: #ffffff;
  --surface-hi: #f1f4f8;
  --frame: #ffffff;
  --frame-edge: #b9c2cd;
  --text: #131822;
  --text-body: #39434f;
  --text-dim: #5d6b7c;
  --accent: #1f6feb;
  --accent-soft: #dbe8fd;
  --on-accent: #ffffff;
  --line: #d3dae2;
  --line-strong: #bcc5d0;
  --line-bright: #9aa6b4;
  --danger: #b3261e;
  --danger-line: #e3a9a5;
  --gold: #9a6b00;
  --board-shadow: 0 8px 24px rgba(15, 26, 41, 0.14);
  --wash: rgba(0, 0, 0, 0.06);
}

[data-appearance="paper"] {
  --bg: #efe7d9;
  --bg-soft: #fdf9f1;
  --surface-hi: #f5eee1;
  --frame: #fffdf8;
  --frame-edge: #cbbfa8;
  --text: #241d12;
  --text-body: #4a3f2e;
  --text-dim: #7a6c55;
  --accent: #96601a;
  --accent-soft: #f6e6c9;
  --on-accent: #fffaf0;
  --line: #ded2bb;
  --line-strong: #c8baa0;
  --line-bright: #a2947b;
  --danger: #9c2b21;
  --danger-line: #d8a89f;
  --gold: #8a6100;
  --board-shadow: 0 8px 24px rgba(60, 45, 20, 0.16);
  --wash: rgba(0, 0, 0, 0.06);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

body {
  min-height: 100dvh;
  display: flex;
  justify-content: center;
}

.app {
  width: 100%;
  max-width: 520px;
  padding: max(12px, env(safe-area-inset-top)) 8px max(14px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ---------- top bar ---------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.topbar h1 {
  margin: 0;
  font-size: clamp(18px, 5vw, 23px);
  font-weight: 650;
  letter-spacing: 0.2px;
}

.icon-btn {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--bg-soft);
  color: var(--text-dim);
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.icon-btn:hover { color: var(--text); border-color: var(--accent); }

/* ---------- mode and hud ---------- */

.mode-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.mode-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px;
}

.mode-row #btn-new {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-width: 100px;
  white-space: nowrap;
}

.hud {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.stat {
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 4px;
  text-align: center;
}

.stat-label {
  display: block;
  font-size: 10px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.stat-value {
  display: block;
  margin-top: 3px;
  font-size: 19px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* Text for assistive tech only. Kept out of the layout entirely so the board's
   size maths (which measures real elements) never sees it. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- board ---------- */

.stage {
  position: relative;
  width: 100%;
  max-width: 432px;
  align-self: center;
}

.frame {
  background: var(--frame);
  border: 1px solid var(--frame-edge);
  border-radius: var(--radius);
  padding: 8px;
  box-shadow: var(--board-shadow);
}

.guide {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: repeat(var(--cols), var(--cell));
  justify-content: center;
}

.guide-cell {
  height: calc(var(--cell) * 0.42);
  border-radius: 6px;
  background: var(--c);
  position: relative;
  display: grid;
  place-items: center;
  color: rgba(255, 255, 255, 0.92);
  font-size: calc(var(--cell) * 0.2);
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.22);
}

.divider {
  height: 2px;
  margin: 8px 2px;
  border-radius: 2px;
  background: repeating-linear-gradient(90deg, var(--frame-edge) 0 8px, transparent 8px 14px);
}

.board {
  position: relative;
  margin: 0 auto;
  width: calc(var(--cols) * var(--cell) + (var(--cols) - 1) * var(--gutter));
  height: calc(var(--rows) * var(--cell) + (var(--rows) - 1) * var(--gutter));
  touch-action: none;
}

/* Sits above the win overlay so confetti falls in front of the card. */
.confetti {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 6;
}

/* The empty cell is the one thing a player has to find every turn, so it has to
   read as a hole rather than a pale block. Tiles are raised with a dark lip at
   the BOTTOM; a recess is the inverse - dark at the top, where the light would
   not reach - plus a rim so the edge still has a boundary on a near-white frame. */
.slot {
  position: absolute;
  width: var(--cell);
  height: var(--cell);
  border-radius: 8px;
  background: var(--slot);
  box-shadow:
    inset 0 5px 7px rgba(0, 0, 0, 0.28),
    inset 0 0 0 1px rgba(0, 0, 0, 0.14);
}

.tile {
  position: absolute;
  width: var(--cell);
  height: var(--cell);
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: var(--c);
  color: rgba(255, 255, 255, 0.92);
  font-size: calc(var(--cell) * 0.3);
  line-height: 1;
  cursor: pointer;
  transition: transform 130ms cubic-bezier(0.3, 0.9, 0.4, 1), box-shadow 130ms ease, filter 130ms ease;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.25);
  outline: 2px solid transparent;
  outline-offset: -2px;
  will-change: transform;
  touch-action: manipulation;
}

.tile:disabled { cursor: default; }

.tile.is-movable:hover { box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.24), 0 0 0 2px rgba(255, 255, 255, 0.75); }

/* Roughly three quarters of the blocks on a fresh board are in the wrong column,
   so marking them cannot work - a badge on almost everything is a badge on
   nothing. Drain the colour out of the wrong ones instead and the few settled
   columns become the only coloured things on the board. grayscale() is used
   rather than opacity because it leaves white untouched, so the colourblind
   symbol stays legible, and it holds luminance, so the block keeps the contrast
   against the board that every theme was tuned for. */
.tile.is-wrong { filter: grayscale(0.92); }

/* The keyboard cursor. .tile sets a transparent outline that suppresses the
   browser default - so without this, tabbing to the board lands on an invisible
   cell. The gap uses :focus rather than :focus-visible because it is focused
   programmatically. */
.tile:focus-visible,
.slot:focus {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
  z-index: 4;
}

/* Accent on accent would vanish, so the cursor gets a dark separating rim. */
.tile:focus-visible { box-shadow: inset 0 0 0 6px rgba(0, 0, 0, 0.45), inset 0 -3px 0 rgba(0, 0, 0, 0.24); }

.tile.is-nudge { animation: nudge 220ms ease; }

/* The solver's suggested block: a steady ring plus a slow pulse. */
.tile.is-hint {
  outline: 3px solid #ffffff;
  outline-offset: -3px;
  animation: hint-pulse 1.1s ease-in-out infinite;
  z-index: 3;
}

@keyframes hint-pulse {
  0%, 100% { box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.24), 0 0 0 0 rgba(255, 255, 255, 0.55); }
  50%      { box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.24), 0 0 0 9px rgba(255, 255, 255, 0); }
}

@keyframes nudge {
  0%, 100% { filter: none; }
  50% { filter: brightness(1.35); }
}

.board.is-solved .tile { box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.24), 0 0 0 2px rgba(255, 255, 255, 0.6); }

/* ---------- win overlay ---------- */

.win {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(10, 13, 17, 0.82);
  border-radius: var(--radius);
  z-index: 5;
}

.win-card {
  text-align: center;
  padding: 22px 26px;
}

.win-badge {
  width: 52px;
  height: 52px;
  margin: 0 auto 10px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 26px;
  background: #1f7a4d;
  color: #fff;
}

.win-card h2 { margin: 0 0 6px; font-size: 26px; }
.win-stats { margin: 0 0 4px; color: var(--text-dim); font-size: 14px; }
.win-best { margin: 0 0 4px; color: var(--gold); font-size: 13px; font-weight: 600; }
.win-streak { margin: 0 0 4px; color: var(--text-dim); font-size: 13px; }
.win-actions { display: flex; gap: 8px; margin-top: 12px; }
.win-actions .btn { flex: 1; }

.win-stars { margin: 0 0 4px; font-size: 20px; letter-spacing: 4px; }
.win-stars .earned { color: #fbc02d; }
.win-stars .missed { color: var(--line-bright); }

.mode-note {
  height: 32px;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.25;
  color: var(--text-dim);
}
.mode-note-text { max-width: 100%; }
.mode-note strong { color: var(--accent); font-weight: 600; }

/* ---------- controls ---------- */

.controls { display: flex; flex-direction: column; gap: 10px; }

.seg {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 4px;
}

.seg-mode { grid-template-columns: repeat(3, 1fr); }

.seg-btn {
  border: 0;
  border-radius: 7px;
  padding: 9px 4px;
  background: transparent;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.seg-btn.is-active { background: var(--accent); color: var(--on-accent); }
.seg-btn:disabled { opacity: .38; cursor: default; }

/* Icons are stroked, not filled, and follow the button's own colour - including
   the dimmed disabled state - because they inherit currentColor. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 17px;
  height: 17px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Fills live on the symbol as presentation attributes: a descendant selector
   like `.icon circle` cannot reach into the shadow tree that <use> clones. */

.action-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) 46px;
  gap: 8px;
}

.action-row .btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 11px 6px;
  font-size: 14px;
  min-height: 44px;
}

.btn-icon { padding: 11px 0; }
.btn-icon .icon { width: 19px; height: 19px; }

.btn {
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  padding: 11px 8px;
  background: var(--bg-soft);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.btn:hover { border-color: var(--line-bright); }
.btn:disabled { opacity: 0.4; cursor: default; }

/* A trailing bare number reads as "hint #3"; a pill reads as a quantity. */
.btn-badge {
  display: inline-grid;
  place-items: center;
  min-width: 16px;
  height: 16px;
  margin-left: 5px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  vertical-align: 1px;
  font-variant-numeric: tabular-nums;
}

.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* The one action that destroys progress should not look like the safe ones. */
.btn.btn-danger { border-color: var(--danger-line); color: var(--danger); }
.btn.btn-danger:hover { border-color: var(--danger); }

/* Cancel is not the point of the sheet, so it must not out-shout the actions
   it sits under. Quiet, borderless, centred - present but never the target. */
.btn.btn-quiet { background: none; border-color: transparent; color: var(--text-dim); }
.btn.btn-quiet:hover { background: var(--wash); color: var(--text); }

/* Stacked buttons in a sheet; .modal-card .btn already spans the full width.
   Menu rows lead with their icon, so the labels align down the left edge. */
.menu-list { display: grid; gap: 8px; }
.menu-list .btn { display: flex; align-items: center; gap: 10px; text-align: left; }
.menu-list .btn.primary,
.menu-list .btn.btn-quiet { justify-content: center; }

.confirm-note { margin: 0 0 16px; font-size: 14px; line-height: 1.5; color: var(--text-dim); }

.toggles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.toggle {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--bg-soft);
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.toggle:hover { border-color: var(--line-strong); }

/* The native control stays focusable but invisible; .switch is the visual. */
.toggle input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
  pointer-events: none;
}

.switch {
  position: relative;
  flex: none;
  width: 34px;
  height: 20px;
  border-radius: 999px;
  background: var(--line-strong);
  box-shadow: inset 0 0 0 1px var(--wash);
  transition: background 0.18s ease;
}

.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--text-dim);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  transition: transform 0.18s ease, background 0.18s ease;
}

.toggle-text { line-height: 1.2; }

.toggle input:checked + .switch { background: var(--accent); }
.toggle input:checked + .switch::after { transform: translateX(14px); background: var(--frame); }

.toggle:has(input:checked) { border-color: rgba(77, 163, 255, 0.5); color: var(--text); }

.toggle:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.toggle:has(input:disabled) { opacity: 0.4; cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .toggle, .switch, .switch::after { transition: none; }
  .help-more > summary::before { transition: none; }
}

/* ---------- modal ---------- */

.modal {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 18px;
  background: rgba(6, 9, 12, 0.78);
}

.modal-card {
  width: 100%;
  max-width: 420px;
  max-height: calc(100dvh - 36px);
  overflow-y: auto;
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 22px;
}

.modal-card h2 { margin: 0 0 12px; font-size: 20px; }
.modal-card ul { margin: 0 0 18px; padding-left: 20px; }
.modal-card li { margin-bottom: 8px; font-size: 14px; line-height: 1.5; color: var(--text-body); }
.modal-card .btn { width: 100%; }

/* Collapsed reference sections keep the help modal short on open. */
.help-more {
  border-top: 1px solid var(--line);
}

.help-more > summary {
  padding: 11px 2px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
}

.help-more > summary::-webkit-details-marker { display: none; }

.help-more > summary::before {
  content: '';
  width: 6px;
  height: 6px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(-45deg);
  transition: transform 0.18s ease;
}

.help-more[open] > summary::before { transform: rotate(45deg); }
.help-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.help-more > ul { margin: 0 0 14px; }

/* The card itself stops scrolling; the body scrolls and the footer is a
   sibling, so content can never pass underneath the dismiss button. */
.modal-card-split {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: 0;
}

/* min-height: 0 is required, or a flex child refuses to shrink below its
   content height and the scroll never engages. */
.modal-scroll {
  overflow-y: auto;
  min-height: 0;
  overscroll-behavior: contain;
}

.modal-foot {
  flex: none;
  margin: 0 -22px;
  padding: 14px 22px 20px;
  background: var(--bg-soft);
  border-top: 1px solid var(--line);
}

/* ---------- settings sheet ---------- */

/* Every heading spaces itself off whatever came before it. This used to be a
   list of previous-sibling types (.seg, .toggles), so a heading that followed
   anything else - a swatch row, say - silently got no gap at all. */
.field-label {
  margin: 18px 0 7px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.field-label:first-child,
h2 + .field-label { margin-top: 0; }

.field-note {
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-dim);
}

.settings-utilities {
  display: grid;
  gap: 8px;
  margin: 18px 0 14px;
}

.data-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.data-backup-card { max-width: 380px; }
.data-backup-intro { margin: 0; font-size: 14px; line-height: 1.5; color: var(--text-body); }
.data-backup-note { margin-bottom: 16px; }

.data-status {
  min-height: 50px;
  display: flex;
  align-items: center;
  margin: 12px 0;
  padding: 11px 13px;
  border: 2px solid var(--line-bright);
  border-radius: 10px;
  background: var(--surface-hi);
  color: var(--text);
  font-size: 13px;
  font-weight: 650;
  line-height: 1.4;
}

.data-status[data-state="progress"] { border-color: var(--accent); }
.data-status[data-state="success"] { border-color: var(--accent); }
.data-status[data-state="error"] { border-color: var(--danger); }

.settings-subdialog-card {
  max-width: 400px;
}

.settings-subdialog-intro {
  margin: 0 0 16px;
  color: var(--text-body);
  font-size: 13px;
  line-height: 1.45;
}

.settings-subdialog-card .toggles {
  margin-bottom: 16px;
}

.settings-subdialog-card .swatch-row {
  margin-bottom: 16px;
}

.settings-entry {
  width: 100%;
  min-height: 58px;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  padding: 9px 13px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--bg-soft);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}

.settings-entry:hover { border-color: var(--line-strong); }
.settings-entry:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.settings-entry-copy { min-width: 0; flex: 1; display: grid; gap: 3px; }
.settings-entry-title { font-size: 14px; font-weight: 700; }
.settings-entry-note { font-size: 12px; line-height: 1.3; color: var(--text-dim); }

.settings-entry-arrow {
  width: 8px;
  height: 8px;
  flex: none;
  border-top: 2px solid var(--text-dim);
  border-right: 2px solid var(--text-dim);
  transform: rotate(45deg);
}

.feedback-card { max-width: 400px; }
.feedback-intro { margin: 0 0 8px; font-size: 14px; line-height: 1.45; color: var(--text-body); }
.feedback-address { margin: 0 0 16px; font-size: 13px; color: var(--text-dim); }
.feedback-address a { color: var(--accent); font-weight: 600; }
.feedback-includes-title { margin: 0 0 5px; font-size: 13px; font-weight: 700; }
.modal-card .feedback-includes { margin-bottom: 10px; }
.modal-card .feedback-includes li { margin-bottom: 3px; font-size: 13px; line-height: 1.35; }
.feedback-privacy { margin: 0 0 14px; font-size: 12px; line-height: 1.45; color: var(--text-dim); }
.feedback-privacy a { color: var(--accent); font-weight: 600; }

.feedback-puzzle {
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
}

.feedback-puzzle input { width: 18px; height: 18px; flex: none; accent-color: var(--accent); }
.feedback-puzzle span { display: grid; gap: 2px; font-size: 13px; }
.feedback-puzzle small { color: var(--text-dim); font-size: 11px; }

.feedback-preview {
  margin-bottom: 4px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.feedback-preview > summary {
  padding: 10px 2px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}

.feedback-preview > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }

.feedback-preview textarea {
  display: block;
  width: 100%;
  min-height: 168px;
  margin: 0 0 10px;
  padding: 10px;
  resize: vertical;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text-body);
  font: 12px/1.45 ui-monospace, SFMono-Regular, Consolas, monospace;
}

.feedback-status { min-height: 18px; margin-bottom: 8px; }
.feedback-actions a.btn { text-decoration: none; }

/* ---------- level picker ---------- */

.levels-summary {
  margin: 0 0 14px;
  font-size: 13px;
  color: var(--text-dim);
}
.levels-summary strong { color: var(--accent); font-weight: 600; }

.levels-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 18px;
  max-height: 46vh;
  overflow-y: auto;
}

.level-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 58px;
  padding: 6px 2px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-soft);
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.level-btn:hover:not(:disabled) { border-color: var(--line-bright); background: var(--surface-hi); }

.level-num { font-size: 16px; font-weight: 600; line-height: 1; }

.level-stars { font-size: 10px; letter-spacing: 1px; color: #fbc02d; min-height: 12px; }
.level-stars .missed { color: var(--line-bright); }

.level-btn.is-current { border-color: var(--accent); background: var(--accent-soft); }

.level-btn:disabled {
  opacity: 0.45;
  cursor: default;
  color: var(--text-dim);
}

[hidden] { display: none !important; }

/* ---------- privacy policy ---------- */

.privacy-page {
  min-height: 100dvh;
  display: block;
  background: var(--cp-bg);
  color: var(--cp-text);
  font-family: "Segoe UI", Aptos, Calibri, -apple-system, BlinkMacSystemFont, sans-serif;
}

.privacy-shell {
  width: min(100%, 760px);
  margin: 0 auto;
  padding: max(18px, env(safe-area-inset-top)) 16px max(40px, env(safe-area-inset-bottom));
}

.privacy-nav { margin-bottom: 14px; }

.privacy-back {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  color: var(--cp-accent);
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
}

.privacy-back:hover { text-decoration: underline; }
.privacy-back:focus-visible { outline: 2px solid var(--cp-accent); outline-offset: 3px; border-radius: 5px; }

.privacy-card {
  padding: clamp(22px, 6vw, 42px);
  border: 1px solid var(--cp-border);
  border-radius: 16px;
  background: var(--cp-surface);
  box-shadow: 0 1px 2px var(--cp-accent-soft);
}

.privacy-header {
  padding-bottom: 22px;
  border-bottom: 1px solid var(--cp-border);
}

.privacy-kicker {
  margin: 0 0 8px;
  color: var(--cp-accent);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.privacy-card h1 {
  margin: 0;
  font-size: clamp(30px, 9vw, 46px);
  line-height: 1.05;
  letter-spacing: -0.035em;
}

.privacy-lead {
  max-width: 36rem;
  margin: 14px 0 10px;
  color: var(--cp-text-muted);
  font-size: clamp(16px, 4.5vw, 19px);
  line-height: 1.5;
}

.privacy-date {
  margin: 0;
  color: var(--cp-text-muted);
  font-size: 12px;
}

.privacy-card section {
  padding-top: 24px;
}

.privacy-card h2 {
  margin: 0 0 8px;
  font-size: 18px;
  line-height: 1.3;
}

.privacy-card p {
  margin: 0;
  color: var(--cp-text-muted);
  font-size: 14px;
  line-height: 1.65;
}

.privacy-card p + p { margin-top: 10px; }
.privacy-card a { color: var(--cp-link); text-underline-offset: 2px; }

@media (max-width: 420px) {
  .privacy-shell { padding-right: 10px; padding-left: 10px; }
  .privacy-card { padding: 22px 20px 28px; }
}

/* ---------- topbar actions ---------- */

.topbar-actions {
  display: flex;
  gap: 8px;
  flex: none;
}

.icon-btn svg {
  width: 15px;
  height: 15px;
  fill: currentColor;
  display: block;
  margin: 0 auto;
}

/* ---------- palette swatches ---------- */

.swatch-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

.swatch {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 11px;
  border: 1px solid var(--line-strong);
  background: var(--bg-soft);
  color: var(--text-dim);
  cursor: pointer;
  text-align: left;
}

.swatch.is-active {
  border-color: var(--accent);
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--accent);
}

/* Five 12px chips plus the longest palette name do not fit on one line at any
   common phone width, so the chips overlap like a stack of cards - 44px instead
   of 68px - and each carries a ring in the card colour to stay separable. */
.swatch-chips {
  display: flex;
  flex: none;
  gap: 0;
}

.swatch-chips span {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  box-shadow: 0 0 0 1.5px var(--bg-soft);
}

.swatch-chips span + span { margin-left: -4px; }

.swatch-name {
  font-size: 12px;
  font-weight: 600;
  /* Wrapping is ugly but readable; an ellipsis on "Accessible" is neither. */
  min-width: 0;
}

/* Each theme swatch re-scopes the theme variables onto itself, so the row is
   its own preview - background, text and accent all come from the theme it is
   offering while the modal around it stays in the current one. */
.swatch-theme {
  gap: 8px;
  padding: 8px 10px;
  background: var(--bg);
  border-color: var(--line-strong);
  color: var(--text);
}

.swatch-theme .swatch-name {
  color: var(--text);
}

.theme-dot {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: inset 0 0 0 1px var(--wash);
}

/* ---------- stats ---------- */

.stats-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin: 12px 0 16px;
}

.stat-card {
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 10px;
}

.stat-card-label {
  display: block;
  font-size: 10px;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.stat-card-value {
  display: block;
  margin-top: 3px;
  font-size: 16px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

/* The inline palette row needs about 107px inside each card for the chips plus
   the longest name. Two columns only provide that from roughly 380px up, and
   below it the name is a single unbreakable word that would spill outside the
   card, so narrow phones keep the original stacked card instead. Theme swatches
   are unaffected - one dot and a short name fit at every width. */
@media (max-width: 379px) {
  #palette-row .swatch {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }

  #palette-row .swatch-chips { gap: 3px; }

  #palette-row .swatch-chips span {
    width: 15px;
    height: 15px;
    border-radius: 4px;
    box-shadow: none;
    margin-left: 0;
  }
}

/* Short viewports - a phone or tablet in landscape, or a small desktop window -
   have plenty of width and very little height. Tighten the vertical chrome so
   the board keeps as much room as possible; the JS cell clamp does the rest. */
@media (max-height: 620px) {
  .app { gap: 8px; padding-top: 8px; padding-bottom: 8px; }
  .topbar h1 { font-size: 17px; }
  .stat { padding: 4px; }
  .stat-value { font-size: 16px; margin-top: 1px; }
  .frame { padding: 8px; }
  .controls { gap: 8px; }
  .action-row .btn { padding: 8px 4px; min-height: 40px; }
}

/* Phone in landscape - the tightest case we support without scrolling. */
@media (max-height: 440px) {
  .app { gap: 6px; }
  .topbar h1 { font-size: 15px; }
  .icon-btn { width: 30px; height: 30px; }
}

/* Landscape on a phone has plenty of width and almost no height, so stacking
   everything vertically leaves the board squeezed under its minimum tile size.
   Put the board in its own full-height column and stack the chrome beside it.
   verticalBudget() detects this geometrically, so the two stay in step. */
/* 520px, not 600: a 568x320 landscape iPhone SE sits between the two, and in
   the stacked layout it overflows by 67px at six rows. */
@media (orientation: landscape) and (max-height: 480px) and (min-width: 520px) {
  .app {
    max-width: 900px;
    display: grid;
    align-content: stretch;
    column-gap: 12px;
    row-gap: 6px;
    /* 1fr, not auto: an auto column would size itself to the board, whose own
       size is derived from the column width. That loop never converges. */
    grid-template-columns: minmax(0, 1fr) minmax(190px, 280px);
    grid-template-rows: auto auto auto 1fr;
    grid-template-areas:
      "stage topbar"
      "stage mode"
      "stage hud"
      "stage controls";
  }

  .topbar    { grid-area: topbar; }
  .mode-panel { grid-area: mode; gap: 4px; }
  .hud       { grid-area: hud; }
  .stage     { grid-area: stage; align-self: center; }
  .controls  { grid-area: controls; align-self: end; }

  .mode-row { grid-template-columns: minmax(0, 1fr) 40px; gap: 6px; }
  .mode-row #btn-new { min-width: 40px; padding: 0; }
  .mode-row #new-label { display: none; }
  .mode-row .seg-btn { padding: 7px 2px; font-size: 11px; }
  .mode-note { height: 28px; font-size: 11px; }

  /* Four buttons will not fit across a 280px column, so use a 2x2 block. */
  .action-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .tile { transition: none; }
  .tile.is-nudge { animation: none; }
  .tile.is-hint { animation: none; }
}
