/* mahjong — solitaire's shell (minesweeper's before it) (the glass, the bar, the menu sheet, the
 * rows, the foot of settings) around a board of tiles.
 *
 * 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>. Jade is a mahjong
 * table's cloth; Slate is the house's own ground under the tiles.
 */

:root {
  --bg:       #e6ebe8;
  --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. Jade is the default: the cloth a mahjong table wears. */
  --felt-1:   #2f8a74;
  --felt-2:   #1f6b5a;
  --felt-ink: #f3fbf5;
  --felt-dim: rgba(243, 251, 245, .62);
  --felt-line: rgba(255, 255, 255, .26);


  --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:       #0d1412;
    --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:   #1c5a4c;
    --felt-2:   #0d2d26;
    --card:     #f1f1ee;
    --card-edge: rgba(0, 0, 0, .5);
  }
}
:root[data-theme="dark"] {
  --bg:       #0d1412;
  --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:   #1c5a4c;
  --felt-2:   #0d2d26;
  --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); }


* { 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 BOARD 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; }
/* The board is not held to the header's measure: on an iPad or a desk it takes
   the whole felt (mahjong-v8, tiles as large as the screen allows). */
#view-game > .board-wrap { max-width: none; }
/* 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 LAYOUT, DRAWN: the mode's own tiles as tiny rects, each layer a
   shade lighter (app.js thumb reads the real layout). */
.mode-thumb { flex: none; width: 58px; height: 74px; align-self: center; }
.mode-thumb svg { width: 100%; height: 100%; display: block; overflow: visible; }
.mode-thumb .tt { fill: #e9e2cf; stroke: rgba(0,0,0,.35); stroke-width: .4; }
.mode-thumb .tz1 { fill: #f2ecdc; } .mode-thumb .tz2 { fill: #f8f4e8; } .mode-thumb .tz3, .mode-thumb .tz4 { fill: #fdfbf4; }
.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); }
/* A board 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 board'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; }


/* ── the board ─────────────────────────────────────────────────────────── */

/* THE ELEMENT IS THE FELT'S PLAYING AREA, and board.js reads its size and
   sets --tw/--th (a tile) and --tx/--ty (the lift of a layer, and a tile's
   thickness), from --lift-x/--lift-y here (fractions of a tile's width). It
   owns every gesture inside it. */
.board { position: relative; flex: 1; min-height: 0; touch-action: none; --tw: 40px; --th: 53px; --tx: 4px; --ty: 6px;
  --lift-x: .12; --lift-y: .14; }
.board * { touch-action: none; }

/* A TILE, AS THE WINDOWS GAMES DREW IT (mahjong-v3; Mahjong Titans, Microsoft
   Mahjong — the owner's pick of four, 2026-10-23): seen straight down at a
   slant, a cream face whose thickness shows down-right — a box-shadow the size
   of the lift, ivory by the face and the JADE BACK of a real tile outside it,
   so a stack shows one stripe per layer and the layers can be counted — a lit
   rim top-left, and a soft shadow cast down-right onto whatever lower layer it
   stands on (board.js "THE LIFT"). A tile on the floor of the board is a shade
   darker (--shade, per layer). `translate` is its place (board.js); `scale` is
   the lift of a selected tile. Never `.card` — that is the settings panels'
   class (IOS.md §25 (a)). */
