/* ─────────────────── fonts, served from here ───────────────────
   Self-hosted rather than linked from Google. The stylesheet link sent every
   visitor's IP and user-agent to a third party on every page view — including
   on the privacy page — and nothing on this site disclosed it.

   Cut to the four subsets these two languages actually need; greek and
   vietnamese are dropped. `unicode-range` is kept verbatim from Google's own
   CSS, so a browser still downloads only the subsets a page really uses: an
   English reader never fetches the Cyrillic files at all. Filenames carry a
   content hash, which is what lets them be cached for a year.             */

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-cyrillic-ext-c6f09ee0.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-cyrillic-7b59d6bf.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-latin-ext-f1050dc5.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-latin-c36f509c.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-cyrillic-ext-311098be.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-cyrillic-d524dc8f.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-latin-ext-77f03e26.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-latin-a76f53ca.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-ext-e8c0b399.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-95a49306.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('fonts/manrope-latin-ext-ce093b34.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('fonts/manrope-latin-e310b55a.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-ext-e8c0b399.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-95a49306.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/manrope-latin-ext-ce093b34.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/manrope-latin-e310b55a.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-ext-e8c0b399.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-95a49306.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/manrope-latin-ext-ce093b34.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fonts/manrope-latin-e310b55a.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-ext-e8c0b399.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-95a49306.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/manrope-latin-ext-ce093b34.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/manrope-latin-e310b55a.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-ext-e8c0b399.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('fonts/manrope-cyrillic-95a49306.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('fonts/manrope-latin-ext-ce093b34.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('fonts/manrope-latin-e310b55a.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Prata';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/prata-cyrillic-ext-4430b5c7.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: 'Prata';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/prata-cyrillic-a334ddd4.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Prata';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/prata-latin-8585ed0b.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Cantle — site styles.
 *
 * Colors are the game's own (cantle/src/theme/colors.ts) and obey the rule
 * written in that file: the chrome stays quiet so the puzzle colors are the
 * only saturated things on screen. Amber, Sky, Jade and Orchid appear only
 * where they carry meaning; gold is the game's leaderboard highlight and is
 * used here the same way — sparingly, for the things that matter.
 *
 * The game is dark-only (app.json sets userInterfaceStyle "dark"), so the site
 * is too. Every color is painted, never inherited.
 */

:root {
  color-scheme: dark;

  --void:      #05070c;
  --bg:        #0b0e14;
  --raised:    #111725;
  --card:      #151c2c;
  --hair:      rgba(255, 255, 255, 0.07);
  --hair-2:    rgba(255, 255, 255, 0.13);
  --edge:      #5a6684;

  --text:      #f4f6fa;
  --text-2:    #a3aec1;
  --text-3:    #6b7488;

  --gold:      #f5c451;
  --gold-dim:  rgba(245, 196, 81, 0.55);
  --accent:    #6c8cff;

  --mast:      5.25rem;   /* the masthead's own fixed row height */

  --amber:     #e8a33d;
  --sky:       #5b8fe0;
  --jade:      #3fa06b;
  --orchid:    #e493c2;

  --display: Prata, "Bodoni MT", Didot, Georgia, serif;
  --sans:    Manrope, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --page:    76rem;
  --reading: 36rem;

  --t: cubic-bezier(0.22, 0.61, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* Where an anchored section comes to rest. Without this a jump puts the
   section's own top edge at the top of the window -- underneath a masthead
   that is sticky and, on a phone, two rows tall. The section's padding hid
   how much was being swallowed; the heading survived and the air above it
   did not. --chrome is the measured height of the banner and the masthead
   together, so this follows them rather than guessing at a number. */
[id] { scroll-margin-top: calc(var(--chrome, 5.25rem) + 1.5rem); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 1.125rem;
  line-height: 1.72;
  font-weight: 400;
  letter-spacing: 0.004em;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
}

/* Ambient depth: three very faint color fields and a fine grain, fixed so the
   page moves over them rather than dragging them along. */
body::before,
body::after { content: ""; position: fixed; inset: 0; pointer-events: none; }
body::before {
  z-index: -2;
  background:
    radial-gradient(58rem 40rem at 78% -6%,  rgba(232, 163, 61, 0.10), transparent 62%),
    radial-gradient(52rem 44rem at 6% 22%,   rgba(91, 143, 224, 0.11), transparent 60%),
    radial-gradient(46rem 40rem at 88% 78%,  rgba(228, 147, 194, 0.07), transparent 62%),
    linear-gradient(180deg, var(--bg) 0%, var(--void) 100%);
}
body::after {
  z-index: -1;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.page    { width: min(100% - 2.5rem, var(--page));    margin-inline: auto; }
.reading { width: min(100% - 2.5rem, var(--reading)); margin-inline: auto; }

/* ───────────────────────── type ───────────────────────── */

h1, h2, h3 { font-family: var(--display); font-weight: 500; text-wrap: balance; margin: 0; }
h1 { font-size: clamp(2.75rem, 5.6vw, 5rem); line-height: 0.98; letter-spacing: -0.028em; }
h2 { font-size: clamp(2rem, 4.6vw, 3.35rem);  line-height: 1.04; letter-spacing: -0.022em; }
h3 { font-size: 1.35rem; line-height: 1.25; letter-spacing: -0.012em; }

p { margin: 0 0 1.15rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--text); text-decoration-color: var(--gold-dim); text-underline-offset: 0.22em; transition: color 0.2s var(--t); }
a:hover { color: var(--gold); }

.label {
  font-family: var(--mono); font-size: 0.82rem; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.2em; color: var(--gold);
  display: block; margin: 0 0 1.6rem;
}

.lede { font-size: clamp(1.125rem, 1.45vw, 1.3rem); line-height: 1.62; color: #b6bfd0; font-weight: 300; text-wrap: pretty; }

/* No border-radius override: an outline follows the element's own corners, and
   forcing 3px put a nearly-square box around circular controls. */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }

/* ───────────────────────── masthead ───────────────────────── */

.masthead {
  position: sticky; top: 0; z-index: 40;
  background: rgba(11, 14, 20, 0.72);
  backdrop-filter: blur(16px) saturate(150%);
  border-bottom: 1px solid var(--hair);
}
.masthead > .page { display: flex; align-items: center; justify-content: space-between; gap: 1rem; height: 5.25rem; }
.masthead-right { display: flex; align-items: center; gap: 1.75rem; }

.wordmark { display: inline-flex; align-items: center; gap: 0.7rem; text-decoration: none; color: var(--text); font-family: var(--display); font-size: 2rem; font-weight: 500; letter-spacing: 0.005em; }
.colophon .wordmark { font-size: 1.65rem; }
.wordmark svg { display: block; }
.wordmark:hover { color: var(--text); }

.nav { display: flex; align-items: center; gap: 2rem; }
/* The LIST wraps to a second row; a label never wraps inside itself. Without
   this "no tracking" broke across two lines whenever the row got tight, which
   reads as two menu items rather than one. Same for the tier chips, where the
   count would drop under its own name. */
.nav a { font-family: var(--mono); font-size: 0.8rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.13em; color: var(--text-3); text-decoration: none; white-space: nowrap; }
.nav a.nav-page { color: var(--text-2); }
.nav a:hover, .nav a[aria-current="page"] { color: var(--text); }
/* Four of these are sections of the home page; Practice is a place you go.
   They looked identical, so the difference was only discoverable by clicking.
   The rule keeps the section index quiet and lets Practice read as the one
   action -- and it is dropped on phones, where the nav wraps to its own row
   and a divider would land at the start of a line. */
.nav-sep { width: 1px; height: 0.85rem; background: rgba(255, 255, 255, 0.14); flex: none; }
@media (max-width: 40rem) {
  .nav-sep { display: none; }
  .nav { gap: 1.1rem; }
  .nav a { font-size: 0.72rem; letter-spacing: 0.1em; }
  /* On a phone the header cannot hold four links, a wordmark and the language
     toggle — the row stops fitting and takes the whole page sideways with it.
     The in-page anchors are the ones to drop: on a screen this narrow they
     point at content you reach by scrolling anyway, while Practice is a page
     of its own and the footer still carries the full set. */
  /* The header gets a second row instead of losing links. Hiding the section
     anchors here did fit the row, but it also cut the only route to Modes,
     Tips and Details on a phone -- the footer carries neither, and from the
     practice page there was no way back to them at all.

     `display: contents` dissolves the right-hand group so the wordmark, the
     language switch and the nav become siblings: the first two share the top
     row, and the nav, at full width, wraps below them. */
  .masthead > .page { height: auto; flex-wrap: wrap; padding-block: 0.7rem; row-gap: 0.5rem; }
  .masthead-right { display: contents; }
  .lang { order: 1; }
  /* wrap, not nowrap: with five items the last one ran off the edge and was
     clipped rather than dropping to a line of its own. */
  .nav { order: 2; width: 100%; justify-content: flex-start;
    flex-wrap: wrap; gap: 0.5rem 1.1rem; }
}

.lang {
  display: inline-flex; align-items: center; flex: none;
  padding: 2px; border-radius: 999px;
  border: 1px solid var(--hair-2); background: rgba(255, 255, 255, 0.035);
}
.lang a {
  font-family: var(--mono); font-size: 0.76rem; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase; line-height: 1;
  padding: 0.38rem 0.72rem; border-radius: 999px;
  color: var(--text-3); text-decoration: none;
  transition: color 0.2s var(--t), background 0.2s var(--t);
}
.lang a:hover { color: var(--text); }
.lang a[aria-current="true"] { background: rgba(255, 255, 255, 0.12); color: var(--text); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08); }
@media (max-width: 40rem) {
  .masthead-right { gap: 0.9rem; }
  .lang a { font-size: 0.68rem; padding: 0.32rem 0.58rem; letter-spacing: 0.05em; }
}

/* ───────────────────────── hero ───────────────────────── */

.hero {
  position: relative;
  padding-block: clamp(3rem, 6.5vw, 5.5rem) clamp(3rem, 7vw, 6rem);
  /* The hero owns the first screen. Left to its content it stopped short of
     the fold, and whatever came next was sliced across the bottom at whatever
     height the window happened to be -- a cut that reads as a mistake rather
     than as an invitation. `min-height`, not `height`: on a short window the
     content still wins and the section grows instead of overflowing. */
  min-height: calc(100vh - var(--chrome, 5.25rem));
  min-height: calc(100svh - var(--chrome, 5.25rem));
  display: flex; flex-direction: column; justify-content: center;
  /* The bloom behind the board is deliberately larger than the board, and on a
     narrow screen that surplus was widening the document — the whole page could
     be dragged sideways to look at a gradient. Clipped here rather than on the
     body, because the masthead is sticky and `clip` on an ancestor would strand
     it; and `clip` rather than `hidden`, which would make this a scroll box. */
  overflow-x: clip;
}
.hero-grid { display: grid; gap: clamp(2.5rem, 6vw, 5rem); align-items: center; }
/* The board gets the wider half. It was sharing evenly and losing — the
   canvas is square, so its width is its height, and the column was the
   binding limit long before max-width ever was. */
@media (min-width: 60rem) { .hero-grid { grid-template-columns: 0.88fr 1.12fr; } }

/* The display size follows the width of the window, which on a short laptop is
   the wrong axis: at 1440x760 the Ukrainian headline runs to four lines and
   pushes the store buttons past the fold. `vh` in the middle of the clamp lets
   the height have a say too, so the first screen keeps its call to action. */
.hero h1 { font-size: clamp(2.4rem, min(5.6vw, 9.5vh), 5rem); }
.hero h1 em { font-style: normal; color: var(--gold); }
.hero .lede { max-width: 30rem; margin-top: 1.75rem; }

/* The board is the hero image. It gets a soft bloom behind it so it reads as
   lit rather than drawn. */
.board-frame { position: relative; width: 100%; max-width: 44rem; margin-inline: auto; }
.board-frame::before {
  content: ""; position: absolute; inset: -14%;
  background: radial-gradient(closest-side, rgba(108, 140, 255, 0.16), transparent 72%);
  filter: blur(8px); pointer-events: none;
}
.board { position: relative; aspect-ratio: 1 / 1; }
.board canvas { width: 100%; height: 100%; display: block; }


.board-meta {
  display: flex; justify-content: center; gap: 1.6rem; margin-top: 1.4rem;
  font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: #8b95aa;
}
.board-meta b { color: var(--text-2); font-weight: 500; }

/* The fold is now clean, which leaves nothing peeking to say there is more.
   This says it instead: quiet, and the only moving thing on the screen. */
/* Pinned to the bottom edge of the first screen, not floated after the content.
   Centerd with the content it landed a couple of hundred pixels above the fold,
   which reads as an element that lost its place rather than as the end of the
   screen. Absolute, so the hero's own content stays vertically centerd. */
.scroll-cue {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: clamp(1.25rem, 3.5vh, 2.5rem);
  display: inline-flex; flex-direction: column; align-items: center; gap: 0.5rem;
  text-decoration: none; color: var(--text-3);
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase;
  transition: color 0.3s var(--t);
}
.scroll-cue:hover { color: var(--text-2); }
/* Only where the hero actually fills the screen. Below that it does not, so
   the next section is already visible and the cue has nothing to announce --
   and, being absolutely placed at the hero's bottom, it landed on top of the
   board's own caption. */
@media (max-width: 60rem) { .scroll-cue { display: none; } }
.scroll-cue svg { display: block; animation: cue-bob 2.4s ease-in-out infinite; }
@keyframes cue-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
@media (prefers-reduced-motion: reduce) { .scroll-cue svg { animation: none; } }

/* A short laptop is short of about 160px, and the hero's own vertical padding
   alone is 184 -- so the fix is air, not type. Measured at 1440x760, where the
   Ukrainian headline runs to four lines and pushed the store buttons out of
   the first screen entirely. */
/* 60rem, not 52: at 1440x900 -- an ordinary laptop -- the hero needs 865px
   and has 815, so it overflowed by fifty and took the cue with it. The
   threshold is the height at which the full padding stops fitting. */
@media (max-height: 60rem) and (min-width: 60rem) {
  .hero { padding-block: clamp(1.25rem, 3vh, 2.75rem); }
  .hero .lede { margin-top: 1rem; }
  .stores { margin-top: 1.5rem; }
  .scroll-cue { bottom: clamp(0.75rem, 2vh, 1.25rem); }
}

/* ───────────────────────── ways to play ───────────────────────── */

.modes { display: grid; gap: 1.35rem; margin-top: clamp(2.5rem, 5vw, 3.5rem); }
@media (min-width: 46rem) { .modes { grid-template-columns: repeat(2, 1fr); } }
.mode {
  position: relative; padding: 2rem 1.9rem 1.8rem; border-radius: 14px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012));
  border: 1px solid var(--hair);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: border-color 0.3s var(--t), transform 0.3s var(--t);
}
.mode:hover { border-color: rgba(245, 196, 81, 0.3); transform: translateY(-3px); }
.mode h3 { margin: 0 0 0.55rem; }
.mode p { color: var(--text-2); font-weight: 300; margin-bottom: 0; }
.mode-tag { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-dim); display: block; margin-bottom: 1rem; }

.tiers { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.15rem; }
.tier {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--mono); font-size: 0.76rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-2); border: 1px solid var(--hair); border-radius: 999px; padding: 0.35rem 0.75rem;
}
.tier i { width: 0.65rem; height: 0.65rem; border-radius: 50%; background: var(--c); }

