/* Colors are tokens, one set for light and one for dark. --on-* = text on that color.
   Default "Green & yellow": the familiar letter colors (green = right spot,
   yellow = wrong spot, gray = not in word), so new players read the tiles
   without the help. Purple is the game's own color and marks the theme layer:
   the theme panel, numbered squares and the active outline (--accent).
   Yellow uses dark text for contrast. */
:root {
  --bg: #ffffff;
  --fg: #1c1b22;
  /* Dark enough to clear 4.5:1 where it sits on the clue box's tint, which is
     the panel colour over --surface and the darkest ground muted text lands on. */
  --muted: #656271;
  --border: #d9d6e3;
  --tile-border: #c4c0d1;
  --border-filled: #8b8799;
  --correct: #4a9d5b;
  --present: #e3b93b;
  --absent: #7c7f8c;
  --revealed: #b3b0bf;
  --on-correct: #ffffff;
  --on-present: #2b2100;
  --on-absent: #ffffff;
  --on-revealed: #ffffff;
  --key: #dedbe8;
  --accent: #7c3aed;
  --on-accent: #ffffff;
  --overlay: rgba(255, 255, 255, 0.6);
  --card: #ffffff;
  --panel: rgba(124, 58, 237, 0.07);
  /* The clue area's own plane, so the theme panel above and the keyboard below
     read as layers over it rather than as more of the same page. */
  --surface: #f2f0f7;
  /* Type scale. Every piece of text in the game is one of these seven steps;
     what used to be here was fifteen sizes, most of them near-misses of each
     other (9/10/11, 13/14/15, 17/18/19). Tiles, theme squares and the emoji
     size themselves from their own boxes and stay out of it. */
  --t-micro: 10px;    /* caps labels over a number */
  --t-small: 12px;    /* counts, legends, anything secondary */
  --t-body: 14px;     /* the default: keys, prose, list values */
  --t-lead: 17px;     /* lead paragraphs, section headings, primary buttons */
  --t-stat: 20px;     /* numbers read at a glance */
  --t-title: 26px;    /* modal titles and statistics figures */
  --t-display: 34px;  /* the wordmark */
  --t-hero: 56px;     /* the one number at the end of a game */
  /* How big a theme square gets when the panel has the room for it; the squares
     shrink from here when a long theme would not fit. */
  --theme-cell: 52px;
  --gap: 5px;
  --cell: min(52px, calc((100vw - 32px) / var(--cols, 6) - var(--gap)));
}
/* Dark mode follows the system unless the player picked Light or Dark in
   Settings (data-theme on <html>). The dark block is repeated for the forced case. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #131218;
    --fg: #f4f3f8;
    --muted: #8f8b9e;
    --border: #36343f;
    --tile-border: #6b6880;
    --border-filled: #8b87a5;
    --correct: #4f9a5c;
    --present: #c9a232;
    --absent: #3b3946;
    --revealed: #57546a;
    --on-correct: #ffffff;
    --on-present: #1f1800;
    --on-absent: #d9d7e0;
    --on-revealed: #e4e2ea;
    --key: #7f7b8f;
    --accent: #a78bfa;
    --on-accent: #1a0d33;
    --overlay: rgba(0, 0, 0, 0.5);
    --card: #1e1d25;
    --panel: rgba(167, 139, 250, 0.1);
    --surface: #0d0c12;
  }
}
:root[data-theme="dark"] {
  --bg: #131218;
  --fg: #f4f3f8;
  --muted: #8f8b9e;
  --border: #36343f;
  --tile-border: #6b6880;
  --border-filled: #8b87a5;
  --correct: #4f9a5c;
  --present: #c9a232;
  --absent: #3b3946;
  --revealed: #57546a;
  --on-correct: #ffffff;
  --on-present: #1f1800;
  --on-absent: #d9d7e0;
  --on-revealed: #e4e2ea;
  --key: #7f7b8f;
  --accent: #a78bfa;
  --on-accent: #1a0d33;
  --overlay: rgba(0, 0, 0, 0.5);
  --card: #1e1d25;
  --panel: rgba(167, 139, 250, 0.1);
  --surface: #0d0c12;
}

/* Alternative tile colors (Settings → Tile colors, testing). Both use teal for
   the theme layer, since their tiles are purple. */
