/* solitaire — minesweeper's shell (the glass, the bar, the menu sheet, the
 * rows, the foot of settings) around a table of cards.
 *
 * TWO THEMES, LIGHT IS THE BASE: the palette on bare :root, dark as a token
 * swap in two guarded blocks (the media query for "system", the attribute for
 * a choice). Nothing outside them names a literal colour except the cards,
 * which are cards: white faces, red and black ink, in both themes.
 *
 * GOLD MEANS YOU (wizard's rule): your row on a leaderboard, your best on a
 * card, the one button that is the next thing to do — and a hint's glow.
 *
 * THE FELT is its own small palette (--felt-1/2, the table's ink), chosen in
 * Settings › Table and worn as data-felt on <html>. Slate is the house's own
 * ground under the cards instead of cloth.
 */

:root {
  --bg:       #e4ebe6;
  --panel:    #ffffff;
  --panel-2:  #f6f6f9;
  --glass:    rgba(255, 255, 255, .78);
  --glass-2:  rgba(255, 255, 255, .55);
  --line:     rgba(60, 60, 67, .16);
  --line-2:   rgba(60, 60, 67, .08);
  --ink:      #000000;
  --ink-2:    #3c3c43;
  --dim:      #8a8a8e;
  --dim-2:    #b0b0b6;
  --pill:     #efeff0;
  --good:     #1a9e4b;
  --warn:     #b8730a;
  --bad:      #e5322b;
  --link:     #007aff;
  --heart:    #e0596e;
  --gold:     #d9a520;
  --gold-lit: #ffd45c;
  --gold-ink: #1a1200;
  --shadow:   0 1px 2px rgba(0,0,0,.04), 0 6px 20px rgba(0,0,0,.05);

  /* The felt. Green is the default: the colour a card table is. */
  --felt-1:   #2f8a57;
  --felt-2:   #1b5e3b;
  --felt-ink: #f3fbf5;
  --felt-dim: rgba(243, 251, 245, .62);
  --felt-line: rgba(255, 255, 255, .26);

  /* The cards. */
  --card:      #ffffff;
  --card-edge: rgba(0, 0, 0, .22);
  --red:       #d0202b;
  --black:     #16161a;
  --club:      var(--black);
  --diamond:   var(--red);
  --back-1:    #23417a;
  --back-2:    #142a55;

  --r-card: 18px;
  --r-mid:  14px;
  --r-sm:   10px;

  --font: -apple-system, BlinkMacSystemFont, system-ui, "SF Pro Text",
          "Helvetica Neue", sans-serif;
  --font-num: ui-rounded, -apple-system, "SF Pro Rounded", system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  /* The card index: a tight, upright face that reads at 15px. */
  --font-card: -apple-system, "SF Pro Display", system-ui, "Helvetica Neue", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:       #0e1512;
    --panel:    #1c1c1e;
    --panel-2:  #2c2c2e;
    --glass:    rgba(28, 28, 30, .86);
    --glass-2:  rgba(44, 44, 46, .75);
    --line:     rgba(84, 84, 88, .5);
    --line-2:   rgba(84, 84, 88, .28);
    --ink:      #ffffff;
    --ink-2:    #ebebf5;
    --dim:      #8e8e93;
    --dim-2:    #636366;
    --pill:     #2c2c2e;
    --good:     #30d158;
    --warn:     #ffd60a;
    --bad:      #ff453a;
    --link:     #0a84ff;
    --gold:     #e8c05a;
    --gold-lit: #ffd77a;
    --shadow:   0 1px 2px rgba(0,0,0,.5), 0 6px 20px rgba(0,0,0,.4);
    --felt-1:   #1d5a39;
    --felt-2:   #0d2d1c;
    --card:     #f1f1ee;
    --card-edge: rgba(0, 0, 0, .5);
  }
}
:root[data-theme="dark"] {
  --bg:       #0e1512;
  --panel:    #1c1c1e;
  --panel-2:  #2c2c2e;
  --glass:    rgba(28, 28, 30, .86);
  --glass-2:  rgba(44, 44, 46, .75);
  --line:     rgba(84, 84, 88, .5);
  --line-2:   rgba(84, 84, 88, .28);
  --ink:      #ffffff;
  --ink-2:    #ebebf5;
  --dim:      #8e8e93;
  --dim-2:    #636366;
  --pill:     #2c2c2e;
  --good:     #30d158;
  --warn:     #ffd60a;
  --bad:      #ff453a;
  --link:     #0a84ff;
  --gold:     #e8c05a;
  --gold-lit: #ffd77a;
  --shadow:   0 1px 2px rgba(0,0,0,.5), 0 6px 20px rgba(0,0,0,.4);
  --felt-1:   #1d5a39;
  --felt-2:   #0d2d1c;
  --card:     #f1f1ee;
  --card-edge: rgba(0, 0, 0, .5);
}

/* THE TABLE'S CLOTH (Settings › Table). Blue is a card room's other cloth;
   Slate puts the cards on the app's own ground, with the page's ink. */
