/* dailypuzzles.co.il — plain CSS, no framework.
   Designed for players 50-80. Large type, generous spacing, every control
   labelled with a Hebrew word, never a bare icon.

   The page chrome is intentionally minimal. The board is the largest thing
   on the page; everything else retreats.
*/

:root {
  --paper:        #fbf7f0;
  --paper-raised: #ffffff;
  --ink:          #1a1714;
  --ink-soft:     #4a443e;
  --ink-faint:    #6f675e;
  --line:         #d8cfbf;
  --line-strong:  #b3a795;
  --accent:       #9a4a06;
  --accent-soft:  #fdefd6;
  --good:         #14652f;
  --bad:          #a11616;
  --shadow:       0 1px 2px rgba(26,23,20,.05), 0 10px 28px -12px rgba(26,23,20,.18);
  --radius:       14px;
  --radius-sm:    10px;
}

* { box-sizing: border-box; }

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

html {
  font-size: 18px;
  background: var(--paper);
  color: var(--ink);
  font-family: "Assistant", "Heebo", "Rubik", "Segoe UI", system-ui, -apple-system, sans-serif;
  line-height: 1.5;
  /* Pin to the warm-paper theme. The dark variant below is opt-in only. */
  color-scheme: light;
}

/* Every screen is exactly one viewport tall and the page itself never scrolls.
   Where content is genuinely unbounded — the leaderboard's rows, the About
   prose — that one region scrolls inside a fixed frame, so the header, the
   footer and the player's place on the page never move. */
body {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}
.page {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* The region allowed to scroll within a page, when there is no other option. */
.scroll-region {
  overflow-y: auto;
  min-height: 0;
  flex: 1;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
}

button, input {
  font: inherit;
  color: inherit;
}

input[type="text"] {
  min-height: 48px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--paper);
  padding: 0 14px;
  font-size: 1.1rem;
  text-align: center;
}
input[type="text"]:focus { border-color: var(--accent); outline: none; }

/* ----- top bar: one row, sparse ----- */

.topbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 24px;
  flex: 0 0 auto;
  max-width: 760px;
  margin: 0 auto;
  width: 100%;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  text-decoration: none;
  color: var(--ink);
  font-size: 1.4rem;
  font-weight: 700;
}

.topnav {
  display: flex;
  gap: 18px;
  font-size: 1.1rem;
}

.topnav a {
  text-decoration: none;
  color: var(--ink-soft);
}
.topnav a:hover { color: var(--ink); text-decoration: underline; }

.page {
  max-width: 760px;
  width: 100%;
  margin: 0 auto;
  padding: 10px 24px 14px;
  flex: 1;
}
/* The puzzle page earns more room than the reading pages: the board and its
   controls sit side by side once there is width for both. */
.page-play { max-width: 1080px; }
/* The home grid wants more width than a column of prose does. */
.page-home { max-width: 1000px; }

/* ----- the play screen is an app shell, not a document ---------------------
   It occupies exactly one viewport and never scrolls: the board is sized to
   whatever space the chrome leaves, so the player always sees the whole board
   and every control at once. */

body.play {
  height: 100dvh;
  overflow: hidden;
}
body.play .topbar {
  padding: 10px 24px;
  max-width: 1080px;
  flex: 0 0 auto;
}
body.play .page-play {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0 16px 12px;
  gap: 8px;
}
body.play .puzzle {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  gap: 8px;
}
body.play .puzzle-header {
  margin-bottom: 0;
  flex: 0 0 auto;
  align-items: center;
  gap: 10px;
}
body.play .puzzle-header h1 { font-size: 1.35rem; margin: 0; }
body.play .big-emoji { font-size: 1.1rem; }
body.play .subtitle { font-size: 0.85rem; }
body.play .timer { padding: 2px 12px; min-width: 92px; }
body.play .timer-value { font-size: 1.1rem; }
body.play .timer-label { font-size: 0.68rem; }
body.play .help-btn { min-height: 40px; padding: 0 14px; font-size: 0.9rem; }

/* On a phone every pixel of chrome is a pixel off the board, so the header
   collapses to one short row and the page hugs the edges. */
@media (max-width: 819px) {
  body.play .page-play { padding: 0 6px 8px; gap: 6px; }
  body.play .puzzle { gap: 6px; }
  body.play .puzzle-header { flex-wrap: nowrap; gap: 8px; }
  body.play .puzzle-header h1 { font-size: 1.05rem; }
  body.play .big-emoji { font-size: 0.95rem; margin-inline-end: 3px; }
  body.play .subtitle { font-size: 0.72rem; gap: 5px; }
  body.play .help-btn {
    min-height: 34px;
    padding: 0 10px;
    font-size: 0.78rem;
    white-space: nowrap;
  }
  /* Label beside the digits instead of above them: same information, half
     the height. */
  body.play .timer {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 5px;
    padding: 4px 9px;
    min-width: 0;
  }
  body.play .timer-label { display: inline; font-size: 0.68rem; }
  body.play .timer-value { font-size: 1rem; }
}