/* ───────────────────────── tips ───────────────────────── */

.tips { counter-reset: tip; display: grid; gap: 0; margin-top: clamp(2.5rem, 5vw, 3.5rem); border-top: 1px solid var(--hair); }
.tip { counter-increment: tip; display: grid; gap: 0.3rem 1.75rem; padding: 1.65rem 0; border-bottom: 1px solid var(--hair); }
@media (min-width: 44rem) { .tip { grid-template-columns: 3.5rem 1fr; } }
.tip::before { content: counter(tip, decimal-leading-zero); font-family: var(--mono); font-size: 0.86rem; letter-spacing: 0.1em; color: var(--gold-dim); padding-top: 0.3rem; }
.tip h3 { margin: 0 0 0.35rem; font-family: var(--sans); font-size: 1.12rem; font-weight: 700; letter-spacing: -0.01em; }
.tip p { color: var(--text-2); font-weight: 300; margin: 0; }

/* ───────────────────────── the details ───────────────────────── */

.spec { display: grid; gap: 0; margin-top: clamp(2.5rem, 5vw, 3.5rem); border-top: 1px solid var(--hair); }
.spec > div { display: grid; gap: 0.3rem 1.75rem; padding: 1.15rem 0; border-bottom: 1px solid var(--hair); align-items: baseline; }
@media (min-width: 44rem) { .spec > div { grid-template-columns: 14rem 1fr; } }
.spec dt { font-family: var(--mono); font-size: 0.8rem; letter-spacing: 0.11em; text-transform: uppercase; color: var(--text-3); }
.spec dd { margin: 0; color: var(--text-2); font-weight: 300; }
.spec dd b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ───────────────────────── practice ───────────────────────── */