:root[data-felt="blue"] { --felt-1: #2f6aa0; --felt-2: #173b62; }
@media (prefers-color-scheme: dark) { :root[data-felt="blue"]:not([data-theme="light"]) { --felt-1: #1f4870; --felt-2: #0c1f36; } }
:root[data-felt="blue"][data-theme="dark"] { --felt-1: #1f4870; --felt-2: #0c1f36; }
:root[data-felt="slate"] { --felt-1: var(--bg); --felt-2: var(--bg); --felt-ink: var(--ink); --felt-dim: var(--dim); --felt-line: var(--line); }

/* FOUR-COLOUR SUITS (Settings): clubs green, diamonds blue — a deck some
   people read faster, never the default. */
:root[data-four="1"] { --club: #12804a; --diamond: #1b5fc4; }

* { box-sizing: border-box; }

/* `[hidden]` MUST WIN, AND BY DEFAULT IT DOES NOT.
 *
 * The browser's own `[hidden] { display: none }` lives in the UA stylesheet, so
 * ANY author rule that sets `display` on the same element beats it — no
 * `!important`, no specificity war, just author-over-UA. `.controls
 * { display: flex }` and `.btn-wide { display: block }` both did, which meant
 * setting `.hidden` from JavaScript did precisely nothing and the element sat
 * there in plain sight. Two of those in one day, in one afternoon's work, and
 * the second one shipped: the "Look at the board" button was drawn on the WIN
 * card too, where it is meant to be hidden.
 *
 * The property was `true` the whole time, so anything that asserts on
 * `el.hidden` — as bin/check_boot.js did — agrees with the code and not with
 * the screen. This ends the class for the whole stylesheet; checks now read the
 * computed display instead. */
[hidden] { display: none !important; }

/* NATIVE CONTROLS FOLLOW THE IN-APP CHOICE (IOS.md §11): switches, text
   fields and scrollbars read `color-scheme`, not data-theme. */
:root { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

html, body {
  margin: 0;
  min-height: 100%;              /* the page scrolls; a board locks it (see "the shell") */
  font-family: var(--font);
  color: var(--ink);
  background-color: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* The whole app refuses text selection and the iOS long-press callout: both
   interrupt a drag. Inputs opt back in below. touch-action on EVERY element,
   not only <html> (IOS.md §27: it is not inherited) — no double-tap zoom. */
*, *::before, *::after { touch-action: manipulation; }
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
input, textarea { -webkit-user-select: text; user-select: text; }

/* ── the glass at the top of the screen ────────────────────────────────────

   iOS 26 blurs the top edge of a full-screen web app against the status bar:
   content does not stop under the bar, it goes SOFT beneath it, and anything in
   that band is unreadable rather than hidden.

   `env(safe-area-inset-top)` does not describe it. That inset is the status
   bar's own height — it is what keeps content out from UNDER the bar, and it is
   already applied everywhere below. The blur reaches past it, so the two are
   different measurements and both are needed. Adding them is the same fix
   `budget` made, and 20px is the same number: about one line of small text,
   which is about what the effect eats.

   It costs a browser tab nothing — 20px of background at the top of a scrolling
   page reads as margin — so it is not gated on display-mode. A media query that
   silently fails to match is worse than a margin nobody notices, and this house
   has been bitten by exactly that shape of thing before. */
:root { --glass-t: 20px; }

/* ── THE GLASS ─────────────────────────────────────────────────────────────
   One recipe for every panel in the app. Add a new surface to this list
   rather than restating three of the four properties on it. */
.card, .signin-card, .mode-card, .overlay-card, .banner, .icon, .btn, .board-list {
  background: var(--glass);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  backdrop-filter: blur(12px) saturate(140%);
  border: .5px solid var(--line);
  box-shadow: var(--shadow);
}

/* ── the shell ─────────────────────────────────────────────────────────────

   THE PAGE SCROLLS; THE TABLE LOCKS IT (IOS.md §15, gems' shape,
   minesweeper's copy). The home and the menu are in-flow content and the
   document is their one scroller, so they reach the bottom edge of an
   installed iPhone. While the table shows (and the menu is closed) app.js
   sets html.locked: the root is the viewport and nothing moves under a drag.
   html.on-table also gives the root the felt's colour, so whatever edge iOS
   shows around a locked page is cloth, not the home's ground (IOS.md §10). */

html.locked, html.locked body {
  height: 100%; min-height: 0; overflow: hidden; overscroll-behavior: none;
}
html.locked main { height: 100%; }
html.on-table, html.on-table body { background-color: var(--felt-2); }

.view {
  box-sizing: border-box;
  padding: calc(8px + var(--glass-t) + env(safe-area-inset-top, 0px)) 16px calc(24px + env(safe-area-inset-bottom, 0px));
  max-width: 560px;
  margin: 0 auto;
}
.view[hidden] { display: none !important; }

#view-game {
  height: 100%; max-width: none;
  display: flex; flex-direction: column; overflow: hidden;
  padding: calc(4px + var(--glass-t) + env(safe-area-inset-top, 0px)) calc(8px + env(safe-area-inset-right, 0px)) calc(10px + env(safe-area-inset-bottom, 0px)) calc(8px + env(safe-area-inset-left, 0px));
  background: radial-gradient(ellipse at 50% 35%, var(--felt-1), var(--felt-2) 78%);
  color: var(--felt-ink);
}
#view-game > * { width: 100%; max-width: 760px; margin-left: auto; margin-right: auto; }
/* WHILE THE MENU IS OPEN the screen under it is hidden and pinned, not
   unmounted — the deal keeps its place (bubbles' `.screen` rule). */
body:has(#sheet-menu:not([hidden])) main { visibility: hidden; position: fixed; inset: 0; height: auto; }

.view-head { padding: 12px 4px 18px; }
/* THE BAR — conquest's lobby header: the title on the left, one round glass
   menu button on the right, and nothing else on the screen to navigate by. */
.view-head.bar { display: flex; align-items: center; gap: 10px; }
.bar-title { flex: 1; min-width: 0; }
.view-head h1 { margin: 0; font-size: 34px; font-weight: 700; letter-spacing: -.6px; }
.view-sub { margin: 4px 0 0; color: var(--dim); font-size: 15px; }

.card {
  border-radius: var(--r-card);
  padding: 16px;
  margin: 0 0 14px;
}
.card h3 { margin: 0 0 12px; font-size: 16px; font-weight: 650; }
.card.rows { padding: 0 16px; }
.card.rows h3 { padding-top: 14px; margin-bottom: 4px; }

.fine {
  margin: 12px 6px 0;
  color: var(--dim);
  font-size: 12.5px;
  line-height: 1.5;
}
.fine strong { color: var(--ink-2); font-weight: 600; }
.fine a { color: var(--link); }
.fine.in-card { margin-left: 0; margin-right: 0; }
.fine.version { font-family: var(--font-num); }
#version-home { text-align: center; margin-top: 18px; opacity: .7; }
.card.rows .fine.in-card { padding-bottom: 14px; }

/* The icons, wherever they sit: a box the size of the text beside them. */
.glyph { line-height: 0; display: inline-block; }
.glyph svg { width: 1em; height: 1em; display: block; }

/* ── buttons ───────────────────────────────────────────────────────────── */

.btn {
  font: inherit; font-size: 16px; font-weight: 600;
  padding: 12px 18px;
  border-radius: var(--r-mid);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.btn:active { opacity: .7; }
/* GOLD MEANS YOU, and the one thing to do next is yours. */
.btn-primary { background: var(--gold); color: var(--gold-ink); border-color: transparent; }
.btn-danger  { background: transparent; color: var(--bad); box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; border-color: transparent; }
.btn-quiet   { background: transparent; color: var(--link); box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; border-color: transparent; }
.btn-wide    { display: flex; width: 100%; margin-top: 18px; }
.btn-small   { padding: 7px 14px; font-size: 13px; }
.btn-glyph .glyph { font-size: 20px; }
.btn:disabled { opacity: .4; cursor: default; }

/* Round glass buttons in the game header — conquest's `.icon`. */
.icon {
  font: inherit; font-size: 20px; line-height: 0;
  width: 40px; height: 40px; border-radius: 20px;
  display: grid; place-items: center;
  color: var(--ink); cursor: pointer; flex: none; padding: 0;
}
.icon:active { opacity: .7; }

/* THE INSTALL PILL, on the home screen: install.js decides whether there is
   anything to offer; this is only the resting look. */
.pill {
  display: block; width: 100%; margin: 0 0 12px;
  font: inherit; font-size: 13.5px; font-weight: 600;
  padding: 10px 14px; border: 0; border-radius: 999px;
  background: color-mix(in srgb, var(--link) 12%, var(--panel)); color: var(--link);
  cursor: pointer;
}

/* ── the gate ──────────────────────────────────────────────────────────── */

.gate { min-height: 100dvh; display: grid; place-items: center; padding: 24px; }
.signin-card {
  max-width: 340px; width: 100%; text-align: center;
  padding: 26px 22px 22px; border-radius: var(--r-card);
}
.signin-card h1 { margin: 14px 0 6px; font-size: 30px; letter-spacing: -.03em; }
.signin-card .lede { margin: 0 0 22px; color: var(--ink-2); font-size: 15px; line-height: 1.45; }
.signin-card .btn-wide { margin-top: 0; }
/* The brand: the app icon itself, in the home screen's squircle-ish corner. */
.brand {
  display: block; width: 88px; height: 88px; margin: 0 auto;
  border-radius: 22.5%; box-shadow: 0 0 0 1px var(--line), 0 6px 18px rgba(0,0,0,.14);
}

/* ── the deals on the home screen ─────────────────────────────────────── */

.mode-cards { display: grid; gap: 12px; }
.mode-card {
  display: flex; gap: 14px; align-items: stretch; width: 100%; text-align: left;
  font: inherit; color: inherit; cursor: pointer;
  padding: 16px; border-radius: var(--r-card);
}
.mode-card:active { background: var(--glass-2); }
.mode-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
.mode-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.mode-card h3 { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -.3px; }
.mode-card .shape { color: var(--dim); font-size: 12.5px; font-family: var(--font-num); white-space: nowrap; }
.mode-card .blurb { margin: 5px 0 0; color: var(--ink-2); font-size: 14px; line-height: 1.45; }
/* THE DEAL, DRAWN: a fan of the cards the mode turns from the stock — one
   for Draw 1, three for Draw 3, a gold-edged one for Today. */
.mode-thumb { flex: none; width: 58px; height: 74px; align-self: center; }
.mode-thumb svg { width: 100%; height: 100%; display: block; overflow: visible; }
.mode-thumb .tc { fill: #fff; stroke: rgba(0,0,0,.25); stroke-width: 1; }
.mode-thumb .tb { fill: var(--back-1); stroke: rgba(0,0,0,.25); stroke-width: 1; }
.mode-thumb .tr { fill: var(--red); }
.mode-thumb .tk { fill: var(--black); }
.mode-card-stats {
  display: flex; gap: 18px; margin-top: 12px;
  padding-top: 11px; border-top: .5px solid var(--line-2);
}
.mode-card-stats div { display: flex; flex-direction: column; gap: 1px; }
.mode-card-stats .v { font-family: var(--font-num); font-size: 18px; font-weight: 600; font-variant-numeric: tabular-nums; }
.mode-card-stats .k { font-size: 11px; color: var(--dim); text-transform: uppercase; letter-spacing: .4px; }
.mode-card-stats .v.none { color: var(--dim-2); }
.mode-card-stats .v.you { color: var(--gold); }
.mode-card.daily { box-shadow: var(--shadow), inset 3px 0 0 var(--gold); }
.mode-card.daily:disabled { opacity: .55; }
/* A deal left in play: the first card, in the felt's colour, one tap back. */
.mode-card.resume { background: radial-gradient(ellipse at 30% 30%, var(--felt-1), var(--felt-2)); color: var(--felt-ink); border-color: transparent; }
.mode-card.resume .blurb, .mode-card.resume .shape { color: var(--felt-dim); }

/* ── the table's header and controls, on the felt ─────────────────────── */

.game-head { display: flex; align-items: center; gap: 8px; padding: 2px 0 8px; flex: none; }
.game-stat { display: flex; flex-direction: column; align-items: center; line-height: 1.05; min-width: 44px; }
.game-stat-time { margin-left: auto; margin-right: 6px; }
.game-head .game-stat:first-of-type { margin-left: 8px; }
.game-stat-v { font-family: var(--font-num); font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums; }
.game-stat-k { font-size: 10.5px; color: var(--felt-dim); text-transform: uppercase; letter-spacing: .5px; }
#view-game .icon, #view-game .controls .btn {
  background: rgba(255, 255, 255, .14); color: var(--felt-ink);
  border-color: var(--felt-line); box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
:root[data-felt="slate"] #view-game .icon, :root[data-felt="slate"] #view-game .controls .btn { background: var(--glass); }
#view-game .controls .btn:disabled { opacity: .38; }

/* THE SPEAKER (CLAUDE.md 5e, letters' .snd-g): drawn like the other glyphs,
   crossed out and a shade dimmer when off. */
.snd-g { width: 22px; height: 22px; fill: currentColor; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.snd-g .ol { fill: none; }
.sound-btn[aria-pressed="true"] .snd-off,
.sound-btn[aria-pressed="false"] .snd-on { display: none; }
.sound-btn[aria-pressed="false"] { opacity: .62; }
.sound-btn[hidden] { display: none; }

.table-wrap { position: relative; flex: 1; min-height: 0; display: flex; }
.controls { display: flex; gap: 8px; margin-top: 8px; flex: none; justify-content: center; }
.controls .btn { flex: 1; max-width: 200px; padding: 11px 12px; font-size: 15px; }
#pause-btn { flex: none; padding-left: 16px; padding-right: 16px; }
.btn-glyph svg { flex: none; }

/* ── the table ─────────────────────────────────────────────────────────── */

/* A PLAYING CARD IS `.pc`, NEVER `.card` — `.card` is the settings panels'
   class, and the glass recipe on it (a backdrop blur and a 20px shadow) drew
   a dark halo round every stacked pile until the names were parted (IOS.md
   §25 (a): Music's `.page` met the same collision).

/* THE ELEMENT IS THE FELT'S PLAYING AREA, and table.js reads its size. It
   owns every gesture inside it: a drag must never scroll anything. */
.table { position: relative; flex: 1; min-height: 0; touch-action: none; --cw: 48px; --ch: 68px; }
.table * { touch-action: none; }

.slot, .pc {
  position: absolute; left: 0; top: 0;
  width: var(--cw); height: var(--ch);
  border-radius: calc(var(--cw) * .1);
}
.slot { border: 1.5px solid var(--felt-line); display: grid; place-items: center; color: var(--felt-line); }
.slot-pip { width: 46%; height: 46%; fill: currentColor; }
.slot-k { font-family: var(--font-card); font-weight: 700; font-size: calc(var(--cw) * .42); opacity: .75; }
.slot.stock { cursor: pointer; }
.slot.stock .slot-recycle { width: 50%; height: 50%; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; display: none; }
.slot.stock.recycle { color: var(--felt-ink); border-color: var(--felt-dim); }
.slot.stock.recycle .slot-recycle { display: block; }
.slot.stock.empty { opacity: .45; }

.pc {
  transition: transform .22s cubic-bezier(.2, .8, .25, 1);
  will-change: transform;
  cursor: pointer;
}
.pc.nofx { transition: none; }
.pc .ci { position: absolute; inset: 0; border-radius: inherit; }
.pc .face, .pc .back {
  position: absolute; inset: 0; border-radius: inherit;
  box-shadow: 0 0 0 .5px var(--card-edge), 0 1px 2px rgba(0,0,0,.25);
}
.pc .face { background: var(--card); color: var(--black); overflow: hidden; }
.pc.su1 .face, .pc.su3 .face { color: var(--red); }
.pc.su2 .face { color: var(--club); }
.pc.su3 .face { color: var(--diamond); }
.pc .back {
  display: none;
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,.10) 0 2px, transparent 2px 7px),
    repeating-linear-gradient(-45deg, rgba(255,255,255,.10) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, var(--back-1), var(--back-2));
  border: calc(var(--cw) * .06) solid #fff;
  box-sizing: border-box;
}
.pc.down .back { display: block; }
.pc.down .face { display: none; }

/* THE FACE (table.js cardFace — Wizard's, wizard/web/styles.css .face):
   the corner is the card, the middle pip is decoration. currentColor is the
   suit's, so Four-colour suits needs nothing here. */
.pc .cf { position: absolute; inset: 0; width: 100%; height: 100%; }
.pc .cf-rank { font: 700 30px ui-rounded, -apple-system, "SF Pro Rounded", system-ui, sans-serif; fill: currentColor; }
.pc .cf-pip  { font: 400 24px -apple-system, BlinkMacSystemFont, system-ui, sans-serif; fill: currentColor; }
.pc .cf-mid  { font: 400 46px -apple-system, BlinkMacSystemFont, system-ui, sans-serif; fill: currentColor; opacity: .22; }

.pc.buried .face, .pc.buried .back { box-shadow: none; }

/* Lifted under a finger: a little bigger shadow, no transition. */
.pc.lift .face { box-shadow: 0 0 0 .5px var(--card-edge), 0 8px 18px rgba(0,0,0,.35); }

/* Turned face up: the card unfolds. On .ci, because .pc's transform is its place. */
.pc.turn .ci { animation: turn .2s ease-out; }
@keyframes turn { from { transform: scaleX(.05); } to { transform: scaleX(1); } }
.pc.shake .ci { animation: shake .32s ease-in-out; }
@keyframes shake { 20% { transform: translateX(-5px); } 45% { transform: translateX(5px); } 70% { transform: translateX(-3px); } 90% { transform: translateX(2px); } }

/* A HINT: the cards that would move and where they would go, in gold. */
.pc.hint .face, .pc.hint .back { box-shadow: 0 0 0 2.5px var(--gold), 0 0 14px 2px color-mix(in srgb, var(--gold) 70%, transparent); }
.slot.hint { border-color: var(--gold); box-shadow: 0 0 12px 1px color-mix(in srgb, var(--gold) 60%, transparent); color: var(--gold); }
.pc.hint .ci { animation: hintpulse 1.1s ease-in-out 3; }
@keyframes hintpulse { 50% { transform: translateY(-3px); } }

/* PAUSED: the cards are not on the table. */
.table.paused .pc { visibility: hidden; }

.game-note {
  position: absolute; left: 8px; right: 8px; bottom: 6px; z-index: 950;
  margin: 0; padding: 8px 12px; border-radius: 12px;
  background: var(--glass); -webkit-backdrop-filter: blur(12px) saturate(140%); backdrop-filter: blur(12px) saturate(140%);
  border: .5px solid var(--line); box-shadow: var(--shadow);
  color: var(--ink-2); font-size: 13px; line-height: 1.45; text-align: center;
  pointer-events: none;
  opacity: 0; transition: opacity .2s;
}
.game-note.show { opacity: 1; }
.game-note.warn { color: var(--warn); }

/* ── the end-of-game overlay ───────────────────────────────────────────── */

/* THE WHOLE SCREEN (minesweeper's overlay): a scrim over the table, the
   panel centred; its own buttons are the way on. */
.overlay {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
  padding: 16px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}
.overlay-card {
  width: min(320px, 90%);
  padding: 24px 20px;
  text-align: center;
  border-radius: var(--r-card);
}
.overlay-mark { font-size: 46px; line-height: 0; display: flex; justify-content: center; color: var(--ink-2); }
.overlay-mark.won { color: var(--gold); }
.overlay-mark.lost { color: var(--bad); }
.overlay-card h2 { margin: 12px 0 4px; font-size: 24px; letter-spacing: -.4px; }
.overlay-line { margin: 0; color: var(--dim); font-size: 14px; line-height: 1.5; }
.overlay-stats {
  display: flex; justify-content: center; gap: 22px;
  margin: 18px 0 0; padding: 14px 0 0;
  border-top: .5px solid var(--line-2);
}
.overlay-stats div { display: flex; flex-direction: column; gap: 2px; }
.overlay-stats dt { font-size: 10.5px; color: var(--dim); text-transform: uppercase; letter-spacing: .4px; order: 2; }
.overlay-stats dd { margin: 0; font-family: var(--font-num); font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; order: 1; }
.overlay-submit { margin: 14px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--dim); min-height: 17px; }
.overlay-submit.bad { color: var(--warn); }
.overlay-submit.good { color: var(--good); }
.overlay-btns { display: flex; gap: 10px; margin-top: 18px; }
.overlay-btns .btn { flex: 1; padding-left: 10px; padding-right: 10px; }

.overlay-card { color: var(--ink); }
.overlay-mark svg { width: 46px; height: 46px; }

/* ── segmented control ─────────────────────────────────────────────────── */

.segmented {
  display: flex; gap: 3px; padding: 3px;
  background: var(--glass-2); border-radius: var(--r-sm);
  border: .5px solid var(--line-2);
}
.segmented button {
  flex: 1; font: inherit; font-size: 14px; font-weight: 600;
  padding: 8px 6px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink); cursor: pointer;
}
.segmented button[aria-selected="true"], .segmented button.is-on {
  background: var(--gold); color: var(--gold-ink);
  box-shadow: var(--shadow);
}

/* ── the boards ────────────────────────────────────────────────────────── */
/* A segmented control as a settings row's control (Appearance, Board colours):
   as wide as its words, at the row's right, like gems' `.seg-sm`. */
.segmented.seg-sm { flex: none; }
.segmented.seg-sm button { flex: none; font-size: 13px; padding: 6px 11px; }

.board-list { margin-top: 12px; padding: 0; overflow: hidden; }
.board-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px;
  border-bottom: .5px solid var(--line-2);
}
.board-row:last-child { border-bottom: 0; }
.board-row.you { background: color-mix(in srgb, var(--gold) 14%, transparent); }
.board-rank {
  width: 26px; flex: none; text-align: right;
  font-family: var(--font-num); font-weight: 700; font-size: 15px;
  font-variant-numeric: tabular-nums; color: var(--dim);
}
.board-row.you .board-rank { color: var(--gold); }
.board-who { flex: 1; min-width: 0; }
.board-name { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.board-row.you .board-name { font-weight: 700; }
.board-meta { font-size: 11.5px; color: var(--dim); margin-top: 1px; }
.board-time {
  font-family: var(--font-num); font-size: 18px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.board-empty { padding: 30px 20px; text-align: center; color: var(--dim); font-size: 14px; line-height: 1.5; }

/* ── settings rows ─────────────────────────────────────────────────────── */

.rows > .row, .rows > div > .row {
  padding: 13px 0;
  border-bottom: .5px solid var(--line-2);
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
}
.rows > .row:last-child, .rows > div > .row:last-child { border-bottom: 0; }
/* `.rows > .row` above is more specific than a bare `.row-stack`, so its
   `align-items: center` won and every stacked row's header shrank to its
   content and sat in the middle of the card instead of spanning it — label
   left, switch right. Matched at the same depth so the override actually
   overrides. */
.rows > .row.row-stack { flex-direction: column; align-items: stretch; gap: 8px; }
.row-head { display: flex; align-items: center; justify-content: space-between; }
.row-tools { display: flex; align-items: center; gap: 10px; }
.row-k { font-size: 15px; font-weight: 500; }
.row-v { font-family: var(--font-num); font-weight: 600; font-variant-numeric: tabular-nums; }
.row-note { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--dim); }
/* Settings › Name (README "Supporters"): a row you tap. A <button> so the
 * haptic switch lands on it like every other control; the value sits at the
 * right like a settings row anywhere, the chevron says it opens. */
.rows > button.row-tap {
  appearance: none; -webkit-appearance: none; width: 100%; background: none; border: 0;
  border-bottom: 1px solid var(--line-2); font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.rows > button.row-tap:active { background: var(--pill); }
.row-tap .row-v { margin-left: auto; font-family: inherit; font-weight: 500; color: var(--dim); display: inline-flex; align-items: center; gap: 6px; }
.row-tap .row-v.own { color: var(--ink); }
.row-tap .row-v .mark { margin-left: 0; }
.row-tap .chev { color: var(--dim); font-size: 22px; line-height: 1; margin-top: -2px; transition: transform .15s; }
.row-tap[aria-expanded="true"] .chev { transform: rotate(90deg); }
.name-actions { display: flex; gap: 8px; align-items: center; }
.name-actions .btn { padding: 8px 14px; font-size: 14px; }
/* 16px: below that iOS zooms the page to the field, and the sheet is a
 * scroll away from where it was. */
.name-input {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 16px; padding: 10px 12px;
  color: var(--ink); background: var(--pill); border: 1px solid var(--line); border-radius: 10px;
}
.name-input:focus { outline: 2px solid var(--gold, #d9a520); outline-offset: 0; border-color: transparent; }

.switch { position: relative; display: inline-block; width: 51px; height: 31px; flex: none; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; z-index: 2; }
.switch span {
  position: absolute; inset: 0; border-radius: 999px;
  background: var(--pill); transition: background .2s ease;
}
.switch span::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 27px; height: 27px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.25);
  transition: transform .2s ease;
}
.switch input:checked + span { background: var(--good); }
.switch input:checked + span::after { transform: translateX(20px); }
.switch input:disabled + span { opacity: .4; }

.prose p { margin: 0 0 12px; font-size: 14px; line-height: 1.6; color: var(--ink-2); }
.prose p:last-child { margin-bottom: 0; }
.prose .btn-wide { margin-top: 12px; }
.prose-meta { font-size: 12px !important; color: var(--dim) !important; font-family: var(--font-mono); }

/* Not dim like the fineprint it sits under: this one is the only line in the
   app that is asking for something to be done. */
.update-note { color: var(--warn) !important; }

/* ── the menu sheet ────────────────────────────────────────────────────── */

/* IN FLOW, NOT FIXED (minesweeper-v32, bubbles-v34's menu): the page is the
   menu's scroller, as it is the home's, so the menu runs under the status
   bar and to the bottom edge. Its head is the home's bar — the title and ✕
   scroll away with the rest. The screen it covers is pinned out of the
   flow (the shell, above). A fixed sheet with its own scroller ends a
   status bar short of the bottom on an installed iPhone (IOS.md §15). */
.sheet { position: relative; z-index: 1100; }
.sheet[hidden] { display: none; }
.sheet-body {
  padding: calc(8px + var(--glass-t) + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-right, 0px)) calc(32px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
  max-width: 560px; width: 100%; margin: 0 auto; box-sizing: border-box;
}
.sheet-body > .view-head { padding-bottom: 4px; }
h2.sec { margin: 26px 4px 10px; font-size: 24px; font-weight: 700; letter-spacing: -.4px; }
.sheet-body section:first-of-type h2.sec { margin-top: 14px; }
.fine.top { margin: 0 4px 12px; }

/* ── the supporter's mark, the toast, the daily card ─────────────────── */

/* The spade, in the games' gold — the same colour the other games' marks use. */
.mark { display: inline-block; color: #c48a12; margin-left: 5px; vertical-align: -2px; cursor: pointer; }
.mark svg { width: .95em; height: .95em; display: inline-block; fill: currentColor; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .mark { color: #f2b93a; } }
:root[data-theme="dark"] .mark { color: #f2b93a; }
/* SUPPORTER PERKS and THE TIP JAR (index.html): a row each, the same
   tappable row as Settings › Name. The tip jar's heart is the one bit of
   colour — thanks, not a price tag. The perks card's own rows sit flush
   inside it, so the card keeps its top padding and gives up the bottom. */
#support-card { padding: 16px 16px 0; }
#support-card .support-thanks, #support-card .support-perks { margin: 0 0 12px; }
#support-card .rows { margin: 0 -16px; }
#support-card .rows > button.row-tap { padding: 13px 16px; border-bottom: 0; border-top: .5px solid var(--line-2); }
#support-card .rows > .row-stack { padding: 4px 16px 14px; border-bottom: 0; }
#tipjar-card { margin-top: 14px; }
#tipjar .tip-glyph { color: var(--heart); font-size: 18px; line-height: 1; margin-right: 2px; }
#tipjar .row-k { flex: 1; }
/* MYJP.CA GAMES (index.html, the foot): the lockup from myjp.ca/games —
   the wordmark in the rounded face, "games" tucked under it in the games'
   gold (games/index.php) — then "More games" and the caret. */
:root { --games-gold: #c48a12; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --games-gold: #f2b93a; } }
:root[data-theme="dark"] { --games-gold: #f2b93a; }
#games-card { margin-top: 14px; }
#games-row { padding: 12px 0; }   /* .rows > .row pads 0 across; the card holds the gutter */
.games-lockup { display: inline-flex; flex-direction: column; align-items: flex-end; line-height: 1; font-family: "SF Pro Rounded", ui-rounded, var(--font); font-weight: 700; }
.games-lockup .gl-mark { font-size: 22px; letter-spacing: -0.01em; color: var(--ink); }
.games-lockup .gl-ca { color: var(--dim); }
.games-lockup .gl-sub { font-size: 10px; letter-spacing: .02em; color: var(--games-gold); margin-top: -1px; margin-right: 1px; }
#games-row .games-more { flex: 1; text-align: right; color: var(--dim); font-weight: 400; }
/* ACCOUNT: who, and Sign out as a row in the danger colour — the perks
   card's shape, hairline and all. */
#account-card { padding: 16px 16px 0; }
#account-card #account-who { margin: 0 0 12px; }
#account-card .rows { margin: 0 -16px; }
#account-card .rows > button.row-tap { padding: 13px 16px; border-bottom: 0; border-top: .5px solid var(--line-2); }
.rows > button.row-tap.danger { color: var(--bad); }
/* TECHNICAL (index.html): a folded card — the summary is a row with a caret
   that turns; inside, a key/value list in the mono face and one note.
   list-style none + the -webkit-details-marker rule hide the browser's own
   triangle. The tap check says when it is wrong, in the warning colour. */
details.technical { padding: 0; overflow: hidden; }
details.technical > summary { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 13px 16px; }
details.technical > summary::-webkit-details-marker { display: none; }
details.technical > summary .chev { color: var(--dim); font-size: 22px; line-height: 1; margin-top: -2px; transition: transform .15s; }
details.technical[open] > summary .chev { transform: rotate(90deg); }
details.technical > summary:active { background: var(--pill); }
details.technical .kv { margin: 0; padding: 4px 16px 12px; border-top: .5px solid var(--line-2); }
details.technical .kv > div { display: flex; gap: 12px; padding: 8px 0; border-bottom: .5px solid var(--line-2); }
details.technical .kv > div:last-child { border-bottom: 0; }
details.technical .kv dt { flex: 0 0 76px; font-size: 13px; color: var(--dim); }
details.technical .kv dd { margin: 0; flex: 1; min-width: 0; font-size: 12px; font-family: var(--font-mono); color: var(--ink-2); line-height: 1.5; overflow-wrap: anywhere; }
details.technical .kv dd.bad { color: var(--warn); }
details.prose-fold > .prose { padding: 4px 16px 14px; border-top: .5px solid var(--line-2); }
details.prose-fold > .prose p:first-child { margin-top: 10px; }
details.technical > .row-note { padding: 0 16px 14px; }

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(24px + env(safe-area-inset-bottom, 0px)); z-index: 1300;
  max-width: min(92vw, 420px); padding: 10px 14px; border-radius: 14px;
  background: var(--glass); -webkit-backdrop-filter: blur(12px) saturate(140%); backdrop-filter: blur(12px) saturate(140%);
  border: .5px solid var(--line); box-shadow: var(--shadow);
  color: var(--ink); font-size: 14px; line-height: 1.4; text-align: center;
}
.toast-link { color: var(--link); font-weight: 600; }

/* ── the banner ────────────────────────────────────────────────────────── */

.banner {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px;
  border-radius: var(--r-mid);
  font-size: 13.5px; line-height: 1.45;
  z-index: 1200;
}

/* The banner's Retry. An installed home-screen app has no address bar and no
   reload button, so a message about a failed read used to be the end of the
   conversation. */
.banner .banner-retry {
  margin-left: auto;
  padding: 6px 13px;
  font: inherit; font-size: 12.5px; font-weight: 600; line-height: 1.2;
  border: 0; border-radius: 999px;
  background: var(--gold); color: var(--gold-ink); cursor: pointer;
}
.banner .banner-retry:disabled { opacity: .45; }
.banner .banner-retry + button { margin-left: 4px; }

/* Why the sign-in screen is showing, when there is a reason worth giving. */
.gate-msg {
  margin: 0 0 18px;
  padding: 11px 13px;
  border-radius: var(--r-mid);
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  color: var(--warn);
  font-size: 13px; line-height: 1.45; text-align: left;
}
.banner button {
  margin-left: auto; flex: none;
  font: inherit; font-size: 20px; line-height: 1;
  border: 0; background: transparent; color: inherit; cursor: pointer;
}
.banner #banner-x { padding: 0 4px; }


/* ── the strip: a goal's progress, a challenge's race ─────────────────── */

.strip { flex: none; margin: -2px 0 8px; padding: 6px 10px 8px; border-radius: 12px; background: rgba(0, 0, 0, .16); }
:root[data-felt="slate"] .strip { background: var(--glass); }
.strip-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; font-weight: 600; line-height: 1.3; }
.strip-row span:last-child { color: var(--felt-dim); }
.strip-bar { position: relative; height: 5px; margin-top: 5px; border-radius: 3px; background: rgba(255, 255, 255, .14); overflow: hidden; }
.strip-bar i { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: 3px; transition: width .3s ease; }
#strip-you { background: var(--gold); }
#strip-them { background: rgba(255, 255, 255, .5); }
/* In a race the challenger's ghost is the full bar and yours rides over it. */
.strip:not(.race) #strip-them { display: none; }
.strip.race #strip-you { height: 3px; top: 1px; }
.strip.warn #strip-you { background: var(--bad); }
.strip.warn .strip-row span:first-child { color: #ffd0cc; }

/* Undo and Hint, off for a clean goal: still tappable, to say why. */
#view-game .controls .btn.off { opacity: .38; }

/* ── home: a challenge offered, the daily goals ───────────────────────── */

.mode-card.offer { box-shadow: var(--shadow), inset 3px 0 0 var(--link); }
.mode-card.goals { box-shadow: var(--shadow), inset 3px 0 0 var(--gold); }
.mode-card.goals .shape { color: var(--gold); font-size: 15px; letter-spacing: 1px; }

/* ── the goals screen ─────────────────────────────────────────────────── */

#view-goals .view-head .icon { margin-right: 4px; }
.cal-card { padding: 12px 12px 14px; }
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cal-head h3 { margin: 0; font-size: 17px; }
.cal-nav { font: inherit; font-size: 26px; line-height: 1; width: 40px; height: 36px; border: 0; border-radius: 10px; background: transparent; color: var(--ink); cursor: pointer; }
.cal-nav:disabled { opacity: .25; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-wd { text-align: center; font-size: 11px; color: var(--dim); padding-bottom: 2px; }
.cal-day {
  aspect-ratio: 1 / 1.05; min-height: 40px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  font: inherit; border: 0; border-radius: 10px; background: var(--glass-2); color: var(--ink); cursor: pointer; padding: 0;
}
.cal-day b { font-size: 14px; font-weight: 600; font-family: var(--font-num); }
.cal-day:disabled { background: transparent; color: var(--dim-2); cursor: default; }
.cal-day.today { box-shadow: inset 0 0 0 1.5px var(--gold); }
.cal-day.sel { background: var(--gold); color: var(--gold-ink); }
.cal-dots { display: flex; gap: 3px; height: 5px; }
.cal-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--line); }
.cal-dots i.on { background: var(--gold); }
.cal-dots i.late { box-shadow: inset 0 0 0 1.2px var(--gold); background: transparent; }
.cal-day.sel .cal-dots i { background: rgba(0, 0, 0, .2); }
.cal-day.sel .cal-dots i.on { background: var(--gold-ink); }

.goal-list { display: grid; gap: 10px; }
.goal-row { display: flex; align-items: center; gap: 12px; margin: 0; padding: 14px; }
.goal-glyph { flex: none; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; font-size: 17px; background: var(--pill); color: var(--ink-2); }
.goal-row.done .goal-glyph { background: var(--gold); color: var(--gold-ink); font-weight: 700; }
.goal-body { flex: 1; min-width: 0; }
.goal-title { font-size: 15.5px; font-weight: 600; }
.goal-sub { font-size: 12.5px; color: var(--dim); margin-top: 2px; }

/* ── the menu: challenges, statistics, achievements ───────────────────── */

.challenge-list .challenge-row { gap: 12px; }
.ch-body { flex: 1; min-width: 0; }
.ch-title { font-size: 15px; font-weight: 600; }
.ch-note { font-size: 12.5px; color: var(--dim); margin-top: 2px; line-height: 1.4; }
.challenge-list .board-empty { padding: 22px 6px; }

.stats-grid { margin-top: 12px; padding: 6px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; }
.stat { padding: 12px 8px; text-align: center; }
.stat b { display: block; font-family: var(--font-num); font-size: 21px; font-weight: 650; font-variant-numeric: tabular-nums; }
.stat span { display: block; margin-top: 2px; font-size: 11px; color: var(--dim); text-transform: uppercase; letter-spacing: .3px; }
.stats-grid .board-empty { grid-column: 1 / -1; }

.sec-count { font-size: 15px; font-weight: 500; color: var(--dim); letter-spacing: 0; margin-left: 6px; }
.ach-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ach { display: flex; gap: 10px; align-items: flex-start; margin: 0; padding: 12px; }
.ach-mark { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--pill); color: var(--dim-2); }
.ach-mark svg { width: 15px; height: 15px; fill: currentColor; }
.ach.earned .ach-mark { background: var(--gold); color: var(--gold-ink); }
.ach-name { font-size: 14px; font-weight: 650; }
.ach:not(.earned) .ach-name { color: var(--ink-2); }
.ach-text { font-size: 11.5px; color: var(--dim); line-height: 1.35; margin-top: 2px; }

.ach-more { grid-column: 1 / -1; }
.ach-more > summary { list-style: none; cursor: pointer; padding: 10px 4px; font-size: 14px; font-weight: 600; color: var(--link); }
.ach-more > summary::-webkit-details-marker { display: none; }
.ach-more > .ach-grid { margin-top: 4px; }