/* "Purple shades": one color family, more vivid = closer. Each state differs in
   brightness, not just hue, so color-blind players can tell them apart. */
:root[data-palette="purple"] {
  --correct: #6d28d9;
  --present: #d4c2fa;
  --absent: #7c7f8c;
  --revealed: #b3b0bf;
  --on-correct: #ffffff;
  --on-present: #2a1650;
  --on-absent: #ffffff;
  --on-revealed: #ffffff;
}
/* "Purple & orange": purple = right spot, orange = wrong spot, slate = not in word. */
:root[data-palette="orange"] {
  --correct: #7b3fe4;
  --present: #e8740c;
  --absent: #6b6f7d;
  --revealed: #a9a6b5;
  --on-correct: #ffffff;
  --on-present: #ffffff;
  --on-absent: #ffffff;
  --on-revealed: #ffffff;
}
:root[data-palette="purple"], :root[data-palette="orange"] {
  --accent: #0d9488;
  --on-accent: #ffffff;
  --panel: rgba(13, 148, 136, 0.07);
}
@media (prefers-color-scheme: dark) {
  :root[data-palette="purple"]:not([data-theme="light"]) {
    --correct: #a67cff;
    --present: #5b44a0;
    --absent: #2c2b33;
    --revealed: #4a4758;
    --on-correct: #1a0d33;
    --on-present: #ffffff;
  }
  :root[data-palette="orange"]:not([data-theme="light"]) {
    --correct: #8352e8;
    --present: #df7212;
    --absent: #3b3946;
    --revealed: #57546a;
  }
  :root[data-palette="purple"]:not([data-theme="light"]),
  :root[data-palette="orange"]:not([data-theme="light"]) {
    --accent: #2dd4bf;
    --on-accent: #0b1f1d;
    --panel: rgba(45, 212, 191, 0.08);
  }
}
:root[data-palette="purple"][data-theme="dark"] {
  --correct: #a67cff;
  --present: #5b44a0;
  --absent: #2c2b33;
  --revealed: #4a4758;
  --on-correct: #1a0d33;
  --on-present: #ffffff;
}
:root[data-palette="orange"][data-theme="dark"] {
  --correct: #8352e8;
  --present: #df7212;
  --absent: #3b3946;
  --revealed: #57546a;
}
:root[data-palette="purple"][data-theme="dark"], :root[data-palette="orange"][data-theme="dark"] {
  --accent: #2dd4bf;
  --on-accent: #0b1f1d;
  --panel: rgba(45, 212, 191, 0.08);
}

* { box-sizing: border-box; }
/* Stop double-tap-to-zoom (e.g. fast Backspace taps); pinch zoom still works. */
* { touch-action: manipulation; }
/* The page itself never scrolls — the clue area, the popups and the opening
   screen each scroll inside themselves. Saying so stops a transform from
   lengthening it: the result bar rising 16px into place was enough to grow the
   document and flash a scrollbar for the length of the animation. */