/* This page is a control surface, not a document. Everything needed to solve
   a board — tiers, clock, board, palette — is sized to the viewport and fits
   between the masthead and the bottom of the screen at any height, so nothing
   has to be scrolled to mid-solve. What explains the page sits below it, where
   scrolling is the right gesture.

   svh, not vh: on a phone `vh` is the height with the browser chrome RETRACTED,
   so a layout built on it is taller than the screen actually is until you
   scroll — which is the one thing this page exists to avoid. */
/* Double-tap on a phone is a zoom gesture, and this whole surface is made of
   things you tap quickly and in a row -- swatches, tier chips, the board. Two
   taps close together zoomed the page instead of playing two moves, and once
   zoomed you are stuck until you pinch back out. The canvas was already
   covered; nothing else was.

   `manipulation` drops the double-tap gesture and leaves panning and pinch
   alone. It is the narrow fix: `user-scalable=no` on the viewport tag would
   take magnification away from anyone who needs it, and iOS has ignored that
   tag for years in any case. */
/* `touch-action` is not inherited, so listing the section alone left the
   canvas itself on `auto` -- and the canvas is the one element people tap
   repeatedly. Several quick taps on a node were read as a double-tap and the
   page zoomed. The board and everything stacked on it are named explicitly. */
/* The play section only, and its gaps with it -- naming the buttons alone left
   the space between them on `auto`, which is where half the accidental zooms
   landed. Not on the root: elsewhere on the site double-tap zoom is a reading
   aid, and the pages below this one are text.
   On iOS 26.5 this does nothing at all -- measured on the device, the gesture
   zooms with the rule applied -- so board.js cancels the second tap itself.
   It stays because it is what works in Chrome, and because it says what the
   surface is for. PINCH is deliberately untouched, here and everywhere. */
.play, .play * {
  touch-action: manipulation;
}
/* A fast second tap also starts a selection, which flashes a highlight over
   the board and can drag a caret out of the hint text underneath. */
/* A long press on the board is a game input -- it clears the node -- and iOS
   was answering it with its own gesture at almost the same moment: the callout
   fires around 500ms and board.js clears at 480, so holding a node produced the
   magnifier and the share sheet on top of the move. `user-select` alone does
   not stop that; the callout needs turning off by name. */
#practice-board, .board-fit, .palette, .hud, .board-veil {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.play { display: flex; flex-direction: column; min-height: 28rem; }
/* A definite height, not a minimum: the board below sizes itself from the
   height left over, and `min-height` leaves that height indefinite — the
   container query resolves to zero and the board vanishes. min-height stays
   only as a floor for absurdly short windows, where scrolling is the honest
   answer; set too high it becomes the overflow it was meant to prevent.

   --chrome is measured and published by site.js, because the banner above the
   masthead is a sentence that wraps — no constant here can be right at every
   width and in both languages. The fallback is the masthead alone. */
.play { height: calc(100vh - var(--chrome, var(--mast))); }
.play { height: calc(100svh - var(--chrome, var(--mast))); }

.play-page {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  gap: clamp(0.5rem, 1.6vh, 1.05rem);
  padding-block: clamp(0.6rem, 2vh, 1.4rem);
}

.play-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 0.75rem 1.25rem; }
.play-head .disc[data-immersive] { order: 3; margin-left: auto; align-self: center; }
.play-head h1 { font-size: clamp(1.15rem, 0.85rem + 1.3vw, 1.75rem); margin: 0; line-height: 1.15; }

.tier-bar { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.tier-btn, .lang a, .disc, .result-go, .start-btn { white-space: nowrap; }
.tier-btn {
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  padding: 0.42rem 0.85rem; border-radius: 999px;
  border: 1px solid var(--hair); background: rgba(255, 255, 255, 0.02);
  font-size: 0.85rem; color: var(--text-2); cursor: pointer;
  transition: color 0.2s var(--t), border-color 0.2s var(--t), background 0.2s var(--t);
}
.tier-btn i { font-family: var(--mono); font-style: normal; font-size: 0.72rem; color: var(--text-3); }
.tier-btn:hover { color: var(--text); border-color: var(--hair-2); }
.tier-btn[aria-selected="true"] {
  background: var(--gold); border-color: var(--gold); color: #1c1400; font-weight: 500;
}
.tier-btn[aria-selected="true"] i { color: rgba(28, 20, 0, 0.55); }

/* The clock is centerd by giving it a column of its own between two equal
   sides, so it does not shift when the buttons beside it change width. */
/* The clock is the thing this row exists for, so it gets air on both sides
   rather than sitting hard against the tier chips above it. */
/* The clock takes the middle of the screen and the tools sit under it.
   A three-column grid could not do that: the empty cell on the left and the
   four discs on the right are not the same width, so the "center" column was
   pushed off-center by however wide the buttons happened to be. Stacking them
   is not a compromise either -- portrait has height to spare, because the
   board there is capped by the width of the screen rather than by its height.
   Landscape puts them back on one row, where 844pt is wide enough for both. */
.hud { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; margin-block: clamp(0.15rem, 1vh, 0.5rem); }
/* The count goes back under the time. Inline, a bold 1:14 sat next to a tiny
   "1 MOVES" and the pair read as debris rather than as a stat -- the label only
   works as a unit written beneath its number. The height that costs is taken
   from the buttons instead, which had more of it to give. */
.hud-clock { text-align: center; }
.hud > span:first-child { display: none; }
.hud-clock { text-align: center; }
.hud-clock > b {
  display: block; line-height: 1;
  font-size: clamp(1.6rem, 1.15rem + 1.8vw, 1.95rem); font-weight: 800;
  color: #ffffff; letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.hud-moves {
  display: block; margin-top: 5px;
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: #8a8a94;
}
.hud-moves b { color: var(--text-2); font-weight: 500; font-variant-numeric: tabular-nums; }

/* Three, not four. The game itself shows two discs here -- undo and clear --
   and everything else added to the row was a different kind of thing: Next is
   web-only, and fullscreen is a view control rather than a move, so it sits up
   with the tier chips instead. What is left is lighter as well as fewer. */
.hud-tools { display: flex; justify-content: center; gap: 0.55rem; }
/* 36px gold-rimmed disc — the app's own header button, same size and border. */
.disc {
  width: 36px; height: 36px; flex: none; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(201, 162, 74, 0.42);
  transition: border-color 0.2s var(--t), background 0.2s var(--t), opacity 0.2s var(--t);
}
.disc:hover { border-color: rgba(201, 162, 74, 0.78); background: rgba(201, 162, 74, 0.09); }
.disc svg { display: block; }

/* The board takes whatever height is left, and is square. `100cqh` is the
   height of this box, so the square is capped by the SHORTER of the two axes
   — the plain min() above it is the fallback for engines without container
   units, which get the old width-driven behavior rather than nothing. */
/* The board is the only row here that flexes, which means it is the only one
   that pays when the window is short — and `flex: 1 1 0` will pay it all the
   way down to nothing. Seen for real: at a viewport too short for the fixed
   rows the canvas measured 0x0 and the board simply was not there. A floor on
   both the row and the square makes the page scroll instead, which is the
   honest failure. */
/* Centerd in the space it is given, not pinned to the top of it: the square
   rarely fills that box exactly, and the leftover belongs on both sides. */
.play-board { flex: 1 1 0; min-height: 9rem; container-type: size;
  display: grid; place-items: center; }
.board-fit { position: relative; margin-inline: auto; aspect-ratio: 1; }
.board-fit { width: min(100%, 34rem); }
.board-fit { width: clamp(9rem, min(100%, 100cqh, 34rem), 34rem); }
/* Mobile browsers paint a translucent box over whatever they judge tappable.
   On the board that box is the size of the board -- most of a phone screen --
   and it flashes on every move. The feedback it provides is already there and
   better: the node settles, a ripple leaves it, the color changes. */
* { -webkit-tap-highlight-color: transparent; }

#practice-board {
  display: block; width: 100%; height: 100%;
  cursor: pointer; touch-action: manipulation;
  /* Clearing a node is a long press, and a long press on a canvas is also how
     iOS opens its save-image menu. Both fired at once: the board cleared and
     the system sheet came up over it. */
  -webkit-touch-callout: none;
  user-select: none; -webkit-user-select: none;
}