.tile {
  position: absolute; left: 0; top: 0;
  width: var(--tw); height: var(--th);
  border-radius: calc(var(--tw) * .12);
  --side: inset 1px 1px 0 rgba(255,255,255,.75),
          calc(var(--tx) * .45) calc(var(--ty) * .45) 0 0 var(--tile-side-hi),
          var(--tx) var(--ty) 0 0 var(--tile-side),
          calc(var(--tx) + .5px) calc(var(--ty) + .5px) 0 0 rgba(0,0,0,.28);
  background: linear-gradient(rgba(40,30,0,var(--shade, 0)), rgba(40,30,0,var(--shade, 0))), linear-gradient(160deg, var(--tile-1), var(--tile-2));
  box-shadow: var(--side), 0 0 0 .5px rgba(0,0,0,.35),
    calc(var(--tx) * 1.6) calc(var(--ty) * 1.6) calc(var(--tw) * .2) rgba(0,0,0,.42);
  cursor: pointer;
  transition: opacity .22s ease, scale .18s ease, filter .2s ease;
  will-change: opacity;
}
:root { --tile-1: #fbf8ef; --tile-2: #efe9d6; --tile-side-hi: #e4d9bb; --tile-side: #2d6a52; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --tile-1: #efe9da; --tile-2: #dcd4bf; --tile-side-hi: #d6cbac; --tile-side: #285e49; } }
:root[data-theme="dark"] { --tile-1: #efe9da; --tile-2: #dcd4bf; --tile-side-hi: #d6cbac; --tile-side: #285e49; }
/* THE FACE: one cell of faces.webp (tiles.js; Cangjie6's tiles, art/CREDITS.md),
   a box of the cell's own shape (168 × 213) centred on the tile, so nothing is
   stretched. 7 × 6 cells, hence the size. */
.tile .tf, .mt .tf {
  position: absolute; left: 11%; right: 11%; top: 50%; translate: 0 -50%;
  aspect-ratio: 168 / 213;
  background: url(faces.webp) no-repeat; background-size: 700% 600%;
}
/* ON THE BOARD THE FACE IS AS BIG AS THE TILE LETS IT BE (mahjong-v8, board.js
   "THE TILES FILL THE FELT"): the biggest box of the art's shape inside 94% of
   the tile's width and 88% of its height (board.js FACE_W, FACE_H), whatever the
   tile's shape; the art's own margin keeps the symbol off the edge. Was 78% wide. */
.tile .tf { left: 50%; right: auto; translate: -50% -50%; width: min(calc(var(--tw) * .94), calc(var(--th) * .88 * 168 / 213)); }

/* Gone: faded and lifted, then out of the way of fingers. */
.tile.gone { opacity: 0; pointer-events: none; scale: 1.18; }
.tile.taking { z-index: 90000 !important; }
/* Dealt in: appears in order (board.js sets each tile's delay). */
.tile.in:not(.gone) { animation: tilein .26s ease-out backwards; }
@keyframes tilein { from { opacity: 0; scale: .7; } to { opacity: 1; scale: 1; } }

/* Picked up: gold-rimmed and a little larger, over its neighbours. */
.tile.sel { scale: 1.08; z-index: 80000 !important; box-shadow: var(--side), 0 0 0 2.5px var(--gold), 0 0 14px 2px color-mix(in srgb, var(--gold) 70%, transparent); }
/* A HINT: the pair, in gold, breathing. */
.tile.hint { box-shadow: var(--side), 0 0 0 2.5px var(--gold), 0 0 14px 2px color-mix(in srgb, var(--gold) 70%, transparent); animation: hintpulse 1.1s ease-in-out 3; z-index: 85000 !important; }
@keyframes hintpulse { 50% { scale: 1.06; } }
/* Blocked, when Settings says to show it. */
.tile.blocked { filter: brightness(.72) saturate(.7); }
/* Refused: the whole tile shakes where it lies. `transform`, never `translate`
   — `translate` is the tile's place, and a keyframe on it sent the tile to the
   board's top-left corner and flew it back (mahjong-v1 to v6). */
.tile.shake { animation: shake .32s ease-in-out; }
@keyframes shake { 20% { transform: translateX(-4px); } 45% { transform: translateX(4px); } 70% { transform: translateX(-2px); } 90% { transform: translateX(2px); } }

/* PAUSED: the tiles are not on the table. */
.board.paused .tile { visibility: hidden; }

.board-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; }

/* SIDEWAYS, THE BUTTONS GO BESIDE THE BOARD (mahjong-v12, the owner's
   screenshot: a phone turned sideways had the header and the controls eating
   the height while the felt's sides stood empty). A short, wide screen is a
   grid: the header folds down the two sides — back and the tile count on the
   left, the clock, ↻, the speaker and the ≡ menu on the right, in the header's
   own order (CLAUDE.md 5e) — Undo and Hint at the foot of the left rail, Pause
   at the foot of the right, and the board takes the whole height between.
   The header and the controls become `display: contents`, so their buttons
   are the grid's own items; the DOM, and so the reading order, is unchanged. */
@media (orientation: landscape) and (max-height: 600px) {
  #view-game {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: auto auto auto auto auto minmax(0, 1fr) auto auto;
    column-gap: 12px; row-gap: 8px;
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
  }
  #view-game > * { width: auto; max-width: none; margin: 0; }
  .game-head, .controls { display: contents; }
  #view-game .strip { grid-column: 2; grid-row: 1; margin: 0; }
  #view-game .board-wrap { grid-column: 2; grid-row: 2 / -1; }
  #game-back { grid-column: 1; grid-row: 2; justify-self: center; }
  .game-head .game-stat:first-of-type { grid-column: 1; grid-row: 3; margin: 0; }
  .game-stat-time { grid-column: 3; grid-row: 2; margin: 0; }
  #game-new { grid-column: 3; grid-row: 3; justify-self: center; }
  #game-sound { grid-column: 3; grid-row: 4; justify-self: center; }
  #open-menu-game { grid-column: 3; grid-row: 5; justify-self: center; }
  #undo-btn { grid-column: 1; grid-row: 7; }
  #hint-btn { grid-column: 1; grid-row: 8; }
  #pause-btn { grid-column: 3; grid-row: 8; }
  /* A control sideways: the glyph over its word, a rail's width. */
  .controls .btn, #pause-btn {
    flex-direction: column; gap: 3px; width: 64px; max-width: none;
    padding: 9px 4px 7px; font-size: 12px;
  }
  #pause-btn { padding-top: 14px; padding-bottom: 14px; }
}