html, body { margin: 0; height: 100%; overflow: hidden; }
body {
  background: var(--bg);
  color: var(--fg);
  font-family: "Clear Sans", "Helvetica Neue", Arial, sans-serif;
  /* Set the base, or anything unstyled — help prose, toasts — falls to the
     browser's 16px, which is a size nobody picked. */
  font-size: var(--t-body);
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* Form controls do not inherit type, so a radio would sit at the browser's own
   13px and the scale would have a sixteenth size hiding in it. */
button, input { font: inherit; color: inherit; }
/* Hidden always wins, even over a component's own display (e.g. the Share button). */
[hidden] { display: none !important; }

header {
  width: 100%;
  max-width: 500px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
}
.header-right { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.header-left { display: flex; align-items: center; gap: 8px; }

/* The running count: every guess, word or theme. */
/* A plain readout, deliberately not a filled shape, so it doesn't look tappable.
   Label stacked over the number keeps it narrow enough for a 320px phone. */
#guessed {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 40px;
  font-weight: bold;
  line-height: 1.05;
  user-select: none;
}
/* Caps and tracking, because at 10px that is what reads — and it matches the
   same label over the big number in the result popup. */
#guessed span { font-size: var(--t-micro); text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
#guessed b { font-size: var(--t-stat); font-variant-numeric: tabular-nums; }
#guessed.final b { color: var(--accent); }
#guessed.bump { animation: pop 0.25s; }
.icon-btn {
  background: none;
  border: 2px solid var(--border);
  border-radius: 50%;
  width: 34px;
  height: 34px;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.icon-btn svg { width: 20px; height: 20px; display: block; }

/* Theme panel: the goal, so it gets a card of its own. */
#theme-panel {
  width: calc(100% - 32px);
  max-width: 468px;
  margin: 10px 16px 4px;
  padding: 10px 14px 12px;
  border: 2px solid var(--accent);
  border-radius: 12px;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
#theme-panel.won { border-color: var(--accent); }
#theme-panel.lost { border-color: var(--muted); }
.theme-label {
  font-size: var(--t-small);
  font-weight: bold;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent);
}

#theme-bar { display: flex; justify-content: center; gap: 6px; width: 100%; }
#theme-bar span {
  /* The squares share out the panel's width: each one takes --theme-cell where
     that fits and shrinks evenly where it doesn't, so a 4-letter theme is as
     big as a clue tile instead of capped at a 6-letter theme's size. */
  flex: 0 1 var(--theme-cell);
  min-width: 0;
  aspect-ratio: 1;
  border: 2px solid var(--accent);
  border-radius: 8px;
  background: var(--card);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  font-size: min(calc(var(--theme-cell) * 0.5),
                 calc((100vw - 60px) / var(--theme-len, 5) * 0.5));
  text-transform: uppercase;
  position: relative;
}
/* Small number matching the clue whose marked square gives this letter. */
#theme-bar span::after {
  content: attr(data-n);
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: var(--t-micro);
  line-height: 1;
  color: var(--accent);
}
#theme-bar span.known::after, #theme-panel.won #theme-bar span::after,
#theme-panel.lost #theme-bar span::after { color: inherit; opacity: 0.75; }
#theme-bar span.known { background: var(--accent); color: var(--on-accent); }
#theme-bar span.cursor { box-shadow: 0 0 0 3px var(--accent); }
#theme-panel.won #theme-bar span { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
#theme-panel.lost #theme-bar span { background: var(--revealed); border-color: var(--revealed); color: var(--on-revealed); }

/* Not a button shape: the panel is the control, and a pill here reads as the
   first thing to press on a screen where the clues are the way in. A line that
   names the action instead, still a real button so a keyboard can reach it and
   a screen reader announces it. Wide enough to tap: the panel takes the tap
   anywhere, this is just where the words are. */
#theme-btn {
  width: 100%;
  background: none;
  border: 0;
  border-radius: 8px;
  /* Room to tap the words themselves, though the panel takes a tap anywhere. */
  min-height: 30px;
  padding: 5px 8px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0 8px;
  font-size: var(--t-small);
  font-weight: bold;
  color: var(--accent);
  cursor: pointer;
  text-wrap: balance;
}
/* One mark in the panel says "press this", and only the control carries it:
   everything else here is text. Faint, because before the first clue is solved
   this line is an aside, not the way in. */
#theme-btn b {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
#theme-btn:not(:disabled):hover b { text-decoration-color: currentColor; }
#theme-btn:disabled { color: var(--muted); font-weight: normal; cursor: default; }
#theme-btn:disabled b { text-decoration: none; }
/* Before the first clue is solved it is an aside, quieter than the line above
   it: the clues are the instruction, this is the option. */
#theme-btn.aside { font-weight: normal; }
/* The count is secondary to the action, and a size down also buys the line the
   room it needs at five banked. */
/* Same size as "6 tries left" under a clue: they are the same kind of thing,
   a count of what you have left to spend, and they read as a pair. */
#theme-btn .left { font-size: var(--t-small); font-weight: normal; color: var(--muted); white-space: nowrap; }
#theme-btn .pips { margin-right: 4px; letter-spacing: 2px; color: var(--accent); }
@media (min-width: 390px) {
  .theme-label { font-size: var(--t-body); }
  #theme-panel { padding: 12px 18px 14px; gap: 10px; }
  #theme-btn { font-size: var(--t-body); padding: 5px 8px; }
}
/* A big phone can give the theme squares more than a clue tile gets; they are
   the thing being hunted. Not on a short screen, where the room is the board's. */