.board-veil {
  position: absolute; inset: 0; z-index: 2;
  display: none; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.9rem; text-align: center; padding: 1.4rem 1.2rem;
  /* The veil is the size of the board, and the board is a square that gets
     small on a phone. Prose overflowed it and the last line was cut in half.
     The copy below is short enough to fit; this is the guard for the case it
     is ever not -- a scroll, never a clipped letter. */
  overflow-y: auto; overscroll-behavior: contain;
}
/* Opaque, not translucent. The board behind it is not drawn at all before the
   clock starts (see board.js), and this matches that: a cover, not a haze.
   A translucent one would also have to be trusted, and it should not be. */
.board-veil[data-veil-start] {
  border-radius: 18px; border: 1px solid var(--hair);
  background: linear-gradient(180deg, #131a2a, #0c1119);
}

[data-phase="idle"] .board-veil[data-veil-start] { display: flex; }

.start-btn {
  border: 0; border-radius: 999px; cursor: pointer;
  padding: 0.7rem 1.7rem; background: var(--gold); color: #1c1400;
  font-family: var(--sans); font-size: 1rem; font-weight: 600;
  transition: transform 0.28s var(--t);
}
.start-btn:hover { transform: translateY(-2px); }
/* The rule, before anything is touched. The veil already stood between the
   player and the board, but it only ever explained the CONTROLS -- how to put a
   color down, not why one would be wrong. Someone who had never seen the game
   pressed Start knowing how to tap and not what they were aiming at. The rule
   goes first and reads larger; the controls follow it. */
.veil-rule {
  max-width: 21rem; margin: 0; font-size: 1.02rem; line-height: 1.45;
  color: var(--text); font-weight: 400; text-wrap: pretty;
}
/* Two shapes, two sentences, left-aligned so the eye reads down one column.
   This was a paragraph of prose: eleven lines on a phone, the last of them
   clipped by the square. What it had to say is two facts about two shapes,
   and the shapes themselves say it faster than any sentence naming them. */
.veil-key {
  list-style: none; margin: 0; padding: 0; text-align: left;
  display: grid; gap: 0.55rem; width: 100%; max-width: 24rem;
}
.veil-key li {
  display: grid; grid-template-columns: 1.1rem 1fr; align-items: start;
  gap: 0.65rem; font-size: 0.86rem; line-height: 1.4;
  color: var(--text-2); font-weight: 300; text-wrap: pretty;
}
.veil-key b { color: var(--text); font-weight: 500; }
/* The same two marks the canvas draws: a filled square for a clue, a dashed
   ring for an empty node the player owns. */
.veil-mark { width: 1.1rem; height: 1.1rem; margin-top: 0.08rem; }
/* Four quarters, not one hue. A solid blue square read as "clues are blue",
   which is not true of anything -- a clue wears whichever of the four the
   board needed. The swatch says "any color" in the only way a static mark
   can, and it uses the same four the palette below does. */
.veil-mark-sq {
  border-radius: 5px; background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--amber) 0 0), linear-gradient(var(--sky) 0 0),
    linear-gradient(var(--jade) 0 0), linear-gradient(var(--orchid) 0 0);
  background-size: 50% 50%;
  background-position: 0 0, 100% 0, 0 100%, 100% 100%;
}
.veil-mark-ci { border-radius: 999px; border: 2px dashed var(--edge); }
.veil-note {
  width: 100%; max-width: 24rem; margin: 0.1rem 0 0; padding-top: 0.8rem;
  border-top: 1px solid var(--hair);
  font-size: 0.81rem; line-height: 1.45; color: var(--text-2); font-weight: 300;
}
.done-flawless { font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold); margin: 0; display: none; }
[data-flawless="true"] .done-flawless { display: block; }

/* A FIXED band, not a minimum. The board is the only row in this column that
   flexes, so every pixel the hint takes comes out of the board -- and a line
   that grows from one row to two moves the graph under the player's finger.
   `min-height` did not prevent that, it only set the floor: the longer
   coaching lines wrap at desktop widths too, which is where it was seen.
   The band holds two rows at this line-height and never changes, so nothing
   below the board can move it. */