.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; }
/* The way back from a dead board: a pill at the top of the felt, over the tiles
   only while there is nothing on them to take. */
.rewind-btn {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%); z-index: 951;
  width: auto; max-width: calc(100% - 16px); padding: 10px 16px; font-size: 14px; white-space: nowrap;
  box-shadow: var(--shadow);
}
.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 tile, 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: .7em; 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.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 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; }

/* ── 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; }



/* ── THE ROOM ON THE FELT (mahjong-v4) ─────────────────────────────────────

   The owner: "make the app menu match the theme of the game". Every screen
   now sits on the table's cloth (Settings › Table; Slate is still the page's
   own ground), and every panel is a TILE: ivory, lit at the top-left, with
   the jade back showing underneath as on the board (the tiles' own --tile-*
   colours, so a card and a tile are one material in both themes).

   TWO TOKEN SCOPES do it without touching a component rule: the GROUND (the
   screens and the menu) takes the felt's ink, and the IVORY (every card) puts
   a dark ink back, so text inside a card is dark in dark mode too, like the
   faces on the tiles. A rule that names --ink or --dim reads whichever it sits
   in. */

:root {
  --felt-glass:   rgba(255, 255, 255, .14);
  --felt-glass-2: rgba(255, 255, 255, .09);
  --felt-link:    #c9f2e2;
  --ivory-shadow: inset 1px 1px 0 rgba(255, 255, 255, .8), 0 0 0 .5px rgba(0, 0, 0, .3),
                  0 3px 0 var(--tile-side-hi), 0 7px 0 var(--tile-side), 0 7px 0 .5px rgba(0, 0, 0, .28),
                  0 14px 22px rgba(0, 0, 0, .26);
}
:root[data-felt="slate"] { --felt-glass: var(--glass); --felt-glass-2: var(--glass-2); --felt-link: var(--link); }

/* On the ROOT, not the body: a short screen's body ends mid-screen, and a
   gradient painted on it stopped there in a seam. */