@media (min-width: 430px) and (min-height: 700px) {
  :root { --theme-cell: 58px; }
}
/* Short phones: tighten the panel so the board keeps some room. */
@media (max-height: 700px) {
  #theme-panel { margin-top: 6px; padding: 6px 12px 8px; gap: 5px; }
  :root { --theme-cell: 40px; }
  #theme-btn { min-height: 26px; padding: 3px 8px; }
}
#theme-msg { font-size: var(--t-body); color: var(--fg); text-align: center; text-wrap: balance; }
/* The one line that celebrates, so it is allowed to be loud. */
#theme-panel.won #theme-msg {
  font-size: var(--t-lead);
  font-weight: bold;
  color: var(--accent);
  animation: rise 380ms cubic-bezier(.5, 0, .3, 1) calc(var(--finale, 0ms) + 180ms) both;
}
#theme-msg:empty, #theme-wrong:empty { display: none; }
#theme-wrong { font-size: var(--t-small); color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
/* Where typing goes: the active box (theme panel or current clue) has the teal
   outline and tint; the other rests with a plain outline, still full strength.
   Tapping a resting box switches to it (game.js). */
#theme-panel:not(.won):not(.lost):not(.editing) {
  cursor: pointer;
  border-color: var(--border);
  background: none;
}
.word-block.dim { cursor: pointer; outline-color: var(--border); background: none; }

/* The clue area is a plane of its own (--surface) so that what scrolls is
   visibly separate from the panel above it and the keyboard below, which both
   sit on the page color. main is the frame; #scroller is what moves. */
main {
  position: relative;
  flex: 1;
  min-height: 0;
  width: 100%;
  max-width: 500px;
  display: flex;
}
#scroller {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  justify-content: center;
  /* No top padding: the how-to-start line pins to this edge, and any gap above
     it would show clues sliding past in the clear. The board carries the air. */
  padding: 0 16px 16px;
  background: var(--surface);
}
/* Which way the clues continue past the edge. Pinned to main, not to the
   scrolled content, and only shown for an edge that has something past it
   (main.more-up / .more-down, set in game.js). --cue-top keeps the top one
   clear of the how-to-start line, which is pinned there too. */
.scroll-cue {
  position: absolute;
  left: 0;
  right: 0;
  height: 24px;
  display: flex;
  justify-content: center;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.18s;
  pointer-events: none;
}
.scroll-cue svg { width: 16px; height: 16px; display: block; }
.scroll-cue.up {
  top: var(--cue-top, 0px);
  align-items: flex-start;
  background: linear-gradient(var(--surface) 20%, transparent);
}
.scroll-cue.down {
  bottom: 0;
  align-items: flex-end;
  background: linear-gradient(transparent, var(--surface) 80%);
}
main.more-up .scroll-cue.up, main.more-down .scroll-cue.down { opacity: 1; }
/* Clues passing under the pinned how-to-start line, so it reads as a layer
   over them rather than as a gap in the stack. */
main.more-up .board-hint { box-shadow: 0 5px 6px -5px rgba(0, 0, 0, 0.4); }
#board {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  /* Pinned to the grid's own width. The active word's box bleeds 8px past it so
     its tiles stay on the board's columns; without this the box would widen the
     board whenever the active clue reaches the right edge, and centering would
     shift every row sideways. --cell keeps this inside the page's gutters. */
  width: calc(var(--cols) * (var(--cell) + var(--gap)) - var(--gap));
  flex-shrink: 0;
  /* the scroller is a row flex container, so stretch would cap this at its
     height and let the clues spill out of a box shorter than they are — which
     breaks any sticky child, since it can only stick inside its own box. */
  align-self: flex-start;
  margin-top: 8px;
}
/* Every line shares the same column grid so the spine tiles align. */
.line {
  display: grid;
  grid-template-columns: repeat(var(--cols), var(--cell));
  gap: var(--gap);
}
.word-gap { height: 4px; }