.play-hint {
  height: 2.9em; margin: 0 auto; text-align: center;
  display: grid; align-content: center;
  font-size: 0.79rem; line-height: 1.35; color: #8a8a94;
  font-variant-numeric: tabular-nums;
  max-width: 24rem; text-wrap: balance;
}
/* The lesson wears the gold the rest of the page uses for "read this". */
.play-hint[data-state="coach"]    { color: var(--gold); }
.play-hint[data-state="clash"]    { color: #ff8f8f; }
.play-hint[data-state="solved"]   { color: var(--text); }
.play-hint[data-state="flawless"] { color: var(--gold); }

/* 54px swatches with a fixed 1-4 label beneath, as the app's tray has them.
   The digits name the palette order so a color can be called by number —
   they are labels, never counts. */
.palette { display: flex; justify-content: center; gap: clamp(0.7rem, 3vw, 1.125rem); }
.chip-btn {
  display: grid; justify-items: center; gap: 0.55rem;
  background: none; border: 0; padding: 0; cursor: pointer;
  --sw: clamp(2.3rem, 1.2rem + 4.4vw, 3.375rem);
}
.chip-sw {
  position: relative; width: var(--sw); height: var(--sw); border-radius: 50%;
  background: var(--c); display: grid; place-items: center;
  font-size: calc(var(--sw) * 0.44); line-height: 1; color: #ffffff;
  transition: transform 0.2s var(--t);
}
.chip-btn:hover .chip-sw { transform: translateY(-2px); }
/* Gold marks the player's own current choice, and only that — never a board
   state, which is what the red conflict ring and the white focus ring are. */
.chip-btn[aria-pressed="true"] .chip-sw::after {
  content: ""; position: absolute; inset: -5px; border-radius: 50%;
  border: 2.5px solid #e8c86a;
}
/* The palette chip is a swatch with a digit beneath it, and the default focus
   box drew around BOTH — a gold rectangle enclosing the whole control, which
   reads as a second, larger selection ring. The indicator belongs on the
   swatch. White, not gold: gold means the color you have chosen, and a focus
   ring is not a choice yet. Suppressed on the chosen chip, which already wears
   its own ring — one ring per swatch, the same rule the board follows. */
.chip-btn:focus-visible { outline: none; }
.chip-btn:not([aria-pressed="true"]):focus-visible .chip-sw::before {
  content: ""; position: absolute; inset: -5px; border-radius: 50%;
  border: 2px solid #ffffff;
}

.chip-n { font-family: var(--mono); font-size: 0.78rem; font-weight: 700; color: #9a9aa4; font-variant-numeric: tabular-nums; }
.chip-btn[aria-pressed="true"] .chip-n { color: #e8c86a; font-weight: 800; }

/* On a phone the head is competing with the board for the same pixels, and
   the board is the thing being used. Tighter chips keep the four tiers on one
   row instead of two, and the page title steps back to a size that no longer
   claims a row of its own — together worth about eighty pixels of board. */
/* On a phone the hero came to 1145px against an 844px screen, and the board --
   the most striking thing on the page -- started thirteen pixels below the
   fold. So the first screen was headline and body copy, and the graph existed
   only for whoever scrolled. Everything here is spacing rather than content:
   nothing is removed, the parts just stop standing so far apart, and the board
   gives up a little width so more of it arrives inside the first screen. */
@media (max-width: 40rem) {
  .hero { padding-block: 1.75rem 2rem; }
  .hero-grid { gap: 1.5rem; }
  .hero .lede { margin-top: 1.1rem; }
  .stores { margin-top: 1.75rem; }
  .hero-cta { margin-top: 2.2rem; }
  .soon-line { margin-top: 1.5rem; }
}
/* The board keeps its full width. Shrinking it bought 30px toward putting it
   above the fold, and the fold is not winnable: phones are 667, 812, 844 and
   896 tall, so whatever lands at the bottom lands differently on each. Given
   that, the board being as large as the screen allows beats it being slightly
   sooner and smaller. The store buttons stay stacked and full-width for the
   same reason -- side by side they would fit only at a font size that makes
   the page's one call to action look like a footnote. */

/* On a phone the board was losing 99px to the rows above it -- capped at 263
   when the width would have allowed 362 -- and the biggest single culprit was
   a decorative heading. "Real boards" is a title for a page; the tier chips
   under it already say what this is, and they say it in the only words that
   are also controls. The rows that stay give up what they can. */
@media (max-width: 40rem) {
  .play-head h1 { display: none; }
  /* The row stops wrapping and the chips wrap inside their own box instead.
     Ukrainian tier names are wider than the English ones and filled the line,
     which pushed the toggle onto a row of its own -- 60px of empty band above
     the board, in one language only. */
  .play-head { gap: 0.4rem 0.6rem; flex-wrap: nowrap; align-items: center; }
  .play-head .tier-bar { flex: 1 1 auto; min-width: 0; }
  .hud { gap: 0.4rem; margin-block: 0.2rem; }
  .hud-clock > b { font-size: 1.7rem; }
  .disc { width: 32px; height: 32px; }
  .play-hint { margin: 0.15rem auto 0; font-size: 0.82rem; }

  /* The masthead is 17% of the height on a phone, and most of that is a second
     row holding four links to sections of a DIFFERENT page. On a page you came
     to in order to play, that is the most expensive strip on the screen. The
     wordmark is still a link home, the language toggle stays, and the footer
     still lists everything -- what goes is a shortcut, what arrives is a sixth
     of the screen for the board.
     `:has()` rather than a class on <body>: a body class is what put `doc` on
     the wrong element and pushed the masthead down by its own padding, and an
     unsupported `:has()` simply leaves the nav where it is. */
  body:has(.play) .nav { display: none; }
}

@media (max-width: 30rem) {
  .play-head { gap: 0.5rem 0.75rem; }
  .play-head h1 { font-size: 1.05rem; }
  .tier-bar { gap: 0.3rem; }
  .tier-btn { padding: 0.3rem 0.55rem; font-size: 0.76rem; gap: 0.28rem; }
  .tier-btn i { font-size: 0.66rem; }
  .hud-clock > b { font-size: 1.5rem; }
  .chip-btn { gap: 0.35rem; }
}
/* The result block is taller than the palette it replaces, and on a phone it
   cleared the bottom of the screen by single digits. Tightened here rather
   than shrinking the time, which is the one thing the screen is for. */
/* Solved, the board gives up 78px to the panel below it. This claws some of
   it back where it costs least: the tier chips are not needed while a result
   is on screen -- Next board is right there, and switching tier is a decision
   for after. Placed here, after the base .result rules, because an earlier
   block at the same specificity loses to them; the first attempt at this sat
   above them and silently did nothing. */
@media (max-width: 40rem) {
  [data-phase="done"] .play-head { display: none; }
  [data-phase="done"] .result-stats { margin-top: 0.55rem; }
  [data-phase="done"] .result-go { margin-top: 0.7rem; }
}

@media (max-width: 30rem), (max-height: 50rem) {
  .result-rule { margin-bottom: 0.55rem; }
  .result-note { margin-top: 0.35rem; }
  .result-time { margin-top: 0.35rem; font-size: clamp(2rem, 1.2rem + 3vw, 2.6rem); }
  .result-stats { margin-top: 0.6rem; }
  .result-go { margin-top: 0.8rem; padding: 0.6rem 1.4rem; }
}

/* A short window is a different squeeze from a narrow one: here the fixed
   rows above and below the board are what has to give. */
@media (max-height: 44rem) {
  .play-page { gap: 0.45rem; padding-block: 0.5rem; }
  .play-head h1 { font-size: 1.05rem; }
  .hud-clock > b { font-size: 1.45rem; }
  .chip-btn { --sw: clamp(2.1rem, 1rem + 4vw, 2.6rem); gap: 0.3rem; }
}

/* Immersive mode: the page gets out of the way so the board can have the
   screen. This is CSS rather than the Fullscreen API because Safari on iPhone
   does not implement fullscreen for elements at all -- a button built on it
   would work on Android and silently do nothing for half the players. The API
   is still requested on top where it exists, but nothing depends on it. */
html[data-immersive] .masthead,
html[data-immersive] footer,
html[data-immersive] .draft { display: none; }
html[data-immersive] .play { height: 100svh; min-height: 0; }
html[data-immersive] .play-head h1 { display: none; }
html[data-immersive] .play-page { padding-block: 0.5rem; }
/* The gutter goes too. In portrait the board is capped by the column's width,
   not by the height the hidden masthead just freed -- so without this the mode
   removed the chrome and changed nothing about the size. 2.5rem of gutter is
   for reading columns; a board is not one. */
html[data-immersive] .play > .page { width: min(100% - 0.75rem, var(--page)); }
html[data-immersive] body { overflow: hidden; }

/* One button, two states: arrows out to enter, arrows in to leave. */
.disc[data-immersive] .ico-shut { display: none; }
html[data-immersive] .disc[data-immersive] .ico-open { display: none; }
html[data-immersive] .disc[data-immersive] .ico-shut { display: block; }
/* It is the way back out, so it survives the solved state that hides the rest. */
/* The way out lives in the tier row now, not among the game actions, so it
   survives the solved state without any of this: `.hud-tools` can stay hidden
   there, and nothing has to be selectively brought back. Three rules removed
   by moving one button to where it belonged. */

/* A phone held sideways is not a short window, it is a different shape. The
   board is square and was capped by the height it had left, so every pixel of
   the width beyond that square went unused -- the nodes came out small on the
   one orientation where there was the most room. Here the page stops stacking:
   the board takes the height, and the palette stands beside it in the space
   that was empty. The heading goes; the tier chips already say where you are. */
@media (orientation: landscape) and (max-height: 34rem) {
  /* The 28rem floor is 448px, and a phone on its side is shorter than that,
     so the floor -- meant to stop the board collapsing -- became the overflow
     it was written to prevent. It is not needed here: this layout is built to
     fit the height it is given rather than to demand one. */
  .play { min-height: 0; }
  .play-head h1 { display: none; }
  .play-page {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(8rem, 12rem);
    grid-template-rows: auto auto minmax(0, 1fr);
    column-gap: clamp(0.6rem, 2vw, 1.25rem);
    row-gap: 0.35rem;
    padding-block: 0.4rem;
  }
  /* The clock keeps the middle of the screen, on its own full-width row, and
     the tools sit at its right end -- the same three-part bar as portrait
     rather than a number pushed into a corner. Everything the row costs in
     height is taken back by compressing it and the masthead above it, so the
     board still ends up larger than it was when the clock lived in a sidebar.
     Only the palette stays beside the board, in the width the square leaves. */
  .play-head  { grid-column: 1; grid-row: 1; }
  .hud        { grid-column: 1 / -1; grid-row: 2; margin: 0;
                flex-direction: row; justify-content: center; position: relative; gap: 0; }
  .hud-tools  { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
  .play-board { grid-column: 1; grid-row: 3; }
  .palette    { grid-column: 2; grid-row: 3; flex-direction: column; justify-content: center; gap: 0.4rem; }
  /* Beside the tier chips, not in the clock's row -- the clock spans the full
     width there, so anything else placed in it lands under the tool buttons. */
  .play-hint  { grid-column: 2; grid-row: 1; margin: 0; align-self: center; }
  .result     { grid-column: 2; grid-row: 3; justify-content: center; }

  /* Every row above the board pays for itself. The masthead loses its fixed
     height, the wordmark and the chips come down a step, and the clock stops
     stacking its move count -- together about 60px, which is 60px of board. */
  .masthead > .page { height: auto; padding-block: 0.35rem; }
  .wordmark { font-size: 1.15rem; }
  .tier-btn { padding: 0.22rem 0.5rem; font-size: 0.72rem; }
  .tier-btn i { font-size: 0.62rem; }

  /* Sized from the height it has, not from a fixed value: four chips and their
     gaps have to fit the column whatever the phone is, and at 2.1rem they were
     running off the bottom of a short one. */
  /* Matched to the tier chips it now sits beside, so the row it joined does
     not grow taller for it -- 36px against a 26px chip cost the board 13px. */
  .play-head .disc[data-immersive] { width: 28px; height: 28px; }
  .play-head .disc[data-immersive] svg { width: 14px; height: 14px; }
  .chip-btn { --sw: min(2.3rem, 9vh); }
  .chip-n { display: none; }
  /* Inline here, stacked in portrait. Sideways the height is the scarce axis
     and the second line costs board; upright there is room, and the count
     reads properly as a unit written under its number. */
  .hud-clock { display: flex; align-items: baseline; justify-content: center; gap: 0.5rem; }
  .hud-clock > b { display: inline-block; }
  .hud-clock > b { font-size: 1.2rem; }
  .hud-moves { font-size: 0.68rem; }
  .result-time { font-size: 1.9rem; margin-top: 0.25rem; }
  .result-stats { gap: 0.9rem; margin-top: 0.4rem; }
  .result-go { margin-top: 0.45rem; padding: 0.45rem 1rem; font-size: 0.85rem; }
  .result-rule { margin-bottom: 0.35rem; }

  /* Solved, in immersive: the way out has to stay reachable, but the sidebar
     cell belongs to the result -- putting the row back there drew the exit
     button on top of the word "Solved". It moves up beside the tier chips,
     the only row with space, and sheds the clock, whose time the panel below
     is already showing at four times the size. */
  html[data-immersive] [data-phase="done"] .hud-clock { display: none; }
}

/* The finished board is the reward, so nothing covers it: the result takes
   the row the palette was using, exactly as the app keeps the board on screen
   and puts its banner beneath. Anatomy and type are the app's SolvedResultPanel
   — a gold hairline that fades at both ends, a small wide-tracked gold title,
   then the time as the largest thing on the screen. */
.result { display: none; flex-direction: column; align-items: center; text-align: center; }
[data-phase="done"] .result { display: flex; animation: result-in 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
[data-phase="done"] .play-hint,
[data-phase="done"] .palette { display: none; }
/* All three go once the board is solved. Undo and Clear have nothing left to
   act on, and Next is already there as the panel's own button -- keeping the
   disc as well left the action offered twice on one screen, the second time
   as a small mark floating beside a clock that had stopped. */
[data-phase="done"] .hud-tools { display: none; }

@keyframes result-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.result-rule {
  width: 104px; height: 1px; margin-bottom: 0.9rem;
  background: linear-gradient(90deg, transparent, rgba(201, 162, 74, 0.85), transparent);
}
.result-title {
  margin: 0; color: var(--gold);
  font-family: var(--sans); font-size: 0.94rem; font-weight: 800;
  letter-spacing: 0.16em; text-transform: uppercase;
}
/* One quiet footnote, never a second celebration competing with the title. */
.result-note { margin: 0.5rem 0 0; color: #a8a8b2; font-size: 0.8rem; font-weight: 600; display: none; }
[data-flawless="true"] .result-note { display: block; }
.result-time {
  margin: 0.5rem 0 0; color: #ffffff; font-weight: 800; letter-spacing: -0.02em;
  font-size: clamp(2.2rem, 1.4rem + 3vw, 3.25rem); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.result-stats { display: flex; align-items: center; gap: 1.375rem; margin-top: 0.9rem; }
.result-stat { display: flex; flex-direction: column; align-items: center; }
.result-stat span {
  color: #8a8a94; font-family: var(--mono); font-size: 0.68rem;
  letter-spacing: 0.05em; text-transform: uppercase; margin-bottom: 3px;
}
.result-stat b { color: #e9e9ed; font-size: 0.94rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.result-div { width: 1px; height: 28px; background: rgba(255, 255, 255, 0.12); }
.result-go {
  margin-top: 1.1rem; cursor: pointer;
  padding: 0.7rem 1.6rem; border-radius: 16px;
  background: var(--gold); color: #0c0304; border: 1.5px solid #c9a24a;
  font-family: var(--sans); font-size: 1rem; font-weight: 700;
  transition: transform 0.28s var(--t);
}
.result-go:hover { transform: translateY(-2px); }

.play-notes { padding-block: clamp(2.5rem, 5vw, 3.5rem); border-top: 1px solid var(--hair); }
.play-notes p { color: var(--text-2); font-weight: 300; margin: 0 0 1rem; }
.play-notes p:last-child { margin-bottom: 0; }

/* ───────────────────────── the note ───────────────────────── */

.note { max-width: 40rem; margin-inline: auto; }
/* Centerd, like every other section's kicker. The body below it stays ranged
   left, because this is the one block of long prose on the page and centring
   it would cost more in readability than the symmetry is worth -- but the
   label had no reason to break the rhythm. */
.note .label { text-align: center; }
.note-body p {
  font-size: clamp(1.125rem, 1.5vw, 1.3rem); line-height: 1.68;
  color: #c3cbd9; font-weight: 300; margin-bottom: 1.4rem;
}
.note-body strong { color: var(--text); font-weight: 600; }
.note-body p:first-child { font-size: clamp(1.25rem, 1.9vw, 1.55rem); line-height: 1.5; color: var(--text); }
.note-sign {
  margin: 2.25rem 0 0; padding-top: 1.6rem; border-top: 1px solid var(--hair);
  font-family: var(--display); font-size: 1.4rem; color: var(--text-2);
}

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

/* Drawn here rather than using Apple's and Google's own artwork, for one
   reason that decides it: Apple ships no Ukrainian badge — its endpoint
   returns the English file byte-for-byte for uk-ua while serving real
   translations for de, fr, ja, pl and ru — so the official set cannot give
   the Ukrainian page a matching pair. Drawing both also lets the two read
   the same way, which the official wording ("Download on the" against "Get
   it on") does not.
   To go back: the official files are one curl away, see README. */

.stores { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 3.25rem; }

.store {
  position: relative; isolation: isolate;
  display: inline-flex; align-items: center; gap: 1.05rem;
  /* Never wider than it wants to be, and never so wide that the pair stops
     fitting on one line — two stacked store buttons read as a list of
     choices to weigh rather than as one obvious next step. */
  min-width: min(16.25rem, calc(50% - 0.5rem)); padding: 0.9rem 1.6rem;
  border-radius: 14px; text-decoration: none; color: var(--text);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.075), rgba(255, 255, 255, 0.022));
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09), 0 12px 28px -20px rgba(0, 0, 0, 0.9);
  transition: transform 0.28s var(--t), border-color 0.28s var(--t), box-shadow 0.28s var(--t);
}

/* A single soft highlight raked across the top edge, so the surface reads as
   lit rather than filled. */
.store::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: radial-gradient(120% 80% at 30% -30%, rgba(255, 255, 255, 0.14), transparent 60%);
  opacity: 0; transition: opacity 0.28s var(--t);
}
.store:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.26); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 18px 34px -22px rgba(0, 0, 0, 1); color: var(--text); }
.store:hover::before { opacity: 1; }

.store-mark { flex: none; display: grid; place-items: center; width: 2.15rem; }
.store-mark svg { display: block; }

/* Two lines, weighted hard toward the second. "Download on" reads identically
   on both buttons and so carries nothing between them; the store's name is the
   only part anyone scans for. Four levers pull the same way — the label is
   smaller, lighter, dimmer and tracked wide, which makes it read as a caption
   rather than a sentence; the name is larger, at the family's heaviest weight,
   and tracked tight, which adds mass without adding size.
   The label was dimmed too far at first — #7d8799 measures 4.9:1 against the
   button's surface, past the 4.5:1 floor for small text, while the name still
   runs at 2.2x its size and twice its weight. Quiet is not the same as faint. */
.store-text   { display: grid; gap: 0.14rem; text-align: left; }
.store-kicker { font-family: var(--mono); font-size: 0.68rem; font-weight: 500; letter-spacing: 0.17em; text-transform: uppercase; color: #7d8799; }
.store-name   { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1.02; }

/* The visitor's own platform, set by assets/site.js. Gold is the game's own
   leaderboard highlight — the one place the page spends it on a control. */
.store[data-primary="true"] {
  background: linear-gradient(180deg, #ffdc86 0%, var(--gold) 55%, #e8b23e 100%);
  border-color: rgba(255, 226, 150, 0.75);
  color: #1c1400;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 16px 34px -18px rgba(245, 196, 81, 0.5);
}
.store[data-primary="true"]:hover { transform: translateY(-2px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 22px 42px -20px rgba(245, 196, 81, 0.6); color: #1c1400; }
.store[data-primary="true"] .store-kicker { color: rgba(28, 20, 0, 0.58); }
.store[data-primary="true"] .store-mark .apple-mark { fill: #1c1400; }

.finale .stores { justify-content: center; }

.store-note { font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.07em; text-transform: uppercase; color: #8b95aa; margin: 1.6rem 0 0; }

/* A second way in, under the two store buttons. The site's strongest asset is
   a board you can actually play, and it was one nav item away while the hero
   asked for an install of something unseen. Quiet on purpose: it is the
   alternative for someone not ready to install, not a competing shout. */
/* ---- The hero's call to action -------------------------------------------
   The store buttons were the two largest things on the page and they were
   `href="#"`: pressing them did nothing, which a visitor reads as a broken
   site rather than as an app that has not shipped yet. A friend playing the
   site said, in as many words, that he did not know what he was meant to do.
   So the one thing that DOES work is now the button, and the stores stay in
   the markup, dimmed and inert, saying out loud that they are not ready.
   To ship: drop data-soon from the two .stores blocks and set the hrefs. */
.hero-cta { margin: 2.9rem 0 0; }
.try-btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.92rem 2rem; border-radius: 999px;
  background: var(--gold); color: #1c1400; text-decoration: none;
  font-family: var(--sans); font-size: 1.04rem; font-weight: 600;
  box-shadow: 0 20px 38px -24px rgba(245, 196, 81, 0.9);
  transition: transform 0.28s var(--t), gap 0.28s var(--t), box-shadow 0.28s var(--t);
}
.try-btn:hover { transform: translateY(-2px); gap: 0.85rem;
  box-shadow: 0 24px 46px -22px rgba(245, 196, 81, 0.75); }
.try-btn svg { display: block; }
.soon-line {
  font-family: var(--mono); font-size: 0.71rem; letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--text-3); margin: 1.9rem 0 0.8rem;
}
.stores[data-soon] { margin-top: 0; opacity: 0.45; }
.stores[data-soon] .store { pointer-events: none; }
.finale .soon-line { text-align: center; }

/* The board draws itself and asks to be touched, so touching it now goes
   somewhere. The pill is a hover affordance for pointers; on a phone the
   button above has already said the same thing. */
.board-link { display: block; text-decoration: none; position: relative; }
.board-link::after {
  content: attr(data-try); position: absolute; left: 50%; bottom: 8%;
  transform: translate(-50%, 6px); pointer-events: none;
  padding: 0.42rem 1rem; border-radius: 999px;
  border: 1px solid var(--hair-2); background: rgba(11, 14, 20, 0.86);
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text-2);
  opacity: 0; transition: opacity 0.3s var(--t), transform 0.3s var(--t);
}
.board-link:hover::after { opacity: 1; transform: translate(-50%, 0); }
@media (hover: none) { .board-link::after { content: none; } }

.hero-try { margin: 1.1rem 0 0; }
.hero-try a {
  display: inline-flex; align-items: center; gap: 0.5rem;
  color: var(--text-2); text-decoration: none;
  font-family: var(--mono); font-size: 0.76rem; letter-spacing: 0.12em; text-transform: uppercase;
  transition: color 0.25s var(--t), gap 0.25s var(--t);
}
.hero-try a:hover { color: var(--gold); gap: 0.75rem; }

/* What Pro is, said once and in full. Nothing else on the page mentioned that
   anything costs money -- someone could install expecting everything free and
   meet a paywall, which is a one-star review and a listing that misdescribes
   itself. */
.pro-line {
  margin: clamp(1.75rem, 4vw, 2.5rem) 0 0; max-width: 54rem;
  color: var(--text-2); font-weight: 300; line-height: 1.65;
}
.pro-tag {
  display: inline-block; margin-right: 0.7rem;
  padding: 0.18rem 0.6rem; border-radius: 999px;
  border: 1px solid rgba(245, 196, 81, 0.35); color: var(--gold);
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.12em;
  text-transform: uppercase; vertical-align: 0.08em;
}

/* ───────────────────────── bands ───────────────────────── */

.band { padding-block: clamp(5rem, 10vw, 9rem); border-top: 1px solid var(--hair); position: relative; }
.band--quiet { background: linear-gradient(180deg, rgba(255, 255, 255, 0.016), transparent 60%); }

.statement { text-align: center; max-width: 40rem; margin-inline: auto; }
.statement .label { justify-content: center; }
.statement .lede { margin: 1.75rem auto 0; max-width: 34rem; }

/* The four colors, stated once and compactly rather than as a chart. */
.chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.55rem 1.6rem; margin-top: 2.25rem; }
.chip { display: inline-flex; align-items: center; gap: 0.6rem; font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.09em; text-transform: uppercase; color: #8b95aa; }
.chip i { width: 1.4rem; height: 1.4rem; border-radius: 50%; display: grid; place-items: center; font-style: normal; font-size: 0.72rem; color: #0b0e14; }

/* Feature cards */
.cards { display: grid; gap: 1.35rem; margin-top: clamp(2.5rem, 5vw, 3.5rem); }
@media (min-width: 46rem) { .cards { grid-template-columns: repeat(2, 1fr); } }
.card {
  position: relative; padding: 2.3rem 2.15rem 2.1rem; border-radius: 14px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.012));
  border: 1px solid var(--hair);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: border-color 0.3s var(--t), transform 0.3s var(--t);
}
.card:hover { border-color: rgba(245, 196, 81, 0.3); transform: translateY(-3px); }
.card h3 { margin-bottom: 0.6rem; }
.card p { color: var(--text-2); font-weight: 300; margin-bottom: 0; }
.card-index { font-family: var(--mono); font-size: 0.8rem; letter-spacing: 0.16em; color: var(--gold-dim); display: block; margin-bottom: 1.1rem; }

/* Closing call to action */
.finale { text-align: center; }
.finale .store-note { text-align: center; }

/* ───────────────────────── documents ───────────────────────── */

.doc { padding-block: clamp(3rem, 7vw, 5rem) clamp(4rem, 9vw, 7rem); }
.doc h1 { font-size: clamp(2.2rem, 5.5vw, 3.6rem); line-height: 1.02; }
.doc h2 { font-size: clamp(1.35rem, 2.6vw, 1.7rem); line-height: 1.2; margin: 3.25rem 0 0.9rem; }
.doc h3 { font-size: 1.08rem; font-family: var(--sans); font-weight: 700; letter-spacing: -0.005em; margin: 2rem 0 0.5rem; }
.doc p, .doc li { color: var(--text-2); font-weight: 300; }
.doc strong { color: var(--text); font-weight: 600; }
.doc ul, .doc ol { padding-left: 1.2rem; }
.doc li { margin-bottom: 0.5rem; }
.doc a { color: var(--gold); text-decoration-color: var(--gold-dim); }

.dateline { font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-3); margin: 1.25rem 0 3rem; }

/* `.doc` is the content column, not the document: it sat on <body> and so
   pushed the masthead down by its own top padding and painted every link in
   the header gold. It belongs on <main>. */
.doc { counter-reset: clause; }
.clause { counter-increment: clause; }
.clause > h2::before {
  content: counter(clause, decimal-leading-zero);
  font-family: var(--mono); font-size: 0.62em; font-weight: 500;
  color: var(--gold-dim); margin-right: 0.9em; vertical-align: 0.22em;
}

.panel {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.01));
  border: 1px solid var(--hair); border-radius: 14px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  padding: 1.6rem 1.75rem; margin: 2rem 0;
}
.panel > :last-child { margin-bottom: 0; }
.panel h2, .panel h3 { margin-top: 0; }

.jurisdiction { border-left: 1px solid var(--gold-dim); padding: 0.15rem 0 0.15rem 1.4rem; margin: 1.75rem 0; }
.jurisdiction > :last-child { margin-bottom: 0; }
.jurisdiction h3 { margin-top: 0; font-family: var(--mono); font-size: 0.82rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.12em; color: var(--gold); }

dl.facts { margin: 0; display: grid; gap: 1.15rem; }
dl.facts > div { display: grid; gap: 0.2rem; }
dl.facts dt { font-family: var(--mono); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.11em; color: var(--text-3); }
dl.facts dd { margin: 0; color: var(--text-2); }

.table-scroll { overflow-x: auto; margin: 1.6rem 0; border-radius: 10px; }
table { width: 100%; border-collapse: collapse; font-size: 0.94rem; min-width: 32rem; }
th, td { text-align: left; padding: 0.75rem 0.9rem 0.75rem 0; border-bottom: 1px solid var(--hair); vertical-align: top; }
th { font-family: var(--mono); font-size: 0.76rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-3); border-bottom-color: var(--hair-2); }
td { color: var(--text-2); font-weight: 300; }
td:first-child { color: var(--text); font-weight: 400; }
th:last-child, td:last-child { padding-right: 0; }