html { background: radial-gradient(140% 90vh at 50% 0, var(--felt-1), var(--felt-2)) no-repeat, var(--felt-2); }
body { background: transparent; }

#view-home, #view-tiles, #view-learn, .sheet-body, .gate {
  --ink: var(--felt-ink); --ink-2: var(--felt-ink);
  --dim: var(--felt-dim); --dim-2: var(--felt-dim);
  --line: var(--felt-line); --line-2: var(--felt-line);
  --glass: var(--felt-glass); --glass-2: var(--felt-glass-2); --pill: var(--felt-glass);
  --link: var(--felt-link);
  color: var(--ink);
}
#view-home .icon, #view-tiles .icon, #view-learn .icon, .sheet-body .icon { box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
.pill { background: var(--felt-glass); color: var(--felt-ink); border: .5px solid var(--felt-line); }

/* THE IVORY: the tokens a tile's face would have, then the tile itself. */
.card, .mode-card, .board-list, .signin-card, .overlay-card {
  --ink: #1e1a12; --ink-2: #4a4232; --dim: #857a64; --dim-2: #b5ab92;
  --line: rgba(90, 70, 30, .22); --line-2: rgba(90, 70, 30, .12);
  --pill: rgba(90, 70, 30, .14); --glass: rgba(255, 255, 255, .55); --glass-2: rgba(90, 70, 30, .07); --panel: #fbf8ef;
  --link: #1f6b5a; --good: #1a8a45; --warn: #a3600a; --bad: #c4302a; --heart: #d4475d;
  --gold: #c99412; --gold-ink: #1a1200; --games-gold: #c48a12;
  color: var(--ink);
  background: linear-gradient(165deg, var(--tile-1), var(--tile-2));
  border: 0;
  box-shadow: var(--ivory-shadow);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.card { margin-bottom: 22px; }
.mode-cards { gap: 20px; }
.board-list { margin-bottom: 4px; }
:is(.card, .board-list, .mode-card) .mark { color: #c48a12; }
/* Pressed: the tile goes down onto its back. */
.mode-card:active { background: linear-gradient(165deg, var(--tile-2), var(--tile-2)); translate: 0 3px; box-shadow: inset 1px 1px 0 rgba(255,255,255,.6), 0 0 0 .5px rgba(0,0,0,.3), 0 2px 0 var(--tile-side-hi), 0 4px 0 var(--tile-side), 0 8px 14px rgba(0,0,0,.24); }
/* A board left in play stays CLOTH (it is the table you left), raised on the felt. */
.mode-card.resume {
  --ink: var(--felt-ink); --ink-2: var(--felt-ink); --dim: var(--felt-dim); --line-2: var(--felt-line);
  background: var(--felt-glass); color: var(--felt-ink);
  box-shadow: inset 0 0 0 1px var(--felt-line), 0 8px 18px rgba(0, 0, 0, .22);
}
.mode-thumb .tt { stroke: rgba(40, 30, 10, .45); }
/* The end-of-game card on the table, and the toast, read as ivory too. */
.overlay { background: color-mix(in srgb, var(--felt-2) 55%, transparent); }

/* A MINI TILE: the board's tile at a fixed size (--mw), for the guide and the cards. */
.mt {
  --mw: 30px;
  position: relative; display: inline-block; flex: none;
  width: var(--mw); height: calc(var(--mw) * 1.32);
  border-radius: calc(var(--mw) * .12);
  background: linear-gradient(160deg, var(--tile-1), var(--tile-2));
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, .75),
              calc(var(--mw) * .05) calc(var(--mw) * .06) 0 0 var(--tile-side-hi),
              calc(var(--mw) * .1) calc(var(--mw) * .12) 0 0 var(--tile-side),
              0 0 0 .5px rgba(0, 0, 0, .35),
              calc(var(--mw) * .14) calc(var(--mw) * .18) calc(var(--mw) * .2) rgba(0, 0, 0, .28);
}
.mt.bounce { animation: mtbounce .42s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes mtbounce { 35% { translate: 0 -9px; rotate: -4deg; } 70% { translate: 0 1px; rotate: 1deg; } }

/* ── Learn to play · Meet the tiles, on the home ───────────────────────── */

.link-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.link-row .link-card { flex-direction: column; align-items: flex-start; gap: 12px; }
.link-row .link-card .blurb { font-size: 13px; }
.link-row .mode-card-body { justify-content: flex-start; }
.link-art { display: flex; gap: 6px; align-self: center; padding: 2px 8px 6px 2px; }
.link-art .mt { --mw: 28px; }
.link-art .mt:nth-child(2) { translate: 0 -5px; rotate: 6deg; }
.link-card.first { box-shadow: var(--ivory-shadow), inset 4px 0 0 var(--gold); }
.link-card.first .link-art { padding-left: 4px; }
.link-card.first .link-art .mt { --mw: 30px; }

/* ── Meet the tiles ────────────────────────────────────────────────────── */

#view-tiles .view-head, #view-learn .view-head { align-items: flex-start; }
#view-tiles .view-head .icon, #view-learn .view-head .icon { margin-top: 2px; }
#view-tiles .view-head h1, #view-learn .view-head h1 { font-size: 30px; }
.guide-body { display: grid; }
.guide-card p { margin: 10px 0 0; font-size: 14.5px; line-height: 1.55; color: var(--ink-2); }
.guide-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.guide-head h3 { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -.3px; }
.guide-zh { font-size: 14px; color: var(--dim); white-space: nowrap; }
.guide-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); gap: 14px 8px; margin: 14px 0 6px; }
.guide-tiles:not(.many) { grid-template-columns: repeat(4, 1fr); }
.gt { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 9px; }
.gt .mt { --mw: 42px; cursor: pointer; }
.guide-tiles:not(.many) .gt .mt { --mw: 50px; }
.gt figcaption { font-size: 11.5px; color: var(--dim); text-align: center; line-height: 1.2; hyphens: manual; }
.numerals { display: grid; grid-template-columns: repeat(9, 1fr); gap: 4px; margin-top: 12px; padding: 10px 6px; border-radius: 12px; background: var(--glass-2); }
.numerals span { display: flex; flex-direction: column; align-items: center; gap: 2px; font-family: var(--font-num); font-size: 12px; color: var(--dim); }
.numerals b { font-size: 21px; font-weight: 600; color: #b8322a; font-family: "PingFang SC", "Hiragino Sans GB", "Songti SC", serif; }
.guide-count p:first-of-type { margin-top: 8px; }
#tiles-learn { margin-top: 4px; }

/* ── Learn to play ─────────────────────────────────────────────────────── */

.learn-card h3 { margin: 0 0 6px; font-size: 19px; font-weight: 700; letter-spacing: -.2px; }
.learn-card p { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--ink-2); }
/* The lesson's table: a patch of deeper cloth, the board inside it. */
.learn-felt {
  display: flex; height: clamp(230px, 40vh, 360px); padding: 12px; margin-top: 4px;
  border-radius: 20px;
  background: radial-gradient(ellipse at 50% 35%, var(--felt-1), var(--felt-2) 85%);
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, .35), 0 0 0 1px var(--felt-line);
}
.learn-felt .board { flex: 1; }
.learn-note { min-height: 1.5em; margin: 12px 4px 0; text-align: center; font-size: 14px; line-height: 1.45; color: var(--felt-ink); }
.learn-note.warn { color: var(--gold-lit); }
.learn-btns { display: flex; gap: 10px; margin-top: 12px; }
.learn-btns .btn { flex: 1; padding-left: 8px; padding-right: 8px; }
.learn-done { display: grid; gap: 10px; margin-top: 4px; }
.learn-done .btn-wide { margin-top: 0; }
.prose-btns { display: flex; gap: 10px; margin-top: 14px; }
.prose-btns .btn { flex: 1; font-size: 14.5px; padding: 10px 8px; }