.tile {
  width: var(--cell);
  height: var(--cell);
  border: 2px solid var(--tile-border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--cell) * 0.55);
  font-weight: bold;
  text-transform: uppercase;
  position: relative;
  user-select: none;
}
.tile.filled { border-color: var(--border-filled); animation: pop 0.1s; }
.tile.correct, .tile.present, .tile.absent, .tile.revealed { border-color: transparent; }
.tile.correct { background: var(--correct); color: var(--on-correct); }
.tile.present { background: var(--present); color: var(--on-present); }
.tile.absent { background: var(--absent); color: var(--on-absent); }
.tile.revealed { background: var(--revealed); color: var(--on-revealed); }

/* How to start, above the clues, until the first guess. Zero width with a full
   min-width so the text wraps inside the board instead of widening it. Sticky,
   because the board area is only ~170px on a short phone: scrolling the active
   clue into view would otherwise carry this line off the top before it is read. */
.board-hint {
  position: sticky;
  top: 0;
  z-index: 2;
  width: 0;
  min-width: 100%;
  text-align: center;
  font-size: var(--t-small);
  line-height: 1.4;
  color: var(--muted);
  /* A card on the clue area's own color, so it reads as sitting over the stack
     rather than as a gap in it. */
  background: var(--bg);
  border-radius: 10px;
  padding: 6px 10px 7px;
  margin-bottom: 4px;
}
.board-hint b { display: block; font-size: var(--t-body); color: var(--fg); }
/* The splash's line, in the splash's colour, so it reads as the same thing
   said twice rather than a new rule appearing on the board. */
.board-hint i { display: block; margin-top: 2px; font-style: normal; font-weight: bold; color: var(--accent); }

/* An open clue you aren't typing into: tap it to move there. */
.line.pick { cursor: pointer; }
.line.pick:hover .tile:not(.correct):not(.present):not(.absent) { border-color: var(--border-filled); }
.line.pick:hover .tile.spine { box-shadow: 0 0 0 3px var(--accent); }
/* A clue you ran out of tries on: closed, its theme letter never revealed. */
.line.closed { opacity: 0.45; }

/* The spine (theme letter) decoration: accent ring + the clue's number in the
   corner, matching the numbered theme square it fills. */
.tile.spine { box-shadow: 0 0 0 2px var(--accent); }
.tile.spine::after {
  content: attr(data-n);
  position: absolute;
  top: 2px;
  right: 3px;
  font-size: max(9px, calc(var(--cell) * 0.2));
  line-height: 1;
  color: var(--accent);
}
.tile.spine.correct, .tile.spine.present, .tile.spine.absent, .tile.spine.revealed {
  box-shadow: 0 0 0 3px var(--accent);
}
.tile.spine.correct::after, .tile.spine.present::after,
.tile.spine.absent::after, .tile.spine.revealed::after { color: inherit; opacity: 0.8; }
/* Copy of a solved clue's marked tile, flying up to the theme bar. */
.tile.ghost { position: fixed; margin: 0; z-index: 5; transform-origin: 0 0; pointer-events: none; }

.tile.flip { animation: flip 0.5s ease both; }
.shake { animation: shake 0.4s; }
.bounce { animation: bounce 0.6s ease both; }

@keyframes pop { 50% { transform: scale(1.1); } }
@keyframes flip {
  0% { transform: rotateX(0); }
  50% { transform: rotateX(90deg); }
  100% { transform: rotateX(0); }
}
@keyframes shake {
  10%, 90% { transform: translateX(-2px); }
  30%, 70% { transform: translateX(4px); }
  50% { transform: translateX(-4px); }
}
@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  40% { transform: translateY(-12px); }
}
/* The finale. A turn rather than a shake: shake is what a rejected guess does,
   so the same motion here would read as "wrong". */
#theme-bar span.cheer { animation: cheer 640ms cubic-bezier(.4, 0, .2, 1) both; }
@keyframes cheer {
  0% { transform: rotateY(0) scale(1); }
  45% { transform: rotateY(180deg) scale(1.2); filter: drop-shadow(0 0 12px var(--accent)); }
  100% { transform: rotateY(360deg) scale(1); filter: none; }
}
/* Each marked square down the board, in the word that carried it. */
.tile.spine.flash { animation: flash 460ms ease both; }
@keyframes flash {
  0% { box-shadow: 0 0 0 3px var(--accent); }
  35% { transform: scale(1.16); box-shadow: 0 0 0 8px var(--accent); }
  100% { box-shadow: 0 0 0 3px var(--accent); }
}
/* Chips of the game's own tiles, thrown up off the word. The layer is fixed and
   clipped: a chip that flies off the edge must not lengthen the page. */