/* A fact only the owner can supply. Must reach zero before launch. */
.fill { display: inline; background: rgba(245, 196, 81, 0.09); color: var(--gold); border-bottom: 1px dashed var(--gold-dim); padding: 0.05em 0.35em; font-family: var(--mono); font-size: 0.88em; letter-spacing: 0.01em; }

.draft { border-bottom: 1px solid var(--hair); background: rgba(245, 196, 81, 0.04); color: #8b95aa; font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.09em; text-transform: uppercase; text-align: center; padding: 0.6rem 1rem; }
.draft b { color: var(--gold); font-weight: 500; }

/* ───────────────────────── colophon ───────────────────────── */

.colophon { border-top: 1px solid var(--hair); padding-block: clamp(3.5rem, 6vw, 5rem) 3.25rem; font-size: 0.95rem; color: #7b8598; }
.colophon-top { display: grid; gap: 2.5rem; margin-bottom: 3rem; }
@media (min-width: 44rem) { .colophon-top { grid-template-columns: 1fr auto auto; gap: 3rem; align-items: start; } }
.colophon-col { display: grid; gap: 0.7rem; align-content: start; }
.colophon-head { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-3); margin: 0 0 0.2rem; }
.colophon-col a { color: var(--text-2); text-decoration: none; }
.colophon-col a:hover { color: var(--gold); }
.colophon-brand { display: grid; gap: 0.9rem; align-content: start; max-width: 22rem; }
.colophon-brand p { margin: 0; color: var(--text-3); }
.colophon-mail { font-family: var(--mono); font-size: 0.86rem; letter-spacing: 0.05em; color: var(--text-2); text-decoration-color: var(--hair-2); }
.colophon-mail:hover { color: var(--gold); }
.colophon a { color: var(--text-3); }
.colophon a:hover { color: var(--gold); }
.colophon-base { display: flex; flex-wrap: wrap; gap: 0.4rem 1.5rem; justify-content: space-between; border-top: 1px solid var(--hair); padding-top: 1.6rem; font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.07em; text-transform: uppercase; }
.colophon-base p { margin: 0; }

/* The flag is an accent, not a shout: the lower band is the site's own --gold
   rather than #ffd700, and the blue is pulled back from #0057b7 so it does not
   glow against a near-black footer. The ring stops the gold band dissolving
   into the background where the two meet. */
.made { display: inline-flex; align-items: center; gap: 0.55rem; }
.flag { width: 1.2em; height: auto; flex: none; border-radius: 2.5px; box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.13); }

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 0.9rem; top: 0.9rem; z-index: 60; background: var(--gold); color: #1a1200; padding: 0.6rem 1rem; border-radius: 8px; }

