/* flight — solitaire's shell (minesweeper's before it, mahjong's copy): the
 * menu sheet, the rows, the foot of settings — around an airfield drawn on a
 * canvas.
 *
 * ONE LOOK, THE GAME'S (flight-v6, the owner: "make the menu the same theme as
 * the game"): every screen is the airfield book's — a brown desk (--desk-*),
 * cream paper cards with brown ink (--glass, --ink…), glossy blue pill buttons
 * and the red SELECT for the one thing to do next, italic capitals for names.
 * No iOS grey anywhere, no frosted glass (paper is opaque).
 *
 * 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) — dark is the same desk at night, the paper turned to leather.
 * The field itself is drawn by art.js in its own day and night.
 *
 * 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.
 *
 * THE FELT (the house's name for the ground around a board) is here the
 * dark green the field sits in; --felt-1/2 and its ink.
 */

:root {
  /* The desk the airfield book lies on, and what is written straight on it. */
  --desk-1:   #6e4c33;
  --desk-2:   #3a2618;
  --desk-ink: #fbf3e6;
  --desk-dim: rgba(251, 243, 230, .7);
  /* The desk is a wooden table (Poly Haven's Wood Table 001) under a wash of the desk's browns. */
  --desk-wash: radial-gradient(ellipse at 50% 40%, rgba(110, 76, 51, .25), rgba(40, 24, 14, .72) 85%);
  --bg:       #3a2618;
  /* The paper: every card, the pause card, the toast. */
  --panel:    #f7f1e3;
  --panel-2:  #efe6d2;
  --glass:    linear-gradient(#f8f2e4, #ece2cb);
  --glass-2:  rgba(70, 45, 20, .09);
  --line:     rgba(70, 45, 20, .24);
  --line-2:   rgba(70, 45, 20, .13);
  --ink:      #2e2015;
  --ink-2:    #4a3626;
  --dim:      #8a735c;
  --dim-2:    #b4a089;
  --pill:     #e3d7bd;
  --good:     #2e9a46;
  --warn:     #b8730a;
  --bad:      #c8302a;
  --link:     #2c66c9;
  --heart:    #e0596e;
  --gold:     #d9a520;
  --gold-lit: #ffd45c;
  --gold-ink: #1a1200;
  --shadow:   0 0 0 1px rgba(0,0,0,.2), 0 6px 18px rgba(0,0,0,.32);
  /* The book's buttons. */
  --blue-btn: linear-gradient(#5d9cf2, #2c66c9);
  --red-btn:  radial-gradient(ellipse at 50% 30%, #ef4a42, #b8211c);

  /* The ground round the field. */
  --felt-1:   #3f6d4a;
  --felt-2:   #263f2c;
  --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"]) {
    --desk-1:   #3f2b1d;
    --desk-2:   #1c120b;
    --desk-wash: radial-gradient(ellipse at 50% 40%, rgba(40, 26, 16, .6), rgba(10, 6, 3, .88) 85%);
    --bg:       #1c120b;
    --panel:    #3a2b1f;
    --panel-2:  #2f2318;
    --glass:    linear-gradient(#3f2f22, #31241a);
    --glass-2:  rgba(255, 235, 200, .07);
    --line:     rgba(255, 235, 200, .2);
    --line-2:   rgba(255, 235, 200, .1);
    --ink:      #f6ecdb;
    --ink-2:    #e5d6bf;
    --dim:      #b39d83;
    --dim-2:    #7d6a55;
    --pill:     #4a382a;
    --good:     #34b356;
    --warn:     #ffc94a;
    --bad:      #ff5b4f;
    --link:     #7fb2ff;
    --gold:     #e8c05a;
    --gold-lit: #ffd77a;
    --shadow:   0 0 0 1px rgba(0,0,0,.5), 0 6px 18px rgba(0,0,0,.5);
    --felt-1:   #1d2d24;
    --felt-2:   #0b130e;
    --card:     #f1f1ee;
    --card-edge: rgba(0, 0, 0, .5);
  }
}
:root[data-theme="dark"] {
  --desk-1:   #3f2b1d;
  --desk-2:   #1c120b;
  --desk-wash: radial-gradient(ellipse at 50% 40%, rgba(40, 26, 16, .6), rgba(10, 6, 3, .88) 85%);
  --bg:       #1c120b;
  --panel:    #3a2b1f;
  --panel-2:  #2f2318;
  --glass:    linear-gradient(#3f2f22, #31241a);
  --glass-2:  rgba(255, 235, 200, .07);
  --line:     rgba(255, 235, 200, .2);
  --line-2:   rgba(255, 235, 200, .1);
  --ink:      #f6ecdb;
  --ink-2:    #e5d6bf;
  --dim:      #b39d83;
  --dim-2:    #7d6a55;
  --pill:     #4a382a;
  --good:     #34b356;
  --warn:     #ffc94a;
  --bad:      #ff5b4f;
  --link:     #7fb2ff;
  --gold:     #e8c05a;
  --gold-lit: #ffd77a;
  --shadow:   0 0 0 1px rgba(0,0,0,.5), 0 6px 18px rgba(0,0,0,.5);
  --felt-1:   #1d2d24;
  --felt-2:   #0b130e;
  --card:     #f1f1ee;
  --card-edge: rgba(0, 0, 0, .5);
}



/* THE GAME'S FACES (flight-v7): Lilita One for every name and button, Bangers for the praise words
   drawn on the field (art.js). Both SIL OFL 1.1, from Google Fonts' repository, served from here
   (web/fonts/, registered in ~/claude/dependencies.json as flight-fonts). */
@font-face { font-family: "Lilita One"; src: url("fonts/LilitaOne-Regular.ttf") format("truetype"); font-display: swap; }
@font-face { font-family: "Bangers"; src: url("fonts/Bangers-Regular.ttf") format("truetype"); font-display: swap; }
:root { --font-game: "Lilita One", var(--font); }

* { 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%;              /* every screen locks the page (see "the shell") */
  font-family: var(--font);
  color: var(--desk-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 PAPER ─────────────────────────────────────────────────────────────
   One recipe for every panel in the app — the airfield book's cream page (flight-v6;
   it was frosted glass). Add a new surface to this list rather than restating it. */
.card, .signin-card, .overlay-card, .banner, .board-list, .toast, .game-note {
  background: var(--glass);
  color: var(--ink);
  border: 0;
  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; }

/* THE TWO LANDSCAPE SCREENS — the airfield book and the field — fill the screen and never
   scroll (html.locked); the safe-area insets reach them as --sat/--sar/--sab/--sal, which the
   sideways rotation below remaps (the notch is the app's left while it is turned). */
:root {
  --sat: env(safe-area-inset-top, 0px); --sar: env(safe-area-inset-right, 0px);
  --sab: env(safe-area-inset-bottom, 0px); --sal: env(safe-area-inset-left, 0px);
}
#view-home, #view-game { height: 100%; max-width: none; margin: 0; padding: 0; overflow: hidden; position: relative; }
#view-game { background: var(--felt-2); color: var(--felt-ink); }
/* 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-family: var(--font-game); font-size: 16px; font-weight: 400; font-style: italic; text-transform: uppercase; letter-spacing: .03em;
  padding: 10px 18px;
  border: 0; border-radius: 999px;
  background: var(--blue-btn); color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.35);
  box-shadow: 0 0 0 2px #fff, 0 3px 8px rgba(0,0,0,.3);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.btn:active { filter: brightness(.88); }
/* THE ONE THING TO DO NEXT is the book's red SELECT. */
.btn-primary { background: var(--red-btn); color: #fff; }
.btn-danger  { background: transparent; color: var(--bad); box-shadow: none; text-shadow: none; }
.btn-quiet   { background: transparent; color: var(--link); box-shadow: none; text-shadow: none; }
.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 buttons — conquest's `.icon`: on the desk a ring of cream (the book's ≡), on paper a darker dot of it. */
.icon {
  font: inherit; font-size: 20px; line-height: 0;
  width: 40px; height: 40px; border-radius: 20px;
  display: grid; place-items: center;
  cursor: pointer; flex: none; padding: 0;
  background: rgba(255,255,255,.14); color: var(--desk-ink); border: 1px solid rgba(255,255,255,.25);
}
.card .icon, .overlay-card .icon { background: var(--pill); color: var(--ink); border-color: var(--line-2); }
.icon:active { opacity: .7; }

/* THE INSTALL PILL sits in the airfield book's top bar (below); install.js decides whether there is
   anything to offer. */

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

/* LANDSCAPE, ON THE DESK (flight-v6): the icon beside the words, so the card fits a phone held sideways. */
.gate {
  height: 100%; min-height: 100%; display: grid; place-items: center;
  padding: max(10px, var(--sat)) calc(16px + var(--sar)) max(10px, var(--sab)) calc(16px + var(--sal));
  background: var(--desk-wash), url("sprites/wood.jpg") center / 420px repeat, var(--desk-2);
}
.signin-card {
  max-width: 560px; width: 100%; text-align: left;
  display: grid; grid-template-columns: auto 1fr; column-gap: 22px; align-items: center;
  padding: 20px 22px; border-radius: var(--r-card);
}
.signin-card .brand { grid-row: 1 / span 5; }
.signin-card h1 { margin: 0 0 4px; font-family: var(--font-game); font-size: 32px; font-weight: 400; font-style: italic; letter-spacing: -.02em; }
.signin-card .lede { margin: 0 0 12px; color: var(--ink-2); font-size: 15px; line-height: 1.45; }
.signin-card .btn-wide { margin-top: 0; }
.signin-card .fine { margin: 10px 0 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 airfield book (home) ──────────────────────────────────────────
   The original's map select: a desk, the airfield's map on a page, a HIGH
   SCORE bubble, and Previous · SELECT · Next along the foot. */
#view-home {
  display: flex; flex-direction: column;
  padding: max(8px, var(--sat)) calc(14px + var(--sar)) max(8px, var(--sab)) calc(14px + var(--sal));
  background: var(--desk-wash), url("sprites/wood.jpg") center / 420px repeat, var(--desk-2);
  color: var(--desk-ink);
}
.home-top { display: flex; align-items: center; gap: 12px; flex: none; padding: 0 2px 8px; }
.home-title { margin: 0; font-family: var(--font-game); font-size: 28px; font-weight: 400; font-style: italic; letter-spacing: -.5px; text-shadow: 0 2px 0 rgba(0,0,0,.35); }
.home-greet { margin: 0; flex: 1; min-width: 0; font-size: 13px; color: rgba(251,243,230,.7); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pill {
  flex: none; font: inherit; font-size: 13px; font-weight: 700;
  padding: 8px 14px; border: 0; border-radius: 999px;
  background: rgba(255,255,255,.9); color: #2f6fd0; cursor: pointer;
}
.book {
  position: relative; flex: 1; min-height: 0;
  margin: 0 auto; width: 100%;
  border-radius: 18px; padding: 9px;
  background: linear-gradient(#f4eedf, #e4dac3);
  box-shadow: 0 0 0 1px rgba(0,0,0,.25), 0 10px 30px rgba(0,0,0,.45), inset 0 -3px 0 rgba(0,0,0,.08);
}
.book-map { display: block; width: 100%; height: 100%; border-radius: 11px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.2); background: #6ea44c; }
.bubble {
  position: absolute; left: 22px; top: 20px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 120px; padding: 10px 22px; border-radius: 50%;
  background: #fbf8f1; color: #262626; box-shadow: 0 0 0 2px rgba(0,0,0,.18), 0 4px 10px rgba(0,0,0,.25);
  font-family: var(--font-game); font-style: italic; font-weight: 400; text-transform: uppercase; line-height: 1.1;
}
.bubble-k { font-size: 13px; letter-spacing: .02em; }
.bubble-v { font-size: 22px; font-family: var(--font-num); font-style: normal; font-variant-numeric: tabular-nums; }
.book-label {
  position: absolute; right: 22px; top: 20px; max-width: 46%;
  padding: 8px 14px; border-radius: 12px; text-align: right;
  background: rgba(30,24,18,.72); color: #fbf3e6;
}
.book-label h2 { margin: 0; font-family: var(--font-game); font-size: 22px; font-weight: 400; font-style: italic; text-transform: uppercase; letter-spacing: .02em; }
.book-label p { margin: 3px 0 0; font-size: 12.5px; line-height: 1.35; color: rgba(251,243,230,.82); }
.book-label p:empty { display: none; }
.book-dots { position: absolute; left: 0; right: 0; bottom: 36px; display: flex; justify-content: center; gap: 7px; pointer-events: none; }
.book-dots span { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,.55); box-shadow: 0 0 0 1px rgba(0,0,0,.25); }
.book-dots span.on { background: #fff; transform: scale(1.3); }
.home-nav {
  flex: none; position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: -26px auto 0; width: 100%; padding: 0 12px;
}
.nav-btn, .select-btn {
  font: inherit; font-family: var(--font-game); font-style: italic; font-weight: 400; text-transform: uppercase; letter-spacing: .03em;
  border: 0; cursor: pointer; color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.35);
}
.nav-btn {
  min-width: 108px; padding: 10px 18px; border-radius: 999px; font-size: 15px;
  background: linear-gradient(#5d9cf2, #2c66c9); box-shadow: 0 0 0 2px #fff, 0 4px 10px rgba(0,0,0,.35);
}
.select-btn {
  min-width: 170px; padding: 14px 30px; border-radius: 50%; font-size: 24px;
  background: radial-gradient(ellipse at 50% 30%, #ef4a42, #b8211c); box-shadow: 0 0 0 3px #fff, 0 6px 14px rgba(0,0,0,.4);
  transform: rotate(-4deg);
}
.nav-btn:active, .select-btn:active { filter: brightness(.88); }
.nav-btn:disabled { opacity: .35; }
.select-btn:disabled { filter: grayscale(.8); opacity: .7; }

/* ── the HUD: the original's counters along the top, two corner buttons ── */
.hud { position: absolute; z-index: 5; pointer-events: none; }
.hud-top {
  top: max(5px, var(--sat)); left: calc(12px + var(--sal)); right: calc(12px + var(--sar));
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}
.hud-count {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--font-game); font-style: italic; font-weight: 400; font-size: 14px; line-height: 1; text-transform: uppercase; letter-spacing: .03em;
  color: #24301f; text-shadow: 0 1px 0 rgba(255,255,255,.4);
}
.night .hud-count { color: #e9f3e6; text-shadow: 0 1px 0 rgba(0,0,0,.6); }
.digits {
  font-family: var(--font-mono); font-style: normal; font-weight: 600; font-size: 16px; letter-spacing: .14em;
  padding: 4px 3px 4px 7px; border-radius: 3px;
  background: rgba(35,38,36,.82); color: #fff; text-shadow: none; font-variant-numeric: tabular-nums;
}
#stat-landed.bump { animation: bump .35s ease-out; }
@keyframes bump { 40% { transform: scale(1.18); background: #c99a14; } }
.hud-btn {
  position: absolute; z-index: 6; width: 54px; height: 50px; padding: 0;
  display: grid; place-items: center; border: 0; cursor: pointer;
  background: rgba(70, 76, 72, .62); color: #fff;
  box-shadow: 0 0 0 1px rgba(255,255,255,.22);
}
.hud-bl { left: var(--sal); bottom: var(--sab); border-top-right-radius: 18px; }
.hud-br { right: var(--sar); bottom: var(--sab); border-top-left-radius: 18px; }
.hud-btn:active { background: rgba(40, 44, 42, .8); }
#ff-btn[aria-pressed="true"] { background: #d9a520; color: #1a1200; }
/* The speaker, on the pause card. */
.pause-head { display: flex; align-items: center; justify-content: center; gap: 12px; }
.pause-head h2 { margin: 0 !important; }
/* 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 field ─────────────────────────────────────────────────────────── */
/* THE BOX THE FIELD FILLS (field.js measures it): the canvas and the touch
   layer are laid in it at the size the world fits, centred. It owns every
   gesture inside it: `none` on the box and on everything in it, so nothing a
   finger does here scrolls, zooms or selects (the owner: one locked viewport). */
.field-wrap { position: absolute; inset: 0; touch-action: none; }
.field-wrap * { touch-action: none; }
.field-canvas, .field-touch { position: absolute; display: block; }
.field-touch { overflow: hidden; -webkit-tap-highlight-color: transparent; }
/* A PLANE'S SWITCH (field.js): invisible, absolutely placed over its plane
   every frame; widened under a drawing finger and slid under it when its line
   reaches the runway (the tick). Never `appearance: none` — the native control
   must stay native or it may stop ticking (haptics.js). */
.field-touch .plane-haptic { position: absolute; margin: 0; opacity: 0; z-index: 2; cursor: crosshair; }
.paused .field-touch { pointer-events: none; }
/* Nothing to see: field.js reads its padding as the safe-area insets on each side of the field (THE EDGES). */
.field-inset { position: absolute; width: 0; height: 0; overflow: hidden; visibility: hidden; pointer-events: none;
  padding: var(--sat) var(--sar) var(--sab) var(--sal); }
.game-note {
  position: absolute; left: calc(70px + var(--sal)); right: calc(70px + var(--sar)); bottom: calc(8px + var(--sab)); z-index: 950;
  margin: 0; padding: 8px 12px; border-radius: 12px;
  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: max(10px, var(--sat)) calc(16px + var(--sar)) max(10px, var(--sab)) calc(16px + var(--sal));
  background: rgba(28, 18, 10, .42);
}
.overlay-card {
  width: min(360px, 100%); max-height: 100%; overflow-y: auto;
  padding: 16px 18px;
  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-family: var(--font-game); font-size: 26px; font-weight: 400; font-style: italic; text-transform: uppercase; letter-spacing: .01em; }
.pause-card .btn-wide { margin-top: 12px; padding-top: 10px; padding-bottom: 10px; }
.pause-card .overlay-btns { margin-top: 8px; }
.pause-card #paused-giveup { margin-top: 4px; }
.pause-card .overlay-stats { margin-top: 10px; padding-top: 10px; }
/* GAME OVER, the original's way: the words in a looping hand over the stilled field, the card under them. */
.overlay.over { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; background: rgba(10, 20, 12, .32); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.over-title {
  font-family: "Snell Roundhand", "Savoye LET", "Brush Script MT", cursive; font-weight: 800; font-size: clamp(44px, 13vh, 80px); line-height: 1;
  color: #fff; -webkit-text-stroke: 1.5px #23302a; text-shadow: 0 3px 0 #23302a, 0 6px 16px rgba(0,0,0,.45);
  padding: 0 12px; flex: none;
}
.overlay.over .overlay-card { width: min(430px, 100%); flex: 0 1 auto; min-height: 0; }
.overlay.over .overlay-stats { margin-top: 10px; padding-top: 10px; }
.overlay.over .overlay-btns { margin-top: 12px; }
.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 { white-space: nowrap; }
.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(--blue-btn); color: #fff; text-shadow: 0 1px 0 rgba(0,0,0,.3);
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
}

/* ── 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`. */
/* On the desk (the leaderboard's tabs) the track is a strip of the paper. */
.sheet .segmented:not(.seg-sm) { background: var(--glass); border: 0; box-shadow: var(--shadow); }
.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 ────────────────────────────────────────────────────── */

/* LANDSCAPE, ON THE DESK (flight-v6, the owner: "make the menu the same theme as the game and make it
   work in landscape too"). The menu is a third locked screen — drawn sideways on an upright phone like
   the book and the field — the size of the screen: its bar along the top, then two columns side by side,
   the leaderboard and everything else, each its own scroller (IOS.md §25's shape: the bar is outside
   them, so a tap on ✕ is never eaten by a scroll still settling). Too narrow for two (a desktop window),
   it is one column that scrolls. */
.sheet {
  position: relative; z-index: 1100; height: 100%;
  display: flex; flex-direction: column;
  padding: max(6px, var(--sat)) calc(14px + var(--sar)) 0 calc(14px + var(--sal));
  background: var(--desk-wash), url("sprites/wood.jpg") center / 420px repeat, var(--desk-2);
  color: var(--desk-ink);
  container-type: inline-size;
}
.sheet[hidden] { display: none; }
.menu-top { display: flex; align-items: center; gap: 12px; flex: none; padding: 4px 2px 8px; }
.menu-top .home-title { flex: 1; }
.sheet-body {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 18px;
}
.sheet-body > section {
  min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  padding: 2px 2px calc(18px + var(--sab));
}
@container (max-width: 600px) {
  .sheet-body { display: block; overflow-y: auto; overscroll-behavior: contain; }
  .sheet-body > section { overflow: visible; padding-bottom: 0; }
  .sheet-body > section:last-child { padding-bottom: calc(18px + var(--sab)); }
}
/* What is written on the desk itself: the book's italic capitals, and its dimmer ink for the notes. */
h2.sec {
  margin: 18px 4px 8px; font-family: var(--font-game); font-size: 19px; font-weight: 400; font-style: italic; text-transform: uppercase; letter-spacing: .03em;
  color: var(--desk-ink); text-shadow: 0 2px 0 rgba(0,0,0,.35);
}
.sheet-body section > h2.sec:first-child { margin-top: 4px; }
.sheet .fine { color: var(--desk-dim); }
.sheet .fine strong { color: var(--desk-ink); }
.sheet .card .fine { color: var(--dim); }
.sheet .card .fine strong { color: var(--ink-2); }
.fine.top { margin: 0 4px 12px; }

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

/* 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 + var(--sab)); z-index: 1300;
  max-width: min(92vw, 420px); padding: 10px 14px; border-radius: 14px;
  font-size: 14px; line-height: 1.4; text-align: center;
}
.toast-link { color: var(--link); font-weight: 600; }

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

.banner {
  position: fixed; left: calc(12px + var(--sal)); right: calc(12px + var(--sar));
  bottom: calc(12px + var(--sab));
  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; }


/* Artwork credits: the notes inside the fold, padded like Technical's. */
#credits .row-note { padding: 0 16px 12px; }
#credits .row-note:first-of-type { padding-top: 12px; border-top: .5px solid var(--line-2); }
#credits a { color: var(--link); }

/* ── SIDEWAYS ON AN UPRIGHT PHONE (conquest's, its styles.css) ───────────
 *
 * This is a landscape game and a native one would simply draw itself
 * sideways; the person picks the phone up, sees that, and turns it — and
 * turning it changes NOTHING on the screen (the owner, 2026-10-22: "rotating
 * the phone to widescreen should not change the layout"). The web cannot ask
 * for landscape on iOS (the manifest's `orientation` is ignored, and there is
 * no screen.orientation.lock() for a page), so while the phone is upright the
 * whole body is ROTATED a quarter turn clockwise — 100vh wide and 100vw tall —
 * and the moment the phone is turned the media query stops matching and the
 * ordinary rendering, the same picture, takes over.
 *
 * EVERY SCREEN ROTATES (`body.sideways`, set by app.js's lockPage()) since
 * flight-v6 — the owner: "remove all portrait elements". Until then the sign-in
 * card and the menu stayed upright. The insets move with the turn: the phone's
 * top (the notch) is the app's left, its bottom the app's right. field.js maps
 * a finger through the body's matrix, so drawing works the same either way up.
 */
@media (orientation: portrait) and (max-width: 700px) {
  html.sideways { overflow: hidden; }
  html.locked body.sideways {
    position: fixed; top: 0; left: 0;
    width: 100vh; height: 100vw;
    width: 100dvh; height: 100dvw;
    min-height: 0;
    transform-origin: top left;
    transform: rotate(90deg) translateY(-100%);
    overflow: hidden;
    --sat: env(safe-area-inset-right, 0px);
    --sab: env(safe-area-inset-left, 0px);
    --sal: env(safe-area-inset-top, 0px);
    --sar: env(safe-area-inset-bottom, 0px);
  }
  /* A fixed layer inside a transformed body is placed by the body already; said outright, as conquest does. */
  body.sideways .overlay, body.sideways .toast, body.sideways .banner, body.sideways .pwa-gate { position: absolute; }
}