#sparks {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 6;
}
.spark {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 2px;
}

/* The active word's box hugs its own columns; the -8px offsets the padding so
   its tiles stay aligned with the board's grid. */
.word-block {
  display: flex;
  flex-direction: column;
  gap: var(--gap);
  --step: calc(var(--cell) + var(--gap));
  margin: 2px 0 2px calc(var(--off) * var(--step) - 8px);
  /* As wide as its word, but never so narrow that the line under it has to
     break mid-word: a 4-letter clue on an 11-column board is a 116px card. The
     extra runs into the empty columns to its right and is clamped to what is
     left of the board, so the board itself never grows and the tiles stay on
     their columns. */
  width: max(
    calc(var(--len) * var(--step) - var(--gap) + 16px),
    min(170px, calc(100% - var(--off) * var(--step) + 8px))
  );
  padding: 8px;
  border-radius: 8px;
  background: var(--panel);
  outline: 2px solid var(--accent);
}
.word-block .line { grid-template-columns: repeat(var(--len), var(--cell)); }
/* Unscored tiles (empty or typed) sit on the card color, like the theme squares. */
.word-block .tile:not(.correct):not(.present):not(.absent) { background: var(--card); }

/* The box is only as wide as its clue word — 100px on an 11-column board — so
   everything in here has to be able to wrap. Only the dots are kept whole. */
/* One rule in both boxes: the line that says what to do is bold, and the count
   beside it is normal and muted — the same weight as the panel's "2 banked". */
.tries {
  text-align: center;
  font-size: var(--t-small);
  line-height: 1.3;
  color: var(--muted);
}
.tries b { display: block; margin-bottom: 2px; font-size: var(--t-body); color: var(--fg); text-wrap: balance; }
.tries .dots { margin-right: 6px; letter-spacing: 2px; color: var(--accent); white-space: nowrap; }
.color-key {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 14px;
  margin-bottom: 8px;
  font-size: var(--t-small);
  color: var(--muted);
}
.color-key span { display: flex; align-items: center; gap: 5px; }
.color-key i { width: 14px; height: 14px; border-radius: 2px; }
.color-key .correct { background: var(--correct); }
.color-key .present { background: var(--present); }
.color-key .absent { background: var(--absent); }

/* Keyboard */
#keyboard {
  width: 100%;
  max-width: 500px;
  /* Top padding keeps the first row off the clue area's edge, which the keys
     otherwise sit flush against. */
  padding: 8px 8px 12px;
}
.kb-row { display: flex; justify-content: center; gap: 6px; margin-bottom: 8px; }
.kb-row .spacer { flex: 0.5; }
.key {
  flex: 1;
  height: 54px;
  border: 0;
  border-radius: 4px;
  background: var(--key);
  font-weight: bold;
  font-size: var(--t-body);
  text-transform: uppercase;
  cursor: pointer;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.key.wide { flex: 1.5; font-size: var(--t-small); }
.key.correct { background: var(--correct); color: var(--on-correct); }
.key.present { background: var(--present); color: var(--on-present); }
.key.absent { background: var(--absent); color: var(--on-absent); }
/* Short phones, part two (after the rules it trims, so it wins on order): the
   how-to-start card and the keyboard each give a little back. A 568px phone
   with a six-clue board is about 15px short of landing its active clue clear
   of that pinned card, and the clue area is what the game is played on. */
@media (max-height: 700px) {
  .board-hint { padding: 4px 8px 5px; }
  .key { height: 50px; }
  .kb-row { margin-bottom: 6px; }
}

/* The end of a game. The keyboard has nothing left to take, so it hands its
   slot to the result: how many guesses it took, the one action left, and when
   to come back. Same width and padding as the keyboard, so nothing shifts. */
#finish {
  width: 100%;
  max-width: 500px;
  padding: 12px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.finish-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
#finish-badge { font-size: var(--t-stat); }
#finish-guesses { font-size: var(--t-small); color: var(--muted); white-space: nowrap; }
#finish-guesses b {
  margin-right: 5px;
  font-size: var(--t-stat);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
#finish-guesses.lost b { color: var(--fg); }
body.ended #finish { animation: rise 420ms cubic-bezier(.5, 0, .3, 1) calc(var(--finale, 0ms) + 420ms) both; }
body.ended #finish-badge { animation: pop 400ms calc(var(--finale, 0ms) + 820ms) both; }
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }
@media (prefers-reduced-motion: reduce) {
  body.ended #finish, body.ended #finish-badge, #theme-panel.won #theme-msg { animation: none; }
}
#finish-share { width: 100%; }
#finish-next { margin: 0; text-align: center; font-size: var(--t-small); color: var(--muted); }
#finish-next:empty { display: none; }
/* A finished board no longer fills its area; center it instead of leaving it
   pinned to the top with all the slack pooled underneath. `safe` keeps a board
   that is still taller than the area at the top, where it can be scrolled. */