/* ───────────────────────── motion ───────────────────────── */

/* Hidden only once the reveal script has announced itself on <html>. Without
   JavaScript — or if it fails to load — every .rise element is simply visible,
   which is the state that matters. Content must never depend on a script to
   exist. */
.js .rise { opacity: 0; transform: translateY(22px); transition: opacity 0.9s var(--t), transform 0.9s var(--t); }
.js .rise.is-in { opacity: 1; transform: none; }
.rise[data-delay="1"] { transition-delay: 0.09s; }
.rise[data-delay="2"] { transition-delay: 0.18s; }
.rise[data-delay="3"] { transition-delay: 0.27s; }
.rise[data-delay="4"] { transition-delay: 0.36s; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .rise { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

/* The 404 borrows the home page's own graph rather than inventing a picture:
   #board is all the hero renderer looks for, so dropping the canvas here gets
   the same endlessly-recoloring dodecahedron for the cost of one element. It
   is sized well below the hero's 44rem -- this is a wrong turn, not a pitch. */
.gone { padding-block: clamp(3rem, 8vw, 6rem); text-align: center; }
.gone-art { width: min(100%, 17rem); margin: 0 auto clamp(1.75rem, 4vw, 2.5rem); }
.gone .label { margin-bottom: 0.9rem; }
/* Smaller than a document title on purpose: this is a wrong turn, not a
   chapter heading, and at .doc's size the sentence broke to four lines. */
.gone h1 { font-size: clamp(1.9rem, 4vw, 2.8rem); margin: 0 0 1.25rem; }
.gone .lede { margin-inline: auto; }
.gone-list { justify-content: center; }
.gone-list { list-style: none; padding: 0; margin: 2rem 0 0; display: flex; flex-wrap: wrap; gap: 0.6rem; }
.gone-list a {
  display: inline-block; padding: 0.5rem 1rem; border-radius: 999px;
  border: 1px solid var(--hair); color: var(--text-2); text-decoration: none;
  transition: color 0.2s var(--t), border-color 0.2s var(--t);
}
.gone-list a:hover { color: var(--text); border-color: var(--hair-2); }