/* On the narrowest phones the date wraps to three lines and costs the board
   30px. The difficulty is the part that matters while solving; the date is on
   the home page and in the countdown. */
@media (max-width: 560px) {
  body.play .subtitle #hebrew-date,
  body.play .subtitle .dot { display: none; }
}

/* The board area takes every pixel the header and the submit button leave. */
body.play .board-host {
  margin: 0;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
body.play .play-stage { flex: 1; min-height: 0; }
body.play .primary-btn { flex: 0 0 auto; margin: 0 auto; min-height: 48px; }
body.play #error { flex: 0 0 auto; margin: 0; }

/* On a phone the rules live behind the help button, as they do on every
   solving app — there is no room for a paragraph above the board. */
body.play .objective { display: none; }
@media (min-width: 820px) {
  body.play .objective {
    display: block;
    font-size: 0.88rem;
    padding: 10px 14px;
    margin: 0;
  }
}

.footer {
  border-top: 1px solid var(--line);
  padding: 8px 24px;
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-soft);
  flex: 0 0 auto;
}
.footer p { margin: 0; }
.footer-small { margin-top: 2px; font-size: 0.8rem; }
@media (max-height: 700px) {
  .footer-small { display: none; }
}

/* ----- home: the daily mosaic ---------------------------------------------
   Laid out like a streaming front page — one featured puzzle, then a grid of
   tiles with a big coloured icon and a name underneath. Netflix does this on
   black; we keep the warm paper, because the audience here skews older and the
   whole design system is built on high contrast. The colour comes from each
   game's own accent, which every game has always declared. */

.home-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex: 0 0 auto;
  margin-bottom: 10px;
}
.home-head-text { min-width: 0; }