body.ended #board { align-self: safe center; }

/* Opening screen */
#splash {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  text-align: center;
  overflow-y: auto;
}
.splash-logo { display: flex; flex-direction: column; gap: 3px; margin-bottom: 18px; }
.splash-logo .r { display: flex; gap: 3px; margin-left: calc(var(--o) * 27px); }
.splash-logo i {
  width: 24px;
  height: 24px;
  border-radius: 3px;
  background: var(--correct);
  color: var(--on-correct);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--t-body);
  font-weight: bold;
  font-style: normal;
  text-transform: uppercase;
}
.splash-logo i.t { background: var(--accent); color: var(--on-accent); border-radius: 6px; }
#splash h1 { margin: 0 0 8px; font-size: var(--t-display); letter-spacing: -0.01em; }
.splash-tag { margin: 0 0 28px; font-size: var(--t-lead); line-height: 1.35; max-width: 320px; text-wrap: balance; }
.splash-tag b { display: block; margin-top: 6px; color: var(--accent); }
/* wrap-reverse: on a narrow phone the buttons stack with Play on top. */
.splash-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap-reverse; }
.splash-btn {
  min-width: 140px;
  height: 50px;
  padding: 0 24px;
  border-radius: 25px;
  border: 2px solid var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--t-lead);
  font-weight: bold;
  cursor: pointer;
}
.splash-btn.quiet { background: none; color: var(--fg); border-color: var(--border-filled); }
.splash-date { margin: 28px 0 0; font-size: var(--t-body); line-height: 1.5; }
.splash-date b, .splash-date span { display: block; }
.splash-date span { color: var(--muted); }

/* Toast */
#toast {
  position: fixed;
  /* Sits at the top of the board, under the theme panel; game.js measures it so
     it doesn't depend on the keyboard's height. This is the fallback. */
  top: 30%;
  left: 50%;
  max-width: calc(100vw - 32px);
  text-align: center;
  transform: translateX(-50%);
  background: var(--fg);
  color: var(--bg);
  padding: 10px 16px;
  border-radius: 6px;
  font-weight: bold;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s;
  z-index: 20;
}
#toast.show { opacity: 1; }

/* Modals */
.modal {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 16px;
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 10;
}
/* margin:auto centers the card when it fits and pins it to the top when it
   doesn't, so its top edge can never end up above the screen. */
.modal-card {
  margin: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  max-width: 420px;
  width: 100%;
  /* dvh = the visible height on phones (excludes the browser's address bar) */
  max-height: calc(100vh - 32px);
  max-height: calc(100dvh - 32px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25);
}
/* The title and close button stay put; only the body scrolls. */
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 12px 8px 24px;
  flex: none;
}
.modal-head h2 { margin: 0; font-size: var(--t-title); }
.modal-body {
  padding: 4px 24px 20px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.close {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: none;
  border: 0;
  font-size: var(--t-title);
  line-height: 1;
  cursor: pointer;
}
/* The small print at the foot of How to play: what leaves the device, and who
   this game is standing on. */
.colophon {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--muted);
  text-align: center;
  text-wrap: pretty;
}
.colophon span { display: block; margin-top: 8px; }
.colophon a { color: inherit; text-underline-offset: 2px; }
.colophon a:hover { color: var(--accent); }
.muted { color: var(--muted); }
.center { text-align: center; }

/* Help screen */
#help-modal p { margin: 6px 0; line-height: 1.4; }
#help-modal .lead { font-size: var(--t-lead); margin-bottom: 14px; }
.help-section { border-top: 1px solid var(--border); padding: 12px 0 8px; }
.help-section h3 { margin: 0 0 6px; font-size: var(--t-lead); }
.demo-row { display: flex; gap: 4px; margin: 10px 0; }
.demo-row .mini { width: 34px; height: 34px; font-size: var(--t-lead); }
.mini {
  width: 20px;
  height: 20px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--border);
  font-weight: bold;
  text-transform: uppercase;
  position: relative;
}
.mini.correct, .mini.present, .mini.absent { border-color: transparent; }
.mini.correct { background: var(--correct); color: var(--on-correct); }
.mini.present { background: var(--present); color: var(--on-present); }
.mini.absent { background: var(--absent); color: var(--on-absent); }
.mini.spine { box-shadow: 0 0 0 2px var(--accent); }
.mini.spine::after {
  content: attr(data-n);
  position: absolute;
  top: 1px;
  right: 2px;
  font-size: var(--t-micro);
  line-height: 1;
  color: var(--accent);
}
.mini.spine.absent::after { color: inherit; }
.legend, .tiers { list-style: none; padding: 0; margin: 8px 0; }
.legend li { display: flex; align-items: center; gap: 10px; margin: 8px 0; }
.tiers li {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.tiers li:last-child { border-bottom: 0; }
.tier-emoji { font-size: var(--t-stat); text-align: center; }
.tiers li span:last-child { color: var(--muted); font-size: var(--t-body); text-align: right; }

/* Three across, two rows: six across wrapped every label on a 320px phone. */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px 4px; text-align: center; margin: 16px 0; }
.stats b { display: block; font-size: var(--t-title); }
.stats span { font-size: var(--t-small); }
#result-guesses { text-align: center; margin: 4px 0 8px; }
#result-guesses b { display: block; font-size: var(--t-hero); line-height: 1; color: var(--accent); }
#result-guesses span { font-size: var(--t-small); text-transform: uppercase; letter-spacing: 0.15em; color: var(--muted); }
/* Results popup: today's result (with Share) on top, all-time stats below. */
#result { padding-bottom: 18px; margin-bottom: 14px; border-bottom: 1px solid var(--border); }
#result-text { text-align: center; margin: 0 0 16px; }
.stats-title { margin: 0; font-size: var(--t-lead); text-align: center; }
.dist-title { margin: 4px 0 8px; font-size: var(--t-lead); text-align: center; }
#dist {
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: 4px 8px;
  align-items: center;
  margin-bottom: 18px;
  font-size: var(--t-body);
}
#dist .range { color: var(--muted); font-variant-numeric: tabular-nums; }
#dist .bar {
  min-width: 24px;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--absent);
  color: var(--on-absent);
  font-size: var(--t-small);
  font-weight: bold;
  text-align: right;
}
#dist .bar.now { background: var(--accent); color: var(--on-accent); }
.primary {
  display: block;
  margin: 0 auto;
  background: var(--accent);
  color: var(--on-accent);
  border: 0;
  border-radius: 24px;
  padding: 12px 32px;
  font-weight: bold;
  font-size: var(--t-lead);
  cursor: pointer;
}

/* Settings */
.setting { border: 0; margin: 0 0 16px; padding: 0; }
.setting legend { font-weight: bold; margin-bottom: 8px; padding: 0; }
.setting legend small { font-weight: normal; color: var(--muted); }
.segmented { display: flex; border: 2px solid var(--border); border-radius: 10px; overflow: hidden; }
/* Labels stretch to the tallest option and their spans fill them, so a label
   that wraps to two lines doesn't leave the others' fill short. */
.segmented label { flex: 1; display: flex; cursor: pointer; }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented span {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 9px 6px;
  text-align: center;
  font-size: var(--t-body);
  font-weight: bold;
  color: var(--muted);
}
.segmented label + label span { border-left: 2px solid var(--border); }
.segmented input:checked + span { background: var(--accent); color: var(--on-accent); }
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: -4px; }
.setting .demo-row { margin-top: 10px; }