.hero-title {
  font-size: clamp(1.3rem, 4.6vw, 2rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  margin: 0 0 2px;
}

.hero-date {
  margin: 0;
  font-size: clamp(0.8rem, 2.9vw, 1rem);
  color: var(--ink-soft);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
}
.hero-dot { color: var(--ink-faint); }
.countdown { color: var(--ink-faint); font-variant-numeric: tabular-nums; }

/* How much of today is done — the one number worth putting up front. */
.progress-chip {
  margin: 0;
  flex: 0 0 auto;
  background: var(--paper-raised);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: clamp(0.72rem, 2.6vw, 0.9rem);
  font-weight: 700;
  white-space: nowrap;
}

/* Three columns on a phone, four once there is width. The featured tile takes
   a full row on narrow screens and a 2x2 block on wide ones. */
.tiles {
  list-style: none;
  margin: 0;
  padding: 0;
  min-height: 0;
  flex: 1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  grid-auto-rows: minmax(78px, 1fr);
}
@media (min-width: 720px) {
  .tiles {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    grid-auto-rows: minmax(92px, 1fr);
  }
}
/* Short screens cannot afford the row minimum, and a tile that overflows the
   fold is worse than a slightly shorter one. */
@media (max-height: 700px) {
  .tiles { gap: 6px; grid-auto-rows: minmax(56px, 1fr); }
  .tile > a { gap: 2px; padding: 5px 4px; border-radius: 11px; }
  .tile-icon { width: 1.6rem; height: 1.6rem; border-radius: 9px; font-size: 0.9rem; }
  .tile-count { display: none; }
  .tile-featured > a { padding: 6px 10px; }
}

.tile { min-width: 0; }
.tile > a {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 8px 6px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--paper-raised);
  text-decoration: none;
  color: var(--ink);
  text-align: center;
  position: relative;
  overflow: hidden;
  transition: border-color 120ms ease, transform 120ms ease;
}
.tile > a:hover { border-color: var(--accent-ink, var(--accent)); text-decoration: none; }
.tile > a:active { transform: scale(0.985); }
.tile > a:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* The icon is the colourful part, exactly as on the reference. */
.tile-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(2rem, 8vw, 2.6rem);
  height: clamp(2rem, 8vw, 2.6rem);
  border-radius: 12px;
  background: var(--accent-wash, var(--accent-soft));
  font-size: clamp(1.05rem, 4.4vw, 1.4rem);
  line-height: 1;
  flex: 0 0 auto;
}
.tile-name {
  font-weight: 700;
  font-size: clamp(0.74rem, 2.9vw, 0.92rem);
  line-height: 1.2;
}
.tile-count {
  font-size: clamp(0.6rem, 2.3vw, 0.72rem);
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* Featured: a full row on a phone, a 2x2 block on a wide screen. On the phone
   it is a two-column layout — icon beside the words — so the width earns its
   keep instead of leaving a hole. */
.tile-featured { grid-column: span 3; }
.tile-featured > a {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "icon name" "icon blurb" "icon count";
  align-items: center;
  align-content: center;
  column-gap: 12px;
  row-gap: 1px;
  text-align: start;
  padding: 10px 14px;
  background: var(--accent-wash, var(--accent-soft));
  border-color: var(--accent-ink, var(--accent));
}
.tile-featured .tile-icon { grid-area: icon; align-self: center; }
.tile-featured .tile-name { grid-area: name; }
.tile-featured .tile-blurb { grid-area: blurb; display: block; }
.tile-featured .tile-count { grid-area: count; }
.tile-blurb {
  font-size: clamp(0.68rem, 2.6vw, 0.8rem);
  color: var(--ink-soft);
  line-height: 1.35;
}
.tile-featured .tile-icon {
  width: clamp(2.6rem, 11vw, 3.4rem);
  height: clamp(2.6rem, 11vw, 3.4rem);
  font-size: clamp(1.4rem, 6vw, 1.9rem);
  background: var(--paper-raised);
}
.tile-featured .tile-name { font-size: clamp(1rem, 4vw, 1.3rem); }
.tile-featured .tile-count { font-size: clamp(0.66rem, 2.5vw, 0.8rem); }

@media (min-width: 720px) {
  .tile-featured { grid-column: span 2; grid-row: span 2; }
  .tile-featured > a {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "icon" "name" "blurb" "count";
    justify-items: center;
    text-align: center;
    row-gap: 6px;
    padding: 16px;
  }
  .tile-featured .tile-blurb { font-size: 0.85rem; max-width: 22rem; }
}
/* No room for a sentence on the shortest screens. */
@media (max-height: 700px) {
  .tile-featured .tile-blurb { display: none; }
}

/* "Today's puzzle" ribbon, and the solved tick that app.js turns on. */
.tile-tag {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  background: var(--accent-ink, var(--accent));
  color: #fff;
  font-size: 0.6rem;
  font-weight: 700;
  padding: 3px 9px;
  border-end-end-radius: 10px;
}
.tile-done {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 6px;
  background: var(--good);
  color: #fff;
  font-size: 0.58rem;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 999px;
}
.tile-done[hidden] { display: none; }
.tile.is-solved > a { border-color: var(--good); }
/* A card standing for several levels, partway through: the badge says how far
   rather than claiming a tick that has not been earned. */
.tile.is-part-solved > a { border-color: var(--accent-ink, var(--accent)); }
.tile.is-part-solved .tile-done { background: var(--accent-ink, var(--accent)); }

/* "3 רמות" on a card that opens a level chooser rather than a board. */
.tile-levels {
  font-size: clamp(0.58rem, 2.2vw, 0.7rem);
  font-weight: 700;
  color: var(--accent-ink, var(--accent));
  background: var(--accent-wash, var(--accent-soft));
  border-radius: 999px;
  padding: 1px 8px;
}
.tile-featured .tile-levels { grid-area: count; justify-self: start; }
@media (max-height: 700px) {
  .tile-levels { display: none; }
}

/* ----- the level chooser --------------------------------------------------
   One card per difficulty of the same game. They are read left to right as a
   ladder, so they get room for a sentence each rather than the tight squares
   of the front page. */
.tiles-levels {
  flex: 0 0 auto;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  grid-auto-rows: minmax(120px, auto);
}
.tiles-levels .tile > a {
  gap: 6px;
  padding: 18px 16px;
  align-items: flex-start;
  text-align: start;
  background: var(--accent-wash, var(--accent-soft));
  border-color: var(--accent-ink, var(--accent));
}
.tiles-levels .tile-name { font-size: clamp(1.05rem, 4vw, 1.35rem); }
.tiles-levels .tile-blurb { display: block; }

/* One accent per game, already declared by every game definition. */
.accent-emerald { --accent-ink: #0f7a4a; --accent-wash: #e4f3ea; }
.accent-sky     { --accent-ink: #15679f; --accent-wash: #e3eefa; }
.accent-violet  { --accent-ink: #6640b8; --accent-wash: #ece5f9; }
.accent-amber   { --accent-ink: #92660a; --accent-wash: #fbf0d5; }
.accent-rose    { --accent-ink: #a93359; --accent-wash: #fbe5ec; }
.accent-teal    { --accent-ink: #0c6f73; --accent-wash: #ddf0f0; }
.accent-indigo  { --accent-ink: #3c48a8; --accent-wash: #e6e8f8; }
.accent-orange  { --accent-ink: #a1500f; --accent-wash: #fbe8d8; }
.accent-lime    { --accent-ink: #55741a; --accent-wash: #eef4d8; }
.accent-cyan    { --accent-ink: #0d6a80; --accent-wash: #ddeff5; }

/* ----- puzzle page ----- */

.puzzle-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 16px;
}
.puzzle-header h1 {
  font-size: 2rem;
  font-weight: 800;
  margin: 0 0 4px;
}
.big-emoji { font-size: 1.5rem; margin-inline-end: 6px; }
.subtitle {
  color: var(--ink-soft);
  font-size: 1rem;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.subtitle .dot { color: var(--ink-faint); }

/* ----- level switch in a grouped game's header ----------------------------
   Three links, one per difficulty. Switching is a plain navigation: each
   level is its own puzzle with its own board, timer and leaderboard, so the
   strip says where you are rather than changing anything in place. */
.tier-switch {
  display: inline-flex;
  gap: 4px;
  padding: 2px;
  border-radius: 999px;
  background: var(--line);
}
.tier-switch .tier {
  padding: 3px 12px;
  border-radius: 999px;
  color: var(--ink-soft);
  font-size: 0.85em;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.tier-switch .tier:hover { color: var(--ink); text-decoration: none; }
.tier-switch .tier-on {
  background: var(--paper-raised);
  color: var(--accent-ink, var(--accent));
  box-shadow: 0 1px 2px rgba(0,0,0,.08);
}
.tier-switch .tier:focus-visible { outline: 3px solid var(--accent); outline-offset: 1px; }

.timer-block {
  display: flex;
  align-items: center;
  gap: 12px;
}
.help-btn {
  min-height: 48px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  cursor: pointer;
  font-weight: 600;
  font-size: 1rem;
}
.help-btn:hover { border-color: var(--accent); }
.timer {
  background: var(--paper-raised);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  padding: 6px 16px;
  text-align: center;
  min-width: 120px;
}
.timer-label { display: block; font-size: 0.78rem; color: var(--ink-faint); }
.timer-value {
  display: block;
  font-size: 1.4rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  direction: ltr;
}

.objective {
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 18px;
  color: var(--ink-soft);
  font-size: 1rem;
  margin: 0 0 18px;
}

.board-host {
  margin: 18px 0;
}

.primary-btn {
  width: 100%;
  max-width: 30rem;
  margin: 8px auto;
  display: block;
  min-height: 56px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  font-size: 1.1rem;
  font-weight: 700;
  cursor: pointer;
}
.primary-btn:hover { background: #7c3a04; }
.primary-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* tutorial modal */

.tutorial {
  position: fixed;
  inset: 0;
  background: rgba(26,23,20,.55);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 50;
  padding: 0;
}
/* The [hidden] attribute is set in JS but our display:flex wins specificity.
   Force it back. */
.tutorial[hidden] { display: none; }
#win[hidden], #error[hidden] { display: none; }
.tutorial-card {
  background: var(--paper-raised);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  padding: 24px;
  width: 100%;
  max-width: 480px;
}
@media (min-width: 540px) {
  .tutorial { align-items: center; padding: 16px; }
  .tutorial-card { border-radius: 16px; }
}
.tutorial-card h2 { margin: 0 0 14px; font-size: 1.3rem; }
.tutorial-step { font-size: 1.1rem; line-height: 1.55; margin: 0; }
.tutorial-demo {
  margin: 16px 0;
  padding: 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--paper);
}

/* The little illustrative board inside a tutorial step. It is laid out LTR
   like the real boards, so a demo row reads in the same order as the game. */
.demo-board {
  display: grid;
  gap: 2px;
  direction: ltr;
  justify-content: center;
}
.demo-board > div {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  min-width: 34px;
  min-height: 34px;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--paper-raised);
  font-weight: 700;
  font-size: 1.05rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Clues sit beside the board, not on it. */
.demo-board > div.demo-clue {
  border: none;
  background: transparent;
  color: var(--ink-soft);
  font-size: 0.95rem;
}
.demo-board > div.demo-highlight {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
  background: var(--accent-soft);
}
.demo-board > div.solid-dark { background: var(--ink); color: var(--paper); }
.demo-board > div.accent-soft { background: var(--accent-soft); }
.demo-board > div.good { background: rgba(20,101,47,.14); color: var(--good); }
.demo-board > div.bad { background: rgba(161,22,22,.12); color: var(--bad); }
.tutorial-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 20px;
}
.tutorial-step-count { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.tutorial-actions { display: flex; gap: 10px; }
.tutorial-btn {
  min-height: 48px;
  padding: 0 18px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  font-weight: 600;
  cursor: pointer;
}
.tutorial-btn-primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* ----- shared board UI ----- */

.error {
  background: rgba(161,22,22,.10);
  border: 1px solid var(--bad);
  color: var(--bad);
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  margin: 12px 0;
}

.board-frame {
  margin: 0 auto;
  width: 100%;
  max-width: 32rem;
  display: grid;
  background: var(--paper-raised);
  border: 2px solid var(--ink);
  border-radius: 14px;
  overflow: hidden;
  /* `none`, not `manipulation`: dragging across cells must paint a selection
     rather than scroll the page under the finger. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  outline: none;
  /* Lets cell type scale with the cell rather than the viewport, so a 12-wide
     board on a phone still draws its symbols inside the square. */
  container-type: inline-size;
}
.board-frame:focus-visible { box-shadow: 0 0 0 4px var(--accent-soft); }

.board-cell, .board-clue {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  color: var(--ink);
  font-weight: 700;
  /* --cells is the column count, set by the renderer. 55% of a cell's width
     keeps even wide glyphs (○, ●) clear of the edges. A fixed min-height used
     to fight aspect-ratio here and squashed cells into non-squares. */
  font-size: calc(55cqw / var(--cells, 9));
  font-variant-numeric: tabular-nums;
  line-height: 1;
  aspect-ratio: 1 / 1;
  min-width: 0;
  cursor: pointer;
  padding: 0;
  transition: background-color 100ms ease;
}
.board-cell[aria-selected="true"] {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
  background: var(--accent-soft);
  z-index: 2;
}
.board-cell.given { color: var(--ink); background: var(--line); }
.board-cell.filled { color: var(--accent); }
.board-cell.peer { background: rgba(0,0,0,.04); }
.board-cell.same-value { background: var(--accent-soft); }
.board-cell[disabled] { cursor: not-allowed; }

/* A sign between two cells is an annotation, not a square you can play. Giving
   it a border and a cell background made futoshiki and tango read as a broken
   lattice — the player could not tell clue from cell. */
.board-clue {
  font-size: calc(38cqw / var(--cells, 9));
  background: transparent;
  border: none;
  color: var(--ink);
  cursor: default;
  aspect-ratio: auto;
  font-weight: 700;
}

.keypad {
  margin: 14px auto 0;
  width: 100%;
  max-width: 30rem;
  display: grid;
  gap: 8px;
}
.keypad button {
  min-height: 56px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  font-size: 1.4rem;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
}
.keypad button:hover { border-color: var(--accent); }
.keypad button:active { transform: scale(0.97); }
.keypad button.used { color: var(--ink-faint); }
.keypad button[disabled] { opacity: 0.4; cursor: not-allowed; }

/* ----- play stage: board beside the controls -------------------------------
   Phones stack the board over the control cluster; from tablet width up the
   cluster moves to the side of the board, which keeps the board as large as
   the viewport allows. Same markup either way. */

.play-stage {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
}
/* The board takes the leftover space; the control cluster is fixed. */
.board-wrap {
  min-width: 0;
  min-height: 0;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The control cluster: actions, digit pad, mode strip. The digit pad is the
   one the thumb uses most, so it takes the space the other two do not need. */
.controls-panel {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr) 4.4rem;
  gap: 8px;
  align-items: start;
  width: 100%;
  max-width: 26rem;
  margin: 0 auto;
  /* The panel takes whatever the board could not use — a board limited by
     width leaves vertical slack, and bigger keys are a better use of it than
     empty paper. */
  flex: 1 1 auto;
  align-content: stretch;
}
.controls-panel.no-modes { grid-template-columns: 3rem minmax(0, 1fr); }
/* One restart button is not a keypad: it should not stretch to fill a screen.
   The stage centres its two rows instead. */
.controls-panel.panel-minimal { flex: 0 0 auto; align-content: start; }
.play-stage.stage-centred { justify-content: center; }
.controls-panel .control-digits { grid-auto-rows: minmax(48px, 1fr); }
.controls-panel .control-actions,
.controls-panel .control-modes { grid-auto-rows: minmax(44px, 1fr); }

@media (min-width: 820px) {
  .play-stage {
    flex-direction: row;
    align-items: stretch;
    gap: 24px;
  }
  .board-wrap { flex: 1 1 0; }
  /* Fixed basis so the panel never steals the board's width. */
  .controls-panel {
    flex: 0 0 19rem;
    align-content: center;
    width: 19rem;
    max-width: none;
    margin: 0;
    align-self: center;
  }
}
/* board.js sets an explicit width from the space the wrapper actually has, on
   both axes, so the whole board is always on screen without scrolling. */
.play-stage .board-frame { margin: auto; max-width: 100%; }

/* The control cluster: actions, digits, modes. */
/* On a phone the guidance lives behind the help button; there is no room for
   a paragraph beside a full-size board. */
.panel-hint { display: none; }
@media (min-width: 820px) {
  .panel-hint {
    display: block;
    grid-column: 1 / -1;
    margin: 2px 0 0;
    font-size: 0.78rem;
    line-height: 1.45;
    text-align: start;
    max-width: none;
  }
}

.control-col { display: grid; gap: 8px; }
.control-actions { grid-template-columns: 1fr; }
.control-digits {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* Digits are LTR content: a keypad reads 1 2 3, never 3 2 1. */
  direction: ltr;
}
.control-modes { grid-template-columns: 1fr; }

.controls-panel button {
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  cursor: pointer;
  font-weight: 700;
  color: var(--ink);
  padding: 0;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.controls-panel button:hover { border-color: var(--accent); }
.controls-panel button:active { transform: scale(0.97); }
.controls-panel button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Digits are the primary action, so they carry the weight. The selector is
   scoped so it beats the shared button rule rather than losing to it. */
.controls-panel .digit-key {
  min-height: 48px;
  font-size: 1.5rem;
  background: var(--accent) !important;
  color: #fff !important;
  border-color: var(--accent) !important;
}
.controls-panel .digit-key:hover { background: #7c3a04 !important; }

.ctl { font-size: 1.3rem; }

/* Mode buttons show what they do and name it, so the meaning never rests on
   an icon alone. */
.mode-key {
  flex-direction: column;
  gap: 3px;
  min-height: 56px;
  padding: 6px 2px;
}
.mode-key b { font-size: 0.62rem; font-weight: 600; color: var(--ink-soft); }
.mode-key.on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.mode-key.on b { color: #fff; }

.m-digit { font-size: 1.15rem; font-weight: 800; }
.m-corner {
  direction: ltr;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  width: 1.5rem;
  height: 1.1rem;
  font-size: 0.55rem;
  font-weight: 700;
}
.m-corner i { font-style: normal; }
.m-corner i:nth-child(1) { text-align: left; }
.m-corner i:nth-child(2) { text-align: right; }
.m-corner i:nth-child(3) { text-align: left; }
.m-centre { direction: ltr; font-size: 0.8rem; font-weight: 700; letter-spacing: -0.02em; }
.m-color {
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  background: conic-gradient(#e8534d, #e8a13a, #e3d24a, #62b168, #4aa6c9, #6a72c4, #b06ac4, #e8534d);
}

/* Colour swatches take over the digit area while colouring. */
.control-swatches {
  display: none;
  grid-column: 1 / -1;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 6px;
}
.control-swatches.show { display: grid; }
.control-swatches button { min-height: 40px; }

/* One palette, used by both the swatch buttons and the cells they paint. */
.swatch-0, .board-cell.swatch-0 { background: #f6cfcd !important; }
.swatch-1, .board-cell.swatch-1 { background: #f8ddb9 !important; }
.swatch-2, .board-cell.swatch-2 { background: #f5efb5 !important; }
.swatch-3, .board-cell.swatch-3 { background: #c9e6c9 !important; }
.swatch-4, .board-cell.swatch-4 { background: #bfe0ea !important; }
.swatch-5, .board-cell.swatch-5 { background: #cdd0ee !important; }
.swatch-6, .board-cell.swatch-6 { background: #e2cdea !important; }
.swatch-7, .board-cell.swatch-7 { background: #ddd6cc !important; }

/* ----- pencil marks ----- */

.board-cell { position: relative; }
.corner-marks {
  position: absolute;
  inset: 6%;
  display: flex;
  flex-wrap: wrap;
  align-content: space-between;
  justify-content: space-between;
  font-size: calc(20cqw / var(--cells, 9));
  font-weight: 700;
  color: var(--ink-soft);
  pointer-events: none;
  line-height: 1;
}
.corner-marks span { flex: 0 0 30%; text-align: center; }
.corner-marks span:nth-child(1) { text-align: left; }
.corner-marks span:nth-child(2) { margin-inline-start: auto; text-align: right; }
.centre-marks {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(22cqw / var(--cells, 9));
  font-weight: 700;
  color: var(--ink-soft);
  pointer-events: none;
  letter-spacing: -0.03em;
  padding: 0 8%;
}
/* A pencil mark for a digit a neighbouring cell has since taken cannot come
   true. It wears the same red as a big digit in the wrong place, so one
   colour carries one meaning across the board. */
.corner-marks span.impossible,
.centre-marks span.impossible { color: var(--bad); }

/* Multi-cell selection. */
.board-cell.selected {
  background: var(--accent-soft) !important;
  box-shadow: inset 0 0 0 3px var(--accent);
  z-index: 3;
}

/* A board whose clues sit inside the grid drops the heavy outer frame, so the
   clue tracks do not read as another row of cells. */
.board-frame.board-open {
  border-color: transparent;
  background: transparent;
  overflow: visible;
}
.board-clue.outside-clue {
  color: var(--ink);
  font-size: calc(46cqw / var(--cells, 8));
  font-weight: 700;
}

/* Answer values drawn as shading rather than a glyph (Hitori, Kakurasu). */
.board-cell.fill-dark { background: var(--ink); color: var(--paper); }
.board-cell.fill-accent { background: var(--accent-soft); }
/* Numbers printed on the board that are not the player's answer. */
.board-cell.printed { color: var(--ink); }
.board-cell.fill-dark.printed { color: var(--paper); }

/* Region washes. Deliberately pale: the heavy outline is the real signal, so
   these stay legible for a colour-blind solver and under dark text. */
.board-cell.region-0 { background: #fdf1dc; }
.board-cell.region-1 { background: #e6f0e8; }
.board-cell.region-2 { background: #e8eef7; }
.board-cell.region-3 { background: #f7e9e9; }
.board-cell.region-4 { background: #f0ebf7; }
.board-cell.region-5 { background: #e6f2f2; }
.board-cell.region-6 { background: #f7f0e2; }
.board-cell.region-7 { background: #eef0e4; }
.board-cell.region-8 { background: #f2e9f0; }
.board-cell.region-9 { background: #e9eef0; }
/* State always beats the region wash. */
.board-cell.given { background: var(--line); }
.board-cell.peer { background: rgba(154,74,6,.10); }
.board-cell[aria-selected="true"] { background: var(--accent-soft); }
.board-cell.fill-dark { background: var(--ink); }
.board-cell.fill-accent { background: var(--accent-soft); }
/* Where a cursor is drawn it is an outline, never a wash over the cell's own
   answer: a pale highlight on top of a fill hides the very thing the player
   just put there. */
.board-cell.fill-dark[aria-selected="true"] { background: var(--ink); }
.board-cell.fill-accent[aria-selected="true"] { background: var(--accent-soft); }

/* Region and box boundaries. */
.board-cell.edge-top { border-top: 3px solid var(--ink); }
.board-cell.edge-left { border-left: 3px solid var(--ink); }
.board-cell.edge-bottom { border-bottom: 3px solid var(--ink); }
.board-cell.edge-right { border-right: 3px solid var(--ink); }

/* Picture puzzles: one image and a row of answer buttons. */
.choice-image {
  width: 100%;
  max-width: 28rem;
  display: block;
  margin: 0 auto;
  border-radius: 14px;
  border: 2px solid var(--line-strong);
  background: #fff;
}
.keypad button.chosen {
  background: var(--accent-soft);
  border-color: var(--accent);
}

/* Sudoku keypad: 3 wide x 3 tall, the most-used layout on sudoku apps. */
.keypad.keypad-3x3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: 18rem;
}

.controls {
  margin: 14px auto 0;
  width: 100%;
  max-width: 30rem;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.controls button {
  flex: 1 1 auto;
  min-height: 48px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  padding: 0 16px;
  font-weight: 600;
  cursor: pointer;
}
.controls button:hover { border-color: var(--accent); }
.controls button.active { background: var(--accent-soft); border-color: var(--accent); }

.hint {
  margin: 12px auto 0;
  text-align: center;
  color: var(--ink-soft);
  font-size: 1rem;
  max-width: 32rem;
}

/* ----- win card ----- */

.win {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-raised);
  padding: 28px 24px;
  text-align: center;
  box-shadow: var(--shadow);
}
.win-emoji { font-size: 3rem; margin: 0 0 12px; }
.win h2 { margin: 0 0 18px; font-size: 1.6rem; }
.win dl {
  display: flex;
  justify-content: center;
  gap: 48px;
  margin: 0 0 22px;
}
.win dt { font-size: 0.95rem; color: var(--ink-faint); }
.win dd {
  margin: 4px 0 0;
  font-size: 2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.win dd.accent { color: var(--accent); }
.win-note {
  max-width: 26rem;
  margin: 0 auto 18px;
  color: var(--ink-soft);
  font-size: 1rem;
  line-height: 1.55;
}

.win form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 22rem;
  margin: 0 auto 16px;
}
.win form label { font-size: 0.95rem; color: var(--ink-soft); text-align: right; }
.win-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 22rem;
  margin: 0 auto;
}
.win-actions a {
  min-height: 52px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  font-weight: 700;
}
.win-actions a.primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.win-actions a:hover { text-decoration: none; }
/* "Admire the puzzle" — a button among the links, so it has to look like
   them rather than like a browser button. */
.win-actions .win-admire {
  min-height: 52px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

/* ----- admiring a finished board ----- */

.admire-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-raised);
}
.admire-bar[hidden] { display: none; }
.admire-label { font-weight: 700; color: var(--ink-soft); }
.admire-back {
  min-height: 38px;
  padding: 0 14px;
  border-radius: 10px;
  border: 1px solid var(--line-strong);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
/* A finished board is there to be looked at, not played: the controls go,
   the board centres in the space they leave, and nothing invites a press. */
.board-host.board-done .controls-panel { display: none; }
.board-host.board-done .play-stage { justify-content: center; }
.board-host.board-done .board-cell { cursor: default; }

/* ----- leaderboard ----- */

.page-header { flex: 0 0 auto; }
.page-header h1 { font-size: clamp(1.4rem, 4vw, 1.9rem); margin: 0 0 2px; font-weight: 800; }
.page-header .subtitle { color: var(--ink-soft); margin: 0 0 18px; }

/* Wrap rather than scroll: with nine tabs and a hidden scrollbar, half the
   games were simply unreachable — nothing on screen said there was more. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 2px 0 8px;
  flex: 0 0 auto;
}
.tab {
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  text-decoration: none;
  color: var(--ink);
  white-space: nowrap;
  min-height: 40px;
  font-size: 0.92rem;
  display: inline-flex;
  align-items: center;
  font-weight: 600;
}
.tab:hover { border-color: var(--accent); text-decoration: none; }
.tab-on { background: var(--accent-soft); border-color: var(--accent); }

.board {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper-raised);
}
.board li {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
  font-size: 1rem;
}
.board li:last-child { border-bottom: 0; }
.rank {
  width: 38px;
  height: 38px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: var(--line);
  color: var(--ink);
  flex-shrink: 0;
}
.rank-gold { background: #f0c560; color: #3b2c00; }
.rank-silver { background: #d6d2cb; color: #2a2826; }
.rank-bronze { background: #c48363; color: #fff; }
.cell-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}
.cell-sub {
  font-size: 0.9rem;
  color: var(--ink-faint);
  min-width: 4rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.cell-points {
  min-width: 4rem;
  text-align: left;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  font-size: 1.1rem;
}

.footnote {
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-faint);
  margin: 8px 0 0;
  flex: 0 0 auto;
}
.empty {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper-raised);
  padding: clamp(16px, 5vh, 36px) 20px;
  text-align: center;
  color: var(--ink-soft);
  font-size: 1.05rem;
  /* It sits where the list would, so it obeys the same fixed frame. */
  flex: 0 1 auto;
}

/* ----- about ----- */

.prose h1 { font-size: clamp(1.4rem, 4vw, 1.9rem); margin: 0 0 10px; font-weight: 800; }
.prose h2 { font-size: 1.2rem; margin: 28px 0 10px; color: var(--ink); }
.prose p { margin: 0 0 14px; color: var(--ink-soft); font-size: 1.05rem; line-height: 1.65; }
.prose a { color: var(--accent); }

/* empty state */
.empty-state {
  text-align: center;
  padding: 60px 16px;
  max-width: 480px;
  margin: 0 auto;
}
.empty-state .big-emoji { font-size: 3rem; }
.empty-state h1 { margin: 12px 0 8px; font-size: 1.4rem; }
.empty-state p { color: var(--ink-soft); }
.btn {
  display: inline-block;
  min-height: 48px;
  padding: 0 18px;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
  background: var(--paper-raised);
  color: var(--ink);
  text-decoration: none;
  line-height: 44px;
  font-weight: 700;
}
.btn:hover { text-decoration: none; border-color: var(--accent); }