/* =========================================================================
   fold2 gallery — the shared stylesheet

   One house style (adopted from bogchat) for every app in the gallery.
   Served at /style.css; each app's page links it and names itself on
   <body class="…">, which scopes that app's own section at the end.

     1. Fonts
     2. Tokens
     3. Reset
     4. Base
     5. Shared components
        5.1 Rails: the fixed top and bottom bars
        5.2 Controls: buttons, pills, inputs
        5.3 Board frame: a board dead center, its boundary and captions
        5.4 Move log: time controls and paired move chips
        5.5 Tank chip: a meter pill filled in its owner's color
        5.6 Inspector: fold's commits, one line each, above the bottom rail
        5.7 About: an app's About card, over the app at #about
     6. Apps
        6.0 Gallery
        6.1 Chat
        6.2 Chess
        6.3 Go
        6.4 Sheets
        6.5 Canvas
        6.6 Derby
        6.7 Poll
        6.8 Sequencer
        6.9 Piano
        6.10 Synth
        6.11 Pads
        6.12 Market
        6.13 Words
        6.14 Blocks
        6.15 Context
        6.16 Voxels
     7. Phones
   ========================================================================= */


/* =========================================================================
   1. Fonts
   ========================================================================= */

@font-face {
  font-family: "ABC Arizona Superfamily";
  src: url("/fonts/arizona.woff2") format("woff2-variations");
  font-display: swap;
  font-style: normal;
  font-weight: 1 1000;
  font-stretch: 50% 200%;
}


/* =========================================================================
   2. Tokens
   ========================================================================= */

:root {
  color-scheme: light dark;

  /* --- color: every value flips with the system theme --- */
  --app-background: light-dark(oklch(100% 0 0), oklch(21.13% 0.0032 17.4));
  --app-overlay-solid: light-dark(oklch(100% 0 0 / 1), oklch(21.13% 0.0032 17.4 / 1));
  --app-overlay-clear: light-dark(oklch(100% 0 0 / 0), oklch(21.13% 0.0032 17.4 / 0));
  --wash-color: light-dark(oklch(94.61% 0 0), oklch(29.72% 0 0));
  --text-color: light-dark(oklch(21.13% 0.0032 17.4), oklch(100% 0 0));
  --active-background: light-dark(oklch(21.13% 0.0032 17.4), oklch(100% 0 0));
  --active-text: light-dark(oklch(100% 0 0), oklch(21.13% 0.0032 17.4));
  --placeholder-color: light-dark(oklch(78.89% 0 0), oklch(68.3% 0 0));
  --muted-color: light-dark(oklch(62.1% 0.006 17.3), oklch(68.3% 0 0));
  --secondary-text-color: light-dark(oklch(51.48% 0.0063 17.4), oklch(68.3% 0 0));
  --danger: light-dark(oklch(55% 0.18 25), oklch(68% 0.19 22));
  /* quiet strokes (a board's boundary, a chip's divider): halfway between
     the muted gray and the wash */
  --hairline-color: color-mix(in oklch, var(--muted-color), var(--wash-color));
  /* tinted board squares, mixed as bogathon's archive grid does */
  --square-tint: color-mix(in lab, var(--text-color) 8%, var(--app-background));

  /* --- type --- */
  --font-family: "ABC Arizona Superfamily", Georgia, "Times New Roman", serif;

  /* --- shape and spacing --- */
  --border-radius: 1.11em;
  --control-inline-padding: 1.11em;
  --spacing-sm: 0.5em;
  --spacing-md: 0.88em;
  --spacing-lg: 1em;

  /* --- the rails: no script measures them, so pages clear fixed heights.
     The bottom one is a pill (1.2em + 2 × 0.88em) plus 1em padding each
     side; boards clear the taller of the two, top and bottom alike --- */
  --top-overlay-height: 4.5em;
  --bottom-overlay-height: 5em;
  --overlay-height: max(var(--top-overlay-height), var(--bottom-overlay-height));

  /* --- boards: every piece's visible size, as a share of its grid
     square: the golden ratio, 1/φ --- */
  --piece: 0.618;
}

/* where supported, corners are squircles rather than circular arcs */
@supports (corner-shape: squircle) {
  :root { --border-radius: 29px; }
  input, button, .pill, .pair, .board::after, .card, .about-card, .context .meter { corner-shape: squircle; }
}


/* =========================================================================
   3. Reset
   ========================================================================= */

* { margin: 0; padding: 0; box-sizing: border-box; }
ol, ul { list-style: none; }
a { color: inherit; }

/* present for screen readers, invisible on the page */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* =========================================================================
   4. Base
   ========================================================================= */

body {
  background-color: var(--app-background);
  color: var(--text-color);
  font-family: var(--font-family);
  font-size: 1.23em;
  font-variation-settings: "wdth" 100, "wght" 333, "SRFF" 33, "slnt" 0;
  font-weight: 333;
  -webkit-font-smoothing: antialiased;
}


/* =========================================================================
   5. Shared components
   ========================================================================= */

/* -------------------------------------------------------------------------
   5.1 Rails: the fixed top and bottom bars. Each fades out whatever passes
   under it; only its contents take clicks.
   ------------------------------------------------------------------------- */

#top-container, #input-container {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 10;
  padding: var(--spacing-lg);
  pointer-events: none;
}
#top-container { top: 0; padding-top: calc(var(--spacing-lg) + env(safe-area-inset-top)); }
#input-container { bottom: 0; padding-bottom: calc(var(--spacing-lg) + env(safe-area-inset-bottom)); }
#top-container::before, #input-container::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
}
#top-container::before { background: linear-gradient(to bottom, var(--app-overlay-solid), var(--app-overlay-clear)); }
#input-container::before { background: linear-gradient(to bottom, var(--app-overlay-clear), var(--app-overlay-solid)); }
#top-container > *, #input-container > * { position: relative; z-index: 1; pointer-events: auto; }

/* the top rail (`top_nav`): Back to Gallery, then the app's own pills,
   scrolling sideways once they outgrow the window */
.pills {
  display: flex;
  gap: 0.44em;
  overflow-x: auto;
  scrollbar-width: none;
}
.pills::-webkit-scrollbar { display: none; }
/* a button that posts sits in the row like any other pill */
.pills .inline { display: contents; }
/* an app's action buttons, pushed to the rail's right end */
.pills .actions { display: flex; gap: 0.44em; margin-left: auto; }
/* a refusal, in the rail where it's seen */
.alert { color: var(--danger); }

/* -------------------------------------------------------------------------
   5.2 Controls: buttons, pills and inputs share one cut, chat's Send
   button: a wash-filled rounded pill, its text one line tall.
   ------------------------------------------------------------------------- */

input, button, .pill {
  border: none;
  outline: none;
  font: inherit;
  font-variation-settings: inherit;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  color: var(--text-color);
  line-height: 1.2em;
  padding: var(--spacing-md) var(--control-inline-padding);
}
input::placeholder { color: var(--placeholder-color); }
.pill { flex: 0 0 auto; white-space: nowrap; text-decoration: none; }

button { cursor: pointer; flex: 0 0 auto; transition: opacity 0.2s ease; }
button:hover:enabled, a.pill:hover { opacity: 0.8; }
button:active:enabled { background-color: var(--active-background); color: var(--active-text); }
button:disabled { cursor: default; }
.pill:disabled { opacity: 0.4; }
a.pill { cursor: pointer; transition: opacity 0.2s ease; }

/* -------------------------------------------------------------------------
   5.3 Board frame: a board sits dead center in the viewport. A
   1fr | board | 1fr grid, padded equally top and bottom, so it clears both
   rails. Every board is nine units wide, its grid inset half a unit, so a
   chess square and a go line spacing are one size. Its boundary and
   captions hang outside it, out of flow, so they never move it.
   ------------------------------------------------------------------------- */

.game {
  /* the boundary reaches (--piece - 0.5) of a unit past the board on every
     side (9 units become 9 + 2(--piece - 0.5)), and a caption needs ~2.5em
     beyond it, mirrored top and bottom to keep the board centered */
  --outer: calc(9 / (9 + 2 * (var(--piece) - 0.5)));
  --board: min(36em, (100dvh - 2 * var(--overlay-height) - 5em) * var(--outer), (100vw - 2 * var(--spacing-lg)) * var(--outer));
  display: grid;
  grid-template-columns: 1fr var(--board) 1fr;
  grid-template-rows: 1fr;
  align-items: center;
  min-height: 100dvh;
  padding: var(--overlay-height) var(--spacing-lg);
}
.board {
  grid-area: 1 / 2;
  width: var(--board);
  aspect-ratio: 1;
  position: relative;
  z-index: 1;
}

/* the boundary: one stroke around the whole board, --piece of a unit
   beyond its grid, which sits half a unit in from the board's edge */
.board::after {
  content: "";
  position: absolute;
  inset: calc(100% / 9 * (0.5 - var(--piece)));
  border: 1px solid var(--hairline-color);
  border-radius: var(--border-radius);
  pointer-events: none;
}

/* captions: diminutive gray lines under the boundary (counts) and above it
   (`.top`: the turn), one line each, centered on the board and free to be
   wider than it */
.caption {
  position: absolute;
  top: calc(100% + 100% / 9 * (var(--piece) - 0.5) + var(--spacing-md));
  left: 50%;
  translate: -50%;
  display: flex;
  align-items: center;
  gap: 1.2em;
  white-space: nowrap;
  color: var(--muted-color);
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
}
.caption.top {
  top: auto;
  bottom: calc(100% + 100% / 9 * (var(--piece) - 0.5) + var(--spacing-md));
}
/* boards that aren't nine units square (sequencer, piano) set a --unit on
   `.game.units` and size the board in it; the boundary then sits --piece
   of a unit outside the board, as chess's sits outside its squares, and
   the captions clear it */
.game.units .board::after { inset: calc(-1 * var(--piece) * var(--unit)); }
.game.units .caption { top: calc(100% + var(--piece) * var(--unit) + var(--spacing-md)); }
.game.units .caption.top { top: auto; bottom: calc(100% + var(--piece) * var(--unit) + var(--spacing-md)); }

/* each item a row of its own: its text and any discs centered together */
.caption > span { display: flex; align-items: center; gap: 0.3em; }
.caption .danger { color: var(--danger); }

/* -------------------------------------------------------------------------
   5.4 Move log: the bottom rail of a board game. One row of chips scrolls
   sideways under the time controls, which stay pinned at its left.
   ------------------------------------------------------------------------- */

.moves {
  display: flex;
  gap: 0.44em;
  overflow-x: auto;
  scrollbar-width: none;
  font-variant-numeric: tabular-nums;
}
.moves::-webkit-scrollbar { display: none; }
.moves > * { flex: none; white-space: nowrap; }
.time {
  position: sticky;
  left: 0;
  z-index: 1;
  display: flex;
  gap: 0.44em;
  padding-right: 0.44em;
  background-color: var(--app-background);
}

/* a pair: one move (chess) or round (go) as one chip, its two plies the
   halves, split by a hairline. Only the current ply's half inverts. The
   halves are square and the chip clips them, so an inverted half takes
   the chip's own outline */
.pair {
  display: flex;
  overflow: hidden;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
}
.pair button { display: flex; align-items: center; gap: 0.3em; background: none; border-radius: 0; }
.pair button + button { border-left: 1px solid var(--hairline-color); }
.pair .n { color: var(--muted-color); }
.pair [aria-current] { background-color: var(--active-background); color: var(--active-text); }
.pair [aria-current] .n { color: inherit; opacity: 0.6; }


/* -------------------------------------------------------------------------
   5.5 Tank chip: a budget that refills over time (canvas's ink,
   sequencer's notes). The pill fills with exactly its owner's color to
   show how full the tank is (`--fill`, kept current by the page). The
   label is clipped to a matching two-tone gradient: the page background
   over the fill, ink over the rest. Identity lightness is pinned per
   theme (identity.rs), so the page background always reads on it. The
   label carries the pill's padding so both gradients span the chip.
   ------------------------------------------------------------------------- */

.tank {
  --fill: 100%;
  padding: 0;
  background-image: linear-gradient(to right, var(--pen) var(--fill), transparent var(--fill));
  font-variant-numeric: tabular-nums;
}
.tank .label {
  display: block;
  padding: var(--spacing-md) var(--control-inline-padding);
  background-image: linear-gradient(to right, var(--app-background) var(--fill), var(--text-color) var(--fill));
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}
/* a refusal: the tank was too low */
.tank.dry { box-shadow: inset 0 0 0 1px var(--danger); }

/* -------------------------------------------------------------------------
   5.6 Inspector (`inspect.rs`): fold's work made visible, off until the
   \ key opens it. A band at the top of the bottom rail, its first
   line a stream's definition (on hover or tap), then the latest commits,
   newest first: who (a dot), what, how long, and each source's and
   view's change. A new line grows in and its chips flash; a refusal
   reads in a muted danger tone. Phones keep the latest line only.
   ------------------------------------------------------------------------- */

/* open, the rail grows by the band, and boards (and Sequencer's drawer)
   clear the taller rail as they always do */
body:has(#inspect:not([hidden])) {
  --inspect-lines: 5;
  --bottom-overlay-height: calc(5em + var(--inspect-lines) * 0.72em * 1.5 + var(--spacing-sm));
  --overlay-height: max(var(--top-overlay-height), var(--bottom-overlay-height));
}
#inspect {
  margin-bottom: var(--spacing-sm);
  font-size: 0.72em;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  color: var(--secondary-text-color);
}
#inspect[hidden] { display: none; }
#inspect .def { height: 1.5em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--muted-color); }
#inspect ol { height: calc((var(--inspect-lines, 5) - 1) * 1.5em); overflow: hidden; }
/* a commit: one line, fading out at the right if it runs long */
#inspect li {
  display: flex;
  align-items: center;
  gap: 0.5em;
  height: 1.5em;
  white-space: nowrap;
  overflow: hidden;
  mask-image: linear-gradient(to right, black calc(100% - 3em), transparent);
  animation: inspect-in 0.3s ease-out;
}
#inspect li.empty { color: var(--muted-color); animation: none; }
#inspect .dot { flex: none; width: 0.7em; aspect-ratio: 1; border-radius: 50%; background: var(--c); }
#inspect .what { color: var(--text-color); }
#inspect .took { color: var(--muted-color); }
#inspect .arrow { color: var(--muted-color); }
/* a stream's change: its name, then +/− rows; a big one in a heavier cut */
#inspect .chip {
  padding: 0 0.3em;
  border-radius: 0.4em;
  cursor: default;
  outline: none;
  animation: inspect-flash 1.2s ease-out;
}
#inspect .chip .d { margin-left: 0.3em; color: var(--text-color); }
#inspect .chip.big .d { font-variation-settings: "wdth" 100, "wght" 600, "SRFF" 33, "slnt" 0; }
#inspect .chip.quiet { color: var(--muted-color); animation: none; }
#inspect .chip:hover, #inspect .chip:focus-visible { background-color: var(--wash-color); }
/* a refusal: the whole line in a muted danger tone */
#inspect li.refused, #inspect li.refused .what, #inspect li.refused .took { color: color-mix(in oklch, var(--danger) 75%, var(--muted-color)); }
#inspect li.refused .chip { animation: none; }
/* the lines a page opens with were already there: nothing grows in */
#inspect li.old, #inspect li.old .chip { animation: none; }
/* a bottom rail that's only the strip (Canvas, Piano, Poll) is there
   only while the strip is open */
#input-container:has(> #inspect[hidden]:only-child) { display: none; }
@keyframes inspect-in { from { height: 0; opacity: 0; } }
@keyframes inspect-flash { from { background-color: var(--wash-color); box-shadow: inset 0 0 0 1px var(--hairline-color); } }
@media (prefers-reduced-motion: reduce) {
  #inspect li, #inspect .chip { animation: none; }
}
@media (width <= 600px) {
  body:has(#inspect:not([hidden])) { --inspect-lines: 1; }
  #inspect .def, #inspect li + li { display: none; }
  #inspect ol { height: 1.5em; }
}


/* -------------------------------------------------------------------------
   5.7 About (`about.rs`): an app's About card, shown over the app while
   the page is at #about (`:target`, so it needs no script). The app
   stays in view behind a dimmed, lightly blurred backdrop, which closes
   the card when clicked. The card is a column of prose at reading width,
   scrolling inside itself when long; phones get it nearly full-screen.
   ------------------------------------------------------------------------- */

.about {
  position: fixed;
  inset: 0;
  /* above every rail (10), board and canvas */
  z-index: 100;
  display: grid;
  place-items: center;
  padding: calc(var(--spacing-lg) * 2) var(--spacing-lg);
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.2s ease, visibility 0s 0.2s;
}
.about:target { visibility: visible; opacity: 1; transition: opacity 0.2s ease; }
.about .backdrop {
  position: absolute;
  inset: 0;
  background-color: light-dark(oklch(21.13% 0.0032 17.4 / 0.25), oklch(0% 0 0 / 0.5));
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  cursor: default;
}
.about-card {
  position: relative;
  width: min(40em, 100%);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  padding: 1.25em 1.75em 1.75em;
  background-color: var(--app-background);
  border-radius: var(--border-radius);
  box-shadow: 0 0 0 1px var(--hairline-color), 0 1em 3em oklch(0% 0 0 / 0.15);
  line-height: 1.45;
  outline: none;
  translate: 0 0.5em;
  transition: translate 0.2s ease;
}
.about:target .about-card { translate: none; }
/* the name and Close, then the app's live line, muted */
.about-head { display: flex; align-items: center; justify-content: space-between; gap: 1em; }
.about .stat { margin-top: 0.3em; color: var(--muted-color); font-size: 0.82em; font-variant-numeric: tabular-nums; }
/* the prose: headings at text size, as everywhere; an h2 is a quiet label */
.about :is(h1, h2, h3) { font-size: 1em; font-weight: inherit; }
.about-card > h2 { margin-top: 1.5em; color: var(--muted-color); }
.about-card > p { margin-top: 0.6em; }
.about-card > .stat + p { margin-top: 1.25em; }
/* the inspector's key, last and quiet */
.about-card > .key { margin-top: 1.5em; color: var(--muted-color); font-size: 0.82em; }
.about kbd { font: inherit; padding: 0 0.35em; border: 1px solid var(--hairline-color); border-radius: 0.3em; }
.about-card > h2 + * { margin-top: 0.3em; }
.about ul, .about ol { margin-top: 0.6em; }
.about li + li { margin-top: 0.35em; }
/* things to try: a calm list, each led by a muted dash */
.about ul.try li { position: relative; padding-left: 1.2em; }
.about ul.try li::before { content: "–"; position: absolute; left: 0; color: var(--muted-color); }
.about code {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.82em;
  padding: 0.1em 0.35em;
  border-radius: 0.4em;
  background-color: var(--wash-color);
}
@media (prefers-reduced-motion: reduce) {
  .about, .about:target, .about-card { transition: none; }
}
@media (width <= 600px) {
  .about { padding: 16px; }
  .about-card { width: 100%; height: 100%; padding: 1em 1.25em 1.5em; }
}


/* =========================================================================
   6. Apps
   ========================================================================= */

/* -------------------------------------------------------------------------
   6.0 Gallery: the index, and pages like "Not found". A column of
   prose and a grid of cards under the same top rail as every app.
   ------------------------------------------------------------------------- */

/* the rail's title: cut exactly like a pill (its padding and line), but
   without the fill, so its text sits where a pill's label would */
.gallery .title { padding: var(--spacing-md) var(--control-inline-padding); line-height: 1.2em; white-space: nowrap; }

.gallery main {
  /* the rail's inset plus a pill's inline padding: the content's left
     edge falls on the rail title's text, and the right mirrors it */
  --gutter: calc(var(--spacing-lg) + var(--control-inline-padding));
  padding: calc(var(--top-overlay-height) + var(--spacing-lg)) var(--gutter) calc(var(--spacing-lg) * 4);
}
/* headings read at text size; quieter ones mark sections */
.gallery h1, .gallery h2, .gallery h3 { font-size: 1em; font-weight: inherit; }
.gallery header { max-width: 36em; margin-bottom: calc(var(--spacing-lg) * 3); }
.gallery header > * + * { margin-top: var(--spacing-sm); }
.gallery header p, .gallery main > p { color: var(--secondary-text-color); line-height: 1.35; }
.gallery section + section { margin-top: calc(var(--spacing-lg) * 3); }
.gallery section > h2 { color: var(--muted-color); margin-bottom: var(--spacing-md); }

/* the cards: wash-filled squares, cut to the pills' radius and corner
   shape, in an even grid; each holds only its title, set in the lower
   left, and in the upper right how many people are in the app right now
   (pushed live; hidden when nobody is) */
.gallery .cards {
  display: grid;
  /* auto-fill: every square keeps its size, the last row left short */
  grid-template-columns: repeat(auto-fill, minmax(min(11em, calc(50% - 0.22em)), 1fr));
  gap: 0.44em;
}
.gallery .card {
  position: relative;
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: calc(var(--control-inline-padding) * 1.2) var(--control-inline-padding);
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  transition: opacity 0.2s ease;
}
/* the title's link stretches over the whole square */
.gallery .card a { text-decoration: none; }
.gallery .card a::after { content: ""; position: absolute; inset: 0; }
.gallery .card:hover { opacity: 0.8; }
.gallery .here {
  position: absolute;
  top: calc(var(--control-inline-padding) * 1.2);
  right: var(--control-inline-padding);
  display: flex;
  align-items: center;
  gap: 0.35em;
  color: var(--muted-color);
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* a small solid dot: someone's in */
.gallery .here::before {
  content: "";
  width: 0.5em;
  aspect-ratio: 1;
  border-radius: 50%;
  background: currentColor;
}
.gallery .here[hidden] { display: none; }

/* the stats (`stats.rs`), between the intro and the cards: a row of
   headline numbers, each over its caption, then a quiet grid of the
   rest, a hairline over each. Every value is pushed as it changes and
   ticks in (`.tick`) */
.gallery .stats .headline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
  gap: var(--spacing-lg) 1.5em;
  font-size: 1em;
  max-width: 54em;
}
.gallery .stats .headline > div { display: flex; flex-direction: column; gap: 0.15em; }
.gallery .stats .headline dt { font-size: 0.82em; }
.gallery .stats .headline dd {
  order: -1;
  font-size: 2em;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  font-variation-settings: "wdth" 100, "wght" 280, "SRFF" 33, "slnt" 0;
}
.gallery .stats .headline p { color: var(--muted-color); font-size: 0.72em; line-height: 1.35; max-width: 16em; }
.gallery .stats .more {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19em, 1fr));
  gap: 0 1.5em;
  margin-top: calc(var(--spacing-lg) * 2);
}
.gallery .stats .more > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1em;
  padding: 0.35em 0;
  border-top: 1px solid var(--hairline-color);
}
.gallery .stats .more dd { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* the refusals' rules run long: a row of their own, wrapping */
.gallery .stats .more > div:has(> [data-stat="rules"]) { grid-column: 1 / -1; }
.gallery .stats .more [data-stat="rules"] { white-space: normal; text-align: left; }
.gallery .tick { animation: stat-tick 0.6s ease-out; }
@keyframes stat-tick { from { opacity: 0.3; translate: 0 0.12em; } }
@media (prefers-reduced-motion: reduce) {
  .gallery .tick { animation: none; }
}
@media (width <= 600px) {
  .gallery .stats .headline { grid-template-columns: 1fr 1fr; }
  .gallery .stats .headline dd { font-size: 1.6em; }
}

/* -------------------------------------------------------------------------
   6.1 Chat: the log fills the window between the rails; the composer
   floats over its bottom edge.
   ------------------------------------------------------------------------- */

.chat .log {
  padding: var(--top-overlay-height) var(--spacing-lg) var(--bottom-overlay-height);
}
.chat .msg { padding: var(--spacing-sm) 0; overflow-wrap: anywhere; }
.chat .msg .meta { display: flex; align-items: baseline; gap: 0.44em; }
.chat .msg b { font-weight: inherit; }
.chat .msg time { color: var(--muted-color); }
.chat .msg .body { line-height: 1.35; }
.chat .hint { color: var(--muted-color); }

/* search: a pill input leading the rail, the counts flowing after it */
.chat .search { flex: 0 0 auto; }
.chat .search input { width: min(11em, 36vw); }
.chat #counts { display: contents; }
/* results: the caption of what the index did, words it matched in a
   wash, and a message arriving into them eased in */
.chat .found { color: var(--muted-color); padding-bottom: var(--spacing-sm); }
.chat mark { background: var(--wash-color); color: inherit; border-radius: 0.25em; padding: 0 0.1em; }
.chat .fresh { animation: chat-arrive 0.6s ease-out; }
@keyframes chat-arrive { from { opacity: 0; transform: translateY(-0.4em); } }
@media (prefers-reduced-motion: reduce) { .chat .fresh { animation: none; } }

/* the composer */
.chat #input-container form { display: flex; gap: 0.44em; align-items: flex-end; }
.chat #text { flex: 1 1 auto; min-width: 0; }

/* -------------------------------------------------------------------------
   6.2 Chess: bogathon's archive grid for squares, pieces as lettered
   discs sized and inked exactly as go's stones.
   ------------------------------------------------------------------------- */

/* the squares: flush tiles, no gap, no radius, eight of the board's nine
   units, centered in it */
.chess .board { display: grid; place-items: center; }
.chess .squares {
  width: calc(100% * 8 / 9);
  display: grid;
  grid-template: repeat(8, 1fr) / repeat(8, 1fr);
  aspect-ratio: 1;
  container-type: inline-size;
}
.chess .sq {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  padding: 0;
  border-radius: 0;
}
/* a square keeps its tint when pressed (buttons otherwise invert) */
.chess .sq, .chess .sq:active:enabled { background-color: var(--tile, var(--app-background)); color: var(--text-color); }
.chess .sq.dark { --tile: var(--square-tint); }
.chess .sq:focus-visible { outline: solid 1px; outline-offset: -4px; }

/* the pieces: discs marked with their letter, ink or paper in an ink ring,
   --piece of the square */
.chess .piece {
  --ink: var(--text-color);
  --paper: var(--app-background);
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border-radius: 50%;
  line-height: 1;
}
.chess .sq .piece { width: calc(100% * var(--piece)); font-size: calc(12.5cqi * var(--piece) * 0.5); }
.chess .piece.black { background: var(--ink); color: var(--paper); }
.chess .piece.white { background: var(--paper); color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }

/* the selected piece: a ring around its disc, the strokes' weight, set off
   from the disc so it reads as a halo for either side */
.chess .sq.sel .piece { outline: 1px solid var(--text-color); outline-offset: 0.12em; }

/* legal targets: a stroke around each square the piece can move to, one
   line weight throughout. Each target draws its top and left edges; `r`
   and `b` add the right and bottom ones where no neighbouring target will */
.chess .sq.to { box-shadow: inset 1px 1px var(--text-color); }
.chess .sq.to.r { box-shadow: inset 1px 1px var(--text-color), inset -1px 0 var(--text-color); }
.chess .sq.to.b { box-shadow: inset 1px 1px var(--text-color), inset 0 -1px var(--text-color); }
.chess .sq.to.r.b { box-shadow: inset 1px 1px var(--text-color), inset -1px -1px var(--text-color); }

/* while a piece is selected, a page-sized link under the board and the
   rails catches clicks anywhere else, and cancels */
.chess .cancel { position: fixed; inset: 0; cursor: default; }

/* captured pieces in the caption: the discs at text size */
.chess .glyphs { display: flex; gap: 0.15em; }
.chess .glyphs .piece { width: 1.25em; font-size: 0.8em; }

/* -------------------------------------------------------------------------
   6.3 Go: stroke-only lines, stones on their crossings.
   ------------------------------------------------------------------------- */

/* the board: nine lines each way, inset half a point so they cross at the
   81 intersections. The point buttons form a 9×9 grid over them, each
   centered on its intersection, so stones sit on the crossings */
.go .board {
  /* stone diameter: --piece of the line spacing */
  --stone: calc(var(--piece) * 100%);
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
}
.go .board::before {
  content: "";
  position: absolute;
  inset: calc(100% / 18);
  /* a line on the top and left of each of the 8×8 cells, and the last
     line on the right and bottom edges */
  background-image:
    linear-gradient(to right, var(--text-color) 1px, transparent 1px),
    linear-gradient(to bottom, var(--text-color) 1px, transparent 1px);
  background-size: 12.5% 12.5%;
  box-shadow: inset -1px -1px 0 var(--text-color);
}

/* the points */
.go .pt {
  --ink: var(--text-color);
  --paper: var(--app-background);
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: 0;
  background: none;
  color: var(--ink);
}
.go .pt:hover { opacity: 1; }
.go .pt:active { background: none; }
.go .pt:focus-visible { outline: solid 1px; outline-offset: -4px; border-radius: 50%; }
/* the stone (or star point) and the last move's dot share the cell */
.go .pt::before, .go .pt::after { content: ""; grid-area: 1 / 1; aspect-ratio: 1; border-radius: 50%; }
.go .pt::before { width: var(--stone); }
.go .pt.star::before { width: 14%; background: var(--ink); }
.go .pt.black::before { width: var(--stone); background: var(--ink); }
.go .pt.white::before { width: var(--stone); background: var(--paper); box-shadow: inset 0 0 0 1px var(--ink); }
/* the last move: a small dot of the other tone on its stone */
.go .pt.last::after { width: 24%; }
.go .pt.last.black::after { background: var(--paper); }
.go .pt.last.white::after { background: var(--ink); }
/* a faint ghost of the next stone on empty playable points */
.go .turn-black .pt:enabled:hover::before { width: var(--stone); background: var(--ink); opacity: 0.18; }
.go .turn-white .pt:enabled:hover::before { width: var(--stone); background: var(--paper); box-shadow: inset 0 0 0 1px var(--ink); opacity: 0.5; }

/* a stone at text size: Arizona's cap height, so it stands as tall as the
   letters beside it. Inked in the text's own color, so it follows a muted
   caption or an inverted chip */
.go .stone {
  flex: none;
  width: 0.72em;
  aspect-ratio: 1;
  border-radius: 50%;
}
.go .stone.black { background: currentColor; }
.go .stone.white { box-shadow: inset 0 0 0 1px currentColor; }

/* a pass placed no stone: it reads quieter than a play */
.go .pair .pass { color: var(--secondary-text-color); }


/* -------------------------------------------------------------------------
   6.4 Sheets: a grid filling the window between the rails and scrolling
   inside it, hairline gridlines, the selection drawn as a single stroke.
   The top rail is the formula bar; the bottom rail is two rows: the
   toolbar (led by Undo and Redo), then the sheet tabs.
   ------------------------------------------------------------------------- */

/* the bottom rail's height: chat's one row of pills plus a second one
   (a pill, 1.2em + 2 × 0.88em, and the rows' gap) */
.sheets { --rail: calc(var(--bottom-overlay-height) + 3.4em); }

/* the sheet: fixed between the rails, the grid taking whatever height the
   caption leaves */
.sheets main.sheet {
  position: fixed;
  inset: var(--top-overlay-height) 0 var(--rail) 0;
  display: flex;
  flex-direction: column;
  padding: 0 var(--spacing-lg);
}
/* the grid scrolls on its own; scroll padding keeps a selected cell clear
   of the sticky column letters and row numbers */
.sheets .grid {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  scroll-padding: 2em 0 0 3em;
  border-top: 1px solid var(--hairline-color);
  border-left: 1px solid var(--hairline-color);
}

/* the table: compact, borderless cells; each draws its right and bottom
   hairline, so a shared edge is one stroke. Separate borders, not
   collapsed, so the sticky headers keep theirs */
.sheets table {
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  user-select: none;
  -webkit-user-select: none;
}
.sheets th, .sheets td {
  height: 2em;
  padding: 0 0.6em;
  border: solid var(--hairline-color);
  border-width: 0 1px 1px 0;
  white-space: nowrap;
}
.sheets td {
  min-width: 7em;
  max-width: 18em;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: cell;
}

/* column letters and row numbers: muted, and stuck to the grid's top and
   left edges as it scrolls; the corner sits over both */
.sheets th {
  position: sticky;
  z-index: 1;
  background-color: var(--app-background);
  color: var(--muted-color);
  font-weight: inherit;
}
.sheets thead th { top: 0; }
.sheets tbody th { left: 0; min-width: 2.6em; text-align: right; }
.sheets thead th:first-child { left: 0; z-index: 2; }

/* values: numbers right-aligned unless the cell says otherwise, errors in
   the danger red. Bold is Arizona's weight axis, set like the base
   settings with a heavier "wght" */
.sheets td.num { text-align: right; }
.sheets td.al-l { text-align: left; }
.sheets td.al-c { text-align: center; }
.sheets td.al-r { text-align: right; }
.sheets td.b { font-variation-settings: "wdth" 100, "wght" 600, "SRFF" 33, "slnt" 0; }
.sheets td.i { font-style: italic; }
.sheets td.err { color: var(--danger); }

/* the selection: a range takes a quiet wash; the anchor cell a 1px ink
   stroke, one line weight, like chess's targets */
.sheets td.in { background-color: var(--wash-color); }
.sheets td.sel { box-shadow: inset 0 0 0 1px var(--text-color); }

/* a formula fold just re-evaluated to a new value: a tint that fades, so
   each change shows how far it reached */
.sheets td.fresh { animation: sheets-fresh 1.6s ease-out; }
@keyframes sheets-fresh { from { background-color: color-mix(in lab, var(--text-color) 16%, var(--app-background)); } }
@media (prefers-reduced-motion: reduce) { .sheets td.fresh { animation: none; } }

/* the caption under the grid: the sheet's stats, the selected column's,
   then the history scrubber at the right. In flow here, not hung off a
   board, so it drops the board caption's positioning */
.sheets .caption {
  position: static;
  translate: none;
  flex-wrap: wrap;
  row-gap: 0.3em;
  padding: var(--spacing-sm) 0;
}
.sheets .caption .recalc { color: var(--secondary-text-color); }
.sheets .history { display: flex; align-items: center; gap: 0.6em; margin-left: auto; }
.sheets .history .label { max-width: 16em; overflow: hidden; text-overflow: ellipsis; color: var(--placeholder-color); }
/* the scrubber is a bare range, inked like the text, not a pill */
.sheets .history input[type="range"] {
  width: 9em;
  padding: 0;
  background: none;
  border-radius: 0;
  accent-color: var(--text-color);
}

/* pills that are "on": the current sheet's tab, and Bold or Italic when
   the selected cell has it. Inverted, like the current move chip */
.sheets .pill[aria-current],
.sheets .pill[aria-pressed="true"] {
  background-color: var(--active-background);
  color: var(--active-text);
}

/* the top rail is the formula bar: Back to Gallery, the selection's
   name, its raw input filling the width, and search. The bottom rail is
   two `.pills` rows, scrolling sideways: the toolbar, then the tabs */
.sheets #input-container { display: flex; flex-direction: column; gap: 0.44em; }
.sheets .bar { display: flex; gap: 0.44em; }
.sheets #name { min-width: 4.5em; text-align: center; color: var(--secondary-text-color); font-variant-numeric: tabular-nums; }
.sheets #raw { flex: 1 1 auto; min-width: 0; }
.sheets .find { position: relative; flex: 0 1 12em; min-width: 6em; }
.sheets #find { width: 100%; appearance: none; -webkit-appearance: none; }

/* search hits: a wash-filled list dropping from the search pill, each hit
   its cell (muted) and its text */
.sheets #hits {
  position: absolute;
  right: 0;
  top: calc(100% + 0.44em);
  width: min(24em, 90vw);
  padding: 0.44em 0;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  font-size: 0.82em;
}
.sheets #hits:empty { display: none; }
.sheets #hits a, .sheets #hits .none {
  display: block;
  padding: 0.4em var(--control-inline-padding);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-decoration: none;
}
.sheets #hits a:hover { opacity: 0.8; }
.sheets #hits span, .sheets #hits .none { color: var(--muted-color); }
@supports (corner-shape: squircle) {
  .sheets #hits { corner-shape: squircle; }
}


/* -------------------------------------------------------------------------
   6.5 Canvas: one shared canvas, several windows wide, running edge to
   edge under the rails. Each canvas pixel is a crisp block of screen
   pixels.
   ------------------------------------------------------------------------- */

/* the window is a viewport onto the canvas: it scrolls corner to corner,
   no padding or frame, the rails floating over its edges */
.canvas main {
  position: fixed;
  inset: 0;
  overflow: auto;
  scrollbar-width: none;
}
.canvas main::-webkit-scrollbar { display: none; }
/* each canvas pixel is 3 screen pixels, nearest-neighbour, never blurred;
   `--size` (its width in canvas pixels) comes from the server */
.canvas #art {
  --scale: 3;
  display: block;
  width: calc(var(--size) * var(--scale) * 1px);
  height: calc(var(--size) * var(--scale) * 1px);
  image-rendering: pixelated;
  cursor: crosshair;
  /* the pointer draws; it doesn't scroll or zoom the page */
  touch-action: none;
}
/* the ink chip is the shared tank chip (5.5) */


/* -------------------------------------------------------------------------
   6.6 Derby: a top-down arena between the rails, scaled to fit. The
   track's shapes come from the server (one geometry for physics and
   drawing); every color here is a token, so it reads in either theme.
   ------------------------------------------------------------------------- */

/* the arena fills the space between the rails, centred, with the hint
   captioned under it */
.derby main {
  position: fixed;
  inset: var(--top-overlay-height) var(--spacing-lg) var(--bottom-overlay-height);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
}
.derby #track { flex: 1 1 auto; min-height: 0; width: 100%; }
.derby .hint, .derby .keyboard { color: var(--muted-color); font-size: 0.82em; }
.derby .keyboard { display: none; margin: auto; }

/* the ground, quietest first: grass is the page with sparse tufts; sand
   a warm wash with grains; asphalt the wash with flecks; kerbs stripe a
   touch of red over the page; walls are hatched and outlined */
.derby .grass { fill: url(#grass); }
.derby .tuft { fill: none; stroke: var(--hairline-color); stroke-width: 0.8; }
.derby .sand { fill: url(#sand); }
.derby .sand-base { fill: color-mix(in oklch, var(--wash-color) 75%, oklch(75% 0.09 80)); }
.derby .grain { fill: var(--muted-color); opacity: 0.5; }
.derby .road, .derby .kerb { fill: none; stroke-linejoin: round; }
.derby .road { stroke: url(#asphalt); }
.derby .tar { fill: var(--wash-color); }
.derby .fleck { fill: var(--hairline-color); }
.derby .kerb { stroke: var(--app-background); }
.derby .kerb.stripes { stroke: color-mix(in oklch, var(--danger) 40%, var(--wash-color)); stroke-dasharray: 9 9; }
.derby .start { stroke: var(--text-color); stroke-width: 5; stroke-dasharray: 5 5; }
.derby .wall { fill: url(#hatch); stroke: var(--muted-color); stroke-width: 1; }
.derby .wall-base { fill: var(--wash-color); }
.derby .hatch { stroke: var(--muted-color); stroke-width: 1; }
.derby .barrel { fill: var(--app-background); stroke: var(--text-color); stroke-width: 1.5; }

/* cars: a body in the driver's color (`--c`) fading towards the ground
   as it takes damage (`--wear`), a windscreen towards the nose, and a
   ring around your own */
.derby .car .body { fill: var(--c); opacity: var(--wear, 1); }
.derby .car .glass { fill: var(--app-background); opacity: 0.85; }
.derby .car .me { fill: none; stroke: var(--text-color); stroke-width: 1.2; }
.derby .car.wrecked .body { stroke: var(--text-color); stroke-dasharray: 3 3; }

/* a destroyed car bursts: a flash and a ring, swelling and fading */
.derby .boom circle { transform-box: fill-box; transform-origin: center; animation: derby-boom 0.9s ease-out forwards; }
.derby .boom .flash { fill: color-mix(in oklch, var(--danger) 70%, var(--app-background)); }
.derby .boom .ring { fill: none; stroke: var(--danger); stroke-width: 2; animation-duration: 0.6s; }
@keyframes derby-boom {
  from { transform: scale(0.3); opacity: 1; }
  to { transform: scale(2.2); opacity: 0; }
}

/* the standings: one chip per driver in the bottom rail, a dot in their
   color, their numbers muted */
.derby .driver { display: flex; align-items: center; gap: 0.44em; font-size: 0.82em; }
.derby .driver i { width: 0.6em; aspect-ratio: 1; border-radius: 50%; background: var(--c); }
.derby .driver .n { color: var(--muted-color); }
.derby .pills .actions { font-variant-numeric: tabular-nums; }

/* no keyboard, no driving: on touch-only or narrow screens, just say so */
@media (hover: none), (width <= 600px) {
  .derby #track, .derby .hint, .derby #input-container { display: none; }
  .derby .keyboard { display: block; }
}


/* -------------------------------------------------------------------------
   6.7 Poll: a column of prose, forms and results under the top rail,
   laid out like the gallery's own page. Choices are wash-filled pills
   that invert when picked; results are bars on wash tracks and
   hairline tables, like sheets' grid.
   ------------------------------------------------------------------------- */

/* the column: the gallery's gutter, so text lines up with the rail */
.poll main {
  --gutter: calc(var(--spacing-lg) + var(--control-inline-padding));
  max-width: calc(46em + 2 * var(--gutter));
  padding: calc(var(--top-overlay-height) + var(--spacing-lg)) var(--gutter) calc(var(--spacing-lg) * 4);
}
/* the rail's title, cut like a pill without the fill (as the gallery's) */
.poll .title { padding: var(--spacing-md) var(--control-inline-padding); line-height: 1.2em; white-space: nowrap; font: inherit; }
.poll h1, .poll h2, .poll h3 { font-size: 1em; font-weight: inherit; }
.poll header h1 { font-size: 1.45em; line-height: 1.2; }
.poll header > * + * { margin-top: var(--spacing-sm); }
.poll header p, .poll .details { color: var(--secondary-text-color); line-height: 1.35; }
.poll .details > * + * { margin-top: var(--spacing-sm); }
.poll section, .poll #live { margin-top: calc(var(--spacing-lg) * 2.5); }
.poll section > h2 { color: var(--muted-color); margin-bottom: var(--spacing-md); }
.poll .meta, .poll .muted, .poll .hint { color: var(--muted-color); }
.poll .meta, .poll .hint { font-size: 0.82em; line-height: 1.35; }
.poll .hint + *, .poll * + .hint { margin-top: var(--spacing-sm); }
.poll .alert { margin-top: var(--spacing-md); }
.poll time { font-variant-numeric: tabular-nums; }

/* the list: the gallery's cards, a search pill above them */
.poll .search { margin-top: var(--spacing-md); }
.poll .search input { width: min(100%, 22em); }
.poll .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(16em, 1fr)); gap: 0.44em; }
.poll .card {
  position: relative;
  height: 100%;
  padding: calc(var(--control-inline-padding) * 1.2) var(--control-inline-padding);
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  transition: opacity 0.2s ease;
}
.poll .card > * + * { margin-top: var(--spacing-sm); }
.poll .card p { line-height: 1.35; overflow-wrap: anywhere; }
.poll .card a { text-decoration: none; }
.poll .card a::after { content: ""; position: absolute; inset: 0; }
.poll .card:hover { opacity: 0.8; }
.poll .cards .none { color: var(--muted-color); }

/* --- fields: a muted caption over a pill-cut control. Textareas and
   selects take the same cut as inputs (5.2 covers only those) --- */
.poll textarea, .poll select {
  border: none;
  outline: none;
  font: inherit;
  font-variation-settings: inherit;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  color: var(--text-color);
  line-height: 1.35;
  padding: var(--spacing-md) var(--control-inline-padding);
}
.poll textarea { resize: vertical; display: block; }
.poll select { appearance: none; -webkit-appearance: none; cursor: pointer; line-height: 1.2em; }
.poll input:focus-visible, .poll textarea:focus-visible, .poll select:focus-visible { box-shadow: inset 0 0 0 1px var(--text-color); }
.poll .field { display: grid; gap: 0.3em; align-content: start; }
.poll .field > span { color: var(--muted-color); font-size: 0.82em; }
.poll .field input, .poll .field textarea, .poll .field select { width: 100%; min-width: 0; }
/* fields side by side where they fit; a wide set stacks */
.poll .fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(11em, 1fr)); gap: var(--spacing-md) 0.44em; }
.poll .fields.wide { grid-template-columns: 1fr; }
.poll .checks { display: grid; gap: var(--spacing-sm); margin-top: var(--spacing-md); grid-column: 1 / -1; }
.poll .check { display: flex; align-items: baseline; gap: 0.5em; line-height: 1.35; cursor: pointer; }
.poll input[type="checkbox"], .poll input[type="radio"] { accent-color: var(--text-color); padding: 0; }

/* --- the editor --- */
/* the methods: one pill each, the chosen one inverted like a current
   move chip; a method fixed by votes is faded */
.poll .methods { display: grid; gap: var(--spacing-sm); }
.poll .methods .group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.44em; }
.poll .methods .group > .muted { width: 6em; font-size: 0.82em; }
.poll .pill[aria-current="true"] { background-color: var(--active-background); color: var(--active-text); }
.poll .pill[aria-disabled="true"] { opacity: 0.4; }
/* option rows: label, meaning, and (with the script) move buttons */
.poll .rows { display: grid; gap: 0.44em; }
.poll .row { display: flex; gap: 0.44em; align-items: center; }
.poll .row input:not([type="hidden"]) { flex: 1 1 0; min-width: 0; }
.poll .row input[name="m"] { flex-grow: 1.4; }
.poll .row .move { display: flex; gap: 0.2em; }
.poll .row .move button, .poll .ranker li button { padding: var(--spacing-md) 0.75em; }
.poll .editor > p { margin-top: calc(var(--spacing-lg) * 2.5); }
.poll .fixed { display: grid; gap: var(--spacing-sm); line-height: 1.35; }

/* --- the ballot: no fieldset chrome; each group of choices a column of
   wash pills that invert when picked --- */
.poll fieldset { border: none; display: grid; gap: var(--spacing-lg); min-width: 0; }
.poll fieldset:disabled { opacity: 0.6; }
.poll .group { display: grid; gap: 0.44em; }
.poll legend { color: var(--muted-color); font-size: 0.82em; margin-bottom: var(--spacing-sm); }
.poll .choice {
  display: flex;
  align-items: baseline;
  gap: 0.6em;
  padding: var(--spacing-md) var(--control-inline-padding);
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  line-height: 1.2em;
  cursor: pointer;
}
.poll .choice small, .poll .slot small { display: block; margin-top: 0.2em; color: var(--muted-color); font-size: 0.82em; }
.poll .choice:has(:checked) { background-color: var(--active-background); color: var(--active-text); }
.poll .choice:has(:checked) small { color: inherit; opacity: 0.7; }
.poll .choice input { accent-color: currentColor; }
/* scores and points: the option, then a short number pill */
.poll .amount { display: flex; align-items: center; justify-content: space-between; gap: 1em; }
.poll .amount input { width: 6.5em; text-align: right; font-variant-numeric: tabular-nums; }
/* rankings: one select per place without the script; with it, the
   ranked list (numbered pills with move buttons) over the unranked */
.poll .ranking { display: grid; gap: 0.44em; }
.poll .ranking[hidden] { display: none; }
.poll .ranking label { display: flex; align-items: center; gap: 1em; }
.poll .ranking label span { width: 2.5em; color: var(--muted-color); font-variant-numeric: tabular-nums; }
.poll .ranker { display: grid; gap: var(--spacing-sm); }
.poll .ranker ol { display: grid; gap: 0.44em; }
.poll .ranker ol li {
  display: flex;
  align-items: center;
  gap: 0.2em;
  padding-left: var(--control-inline-padding);
  background-color: var(--active-background);
  color: var(--active-text);
  border-radius: var(--border-radius);
}
.poll .ranker .n { width: 1.6em; opacity: 0.6; font-variant-numeric: tabular-nums; }
.poll .ranker .name { flex: 1 1 auto; }
.poll .ranker ol li button { background: none; color: inherit; }
.poll .ranker ul { display: flex; flex-wrap: wrap; gap: 0.44em; }
/* time slots: the slot, then a segmented pill of three choices */
.poll .slot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--spacing-sm) 1em; }
.poll .segments { display: flex; overflow: hidden; background-color: var(--wash-color); border-radius: var(--border-radius); }
.poll .segments label { position: relative; cursor: pointer; }
.poll .segments input { position: absolute; opacity: 0; }
.poll .segments span { display: block; padding: var(--spacing-md) 0.9em; line-height: 1.2em; font-size: 0.82em; }
.poll .segments label + label span { border-left: 1px solid var(--hairline-color); }
.poll .segments label:has(:checked) span { background-color: var(--active-background); color: var(--active-text); }
.poll .segments label:has(:focus-visible) span { box-shadow: inset 0 0 0 1px var(--text-color); }

/* --- results: a bar per option (name | track | value), the method's
   table, and for elections the rounds --- */
.poll .outcome { line-height: 1.35; }
.poll .outcome strong { font-weight: inherit; }
.poll .outcome .muted { font-size: 0.82em; }
.poll .bars { display: grid; gap: var(--spacing-sm); margin-top: var(--spacing-md); }
.poll .bars li { display: grid; grid-template-columns: minmax(6em, 16em) 1fr 5.5em; align-items: center; gap: 1em; }
.poll .bars .name { line-height: 1.2; overflow-wrap: anywhere; }
.poll .bars .value { color: var(--muted-color); font-size: 0.82em; text-align: right; font-variant-numeric: tabular-nums; }
.poll .track { display: flex; height: 0.7em; overflow: hidden; background-color: var(--wash-color); border-radius: var(--border-radius); }
.poll .track i, .poll .legend i { display: block; background-color: var(--active-background); }
.poll .track i.if-needed, .poll .legend i.if-needed { background-color: var(--muted-color); }
.poll .bars .elected .name { text-decoration: underline; text-underline-offset: 0.2em; }
.poll .bars .eliminated, .poll .bars .not-elected { color: var(--secondary-text-color); }
.poll .legend i { display: inline-block; width: 0.7em; aspect-ratio: 1; border-radius: 50%; vertical-align: -0.05em; }
.poll .legend i.unavailable { background-color: var(--wash-color); }
/* tables: sheets' hairline grid, scrolling sideways when wide */
.poll .table { overflow-x: auto; margin-top: var(--spacing-md); }
.poll table { border-collapse: collapse; font-size: 0.82em; font-variant-numeric: tabular-nums; }
.poll th, .poll td { padding: 0.45em 1em 0.45em 0; text-align: left; border-bottom: 1px solid var(--hairline-color); white-space: nowrap; }
.poll th { color: var(--muted-color); font-weight: inherit; }
.poll .rounds td { text-align: right; }
.poll .rounds td.won { font-variation-settings: "wdth" 100, "wght" 600, "SRFF" 33, "slnt" 0; }
.poll .rounds td.out { color: var(--muted-color); text-decoration: line-through; }
.poll details { margin-top: var(--spacing-md); }
.poll summary { color: var(--muted-color); cursor: pointer; font-size: 0.82em; }
.poll .reasons { display: grid; gap: var(--spacing-sm); margin-top: var(--spacing-sm); line-height: 1.35; overflow-wrap: anywhere; }
.poll .reasons strong { font-weight: inherit; color: var(--muted-color); }

/* --- the history: newest first, each editor's id in their color --- */
.poll .history ol { display: grid; gap: 0.3em; margin-top: var(--spacing-md); font-size: 0.82em; line-height: 1.35; }
.poll .who { display: inline-flex; align-items: center; gap: 0.35em; font-variant-numeric: tabular-nums; }
.poll .who::before { content: ""; width: 0.6em; aspect-ratio: 1; border-radius: 50%; background: var(--c); }

@supports (corner-shape: squircle) {
  .poll textarea, .poll select, .poll .choice, .poll .segments, .poll .ranker ol li { corner-shape: squircle; }
}

/* narrow screens: rows and bars stack where they'd crowd */
@media (width <= 600px) {
  .poll .row { flex-wrap: wrap; }
  .poll .row input:not([type="hidden"]) { flex-basis: 100%; }
  .poll .bars li { grid-template-columns: 1fr 4.5em; }
  .poll .bars .track { grid-column: 1 / -1; grid-row: 2; }
  .poll .methods .group > .muted { width: 100%; }
}


/* -------------------------------------------------------------------------
   6.8 Sequencer: a piano roll on the board frame, 16 steps by 25 pitches.
   Each cell is a rounded square at --piece of its unit, lit in its
   author's color; the C rows are tinted faintly so the octaves read, the
   way go marks its star points, and the playhead is a tinted column.
   ------------------------------------------------------------------------- */

/* the unit: as large as fits between the rails (and above the Sound
   drawer, while it's open) and across the window, with room for the
   boundary on every side */
.sequencer .game {
  --drawer: 0px;
  --unit: min(2em,
    (100dvh - 2 * var(--overlay-height) - 5em - var(--drawer)) / (25 + 2 * var(--piece)),
    (100vw - 2 * var(--spacing-lg)) / (16 + 2 * var(--piece)));
  --board: calc(16 * var(--unit));
}
.sequencer .board { aspect-ratio: 16 / 25; display: grid; grid-template-columns: repeat(16, 1fr); }
.sequencer:has(details.sound[open]) .game { --drawer: 8.5em; padding-bottom: calc(var(--overlay-height) + var(--drawer)); }

/* the cells: flush tiles, tinted on the C rows; the note is drawn
   inside, wash when dark, its author's color when lit. A tile keeps its
   tint when pressed (buttons otherwise invert), as chess's squares do */
.sequencer .cell {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  padding: 0;
  border-radius: 0;
}
.sequencer .cell, .sequencer .cell:active:enabled { background-color: var(--row, transparent); }
.sequencer .cell.c { --row: color-mix(in oklch, var(--text-color) 5%, transparent); }
/* with a kit, rows are drum pieces: the octave tint gives way to bands,
   every other piece tinted, so each piece's rows read together */
.sequencer .kit .cell.c { --row: transparent; }
.sequencer .kit .cell.b { --row: color-mix(in oklch, var(--text-color) 5%, transparent); }
.sequencer .cell::before {
  content: "";
  width: calc(100% * var(--piece));
  aspect-ratio: 1;
  border-radius: 24%;
  background: var(--wash-color);
}
.sequencer .cell.on::before { background: var(--c); }
.sequencer .cell:hover:enabled { opacity: 1; }
.sequencer .cell:hover::before { box-shadow: inset 0 0 0 1px var(--text-color); }
.sequencer .cell:focus-visible { outline: solid 1px; outline-offset: -2px; }

/* the playhead: a tinted column under the cells, moved by the script
   (`--step`); without it, there's no beat to show */
.sequencer .playhead {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(100% / 16 * var(--step, 0));
  width: calc(100% / 16);
  background: var(--square-tint);
  border-radius: calc(var(--unit) * 0.3);
  pointer-events: none;
}
.sequencer .playhead[hidden] { display: none; }

/* the bottom rail: the time controls (a form of their own), the Sound
   pill, and the budget chip at the right end */
.sequencer .moves .tank { margin-left: auto; }
.sequencer .moves details > summary { list-style: none; cursor: pointer; }
.sequencer .moves details > summary::-webkit-details-marker { display: none; }
.sequencer .moves details[open] > summary { background-color: var(--active-background); color: var(--active-text); }

/* the Sound drawer: a row of groups just above the bottom rail, one per
   stage of the player's chain, each a wash card of small labelled
   controls (two columns where there are more than two). It scrolls
   sideways when the window is narrower than the row */
.sequencer .drawer {
  position: fixed;
  left: var(--spacing-lg);
  right: var(--spacing-lg);
  bottom: calc(var(--bottom-overlay-height) + env(safe-area-inset-bottom));
  display: flex;
  gap: 0.44em;
  overflow-x: auto;
  scrollbar-width: none;
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
}
.sequencer .drawer::-webkit-scrollbar { display: none; }
.sequencer .drawer .group {
  flex: none;
  display: grid;
  grid-template-columns: repeat(2, auto);
  align-content: start;
  gap: 0.35em 1.2em;
  padding: var(--spacing-md) var(--control-inline-padding);
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
}
.sequencer .drawer .group[hidden], .sequencer .drawer .control[hidden] { display: none; }
.sequencer .drawer .title { grid-column: 1 / -1; color: var(--muted-color); }
.sequencer .control { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 0.1em 0.5em; width: 7.5em; }
.sequencer .control > :last-child { grid-column: 1 / -1; }
.sequencer .control output { color: var(--secondary-text-color); }
.sequencer .drawer input[type="range"] {
  width: 100%;
  padding: 0;
  background: none;
  border-radius: 0;
  accent-color: var(--text-color);
}
/* selects take the pill's cut (5.2 covers only inputs and buttons) */
.sequencer select {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  font: inherit;
  font-variation-settings: inherit;
  background-color: var(--app-background);
  border-radius: var(--border-radius);
  color: var(--text-color);
  line-height: 1.2em;
  padding: 0.35em 0.8em;
  cursor: pointer;
}
.sequencer select.pill { padding: var(--spacing-md) var(--control-inline-padding); }
.sequencer .drawer .save { justify-self: start; }
@supports (corner-shape: squircle) {
  .sequencer select, .sequencer .drawer .group { corner-shape: squircle; }
}
/* phones: the drawer overlays the grid, a scrolling column */
@media (width <= 600px) {
  .sequencer:has(details.sound[open]) .game { --drawer: 0px; padding-bottom: var(--overlay-height); }
  .sequencer .drawer {
    flex-direction: column;
    right: auto;
    width: calc(100vw - 2 * var(--spacing-lg));
    max-height: 55dvh;
    overflow: hidden auto;
    bottom: calc(var(--bottom-overlay-height) + 0.8em);
  }
  /* the players' chips wrap rather than widen the page */
  .sequencer #who { flex-wrap: wrap; justify-content: center; row-gap: 0.2em; width: calc(100vw - 2 * var(--spacing-lg)); white-space: normal; }
  .sequencer .drawer .group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .sequencer .control { width: auto; min-width: 0; }
}

/* an author in a caption (sequencer's notes, piano's players, pads'
   hits): a dot in their color, then their label and count */
.sequencer .who, .piano .who, .synth .who, .pads .who { display: flex; align-items: center; gap: 0.35em; }
.sequencer .who::before, .piano .who::before, .synth .who::before, .pads .who::before {
  content: "";
  width: 0.6em;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--c);
}


/* -------------------------------------------------------------------------
   6.9 Piano: one shared 88-key keyboard on the board frame (Synth plays
   the same keyboard, so this section serves both). White keys
   are ivory with hairline edges, black keys ebony; a held key fills with
   its player's color, in bands when several hold it. Under the keys, each
   player's seat is a thin band in their color.
   ------------------------------------------------------------------------- */

/* the unit is a white key's width: the whole keyboard fits the window
   where it can; narrower than --min, it keeps --min and scrolls inside its
   frame instead. `--whites` (52) comes from the server */
:is(.piano, .synth) .game {
  --min: 0.9em;
  --fit: calc((100vw - 2 * var(--spacing-lg)) / (var(--whites, 52) + 2 * var(--piece)));
  --unit: max(var(--fit), var(--min));
  --board: min(52 * var(--unit), 100vw - 2 * var(--spacing-lg) - 2 * var(--piece) * var(--unit));
  /* keys six units long; under them, the seats' lanes */
  --keys: calc(6 * var(--unit));
  --lanes: 28px;
}
@media (width <= 800px) { :is(.piano, .synth) .game { --min: 2.2em; } }
:is(.piano, .synth) .board { aspect-ratio: auto; height: auto; }
:is(.piano, .synth) .scroller {
  height: calc(var(--keys) + var(--lanes));
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
:is(.piano, .synth) .scroller::-webkit-scrollbar { display: none; }
:is(.piano, .synth) .keyboard {
  position: relative;
  display: flex;
  align-items: flex-start;
  width: calc(var(--whites, 52) * var(--unit));
  height: 100%;
  user-select: none;
  -webkit-user-select: none;
}

/* ivory and ebony: the page's paper and ink, swapped in the dark so the
   keyboard still reads as a piano (light keys, dark sharps) */
:is(.piano, .synth) .board { --ivory: var(--app-background); --ebony: var(--text-color); }
@media (prefers-color-scheme: dark) {
  :is(.piano, .synth) .board { --ivory: var(--text-color); --ebony: var(--app-background); }
}

:is(.piano, .synth) .key {
  height: var(--keys);
  padding: 0 0 calc(var(--unit) * 0.3);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  border-radius: 0 0 calc(var(--unit) * 0.2) calc(var(--unit) * 0.2);
  touch-action: none;
  -webkit-touch-callout: none;
  transition: none;
}
:is(.piano, .synth) .key:hover:enabled { opacity: 1; }
:is(.piano, .synth) .key.white, :is(.piano, .synth) .key.white:active:enabled {
  flex: 1;
  background: var(--fill, var(--ivory));
  color: var(--muted-color);
  box-shadow: inset 0 0 0 0.5px var(--hairline-color);
}
/* a real keyboard's proportions: a black key is about 0.58 of a white
   key's width and 0.64 of its length */
:is(.piano, .synth) .key.black, :is(.piano, .synth) .key.black:active:enabled {
  position: absolute;
  z-index: 1;
  top: 0;
  left: calc(100% / var(--whites, 52) * var(--at));
  translate: -50%;
  width: calc(100% / var(--whites, 52) * 0.58);
  height: calc(var(--keys) * 0.64);
  background: var(--fill, var(--ebony));
  color: var(--secondary-text-color);
}
:is(.piano, .synth) .key.down { color: var(--app-background); }
/* the letter is sized on its own span: --unit is in ems, so the key itself
   keeps the board's font size */
:is(.piano, .synth) .key span { font-size: min(0.82em, calc(var(--unit) * 0.45)); }
:is(.piano, .synth) .key:focus-visible { outline: solid 1px var(--text-color); outline-offset: -4px; }
/* keys in the room's key (`.in`): your letters are underlined, thin and
   muted, easing in and out as the key changes. Keys outside your seat
   have no letter, so no mark */
:is(.piano, .synth) .key span {
  text-decoration: underline 1px transparent;
  text-underline-offset: 0.3em;
  transition: text-decoration-color 0.8s ease;
}
:is(.piano, .synth) .key.in span { text-decoration-color: var(--muted-color); }

/* the seats: two octaves and the C above (15 white keys, fewer at the
   bottom where a seat hangs off the piano) each, in the
   player's color. Yours is thicker, on the lane nearest the keys; others
   stack below it so overlapping seats stay visible */
:is(.piano, .synth) .seat {
  position: absolute;
  top: calc(var(--keys) + 4px + var(--lane) * 5px + min(var(--lane), 1) * 2px);
  left: calc(100% / var(--whites, 52) * var(--at));
  width: calc(100% / var(--whites, 52) * var(--span, 15));
  height: 2px;
  border-radius: 1px;
  background: var(--c);
  pointer-events: none;
}
:is(.piano, .synth) .seat.mine { height: 4px; border-radius: 2px; }

/* a key the loop is playing: a short muted bar along its foot, for as
   long as the note sounds */
:is(.piano, .synth) .key.white.looped { box-shadow: inset 0 0 0 0.5px var(--hairline-color), inset 0 -3px 0 var(--muted-color); }
:is(.piano, .synth) .key.black.looped { box-shadow: inset 0 -3px 0 var(--muted-color); }

/* the looper's row under the keys: the loop's length and progression, a
   chip per take in its player's color, and Clear. Across its top, a
   hairline fills as the loop goes round */
:is(.piano, .synth) .looper {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4em 1em;
  padding: calc(var(--unit) * 0.4) calc(var(--unit) * 0.2);
  border-top: 1px solid var(--hairline-color);
  color: var(--muted-color);
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
}
:is(.piano, .synth) .looper .pos {
  position: absolute;
  top: -1px;
  left: 0;
  width: calc(var(--pos, 0) * 100%);
  height: 1px;
  background: var(--secondary-text-color);
  pointer-events: none;
}
:is(.piano, .synth) .looper:not(.on) :is(.pos, .clear) { display: none; }
:is(.piano, .synth) .looper .loop { display: flex; gap: 1em; white-space: nowrap; }
:is(.piano, .synth) #progression { color: var(--secondary-text-color); }
:is(.piano, .synth) #takes { display: flex; flex-wrap: wrap; gap: 0.4em; flex: 1; }
/* a take: its player's dot, label and notes; muted, the dot empties */
:is(.piano, .synth) .take { display: inline-flex; align-items: center; border-radius: var(--border-radius); background: var(--wash-color); }
:is(.piano, .synth) .take button { background: none; padding: 0.25em 0.6em; }
:is(.piano, .synth) .take button[name=mute]::before {
  content: "";
  display: inline-block;
  width: 0.55em;
  aspect-ratio: 1;
  margin-right: 0.4em;
  border-radius: 50%;
  background: var(--c);
  vertical-align: 0.05em;
}
:is(.piano, .synth) .take button[name=drop] { padding-left: 0; color: var(--muted-color); }
:is(.piano, .synth) .take.muted button[name=mute] { color: var(--muted-color); }
:is(.piano, .synth) .take.muted button[name=mute]::before { background: none; box-shadow: inset 0 0 0 1px var(--c); }
:is(.piano, .synth) .looper .clear { margin-left: auto; padding: var(--spacing-sm) var(--control-inline-padding); }

/* Record: while recording, a small dot in the danger color, and over a
   loop, the pill fills as the take runs */
:is(.piano, .synth) #record.rec {
  background: linear-gradient(to right, color-mix(in oklch, var(--danger) 18%, var(--wash-color)) var(--p, 0%), var(--wash-color) var(--p, 0%));
}
:is(.piano, .synth) #record.rec::before {
  content: "";
  display: inline-block;
  width: 0.5em;
  aspect-ratio: 1;
  margin-right: 0.45em;
  border-radius: 50%;
  background: var(--danger);
  vertical-align: 0.08em;
}

/* the keyboard hint only helps where there's a keyboard */
@media (hover: none) { :is(.piano, .synth) .hint { display: none; } }
:is(.piano, .synth) .who .range, :is(.piano, .synth) .who .n { color: var(--secondary-text-color); }
/* a player's part (their chord, or the tones they add to the room's)
   ends their chip, quietly; the key waits in the top caption until it's
   heard */
:is(.piano, .synth) .who .part { color: var(--muted-color); }
:is(.piano, .synth) .who .part:empty, :is(.piano, .synth) .caption [hidden] { display: none; }


/* -------------------------------------------------------------------------
   6.10 Synth: Piano's keyboard with a patch board under it, one body in
   one frame. The board is a single dense band of knobs, grouped like a
   hardware panel; each knob's arc wears the color of whoever set it, and
   a knob someone is turning right now is ringed in theirs.
   ------------------------------------------------------------------------- */

/* the frame grows to hold the board under the keys */
.synth .board { height: auto; }
.synth .panel {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: calc(var(--unit) * 0.4) calc(var(--unit) * 0.6);
  padding: calc(var(--unit) * 0.6) calc(var(--unit) * 0.2) calc(var(--unit) * 0.2);
  border-top: 1px solid var(--hairline-color);
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
}
.synth .section { display: grid; gap: 0.3em; align-content: start; }
.synth .section[hidden], .synth .knob[hidden] { display: none; }
.synth .section .title { color: var(--muted-color); white-space: nowrap; }
.synth .section .row { display: flex; gap: calc(var(--unit) * 0.15); }
/* whoever chose the preset, as a dot by its title */
.synth .by { display: inline-block; width: 0.55em; aspect-ratio: 1; border-radius: 50%; background: var(--c, transparent); vertical-align: 0.05em; }
/* selects take the pill's cut (5.2 covers only inputs and buttons) */
.synth select {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  font: inherit;
  font-variation-settings: inherit;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  color: var(--text-color);
  line-height: 1.2em;
  padding: var(--spacing-sm) var(--control-inline-padding);
  cursor: pointer;
}
.synth .panel .save { padding: var(--spacing-sm) var(--control-inline-padding); justify-self: start; }

/* a knob: an arc of 270°, its value drawn over the track in the color of
   whoever set it (ink until anyone has), its name and value under it */
.synth .knob {
  position: relative;
  display: grid;
  justify-items: center;
  width: calc(var(--unit) * 2);
  min-width: 3em;
  touch-action: none;
  cursor: ns-resize;
  user-select: none;
  -webkit-user-select: none;
}
.synth .knob svg { width: 78%; aspect-ratio: 1; overflow: visible; }
.synth .knob circle { fill: none; transform: rotate(135deg); transform-origin: center; }
.synth .knob .track { stroke: var(--hairline-color); stroke-width: 3; stroke-dasharray: 75 100; }
.synth .knob .value { stroke: var(--c, var(--text-color)); stroke-width: 3; stroke-linecap: round; stroke-dasharray: calc(var(--v) * 75) 100; }
.synth .knob .ring { stroke: transparent; stroke-width: 1.5; }
.synth .knob.held .ring { stroke: var(--me); }
.synth .knob.grabbed .ring { stroke: var(--g); }
.synth .knob:focus-within .ring { stroke: var(--text-color); }
.synth .knob .name, .synth .knob output { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.25; }
.synth .knob output { color: var(--secondary-text-color); font-size: 0.9em; }
/* without the script the range inputs are the controls; with it they're
   the knobs' focusable, arrow-key-driven insides */
.synth .knob input { width: 100%; padding: 0; background: none; border-radius: 0; accent-color: var(--text-color); }
.synth .panel.js .knob input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; pointer-events: none; }

/* a player's role, before their chip: tuning or playing */
.synth .who .role { color: var(--text-color); }

/* narrow screens: the keyboard scrolls in its frame, the board wraps
   under it a section at a time, smaller knobs, and the captions wrap
   rather than widen the page (with room above for the top one) */
@media (width <= 800px) {
  .synth .knob { width: 3.6em; }
  .synth .game { padding-top: calc(var(--overlay-height) + 3em); }
  :is(.piano, .synth) .caption { flex-wrap: wrap; justify-content: center; row-gap: 0.2em; width: calc(100vw - 2 * var(--spacing-lg)); white-space: normal; }
}


/* -------------------------------------------------------------------------
   6.11 Pads: a 4×4 grid of sample pads on the board frame, like an MPC.
   Each pad is a rounded square at --piece of its unit, wash when empty,
   tinted in the color of whoever loaded it; a hit flashes it in the
   player's color (the script animates that), and the selected pad is
   ringed as chess rings its selected piece.
   ------------------------------------------------------------------------- */

/* the unit is a pad's cell: as large as fits between the rails and
   across the window, with room for the boundary on every side. The
   boundary sits as far outside the outer pads as the pads sit apart
   (--gap), rather than a whole --piece out, so the grid reads as one
   instrument */
.pads .game {
  --gap: calc((1 - var(--piece)) * var(--unit));
  --unit: min(8em,
    (100dvh - 2 * var(--overlay-height) - 5em) / (4 + 2 * (1 - var(--piece))),
    (100vw - 2 * var(--spacing-lg)) / (4 + 2 * (1 - var(--piece))));
  --board: calc(4 * var(--unit));
}
.pads .game .board::after { inset: calc(-0.5 * var(--gap)); }
.pads .game .caption { top: calc(100% + 0.5 * var(--gap) + var(--spacing-md)); }
.pads .game .caption.top { top: auto; bottom: calc(100% + 0.5 * var(--gap) + var(--spacing-md)); }
.pads .board {
  display: grid;
  grid-template: repeat(4, 1fr) / repeat(4, 1fr);
  place-items: center;
  user-select: none;
  -webkit-user-select: none;
}

/* a pad: its key in the top corner, its sample's name at the foot, two
   lines at most. It keeps its fill when pressed (buttons otherwise
   invert): the flash is the feedback */
.pads .pad {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: calc(100% * var(--piece));
  aspect-ratio: 1;
  padding: calc(var(--unit) * 0.08) calc(var(--unit) * 0.1);
  border-radius: 18%;
  overflow: hidden;
  text-align: left;
  font-size: min(0.82em, calc(var(--unit) * 0.14));
  line-height: 1.15;
  touch-action: none;
  -webkit-touch-callout: none;
  transition: none;
}
.pads .pad, .pads .pad:active:enabled { background-color: var(--wash-color); color: var(--text-color); }
.pads .pad.loaded, .pads .pad.loaded:active:enabled { background-color: color-mix(in oklch, var(--c) 40%, var(--app-background)); }
.pads .pad:hover:enabled { opacity: 1; }
.pads .pad .key { color: var(--secondary-text-color); }
.pads .pad .name {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.pads .pad.sel { outline: 1px solid var(--text-color); outline-offset: calc(var(--unit) * 0.06); }
.pads .pad.drop { outline: 1px dashed var(--text-color); outline-offset: calc(var(--unit) * 0.06); }
.pads .pad:focus-visible { outline: 1px solid var(--text-color); outline-offset: -4px; }
/* a sample on its way: fetching and decoding, or being loaded */
.pads .pad.loading { animation: pads-loading 0.8s ease-in-out infinite alternate; }
@keyframes pads-loading { to { opacity: 0.4; } }

.pads .caption [hidden] { display: none; }
.pads .who .n { color: var(--secondary-text-color); }
/* the keyboard hint only helps where there's a keyboard */
@media (hover: none) { .pads .hint { display: none; } }
@media (width <= 800px) {
  .pads .caption { flex-wrap: wrap; justify-content: center; row-gap: 0.2em; width: calc(100vw - 2 * var(--spacing-lg)); white-space: normal; }
}

/* -------------------------------------------------------------------------
   6.12 Market: a trading desk between the rails, denser than a board.
   Three columns (the watchlist; the chosen company, its chart, book,
   trades and the ticket; your account and the leaderboards), two on a
   narrow window and one on a phone; the news runs along the bottom rail.
   Tables are hairline-ruled like sheets'. Gains and losses read apart by
   sign and shape first (a hollow candle rose, a filled one fell) and a
   low-chroma tint second, pinned per theme so both read on either.
   ------------------------------------------------------------------------- */

.market {
  --gain: light-dark(oklch(50% 0.09 155), oklch(78% 0.09 155));
  --loss: light-dark(oklch(53% 0.12 28), oklch(74% 0.11 28));
}
.market h1, .market h2, .market h3 { font-size: 1em; font-weight: inherit; }
.market .up { color: var(--gain); }
.market .down { color: var(--loss); }
.market .muted, .market .flat { color: var(--muted-color); }
.market .alert { color: var(--danger); }
.market .desk {
  display: grid;
  grid-template-columns: minmax(13em, 16em) minmax(0, 1fr) minmax(16em, 20em);
  gap: calc(var(--spacing-lg) * 2.5);
  align-items: start;
  padding: calc(var(--top-overlay-height) + var(--spacing-lg)) calc(var(--spacing-lg) + var(--control-inline-padding)) calc(var(--bottom-overlay-height) + var(--spacing-lg) * 2);
  font-variant-numeric: tabular-nums;
}
/* section titles: quiet, like the gallery's */
.market .ranks h2, .market .account h2, .market .trades h2, .market caption {
  color: var(--muted-color);
  text-align: left;
  margin-bottom: var(--spacing-sm);
}
.market .dot { display: inline-block; flex: none; width: 0.6em; aspect-ratio: 1; border-radius: 50%; background: var(--c); }

/* --- the watchlist: a row per company, the chosen one washed --- */
.market .index { display: flex; justify-content: space-between; align-items: baseline; padding: 0 0.7em var(--spacing-md); }
.market .index b { font-weight: inherit; }
.market .tickers { display: grid; gap: 0.15em; }
.market .tickers a {
  display: grid;
  grid-template-columns: 2.8em minmax(2em, 1fr) 4.4em 4.2em;
  align-items: center;
  gap: 0.5em;
  padding: 0.5em 0.7em;
  border-radius: var(--border-radius);
  font-size: 0.86em;
  text-decoration: none;
  transition: background-color 0.2s ease;
}
.market .tickers a:hover, .market .tickers a[aria-current] { background-color: var(--wash-color); }
.market .tickers .px, .market .tickers .chg { text-align: right; }
.market .spark { display: block; width: 100%; height: 1.3em; overflow: visible; color: var(--muted-color); }
.market .spark path { fill: none; stroke: currentColor; stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.market .tickers a:has(.chg.up) .spark { color: var(--gain); }
.market .tickers a:has(.chg.down) .spark { color: var(--loss); }
.market .note { margin-top: var(--spacing-lg); padding: 0 0.7em; color: var(--muted-color); font-size: 0.72em; line-height: 1.4; }

/* --- the chosen company --- */
.market .quote h1 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3em 0.6em; }
.market .quote .sector { color: var(--muted-color); font-size: 0.82em; }
.market .quote .big { margin-top: 0.15em; font-size: 2.2em; line-height: 1.15; }
.market .quote .big .chg { font-size: 0.45em; vertical-align: 0.4em; }
.market .quote .stats { display: flex; flex-wrap: wrap; gap: 0.2em 1.2em; margin-top: 0.3em; color: var(--secondary-text-color); font-size: 0.82em; }
.market figure.chart { margin: var(--spacing-lg) 0; }
.market svg.chart { display: block; width: 100%; height: auto; }
.market svg.chart text { fill: var(--muted-color); font: inherit; font-size: 11px; }
.market svg.chart .grid { stroke: var(--hairline-color); stroke-width: 1; vector-effect: non-scaling-stroke; }
.market svg.chart g line { stroke-width: 1; vector-effect: non-scaling-stroke; }
.market svg.chart .up line, .market svg.chart .up rect { stroke: var(--gain); fill: var(--app-background); vector-effect: non-scaling-stroke; }
.market svg.chart .down line, .market svg.chart .down rect { stroke: var(--loss); fill: var(--loss); }
.market svg.chart rect.vol { fill: var(--hairline-color); stroke: none; }
.market .empty { padding: 3em 0; color: var(--muted-color); text-align: center; }

/* the book and the tape: hairline tables, each level barred by its size */
.market .panes { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 1.5em; align-items: start; }
.market .depth { display: grid; grid-template-columns: 1fr 1fr; gap: 1em; align-items: start; }
.market table { width: 100%; border-collapse: collapse; font-size: 0.82em; }
.market th { font-weight: inherit; color: var(--muted-color); text-align: right; padding: 0.2em 0; border-bottom: 1px solid var(--hairline-color); }
.market td { text-align: right; padding: 0.18em 0; }
.market th:first-child, .market td:first-child { text-align: left; }
.market .depth td:first-child { padding-left: 0.3em; }
.market .bids tr[style] { background: linear-gradient(to left, color-mix(in oklch, var(--gain) 16%, transparent) var(--w), transparent var(--w)); }
.market .asks tr[style] { background: linear-gradient(to left, color-mix(in oklch, var(--loss) 16%, transparent) var(--w), transparent var(--w)); }
.market .bids td:first-child { color: var(--gain); }
.market .asks td:first-child { color: var(--loss); }
.market .trades ol { display: grid; font-size: 0.82em; }
.market .trades h2 { margin: 0; padding: 0.2em 0; border-bottom: 1px solid var(--hairline-color); font-size: 0.82em; }
.market .trades li { display: grid; grid-template-columns: 1fr 1fr 2.4em; padding: 0.18em 0; text-align: right; }
.market .trades li .px { text-align: left; }
.market .trades li .q { color: var(--text-color); }
.market .trades li .ago { color: var(--muted-color); }

/* the ticket: segmented pills for side and type, then the numbers */
.market .ticket {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 0.44em;
  margin-top: calc(var(--spacing-lg) * 1.5);
  padding-top: calc(var(--spacing-lg) * 1.5);
  border-top: 1px solid var(--hairline-color);
}
.market .seg { display: flex; border: none; background-color: var(--wash-color); border-radius: var(--border-radius); }
.market .seg label { cursor: pointer; }
.market .seg input { position: absolute; opacity: 0; pointer-events: none; }
.market .seg span { display: block; padding: var(--spacing-md) var(--control-inline-padding); line-height: 1.2em; border-radius: var(--border-radius); }
.market .seg label:has(:checked) span { background-color: var(--active-background); color: var(--active-text); }
.market .seg label:has(:focus-visible) span { box-shadow: inset 0 0 0 1px var(--text-color); }
.market .field { display: grid; gap: 0.2em; }
.market .field[hidden] { display: none; }
.market .field > span { padding-left: var(--control-inline-padding); color: var(--muted-color); font-size: 0.72em; }
.market .field input { width: 6.5em; text-align: right; }
.market input:focus-visible { box-shadow: inset 0 0 0 1px var(--text-color); }
.market .ticket .msg { flex-basis: 100%; min-height: 1.4em; color: var(--secondary-text-color); font-size: 0.82em; line-height: 1.4; }
.market .ticket .msg.alert { color: var(--danger); }

/* --- the side: your account, then the leaderboards --- */
.market .side > * + * { margin-top: calc(var(--spacing-lg) * 2); }
.market .account h2 { display: flex; align-items: center; gap: 0.45em; }
.market .money { display: grid; grid-template-columns: 1fr auto; gap: 0.2em 1em; font-size: 0.9em; }
.market .money dt { color: var(--muted-color); }
.market .money dd { text-align: right; }
.market .account > p { margin-top: var(--spacing-md); font-size: 0.82em; line-height: 1.4; }
.market .holdings { margin-top: var(--spacing-md); }
.market .account h3, .market .ranks h3 { margin: var(--spacing-lg) 0 0.3em; color: var(--muted-color); font-size: 0.82em; }
.market .orders li { display: flex; align-items: center; justify-content: space-between; gap: 0.5em; padding: 0.15em 0; font-size: 0.86em; }
.market .orders .pill { padding: 0.35em 0.8em; font-size: 0.9em; }
.market .ranks ol { display: grid; gap: 0.2em; font-size: 0.86em; counter-reset: rank; }
.market .ranks li { display: flex; align-items: baseline; gap: 0.5em; }
.market .ranks li::before { counter-increment: rank; content: counter(rank); width: 1.2em; color: var(--muted-color); }
.market .ranks li.muted::before { content: none; }
.market .ranks .who { flex: 1; display: flex; align-items: center; gap: 0.45em; min-width: 0; white-space: nowrap; }
.market .ranks small { color: var(--muted-color); font-size: 0.85em; }
/* bots wear a hollow dot: they're accounts, not people */
.market .ranks .bot .dot { background: none; box-shadow: inset 0 0 0 1px var(--muted-color); }
.market .ranks .you { text-decoration: underline; text-decoration-color: var(--hairline-color); text-underline-offset: 0.25em; }

/* --- the news, along the bottom rail, the newest flashing in --- */
.market #news .headline { color: var(--secondary-text-color); font-size: 0.82em; }
.market #news .headline b { font-weight: inherit; color: var(--text-color); }
.market #news .fresh { animation: market-fresh 2s ease-out; }
@keyframes market-fresh { from { background-color: var(--active-background); color: var(--active-text); } }
@media (prefers-reduced-motion: reduce) { .market #news .fresh { animation: none; } }

/* a narrow window: the side goes under the other two */
@media (width <= 1100px) {
  .market .desk { grid-template-columns: minmax(12em, 15em) minmax(0, 1fr); }
  .market .side { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--spacing-lg) * 2.5); }
  .market .side > * + * { margin-top: 0; }
}
/* a phone: one column, the watchlist in two */
@media (width <= 700px) {
  .market .desk { grid-template-columns: minmax(0, 1fr); gap: calc(var(--spacing-lg) * 2); padding-inline: var(--spacing-lg); }
  .market .tickers { grid-template-columns: 1fr 1fr; }
  .market .tickers a { grid-template-columns: 2.8em 1fr 3.8em; }
  .market .tickers .spark { display: none; }
  .market .side, .market .panes { grid-template-columns: minmax(0, 1fr); }
  .market .quote .big { font-size: 1.8em; }
}


/* -------------------------------------------------------------------------
   6.13 Words: every page a place to write, with no title but its first
   word. Its plain text is a flat textarea in the page's own type, at
   reading width, growing with what's written, so the page scrolls as a
   whole; under it, what mentions the page. The top rail holds search;
   the bottom rail, every page this text makes, in the order its words
   first appear, each growing in as its word is finished, and a page
   just born flashing once.
   ------------------------------------------------------------------------- */

.words main.page {
  --gutter: calc(var(--spacing-lg) + var(--control-inline-padding));
  max-width: calc(40em + 2 * var(--gutter));
  margin: 0 auto;
  padding: calc(var(--top-overlay-height) + var(--spacing-lg) * 1.5) var(--gutter) calc(var(--bottom-overlay-height) + var(--spacing-lg) * 3);
  line-height: 1.5;
}
.words h2 { font-size: 1em; font-weight: inherit; }

/* the text: no box, no fill, no border; it reads as the page. It grows
   to fit (`field-sizing`, or the script where that's missing), and
   starts tall enough to click anywhere into */
.words #text {
  display: block;
  width: 100%;
  min-height: 9em;
  field-sizing: content;
  overflow: hidden;
  resize: none;
  padding: 0;
  border: none;
  border-radius: 0;
  outline: none;
  background: none;
  color: var(--text-color);
  font: inherit;
  font-variation-settings: inherit;
  line-height: 1.5;
  overflow-wrap: break-word;
}
.words #text::placeholder { color: var(--placeholder-color); }
.words #editor .under { display: flex; align-items: center; gap: var(--spacing-md); }
.words #editor .under:has(button) { margin-top: var(--spacing-md); }
.words #said:not(:empty) { margin-top: var(--spacing-sm); }
/* another writer moved this page's text away: a quiet note where to */
.words #said.note { color: var(--muted-color); font-size: 0.82em; }

/* the mentions: a count, then a snippet a line, the word lightly marked */
.words #mentions { margin-top: calc(var(--spacing-lg) * 2.5); }
.words #mentions h2 { color: var(--muted-color); margin-bottom: var(--spacing-sm); }
.words .related { color: var(--muted-color); font-size: 0.82em; margin-bottom: var(--spacing-lg); }
.words .related a { text-decoration: none; }
.words .related a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
.words .mentions li { border-top: 1px solid var(--hairline-color); }
.words .mentions a { display: block; padding: 0.55em 0; text-decoration: none; color: var(--secondary-text-color); }
.words .mentions a:hover { color: var(--text-color); }
.words .mentions .from { color: var(--text-color); margin-right: 0.3em; }
.words mark { background: none; color: var(--text-color); text-decoration: underline; text-decoration-color: var(--hairline-color); text-underline-offset: 0.18em; }
.words .more { margin-top: var(--spacing-md); }

/* the top rail, as Sheets' */
.words .bar { display: flex; gap: 0.44em; align-items: flex-start; }
.words .find { position: relative; flex: 0 1 12em; min-width: 6em; margin-left: auto; }
.words #find { width: 100%; appearance: none; -webkit-appearance: none; }
.words #hits {
  position: absolute;
  right: 0;
  top: calc(100% + 0.44em);
  width: min(26em, 90vw);
  padding: 0.44em 0;
  background-color: var(--wash-color);
  border-radius: var(--border-radius);
  font-size: 0.82em;
}
.words #hits:empty { display: none; }
.words #hits a, .words #hits .none {
  display: block;
  padding: 0.4em var(--control-inline-padding);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-decoration: none;
}
.words #hits a:hover { opacity: 0.8; }
.words #hits span, .words #hits .none { color: var(--muted-color); }
/* the rail: the page itself inverted, like Sheets' current tab; a tab
   new to it grows in, and one whose page was just born flashes an ink
   outline once */
.words #rail .pill[aria-current] { background-color: var(--active-background); color: var(--active-text); }
.words #rail .fresh { animation: words-in 0.35s ease-out; }
.words #rail .born { animation: words-in 0.35s ease-out, words-born 1.8s ease-out; }
@keyframes words-in { from { opacity: 0; scale: 0.9; } }
@keyframes words-born { from { box-shadow: inset 0 0 0 1px var(--text-color); } }
@media (prefers-reduced-motion: reduce) {
  .words #rail .fresh, .words #rail .born { animation: none; }
}
@supports (corner-shape: squircle) {
  .words #hits { corner-shape: squircle; }
}
/* phones: search wraps to a second row of the top rail */
@media (width <= 600px) {
  .words { --top-overlay-height: 8.3em; }
  .words .bar { flex-wrap: wrap; }
  .words .find { flex: 1 1 8em; margin-left: 0; }
}


/* -------------------------------------------------------------------------
   6.16 Voxels: a world in WebGL filling the window under the rails, a tab
   per strategy in the top rail, the tools along the bottom over the
   inspector, and a line about the world in the top right.
   ------------------------------------------------------------------------- */

.voxels #world {
  position: fixed;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  /* the pointer turns and zooms the world, never the page */
  touch-action: none;
}
.voxels .pill[aria-current] { background-color: var(--active-background); color: var(--active-text); }
.voxels .tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.44em; }
.voxels .mode[aria-pressed="true"] { background-color: var(--active-background); color: var(--active-text); }
.voxels .swatches { display: flex; gap: 0.35em; padding: 0 0.3em; }
/* a material: a dot of its colour, ringed when chosen */
.voxels .swatch {
  width: 1.9em;
  height: 1.9em;
  padding: 0;
  border-radius: 50%;
  background-color: var(--swatch);
}
.voxels .swatch[aria-pressed="true"] { outline: 2px solid var(--text-color); outline-offset: 2px; }
.voxels .size { display: flex; align-items: center; gap: 0.5em; }
.voxels .size input { width: 7em; padding: 0; background: none; accent-color: var(--text-color); }
.voxels #inspect { margin-top: var(--spacing-md); }
.voxels .about-world {
  position: fixed;
  top: var(--top-overlay-height);
  right: var(--spacing-lg);
  z-index: 5;
  max-width: 22em;
  color: var(--secondary-text-color);
  font-size: 0.9em;
  pointer-events: none;
}
.voxels .about-world p { margin: 0 0 0.4em; }
.voxels .about-world .count { color: var(--muted-color); font-variant-numeric: tabular-nums; }
.voxels .nogl { padding: var(--top-overlay-height) var(--spacing-lg); }


/* =========================================================================
   7. Phones
   ========================================================================= */

@media (width <= 600px) {
  /* phones read at 1rem; everything downstream is em-based */
  body { font-size: 1rem; }
  input, button, .pill { padding: 0.9em var(--control-inline-padding); }
  /* a tank chip's label carries its padding (5.5), not the chip */
  .tank { padding: 0; }
  .tank .label { padding: 0.9em var(--control-inline-padding); }
  /* Voxels: the world's line gives the world room (About says it too) */
  .voxels .about-world { display: none; }
}


/* -------------------------------------------------------------------------
   6.14 Blocks: a shared world of blocks, drawn by WebGL edge to edge under
   the rails. A crosshair at the centre; until the pointer is locked, a
   card of how to play over it. The bottom rail holds the hotbar: each
   block a square swatch, its top colour over its side's, and the one
   chosen ringed.
   ------------------------------------------------------------------------- */

.blocks main { position: fixed; inset: 0; }
/* the world runs edge to edge: no fade behind the top rail or the cards */
.blocks #top-container::before, .blocks #input-container::before { display: none; }
.blocks #world {
  display: block;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  /* the pointer looks around; it doesn't scroll or zoom the page */
  touch-action: none;
}

/* The title screen (`/blocks`, title.js): the match from above, turning,
   behind the name in huge white Arizona Flare (its SRFF axis at 25),
   exactly in the middle, and PLAY NOW under it; nothing else shows. Off
   it, the name is the page's heading for screen readers alone. */
.blocks #title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.blocks #play { display: none; }
html.titling body.blocks > :not(main),
html.titling body.blocks main > :not(#world, #title) { display: none !important; }
html.titling .blocks #world { cursor: default; }
html.titling .blocks #title {
  position: fixed;
  inset: 0;
  width: auto;
  height: auto;
  overflow: hidden;
  clip-path: none;
  white-space: normal;
  color: white;
  text-shadow: none;
}
html.titling .blocks #title h1 {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  font-size: min(21vw, 34vh);
  line-height: 0.9;
  /* Arizona Flare Thin */
  font-weight: 200;
  font-variation-settings: "wdth" 100, "wght" 200, "SRFF" 25, "slnt" 0;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
}
html.titling .blocks #title-count {
  display: block;
  position: absolute;
  left: 50%;
  top: calc(50% + min(21vw, 34vh) * 0.45 + max(0.5rem, 1.2vh));
  translate: -50% 0;
  font-size: clamp(1.05rem, 0.8rem + 1.1vw, 1.55rem);
  font-variation-settings: "wdth" 100, "wght" 500, "SRFF" 25, "slnt" 0;
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;
}
html.titling .blocks #title-count:empty { visibility: hidden; }
html.titling .blocks #play {
  display: block;
  position: absolute;
  left: 50%;
  top: calc(50% + min(21vw, 34vh) * 0.45 + max(3.2rem, 7vh));
  translate: -50% 0;
  padding: 0.7em 1.6em;
  border: none;
  border-radius: 999px;
  background-color: white;
  color: oklch(21.13% 0.0032 17.4);
  font-family: var(--font-family);
  font-size: clamp(1rem, 0.7rem + 1.4vw, 1.6rem);
  font-weight: 600;
  font-variation-settings: "wdth" 100, "wght" 600, "SRFF" 25, "slnt" 0;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-shadow: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: none;
  touch-action: manipulation;
  transition: scale 120ms ease-out, box-shadow 120ms ease-out;
}
html.titling .blocks #play:hover { scale: 1.04; }
html.titling .blocks #play:active { scale: 0.97; }
html.titling .blocks #play:focus-visible { outline: 3px solid white; outline-offset: 4px; }

/* two hairlines, inverted against whatever is behind them */
.blocks #crosshair {
  position: fixed;
  left: 50%;
  top: 50%;
  width: 1.1em;
  aspect-ratio: 1;
  translate: -50% -50%;
  pointer-events: none;
  mix-blend-mode: difference;
  background:
    linear-gradient(white, white) center / 100% 2px no-repeat,
    linear-gradient(white, white) center / 2px 100% no-repeat;
}

/* how to play: a wash card in the middle, which the click passes through */
.blocks #intro {
  position: fixed;
  z-index: 21;
  left: 50%;
  top: 50%;
  /* centred, unless it's taller than the window: then its top (the play
     line and the class picker) stays below the pills, the help running on */
  translate: -50% max(-50%, calc(-50vh + 6em));
  width: min(26em, calc(100% - 2 * var(--spacing-lg)));
  padding: var(--spacing-lg) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--app-overlay-solid);
  box-shadow: 0 0 0 1px var(--hairline-color);
  font-size: 0.86em;
  line-height: 1.45;
  text-align: center;
  color: var(--secondary-text-color);
  pointer-events: none;
}
/* the play line and the class grid: wide enough for the grid where
   there's room */
@media (width > 640px) {
  .blocks #intro { width: min(48em, calc(100% - 2 * var(--spacing-lg))); font-size: 0.8em; }
}
/* after the first click, only the line that asks for it back */
.blocks #intro.resume { width: auto; padding: var(--spacing-sm) var(--control-inline-padding); }
.blocks #intro.resume > :not(.go) { display: none; }
.blocks #settings .howto { margin-top: var(--spacing-sm); }
.blocks #settings .howto summary { cursor: pointer; color: var(--text-color); }
.blocks #intro[hidden] { display: none; }
.blocks #intro p + p { margin-top: var(--spacing-sm); }
.blocks #intro .go { color: var(--text-color); font-size: 1.16em; }
@supports (corner-shape: squircle) {
  .blocks #intro { corner-shape: squircle; }
}

/* the launcher chosen: the crosshair gains a ring */
.blocks #crosshair.armed {
  width: 1.6em;
  background:
    linear-gradient(white, white) center / 30% 2px no-repeat,
    linear-gradient(white, white) center / 2px 30% no-repeat,
    radial-gradient(circle, transparent 58%, white 60% 68%, transparent 70%);
}

/* something of yours landed (a blow): an X over the crosshair, briefly */
.blocks #crosshair.hit::after {
  content: "";
  position: absolute;
  inset: -0.35em;
  background:
    linear-gradient(45deg, transparent 46%, white 46% 54%, transparent 54%),
    linear-gradient(-45deg, transparent 46%, white 46% 54%, transparent 54%);
  animation: blocks-hit 0.3s ease-out forwards;
}
@keyframes blocks-hit { from { opacity: 1; scale: 1.25; } to { opacity: 0; scale: 1; } }
/* a shot of yours that looks, on your screen, to have landed: a faint X
   at once, before the server says so (it then shows the solid one) */
.blocks #crosshair.guess:not(.hit)::after {
  content: "";
  position: absolute;
  inset: -0.3em;
  background:
    linear-gradient(45deg, transparent 47%, white 47% 53%, transparent 53%),
    linear-gradient(-45deg, transparent 47%, white 47% 53%, transparent 53%);
  animation: blocks-guess 0.25s ease-out forwards;
}
@keyframes blocks-guess { from { opacity: 0.45; } to { opacity: 0; } }

/* fragged: the intro's card again, saying by whom, while the respawn waits */
.blocks #dead {
  position: fixed;
  z-index: 21;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: min(22em, calc(100% - 2 * var(--spacing-lg)));
  padding: var(--spacing-lg) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--app-overlay-solid);
  box-shadow: 0 0 0 1px var(--hairline-color);
  font-size: 0.86em;
  text-align: center;
  color: var(--secondary-text-color);
  pointer-events: none;
}
.blocks #dead[hidden] { display: none; }
/* dead, the how-to-play card waits */
.blocks main:has(#dead:not([hidden])) #intro { display: none; }
.blocks #dead .go { color: var(--text-color); font-size: 1.16em; margin-bottom: var(--spacing-sm); }

/* hurt: a red wash at the edges that fades */
.blocks #hurt {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at center, transparent 55%, oklch(55% 0.22 25 / 0.55));
}
.blocks #hurt.on { animation: blocks-hurt 0.6s ease-out; }
@keyframes blocks-hurt { from { opacity: 1; } to { opacity: 0; } }

/* aimed down iron sights or a rail: the crosshair draws in, a dot and
   short ticks; through a scope, the scope's reticle is the crosshair */
.blocks #crosshair.ads { scale: 0.55; opacity: 0.8; }
.blocks #crosshair.scoped { display: none; }
/* The scope (the carbine's): a circle of glass the height of the view,
   black beyond it, a reticle across it (fine lines, thicker posts out
   toward the rim, a dot in the middle), the lens darker toward its edge
   and a vignette round it. One fixed layer of gradients, its opacity the
   aim's blend (--k), so nothing is redrawn but a number. */
.blocks #scope {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: var(--k, 0);
  background:
    radial-gradient(circle closest-side, transparent 0 55%, rgb(0 0 0 / 0.18) 80%, rgb(0 0 0 / 0.55) 93.5%, black 94.5%),
    radial-gradient(circle closest-side at 47% 45%, rgb(255 255 255 / 0.05), transparent 40%);
}
.blocks #scope[hidden] { display: none; }
.blocks #scope .reticle {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 94vmin;
  aspect-ratio: 1;
  translate: -50% -50%;
  background:
    radial-gradient(circle, oklch(70% 0.2 25) 0 0.18vmin, transparent 0.24vmin),
    linear-gradient(black, black) center / 100% 0.12vmin no-repeat,
    linear-gradient(black, black) center / 0.12vmin 100% no-repeat,
    linear-gradient(90deg, black 0 30%, transparent 30% 70%, black 70%) center / 100% 0.7vmin no-repeat,
    linear-gradient(black 0 30%, transparent 30% 70%, black 70%) center / 0.7vmin 100% no-repeat;
  mask: radial-gradient(circle closest-side, black 99%, transparent 100%);
}
@media (prefers-reduced-motion: reduce) { .blocks #scope { transition: none; } }

/* the boost dash's readiness: a small ring under the crosshair, its
   arc filling (--k) as the dash cools down, a dot in it once it's ready */
.blocks #dashpip {
  position: fixed;
  left: calc(50% + 1.1em);
  top: calc(50% + 0.7em);
  width: 0.7em;
  aspect-ratio: 1;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0.6;
  background: conic-gradient(white calc(var(--k, 1) * 360deg), rgb(255 255 255 / 0.18) 0);
  mask: radial-gradient(circle, transparent 42%, black 46%);
  mix-blend-mode: difference;
}
.blocks #dashpip.ready { opacity: 0.85; mask: radial-gradient(circle, black 30%, transparent 34%, transparent 42%, black 46%); }
.blocks #dashpip[hidden] { display: none; }
/* ---- classes (classes.js): the picker, the abilities' HUD, the radial,
   an Auditor's ledger, a Calculator's numbers, a Gambler's reels ---- */
.blocks .classpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(10.5em, 1fr)); gap: 0.4em; margin-top: var(--spacing-sm); column-span: all; pointer-events: auto; text-align: left; }
.blocks .classpick.compact { grid-template-columns: repeat(auto-fill, minmax(7.2em, 1fr)); gap: 0.3em; }
.blocks .classcard { display: flex; flex-direction: column; gap: 0.15em; padding: 0.45em 0.6em; border: 0; border-radius: 0.5em; background: rgb(127 127 127 / 0.12); box-shadow: inset 0 0 0 1px var(--hairline-color); color: var(--text-color); font: inherit; font-size: 0.86em; text-align: left; cursor: pointer; }
.blocks .classcard:hover { background: rgb(127 127 127 / 0.22); }
.blocks .classcard b { font-weight: 600; }
.blocks .classcard span { color: var(--secondary-text-color); font-size: 0.86em; line-height: 1.3; }
.blocks .classcard .ab { color: var(--text-color); }
.blocks .classcard.mine { box-shadow: inset 0 0 0 2px var(--text-color); }
.blocks .classcard.wanted { box-shadow: inset 0 0 0 2px oklch(75% 0.15 85); }
.blocks .classpick.compact .classcard .blurb { font-size: 0.78em; }
.blocks .classnow { column-span: all; margin-top: var(--spacing-sm); color: var(--text-color); }
.blocks #dead { width: min(40em, calc(100% - 2 * var(--spacing-lg))); }
.blocks #classes { max-height: 70vh; overflow: auto; }
.blocks #abilities { position: fixed; left: 50%; bottom: calc(var(--bottom-overlay-height, 5.5em) + 0.6em); translate: calc(-50% + 16em) 0; display: flex; gap: 2.4em; pointer-events: auto; }
.blocks #abilities[hidden] { display: none; }
.blocks .abicon { position: relative; width: 3.4em; height: 3.4em; border: 0; border-radius: 50%; background: rgb(18 20 26 / 0.55); color: white; font: inherit; font-size: 0.8em; display: grid; place-items: center; cursor: pointer; padding: 0; }
.blocks .abicon .ring { position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(rgb(255 255 255 / 0.9) calc(var(--k, 1) * 360deg), rgb(255 255 255 / 0.15) 0); mask: radial-gradient(circle, transparent 62%, black 64%); }
.blocks .abicon.ready .ring { background: oklch(80% 0.15 145); }
.blocks .abicon .key { position: absolute; top: 0.35em; font-weight: 700; font-size: 0.9em; }
.blocks .abicon .name { position: absolute; top: calc(100% + 0.2em); width: 7em; text-align: center; line-height: 1.1; font-size: 0.78em; text-shadow: 0 1px 2px rgb(0 0 0 / 0.7); }
.blocks .abicon .left { font-size: 1.15em; font-weight: 600; font-variant-numeric: tabular-nums; }
.blocks .abicon .charge { position: absolute; left: 15%; right: 15%; bottom: 0.45em; height: 0.2em; border-radius: 0.1em; background: linear-gradient(to right, oklch(80% 0.16 85) calc(var(--c, 0) * 100%), rgb(255 255 255 / 0.2) 0); }
.blocks .abicon.s0 .charge { display: none; }
.blocks #radial { position: fixed; z-index: 20; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; }
.blocks #radial[hidden] { display: none; }
.blocks #radial button { position: absolute; translate: calc(-50% + var(--x)) calc(-50% + var(--y)); padding: 0.35em 0.7em; border: 0; border-radius: 1em; background: rgb(18 20 26 / 0.75); color: white; font: inherit; font-size: 0.86em; white-space: nowrap; pointer-events: auto; }
.blocks #radial button.on { background: white; color: #14161c; }
.blocks #ledger { position: fixed; right: 1em; top: calc(var(--top-overlay-height) + 6em); margin: 0; padding: 0.5em 0.8em; list-style: none; border-radius: 0.5em; background: rgb(18 20 26 / 0.55); color: white; font-size: 0.8em; font-variant-numeric: tabular-nums; pointer-events: none; }
.blocks #ledger[hidden] { display: none; }
.blocks #ledger::before { content: "Ledger · 15 s"; display: block; opacity: 0.6; margin-bottom: 0.2em; }
.blocks #numbers { position: fixed; left: calc(50% + 1.8em); top: calc(50% - 2.4em); display: flex; flex-direction: column; gap: 0.1em; color: oklch(85% 0.15 85); font-weight: 600; font-variant-numeric: tabular-nums; text-shadow: 0 1px 2px rgb(0 0 0 / 0.8); pointer-events: none; }
.blocks #reels { position: fixed; left: 50%; top: 32%; translate: -50% 0; display: flex; gap: 0.6em; align-items: center; padding: 0.5em 0.9em; border-radius: 0.6em; background: rgb(18 20 26 / 0.8); color: white; font-weight: 600; pointer-events: none; }
.blocks #reels[hidden] { display: none; }
.blocks #reels .reel { min-width: 8em; padding: 0.3em 0.5em; border-radius: 0.3em; background: rgb(255 255 255 / 0.12); text-align: center; }
.blocks #reels .won { color: oklch(85% 0.17 85); }
.blocks #touch .t-tactical { --x: 270; --y: 196; --s: 46; background: conic-gradient(rgb(255 255 255 / 0.55) calc(var(--k, 1) * 360deg), rgb(18 20 26 / 0.42) 0); }
.blocks #touch .t-prime { --x: 322; --y: 168; --s: 50; background: conic-gradient(rgb(255 255 255 / 0.55) calc(var(--k, 1) * 360deg), rgb(18 20 26 / 0.42) 0); }
.blocks #touch .tb.ab.ready { box-shadow: inset 0 0 0 2px oklch(80% 0.15 145), 0 0 0 1px rgb(0 0 0 / 0.2); }
.blocks.touch #abilities { display: none; }
/* a boost dash: lines of speed rushing out from the middle, a moment */
.blocks #rush {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: repeating-conic-gradient(from 3deg, rgb(255 255 255 / 0.5) 0 0.6deg, transparent 0.6deg 9deg);
  mask: radial-gradient(circle, transparent 28%, black 75%);
}
.blocks #rush.on { animation: blocks-rush 0.32s ease-out; }
@keyframes blocks-rush { from { opacity: 0.55; scale: 1; } to { opacity: 0; scale: 1.15; } }
/* a shot of yours a boost dash took nothing from */
.blocks #crosshair.dodged::before {
  content: "dodged";
  position: absolute;
  left: 50%;
  top: 130%;
  translate: -50% 0;
  font-size: 0.7em;
  color: oklch(85% 0.12 210);
  mix-blend-mode: normal;
  animation: blocks-dodged 0.6s ease-out forwards;
}
@keyframes blocks-dodged { from { opacity: 1; } to { opacity: 0; } }
/* a parry of yours (parry.rs): PARRY pops over the crosshair, and the
   view flashes a cold white a moment */
.blocks #parrypop {
  position: fixed;
  left: 50%;
  top: calc(50% - 3.2em);
  translate: -50% 0;
  pointer-events: none;
  opacity: 0;
  font-weight: 800;
  font-size: 1.5em;
  letter-spacing: 0.22em;
  color: oklch(97% 0.04 220);
  text-shadow: 0 0 0.4em oklch(80% 0.14 220), 0 0 0.12em oklch(60% 0.15 240);
  mix-blend-mode: normal;
}
.blocks #parrypop.on { animation: blocks-parrypop 0.75s cubic-bezier(0.2, 1.4, 0.4, 1) forwards; }
@keyframes blocks-parrypop {
  0% { opacity: 1; scale: 1.9; }
  18% { opacity: 1; scale: 1; }
  70% { opacity: 1; scale: 1; }
  100% { opacity: 0; scale: 1.06; translate: -50% -0.4em; }
}
.blocks #flash.parry { background: oklch(94% 0.06 215); }
.blocks #flash.parry.on { animation: blocks-flash 0.32s ease-out; }

/* the kill feed: the last few frags, top right under the rail, fading */
.blocks #feed {
  position: fixed;
  right: var(--spacing-lg);
  top: calc(var(--top-overlay-height) + var(--spacing-sm));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--spacing-xs, 0.25em);
  list-style: none;
  pointer-events: none;
}
.blocks #feed li {
  padding: 0.3em var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--wash-color);
  font-size: 0.86em;
  animation: blocks-feed 6s ease-in forwards;
}
@keyframes blocks-feed { 0%, 80% { opacity: 1; } 100% { opacity: 0; } }
/* revenge and domination, louder in the feed; your nemesis in the rail */
.blocks #feed li.grudge { font-weight: 600; color: light-dark(oklch(48% 0.2 25), oklch(74% 0.17 25)); }
/* a melee kill: `3f2a… clobbered 9b7c…`, in italics, in its own colour */
.blocks #feed li.melee { font-style: italic; font-weight: 600; color: light-dark(oklch(45% 0.13 60), oklch(80% 0.13 75)); }
/* a kill by something parried back: `3f2a… parried 9b7c…'s rocket back` */
.blocks #feed li.parried { font-weight: 700; color: light-dark(oklch(45% 0.15 235), oklch(84% 0.11 215)); }
.blocks #nemesis { color: light-dark(oklch(48% 0.2 25), oklch(74% 0.17 25)); }
/* spawn protection: your own countdown */
.blocks #shield { color: light-dark(oklch(45% 0.12 240), oklch(80% 0.1 230)); }
/* your speed while bunny-hopping, faint, under the crosshair */
.blocks #speed { position: fixed; left: 50%; top: calc(50% + 2.2em); translate: -50% 0; margin: 0; font-size: 0.8em; font-variant-numeric: tabular-nums; opacity: 0.55; pointer-events: none; text-shadow: 0 0 3px var(--app-background); }
.blocks #speed[hidden] { display: none; }
/* ---- Blocks' overlay regions ----
   Each overlay over the scene has a region of its own, so none covers
   another, nor the crosshair, at any width:
     the top rail: the pills; under it, the toast (centred) and the kill
       feed (right);
     the middle: the crosshair, your speed and the dash ring under it, the
       death card, the scope;
     the bottom rail: the weapon cards; just above it, centred, the
       prompt band (`--hint-band`: E's prompt, a support item's hint);
     bottom left, over the prompt band (`--dock-bottom`): the inspector
       (\), and the chat dock over that while it's open (`--inspect-h`),
       never wider than 40% of the view, so it stops short of the
       crosshair; the vote card (#match) ends above the dock's lines.
   The TAB screen and the vote card are dialogs over the middle. */
/* (registered as lengths, so each is worked out once, on the body, in
   its ems, and every overlay reads the same pixels whatever its own
   font size: the inspector's is smaller) */
@property --dock-bottom { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --inspect-h { syntax: "<length>"; inherits: true; initial-value: 0px; }
@property --hint-band { syntax: "<length>"; inherits: true; initial-value: 0px; }
body.blocks {
  --hint-band: 3.2em;
  --dock-bottom: calc(var(--bottom-overlay-height) + var(--hint-band));
  --inspect-h: 0px;
  --chat-w: min(24em, 40vw, calc(100% - 2 * var(--spacing-lg)));
  /* six lines of chat and the bar under them */
  --chat-band: 12.5em;
}
body.blocks:has(#inspect:not([hidden])) { --inspect-h: calc(var(--inspect-lines, 5) * 0.72em * 1.5 + 2 * 0.72 * var(--spacing-sm) + var(--spacing-sm)); }
/* the chat dock: up to six recent lines, fading; the whole history,
   scrolling, while the bar is open; each line edged in its speaker's
   colour, a command's answer apart */
.blocks #chat {
  position: fixed;
  left: var(--spacing-lg);
  bottom: calc(var(--dock-bottom) + var(--inspect-h));
  width: var(--chat-w);
  display: flex;
  flex-direction: column;
  gap: 0.3em;
  pointer-events: none;
  z-index: 15;
}
.blocks #said {
  display: flex;
  flex-direction: column;
  gap: 0.2em;
  margin: 0;
  padding: 0;
  list-style: none;
}
.blocks #said li { padding: 0.2em var(--control-inline-padding) 0.2em calc(var(--control-inline-padding) - 2px); border-left: 3px solid var(--c, transparent); border-radius: var(--border-radius); background-color: color-mix(in oklch, var(--wash-color) 85%, transparent); font-size: 0.86em; line-height: 1.35; overflow-wrap: anywhere; }
.blocks #said li.fresh { animation: blocks-said 10s ease-in forwards; }
.blocks #said:not(.open) li:not(.fresh) { display: none; }
.blocks #said:not(.open) li:nth-last-child(n + 7) { display: none; }
.blocks #said .who { font-weight: 600; margin-right: 0.5em; }
/* Empty history has no panel (including its padding) above the input. */
.blocks #said:empty { display: none; }
/* open: the whole history, scrolling, on a panel of its own */
.blocks #said.open {
  max-height: min(22em, calc(100dvh - var(--top-overlay-height) - var(--dock-bottom) - var(--inspect-h) - 4em));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--spacing-xs, 0.25em);
  border-radius: var(--border-radius);
  background: color-mix(in oklch, var(--app-overlay-solid) 70%, transparent);
  backdrop-filter: blur(6px);
  pointer-events: auto;
  scrollbar-width: thin;
}
/* a command's answer (yours alone), an announcement (everyone's), an emote */
.blocks #said li.sys { border-left-color: var(--muted-color); background-color: transparent; box-shadow: inset 0 0 0 1px var(--hairline-color); color: var(--secondary-text-color); font-style: italic; white-space: pre-wrap; }
.blocks #said li.sys::before { content: "› "; font-style: normal; color: var(--muted-color); }
.blocks #said li.sys.all { border-left-color: var(--text-color); color: var(--text-color); font-style: normal; font-weight: 600; }
.blocks #said li.emote span:last-child { font-style: italic; }
.blocks #match .vote button.nominated .note { color: var(--text-color); font-weight: 600; }
@keyframes blocks-said { 0%, 85% { opacity: 1; } 100% { opacity: 0; } }
/* the bar: plain to read, its keys at its end */
.blocks #sayform { position: relative; pointer-events: auto; }
.blocks #sayform[hidden] { display: none; }
.blocks #say { width: 100%; padding-right: 6.5em; box-shadow: 0 0 0 2px var(--text-color); }
.blocks #say::placeholder { color: var(--muted-color); }
.blocks #sayform .keys { position: absolute; right: var(--control-inline-padding); top: 50%; translate: 0 -50%; font-size: 0.75em; color: var(--muted-color); pointer-events: none; }
@media (width <= 600px) { body.blocks { --chat-w: calc(100% - 2 * var(--spacing-lg)); } }
/* (a narrow bar keeps its room for the placeholder) */
@media (width <= 960px) { .blocks #sayform .keys { display: none; } .blocks #say { padding-right: var(--control-inline-padding); } }
/* the scoreboard, held on TAB */
.blocks #scoreboard {
  position: fixed;
  left: 50%;
  top: calc(var(--top-overlay-height) + var(--spacing-lg));
  translate: -50% 0;
  z-index: 13;
  max-width: calc(100% - 2 * var(--spacing-md, 1em));
  max-height: calc(100% - var(--top-overlay-height) - 8em);
  overflow: auto;
  padding: var(--spacing-sm) var(--spacing-md, 1em);
  border-radius: var(--border-radius);
  background-color: color-mix(in oklch, var(--app-background) 90%, transparent);
  backdrop-filter: blur(6px);
  pointer-events: none;
}
.blocks #scoreboard[hidden] { display: none; }
.blocks #scoreboard table { border-collapse: collapse; font-variant-numeric: tabular-nums; font-size: 0.9em; }
.blocks #scoreboard th, .blocks #scoreboard td { padding: 0.25em 0.7em; text-align: right; white-space: nowrap; }
.blocks #scoreboard th:first-child, .blocks #scoreboard td:first-child { text-align: left; }
.blocks #scoreboard th { color: var(--muted-color); font-weight: 500; }
.blocks #scoreboard tr.you td { font-weight: 700; background-color: var(--wash-color); }
.blocks #scoreboard tr.nemesis td { color: light-dark(oklch(48% 0.2 25), oklch(74% 0.17 25)); font-weight: 600; }
.blocks #scoreboard .swatch { display: inline-block; width: 0.8em; height: 0.8em; margin-right: 0.5em; border-radius: 2px; vertical-align: -0.05em; }
@media (max-width: 40em) { .blocks #scoreboard th:nth-child(5), .blocks #scoreboard td:nth-child(5), .blocks #scoreboard th:nth-child(8), .blocks #scoreboard td:nth-child(8) { display: none; } }
.blocks #hp, .blocks #ammo { min-width: 7.5em; }

/* news (a beacon lit, a refusal): a pill under the top rail that fades */
.blocks #toast {
  position: fixed;
  left: 50%;
  top: calc(var(--top-overlay-height) + var(--spacing-sm));
  translate: -50% 0;
  z-index: 11;
  padding: var(--spacing-sm) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--wash-color);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}
.blocks #toast.on { opacity: 1; transition-duration: 0.1s; }
/* a how-to (the first pickup of a support item): wrapped, and held longer */
.blocks #toast.long { white-space: normal; width: max-content; max-width: min(34em, calc(100% - 2 * var(--spacing-lg))); text-align: center; }

/* the support slot chosen: what it does, a line over the weapon bar,
   clear of the crosshair and the arc you aim along */
.blocks #hint {
  position: fixed;
  left: 50%;
  bottom: calc(var(--bottom-overlay-height) + var(--spacing-sm));
  translate: -50% 0;
  max-width: min(36em, calc(100% - 2 * var(--spacing-lg)));
  padding: 0.3em var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--wash-color);
  font-size: 0.8em;
  text-align: center;
  color: var(--secondary-text-color);
  pointer-events: none;
}
.blocks #hint[hidden] { display: none; }

/* the weapon bar: centred, a tank chip per slot (its fill the weapon's
   ammo, or the build tool's material), the number key first; a slot whose
   weapon you haven't found is dimmed and can't be chosen */
.blocks #hotbar { display: flex; justify-content: center; flex-wrap: wrap; gap: 0.44em; }
.blocks #hotbar .slot { min-width: 6.2em; font-size: 0.86em; }
.blocks #hotbar .slot .key { opacity: 0.7; }
/* the support card's second key (3: the next item), a small boxed numeral */
.blocks #hotbar .slot .next { display: inline-block; margin-left: 0.3em; padding: 0 0.3em; border-radius: 0.3em; box-shadow: inset 0 0 0 1px currentColor; font-size: 0.78em; opacity: 0.7; }
.blocks #hotbar .slot[aria-checked="true"] { outline: 2px solid var(--text-color); outline-offset: 2px; }
.blocks #hotbar .slot[aria-disabled="true"] { opacity: 0.35; cursor: not-allowed; }
.blocks #hp, .blocks #armor { min-width: 7.5em; }
/* your portals: a dot each, filled while open, a ring while its wall is gone */
.blocks .portal-dot { display: inline-block; width: 0.7em; aspect-ratio: 1; margin-left: 0.25em; border-radius: 50%; background: var(--c); opacity: 0.25; }
.blocks .portal-dot.open { opacity: 1; }
.blocks .portal-dot.shut { opacity: 1; background: transparent; box-shadow: inset 0 0 0 2px var(--c); }
/* the portal gun chosen: the crosshair is a blue and an orange half */
.blocks #crosshair.portal {
  width: 1.4em;
  mix-blend-mode: normal;
  background: linear-gradient(90deg, #3d7fe0 0 50%, #f08a2c 50%) center / 100% 3px no-repeat;
}

/* the Beam: a white flash for everyone who saw it, fading */
.blocks #flash {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: white;
}
.blocks #flash.on { animation: blocks-flash 0.9s ease-out; }
@keyframes blocks-flash { from { opacity: var(--k, 0.8); } to { opacity: 0; } }

/* the ground under you hasn't arrived yet */
.blocks #loading {
  position: fixed;
  left: 50%;
  bottom: calc(var(--bottom-overlay-height) + 6em);
  translate: -50% 0;
  padding: var(--spacing-sm) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background-color: var(--wash-color);
  font-size: 0.86em;
  pointer-events: none;
}
.blocks #loading[hidden] { display: none; }

/* the inspector: in Blocks a translucent panel over the bottom-left of
   the scene, above the rail, so it never covers the weapon bar or the
   HUD. The rail keeps its own height. */
body.blocks:has(#inspect:not([hidden])) { --bottom-overlay-height: 5em; }
.blocks #inspect {
  position: fixed;
  left: var(--spacing-lg);
  bottom: var(--dock-bottom);
  width: min(44em, calc(100% - 2 * var(--spacing-lg)));
  margin: 0;
  padding: var(--spacing-sm) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background: color-mix(in oklch, var(--app-overlay-solid) 78%, transparent);
  box-shadow: 0 0 0 1px var(--hairline-color);
  backdrop-filter: blur(6px);
}
@media (width <= 600px) {
  .blocks #hotbar { gap: 0.15em; }
  .blocks #hotbar .slot { min-width: 0; font-size: 0.72em; }
  .blocks #hotbar .slot .name { display: none; }
  /* no room in the rail for the fight's numbers beyond health */
  .blocks #armor, .blocks #frags, .blocks #portals { display: none; }
  .blocks #hp { min-width: 0; }
}

/* ---- Blocks on a touch screen (touch.js; `body.touch`) ----
   Two thumbs, and the same overlay regions, moved for them, every one
   inside the safe areas (the notch, the home bar):
     the whole view: where a thumb lands (#touch's zone, under the
       rails and every dialog): the left 40% a stick that appears under
       it, the rest a look;
     bottom right, round the right thumb: fire, aim, jump and duck,
       large; the smaller keys above and beside them; E only while the
       prompt is up;
     the top rail: smaller, with Bag, Chat and Score after Back;
     bottom middle, between the thumbs: the weapon cards, small, and the
       prompt band over them; in build mode the four materials over that;
     top left, under the rail: the chat dock (the keyboard covers the
       bottom while it's open), three lines;
   the TAB screen and the vote card over everything, scrolling. Upright,
   a line says to turn the phone; it still plays. */
body:not(.touch) .touch-only { display: none !important; }
body.blocks.touch {
  --top-overlay-height: calc(2.7em + env(safe-area-inset-top));
  --bottom-overlay-height: calc(2.5em + env(safe-area-inset-bottom));
  --hint-band: 2.2em;
  --chat-w: min(20em, 36vw);
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
.blocks.touch #world { cursor: default; }
.blocks.touch #top-container {
  z-index: 13;
  padding: calc(0.45em + env(safe-area-inset-top)) calc(0.6em + env(safe-area-inset-right)) 0.3em calc(0.6em + env(safe-area-inset-left));
  font-size: 0.72em;
}
.blocks.touch #top-container .pills, .blocks.touch #top-container .actions { align-items: center; }
.blocks.touch #top-container .pill:not(.tank), .blocks.touch #top-container .tank .label { padding: 0.55em 0.9em; }
.blocks.touch #top-container .tank { padding: 0; }
.blocks.touch #frags { display: none; }
.blocks.touch #top-container .pill.on { background-color: var(--active-background); color: var(--active-text); }
.blocks.touch #made { display: none; }
.blocks.touch #input-container {
  z-index: 13;
  padding: 0 0 calc(0.35em + env(safe-area-inset-bottom));
  font-size: 0.66em;
}
.blocks.touch #hotbar { pointer-events: none; gap: 0.3em; }
/* (sideways, the cards keep left of the right thumb's buttons) */
@media (orientation: landscape) { .blocks.touch #input-container { padding-right: calc(env(safe-area-inset-right) + 250px * var(--tbs, 1)); padding-left: calc(env(safe-area-inset-left) + 40px); } }
.blocks.touch #hotbar .slot { pointer-events: auto; min-width: 0; }
.blocks.touch #hotbar #hand1, .blocks.touch #hotbar #hand2 { min-width: 0; }
.blocks.touch #hotbar .slot .label { padding: 0.62em 0.9em; }
.blocks.touch #hotbar .slot:not([aria-checked="true"]) .name { display: none; }
.blocks.touch #hint { bottom: calc(var(--bottom-overlay-height) + 0.2em); max-width: 38vw; font-size: 0.72em; }
.blocks.touch #feed { right: calc(0.6em + env(safe-area-inset-right)); top: calc(var(--top-overlay-height) + 0.2em); font-size: 0.72em; }
.blocks.touch #toast { top: calc(var(--top-overlay-height) + 0.2em); font-size: 0.78em; }
.blocks.touch #chat { top: calc(var(--top-overlay-height) + 0.3em); bottom: auto; left: calc(0.6em + env(safe-area-inset-left)); font-size: 0.78em; }
.blocks.touch #said:not(.open) li:nth-last-child(n + 4) { display: none; }
.blocks.touch #said.open { max-height: min(14em, 40dvh); }
.blocks.touch #intro { font-size: 0.78em; columns: 1; width: min(30em, calc(100% - 2 * var(--spacing-lg))); text-align: center; }
.blocks.touch #intro p:not(.go):not(.touch-tip) { display: none; }
.blocks.touch #intro .touch-tip { margin-top: var(--spacing-sm); }
.blocks.touch #scoreboard {
  top: calc(var(--top-overlay-height) + 0.3em);
  max-height: calc(100dvh - var(--top-overlay-height) - var(--bottom-overlay-height) - 0.6em);
  max-width: calc(100% - 1.2em - env(safe-area-inset-left) - env(safe-area-inset-right));
  font-size: 0.8em;
  pointer-events: auto;
  overscroll-behavior: contain;
}
.blocks.touch #scoreboard .tabs span { padding: 0.35em 0; }
.blocks.touch #inventory li { min-height: 4.8em; }
.blocks.touch #match {
  top: calc(var(--top-overlay-height) + 0.3em);
  max-height: calc(100dvh - var(--top-overlay-height) - 0.6em - env(safe-area-inset-bottom));
  font-size: 0.8em;
  overscroll-behavior: contain;
}
.blocks.touch #match .vote { grid-template-columns: repeat(auto-fill, minmax(7em, 1fr)); }
.blocks.touch #match .vote canvas { width: 4em; height: 4em; }
.blocks.touch:has(#match:not([hidden])) #chat { display: none; }
.blocks #settings .set-group { display: grid; gap: var(--spacing-sm); }
.blocks #settings .touch-set { display: grid; gap: var(--spacing-sm); }
.blocks #settings .set-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-sm); margin: 0; }
.blocks #settings .set-row:not(:has(> :not([hidden]))) { display: none; }
.blocks #settings select { font: inherit; color: inherit; background: var(--wash-color); border: none; border-radius: var(--border-radius); padding: 0.35em 0.7em; }

/* the layer the thumbs land on, and its buttons: sized by Settings'
   button size (`--tb`), as opaque as its opacity (`--ta`) */
.blocks #touch {
  position: fixed;
  inset: 0;
  z-index: 12;
  pointer-events: none;
  touch-action: none;
  --u: calc(var(--tb, 1) * 1px);
  --sl: env(safe-area-inset-left);
  --sr: env(safe-area-inset-right);
  --sb: env(safe-area-inset-bottom);
  color: white;
  font-family: var(--font-family);
}
.blocks #touch[hidden], .blocks #touch.vote { display: none; }
.blocks #touch .zone { position: absolute; inset: 0; pointer-events: auto; touch-action: none; }
.blocks #touch .stick {
  position: absolute;
  left: 0;
  top: 0;
  width: 108px;
  height: 108px;
  margin: -54px 0 0 -54px;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.1);
  box-shadow: inset 0 0 0 2px rgb(255 255 255 / 0.55), 0 0 0 1px rgb(0 0 0 / 0.25);
  opacity: max(var(--ta, 0.55), 0.6);
  pointer-events: none;
}
.blocks #touch .stick[hidden] { display: none; }
.blocks #touch .stick.run { box-shadow: inset 0 0 0 3px white, 0 0 0 3px rgb(255 255 255 / 0.35); }
.blocks #touch .stick .knob { position: absolute; left: 50%; top: 50%; width: 50px; height: 50px; margin: -25px; border-radius: 50%; background: rgb(255 255 255 / 0.6); box-shadow: 0 1px 4px rgb(0 0 0 / 0.35); }
/* where the stick waits: a faint ring, bottom left */
.blocks #touch .stick.idle { translate: calc(var(--sl) + 120px) calc(100dvh - var(--sb) - var(--lift, 0px) - 112px); opacity: calc(var(--ta, 0.55) * 0.55); box-shadow: inset 0 0 0 2px rgb(255 255 255 / 0.5); background: none; }
.blocks #touch:has(.stick:not(.idle):not([hidden])) .stick.idle { display: none; }
.blocks #touch .tb {
  position: absolute;
  right: calc(var(--sr) + var(--x) * var(--u));
  bottom: calc(var(--sb) + var(--lift, 0px) + var(--y) * var(--u));
  width: calc(var(--s, 44) * var(--u));
  height: calc(var(--s, 44) * var(--u));
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: 50%;
  background: rgb(18 20 26 / 0.42);
  box-shadow: inset 0 0 0 2px rgb(255 255 255 / 0.62), 0 0 0 1px rgb(0 0 0 / 0.2);
  color: white;
  font-size: calc(14 * var(--u));
  font-weight: 600;
  line-height: 1;
  opacity: var(--ta, 0.55);
  pointer-events: auto;
  touch-action: none;
  transition: none;
  corner-shape: round;
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.6);
}
.blocks #touch .tb:hover { opacity: var(--ta, 0.55); }
.blocks #touch .tb.on, .blocks #touch.ads .t-aim { background: rgb(255 255 255 / 0.75); color: #14161c; opacity: max(var(--ta, 0.55), 0.85); }
.blocks #touch .tb.big { font-size: calc(15 * var(--u)); }
.blocks #touch .t-fire { --x: 26; --y: 40; --s: 92; background: rgb(150 28 28 / 0.45); }
.blocks #touch .t-jump { --x: 134; --y: 18; --s: 70; }
.blocks #touch .t-crouch { --x: 218; --y: 12; --s: 58; }
.blocks #touch .t-aim { --x: 36; --y: 150; --s: 66; }
.blocks #touch .t-reload { --x: 142; --y: 104; --s: 48; }
.blocks #touch .t-melee { --x: 116; --y: 166; --s: 46; }
.blocks #touch .t-support { --x: 200; --y: 88; --s: 46; }
.blocks #touch .t-next { --x: 254; --y: 84; --s: 34; font-size: calc(12 * var(--u)); }
.blocks #touch .t-swap { --x: 168; --y: 168; --s: 42; }
.blocks #touch .t-toss { --x: 44; --y: 230; --s: 40; }
.blocks #touch .t-craft { --x: 94; --y: 222; --s: 40; font-size: calc(10 * var(--u)); }
.blocks #touch .t-use { --x: 216; --y: 146; --s: 56; background: rgb(255 255 255 / 0.22); }
.blocks #touch:not(.prompt) .t-use, .blocks #touch.screen .t-use { display: none; }
.blocks #touch.nosight .t-aim { opacity: calc(var(--ta, 0.55) * 0.55); }
.blocks #touch.crafting .t-craft { background: rgb(255 255 255 / 0.75); color: #14161c; }
/* upright: smaller, and a word about turning it */
.blocks #touch .rotate { display: none; }
@media (orientation: portrait) {
  .blocks #touch { --u: calc(var(--tb, 1) * 0.86px); --lift: 2.6em; }
  .blocks #touch .rotate {
    display: block;
    position: absolute;
    left: 50%;
    top: calc(var(--top-overlay-height) + 3.2em);
    translate: -50% 0;
    max-width: calc(100% - 2em);
    padding: 0.5em 1em;
    border-radius: var(--border-radius);
    background: rgb(18 20 26 / 0.7);
    font-size: 0.8em;
    text-align: center;
    pointer-events: none;
  }
  .blocks.touch #chat { top: calc(var(--top-overlay-height) + 5.8em); --chat-w: 60vw; }
}
@media (prefers-reduced-motion: reduce) { .blocks #touch .tb { transition: none; } }

/* The lab (/blocks/lab): the two views, each the game's page in a frame,
   side by side over the controls' column, or stacked on a narrow screen.
   Either can be made large, the other kept as an inset. */
.blocks-lab #lab {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 21em;
  gap: var(--spacing-lg);
  align-items: start;
  padding: var(--top-overlay-height) var(--spacing-lg) var(--spacing-lg);
}
.blocks-lab .views {
  position: sticky;
  top: var(--top-overlay-height);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--spacing-md);
  --aspect: 1.7778;
}
.blocks-lab .view {
  position: relative;
  aspect-ratio: var(--aspect);
  max-height: calc(100dvh - var(--top-overlay-height) - 2 * var(--spacing-lg));
  overflow: hidden;
  border-radius: var(--border-radius);
  background: var(--wash-color);
  box-shadow: 0 0 0 1px var(--hairline-color);
}
.blocks-lab .view iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
.blocks-lab .grab { position: absolute; inset: 0; cursor: grab; touch-action: none; outline: none; }
.blocks-lab .grab:active { cursor: grabbing; }
.blocks-lab .grab:focus-visible { box-shadow: inset 0 0 0 2px var(--text-color); border-radius: var(--border-radius); }
.blocks-lab .view figcaption {
  position: absolute;
  left: var(--spacing-sm);
  bottom: var(--spacing-sm);
  display: flex;
  align-items: center;
  gap: 0.44em;
  font-size: 0.72em;
  pointer-events: none;
}
.blocks-lab .view figcaption span { padding: 0.3em 0.8em; border-radius: var(--border-radius); background: color-mix(in oklch, var(--app-overlay-solid) 75%, transparent); }
.blocks-lab .view figcaption button { pointer-events: auto; padding: 0.3em 0.8em; }
.blocks-lab .views[data-big="tp"], .blocks-lab .views[data-big="fp"] { grid-template-columns: 1fr; }
.blocks-lab .views[data-big="tp"] .fp, .blocks-lab .views[data-big="fp"] .tp {
  position: absolute;
  right: var(--spacing-md);
  bottom: var(--spacing-md);
  width: 30%;
  z-index: 2;
  box-shadow: 0 0 0 2px var(--app-background), 0 4px 18px rgb(0 0 0 / 0.3);
}
.blocks-lab .views[data-big="fp"] .fp { order: -1; }
.blocks-lab .panel { display: flex; flex-direction: column; gap: var(--spacing-lg); font-size: 0.8em; min-width: 0; }
.blocks-lab .panel h2 { font-size: 1em; font-weight: 600; margin-bottom: var(--spacing-sm); }
.blocks-lab .panel h3 { font-size: 0.86em; color: var(--secondary-text-color); font-weight: 400; margin: var(--spacing-md) 0 0.25em; }
.blocks-lab .panel section > * + * { margin-top: var(--spacing-sm); }
.blocks-lab .row { display: flex; flex-wrap: wrap; gap: 0.35em; align-items: center; }
.blocks-lab .panel .pill, .blocks-lab .panel button { padding: 0.45em 0.85em; }
.blocks-lab .slider { display: grid; grid-template-columns: 7.5em minmax(0, 1fr) auto; align-items: center; gap: var(--spacing-sm); }
.blocks-lab .slider input[type="range"] { padding: 0; background: none; accent-color: var(--text-color); width: 100%; }
.blocks-lab .slider output { min-width: 3.4em; text-align: right; color: var(--secondary-text-color); font-variant-numeric: tabular-nums; }
.blocks-lab select { font: inherit; color: inherit; background: var(--wash-color); border: none; border-radius: var(--border-radius); padding: 0.35em 0.7em; }
.blocks-lab .check { display: inline-flex; align-items: center; gap: 0.35em; margin-right: 0.6em; }
.blocks-lab .check input { padding: 0; accent-color: var(--text-color); }
.blocks-lab .swatch { width: 1.2em; aspect-ratio: 1; border-radius: 50%; background: var(--muted-color); }
.blocks-lab .now { display: flex; justify-content: space-between; gap: var(--spacing-sm); font-weight: 600; }
.blocks-lab .now output { font-weight: 333; color: var(--secondary-text-color); font-variant-numeric: tabular-nums; white-space: nowrap; }
.blocks-lab .heard { color: var(--secondary-text-color); }
.blocks-lab .note { color: var(--secondary-text-color); font-size: 0.9em; }
.blocks-lab [aria-pressed="true"], .blocks-lab [role="radio"][aria-checked="true"] { background-color: var(--active-background); color: var(--active-text); }
.blocks-lab #anims ul { display: flex; flex-direction: column; gap: 1px; }
.blocks-lab #anims li { display: flex; align-items: center; gap: 0.3em; border-radius: 0.6em; }
.blocks-lab #anims li .name { flex: 1; text-align: left; background: none; padding: 0.3em 0.6em; border-radius: 0.6em; line-height: 1.25; white-space: normal; }
.blocks-lab #anims li .again { padding: 0.2em 0.6em; background: none; color: var(--secondary-text-color); }
.blocks-lab #anims li:hover { background: var(--wash-color); }
.blocks-lab #anims li.on { background: var(--active-background); }
.blocks-lab #anims li.on .name, .blocks-lab #anims li.on .again { color: var(--active-text); }
.blocks-lab #lab-toast {
  position: fixed;
  left: 50%;
  bottom: var(--spacing-lg);
  translate: -50% 0;
  padding: var(--spacing-sm) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background: var(--active-background);
  color: var(--active-text);
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}
.blocks-lab #lab-toast.on { opacity: 1; transition-duration: 0.1s; }
@media (width <= 1100px) {
  .blocks-lab #lab { grid-template-columns: minmax(0, 1fr); }
  .blocks-lab .views { position: static; }
  .blocks-lab .panel { display: grid; grid-template-columns: repeat(auto-fill, minmax(19em, 1fr)); align-items: start; }
}
@media (width <= 820px) {
  .blocks-lab .views { grid-template-columns: 1fr; justify-items: center; }
  /* each view no taller than most of the screen, at its aspect */
  .blocks-lab .views[data-big="both"] .view { width: min(100%, calc(58dvh * var(--aspect))); }
  .blocks-lab .views[data-big="tp"] .fp, .blocks-lab .views[data-big="fp"] .tp { width: 40%; }
}
/* The style lab (/blocks/lab/styles): a grid of views, one per look,
   each the game's page in a frame, the selected one ringed, beside the
   selected look's knobs; one view can be made large on its own. */
.blocks-styles #styles {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 22em;
  gap: var(--spacing-lg);
  align-items: start;
  padding: var(--top-overlay-height) var(--spacing-lg) var(--spacing-lg);
}
.blocks-styles .grid {
  position: sticky;
  top: var(--top-overlay-height);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--spacing-sm);
}
.blocks-styles .view { aspect-ratio: 4 / 3; }
.blocks-styles .view.on { box-shadow: 0 0 0 2px var(--text-color); }
.blocks-styles .view figcaption { right: var(--spacing-sm); justify-content: space-between; }
.blocks-styles .view figcaption output { padding: 0.3em 0.6em; border-radius: var(--border-radius); background: color-mix(in oklch, var(--app-overlay-solid) 75%, transparent); font-variant-numeric: tabular-nums; }
.blocks-styles .grid.enlarged { grid-template-columns: minmax(0, 1fr); }
.blocks-styles .grid.enlarged .view:not(.big) { display: none; }
.blocks-styles .grid.enlarged .view.big { aspect-ratio: 16 / 9; }
.blocks-styles .panel details { border-top: 1px solid var(--hairline-color); padding-top: var(--spacing-sm); }
.blocks-styles .panel details > * + * { margin-top: var(--spacing-sm); }
.blocks-styles .panel summary { cursor: pointer; font-weight: 600; color: var(--secondary-text-color); }
.blocks-styles .slider input[type="color"] { width: 100%; height: 1.6em; padding: 0; border: none; background: none; }
.blocks-styles .slider select { width: 100%; }
.blocks-styles #gl-errors pre { white-space: pre-wrap; font-size: 0.85em; }
@media (width <= 1100px) {
  .blocks-styles #styles { grid-template-columns: minmax(0, 1fr); }
  .blocks-styles .grid { position: static; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (width <= 560px) {
  .blocks-styles .grid { grid-template-columns: minmax(0, 1fr); }
}
/* The levels lab (/blocks/lab/levels): a strip of dioramas over one
   large view, the game's page in a frame, with the controls and numbers
   beside it (under it on a narrow screen). A second frame, off-screen,
   draws the thumbnails. */
.blocks-levels #levels {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 22em;
  grid-template-areas: "strip strip" "view panel";
  gap: var(--spacing-lg);
  align-items: start;
  padding: var(--top-overlay-height) var(--spacing-lg) var(--spacing-lg);
}
.blocks-levels .strip {
  grid-area: strip;
  display: flex;
  gap: var(--spacing-sm);
  overflow-x: auto;
  list-style: none;
  margin: 0;
  padding: 0 0 var(--spacing-sm);
  scrollbar-width: thin;
}
.blocks-levels .strip li { flex: none; }
.blocks-levels .thumb {
  display: flex;
  flex-direction: column;
  gap: 0.3em;
  width: 9.5em;
  padding: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.blocks-levels .thumb .shot {
  display: block;
  aspect-ratio: 16 / 10;
  border-radius: var(--border-radius);
  background: var(--wash-color) center / cover no-repeat;
  box-shadow: 0 0 0 1px var(--hairline-color);
}
.blocks-levels .thumb .name { font-size: 0.72em; line-height: 1.25; white-space: normal; color: var(--secondary-text-color); }
.blocks-levels .thumb[aria-pressed="true"] .shot { box-shadow: 0 0 0 2px var(--text-color); }
.blocks-levels .thumb[aria-pressed="true"] .name { color: var(--text-color); font-weight: 600; }
.blocks-levels .thumb:focus-visible { outline: none; }
.blocks-levels .thumb:focus-visible .shot { box-shadow: 0 0 0 2px var(--text-color), 0 0 0 4px var(--app-background); }
.blocks-levels .view {
  grid-area: view;
  position: sticky;
  top: var(--top-overlay-height);
  aspect-ratio: 16 / 10;
  max-height: calc(100dvh - var(--top-overlay-height) - 2 * var(--spacing-lg));
  width: 100%;
  margin: 0;
}
.blocks-levels .grab.walking { cursor: crosshair; }
.blocks-levels .busy {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  padding: var(--spacing-sm) var(--control-inline-padding);
  border-radius: var(--border-radius);
  background: color-mix(in oklch, var(--app-overlay-solid) 85%, transparent);
  font-size: 0.86em;
  pointer-events: none;
}
.blocks-levels .busy[hidden] { display: none; }
.blocks-levels .panel { grid-area: panel; }
.blocks-levels .panel h2#name { font-size: 1.15em; }
.blocks-levels input[type="number"] { font: inherit; color: inherit; background: var(--wash-color); border: none; border-radius: var(--border-radius); padding: 0.35em 0.7em; width: 7em; }
.blocks-levels .row .slider { grid-template-columns: auto auto; }
.blocks-levels table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
.blocks-levels .metrics th, .blocks-levels .metrics td { padding: 0.3em 0; border-bottom: 1px solid var(--hairline-color); vertical-align: top; }
.blocks-levels .metrics th { font-weight: 400; color: var(--secondary-text-color); padding-right: var(--spacing-md); text-align: left; }
.blocks-levels .metrics td { text-align: right; }
.blocks-levels abbr { text-decoration: underline dotted; text-underline-offset: 0.2em; cursor: help; }
.blocks-levels .scroll { overflow-x: auto; }
.blocks-levels .compare { font-size: 0.9em; }
.blocks-levels .compare th, .blocks-levels .compare td { padding: 0.25em 0.35em; text-align: right; border-bottom: 1px solid var(--hairline-color); white-space: nowrap; }
.blocks-levels .compare thead th { font-weight: 400; color: var(--secondary-text-color); cursor: help; }
.blocks-levels .compare th:first-child { text-align: left; padding-left: 0; }
.blocks-levels .compare tbody th button { background: none; padding: 0; font: inherit; color: inherit; text-align: left; white-space: nowrap; }
.blocks-levels .compare tr.on { background: var(--active-background); color: var(--active-text); }
.blocks-levels .compare tr.base th button { font-style: italic; }
.blocks-levels .thumbnailer { position: fixed; left: -10000px; top: 0; width: 480px; height: 300px; border: 0; visibility: visible; pointer-events: none; }
@media (width <= 1100px) {
  .blocks-levels #levels { grid-template-columns: minmax(0, 1fr); grid-template-areas: "strip" "view" "panel"; }
  .blocks-levels .view { position: relative; top: 0; }
}
@media (width <= 820px) {
  .blocks-levels #levels { padding-inline: var(--spacing-md); }
  .blocks-levels .thumb { width: 7.5em; }
  .blocks-levels .view { aspect-ratio: 4 / 3; }
}

/* a lab view: the game's page in a frame. No card to click past, nothing
   to leave by; the HUD only in first person, and only if asked for */
.lab-view #intro, .lab-view #top-container a[href="/"] { display: none !important; }
.lab-view:not(.lab-hud) :is(#top-container, #input-container, #feed, #toast, #hint, #dashpip) { display: none !important; }
.lab-view.watcher #crosshair, .lab-view.watcher #dashpip, .lab-view.watcher #rush, .lab-view.watcher #dead, .lab-view.watcher #hurt, .lab-view.watcher #loading { display: none !important; }
.lab-view #world { cursor: default; }


/* -------------------------------------------------------------------------
   6.15 Context: a chat room with a model in it, and the model's context
   window beside it. The room is Chat's log and composer, on the left;
   the window is a column fixed between the rails on the right: a gauge
   and the budget slider, then the context as a stack of segments in
   prompt order, each as tall as its share of the budget, so the space
   left at the bottom is room to spare. Segments ease to their new sizes
   when the budget moves, shrink into a summary, and grow back out of it.
   Phones fold the window to its gauge, opened with a tap.
   ------------------------------------------------------------------------- */

.context {
  --window: min(27em, 42vw);
  --gap: calc(var(--spacing-lg) * 1.5);
  /* the sidebar's whole width: the window and its padding either side */
  --side: calc(var(--window) + 2 * var(--spacing-lg));
  /* the model's own lines: no identity color, the text's own */
  --haiku: var(--text-color);
}

/* the room: Chat's log, clearing the sidebar on its right */
.context .log {
  padding: var(--top-overlay-height) calc(var(--side) + var(--spacing-lg)) var(--bottom-overlay-height) var(--spacing-lg);
}

/* the rails belong to the room's column: they end at the sidebar, and
   mask the log scrolling under them, solid behind the pills and the
   composer, fading only past their edge, so no text shows behind them */
.context #top-container, .context #input-container { right: var(--side); }
.context #top-container::before {
  bottom: -1.25em;
  background: linear-gradient(to bottom, var(--app-overlay-solid) calc(100% - 1.25em), var(--app-overlay-clear));
}
.context #input-container::before {
  top: -1.25em;
  background: linear-gradient(to bottom, var(--app-overlay-clear), var(--app-overlay-solid) 1.25em);
}
.context #chip { display: contents; }
.context .msg { padding: var(--spacing-sm) 0; overflow-wrap: anywhere; transition: opacity 0.3s ease; }
.context .msg .meta { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 0.44em; min-height: 1.6em; }
/* each marker whole: the row wraps between them, never inside one */
.context .msg .meta > * { white-space: nowrap; }
.context .msg b { font-weight: inherit; }
.context .msg b.haiku { color: var(--haiku); font-variation-settings: "wdth" 100, "wght" 560, "SRFF" 33, "slnt" 0; }
.context .msg time { color: var(--muted-color); }
.context .msg .body { line-height: 1.35; }
.context .msg.note .body { color: var(--muted-color); font-size: 0.86em; }
.context .msg.note time { font-size: inherit; }
.context mark.at { background: var(--wash-color); color: inherit; border-radius: 0.25em; padding: 0 0.1em; }
/* where a message stands in the context: in it, pinned above it, or
   folded into a summary (quieter, with a link to the summary) */
.context .msg.folded { opacity: 0.55; }
.context .msg.folded:hover, .context .msg.folded:focus-within { opacity: 1; }
.context .msg .where { color: var(--muted-color); font-size: 0.82em; text-decoration: none; }
.context .msg a.where:hover { text-decoration: underline; }
.context .msg.pinned .where { color: var(--text-color); }
/* pin and unpin: a small pill that shows on hover or focus; on touch
   screens, always there, quietly */
.context .pin { margin-left: auto; opacity: 0; transition: opacity 0.2s ease; }
.context .pin button { font-size: 0.72em; padding: 0.3em 0.8em; }
.context .msg:hover .pin, .context .pin:focus-within, .context .msg.pinned .pin { opacity: 1; }
.context .msg.pinned .pin button { background-color: var(--active-background); color: var(--active-text); }
@media (hover: none) { .context .pin { opacity: 0.6; } }
.context .msg.busy .pin { opacity: 0.4; }
/* Jev's judgment of a message without a mention, quiet unless it led to
   a reply; and an implicit reply's link to the message it answers */
.context .judged { color: var(--muted-color); font-size: 0.82em; cursor: help; }
.context .judged.yes { color: var(--text-color); }
.context .judged.pending { animation: context-squeeze 1.2s ease-in-out infinite; }
.context .answers { color: var(--muted-color); font-size: 0.82em; text-decoration: none; }
.context .answers:hover { text-decoration: underline; }
.context .msg:target { background-color: var(--wash-color); border-radius: 0.4em; }

/* a reply being written, and a message arriving */
.context .typing .body::after { content: "▍"; margin-left: 0.1em; animation: context-caret 1s steps(2) infinite; }
.context .fresh { animation: chat-arrive 0.6s ease-out; }
.context .msg.squeeze { animation: context-squeeze 1.2s ease-in-out infinite; }

/* the composer, across the room's width */
.context #say { display: flex; gap: 0.44em; align-items: flex-end; }
.context #text { flex: 1 1 auto; min-width: 0; }
.context #say.busy button { opacity: 0.5; }
.context #counts { display: contents; }
.context .tank.offline { opacity: 0.6; }

/* the window: a sidebar the page's full height, beside the room's
   column and its rails, a hairline between them; it scrolls inside if it
   must */
.context .window {
  position: fixed;
  z-index: 11;
  top: 0;
  bottom: 0;
  right: 0;
  width: var(--side);
  padding: calc(var(--spacing-lg) + env(safe-area-inset-top)) var(--spacing-lg) calc(var(--spacing-lg) + env(safe-area-inset-bottom));
  border-left: 1px solid var(--hairline-color);
  background-color: var(--app-background);
  display: flex;
  flex-direction: column;
  /* its clusters apart by the rails' gap */
  gap: 0.44em;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  font-variant-numeric: tabular-nums;
}
.context .window::-webkit-scrollbar { display: none; }
/* nothing in the window shrinks below its own height (squeezed, the
   pill and the prompt overlapped their neighbours); the stack alone
   grows into what's left, and the window scrolls when it must */
.context .window > *, .context #fold > * { flex-shrink: 0; }

/* the gauge: cut like the site's buttons and inputs, filled to the
   context's share of the budget, the fill clipped by its corners */
.context .meter {
  --share: calc(var(--used) / var(--budget) * 100%);
  position: relative;
  flex: none;
  overflow: hidden;
  border-radius: var(--border-radius);
  background-color: var(--wash-color);
  background-image: linear-gradient(to right, var(--fill-color) var(--share), transparent var(--share));
  --fill-color: color-mix(in oklch, var(--text-color) 16%, var(--wash-color));
}
.context .meter.full { --fill-color: color-mix(in oklch, var(--danger) 30%, var(--wash-color)); }
.context .meter .label { display: block; padding: var(--spacing-md) var(--control-inline-padding); line-height: 1.2em; white-space: nowrap; }
.context .meter b { font-weight: inherit; font-variation-settings: "wdth" 100, "wght" 560, "SRFF" 33, "slnt" 0; }

/* the budget: a slider anyone moves, and who moved it last */
.context .budget { display: flex; align-items: center; gap: 0.6em; padding: 0 var(--spacing-sm); }
.context .budget label { color: var(--muted-color); }
.context .budget input[type=range] { flex: 1 1 auto; min-width: 0; padding: 0; background: none; accent-color: var(--text-color); }
.context .budget output { min-width: 3.2em; text-align: right; }
.context .budget button { font-size: 0.82em; padding: 0.4em 0.9em; }
.context .budget.busy { opacity: 0.6; }
.context .mover { padding: 0 var(--spacing-sm); color: var(--muted-color); font-size: 0.82em; }
.context .mover b { font-weight: inherit; }

/* the panel folds to the gauge on phones; on desktops it's always open
   and its toggle hidden */
.context #fold { display: contents; }
.context #fold > summary { display: none; }

/* the stack: the rest of the sidebar's height inside a dashed edge,
   under what describes it; segments in
   prompt order, tiles apart by the rails' gap, each its share of the
   budget tall but never less than a line, so none is squashed (when
   small ones add up past the budget's height, the stack scrolls) */
.context .stack {
  display: flex;
  flex-direction: column;
  gap: 0.44em;
  flex: 1 0 14em;
  padding: 0.44em;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  border: 1px dashed var(--hairline-color);
  border-radius: calc(var(--border-radius) * 0.6);
}
.context .stack::-webkit-scrollbar { display: none; }
.context .seg {
  flex: 0 0 calc(var(--t) / var(--budget) * 100%);
  min-height: calc(1.35em + 1.1em);
  overflow: hidden;
  padding: 0.55em 0.75em;
  border-radius: calc(var(--border-radius) * 0.4);
  font-size: 0.72em;
  line-height: 1.35;
  /* a tile's last line fades into its padding rather than being cut */
  mask-image: linear-gradient(to bottom, black calc(100% - 0.55em), transparent);
  transition: flex-basis 0.5s ease, min-height 0.5s ease, opacity 0.4s ease, background-color 0.3s ease;
}
.context .seg .head { color: var(--text-color); }
.context .seg .tag { text-decoration: none; }
.context .seg .n { color: var(--muted-color); }
.context .seg .text { color: var(--secondary-text-color); }
.context .seg.system { background-color: var(--wash-color); }
.context .seg.msg { background-color: color-mix(in oklch, var(--c, var(--text-color)) 18%, var(--app-background)); }
.context .seg.model { background-color: color-mix(in oklch, var(--text-color) 9%, var(--app-background)); }
.context .seg.pinned {
  background-color: color-mix(in oklch, var(--c, var(--text-color)) 18%, var(--app-background));
  box-shadow: inset 3px 0 0 var(--text-color);
}
/* a summary: hatched, its label a link to the messages it stands for */
.context .seg.summary {
  background: repeating-linear-gradient(-45deg, var(--wash-color) 0 6px, color-mix(in oklch, var(--wash-color), var(--app-background)) 6px 12px);
}
.context .seg.summary .tag { text-decoration: underline; text-decoration-color: var(--hairline-color); text-underline-offset: 0.15em; }
/* growing in from nothing, and shrinking away */
.context .seg.entering, .context .seg.leaving { flex-basis: 0; min-height: 0; padding-block: 0; opacity: 0; }
/* the run a compaction is folding, while the summary is written */
.context .seg.squeeze { animation: context-squeeze 1.2s ease-in-out infinite; }

.context .parts, .context .footer { padding: 0.25em var(--spacing-sm); color: var(--muted-color); font-size: 0.72em; line-height: 1.6; }
.context #show-prompt { align-self: flex-start; font-size: 0.82em; padding: 0.5em 1em; }
.context #prompt {
  flex: none;
  max-height: 50vh;
  overflow: auto;
  padding: var(--spacing-md);
  border-radius: calc(var(--border-radius) * 0.6);
  background-color: var(--wash-color);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.66em;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.context #prompt[hidden], .context .lens[hidden] { display: none; }

/* a summary opened: what it says, then the messages it stands for */
.context .lens {
  flex: none;
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: calc(var(--border-radius) * 0.6);
  box-shadow: inset 0 0 0 1px var(--hairline-color);
  font-size: 0.82em;
  line-height: 1.4;
}
.context .lens-head { display: flex; justify-content: space-between; gap: 1em; color: var(--muted-color); }
.context .lens .said { margin: 0.4em 0; }
.context .lens .covered { max-height: 16em; overflow-y: auto; }
.context .lens .covered li { padding: 0.15em 0; }
.context .lens .covered b { font-weight: inherit; }
.context .lens .covered time { color: var(--muted-color); }

/* importance: four quiet dots filled to the level; "next to go", the
   messages compaction takes next, least important first */
.context .imp { color: var(--muted-color); font-size: 0.6em; letter-spacing: 0.1em; cursor: help; white-space: nowrap; }
.context .going { color: var(--danger); font-size: 0.82em; }
.context .msg.going .body { text-decoration: underline dotted color-mix(in oklch, var(--danger) 50%, transparent); text-underline-offset: 0.2em; }
.context .seg.going { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--danger) 60%, transparent), inset 0 -1px 0 var(--app-background); }

/* what a reply saw: a link on it, its text opening the same, and the
   alternatives regenerated without one segment, side by side under it */
.context .saw-link { color: var(--muted-color); font-size: 0.82em; text-decoration: none; }
.context .saw-link:hover { text-decoration: underline; }
.context .msg.model[id] .body { cursor: pointer; }
.context .alts { display: grid; grid-template-columns: repeat(auto-fit, minmax(12em, 1fr)); gap: var(--spacing-sm); margin-top: var(--spacing-sm); }
.context .alt { padding: var(--spacing-sm) 0.7em; border-radius: 0.6em; box-shadow: inset 0 0 0 1px var(--hairline-color); font-size: 0.9em; line-height: 1.35; }
.context .alt .without { color: var(--muted-color); font-size: 0.86em; margin-bottom: 0.2em; }
.context .alt .writing { color: var(--muted-color); animation: context-squeeze 1.2s ease-in-out infinite; }

/* the panel in "as it saw it" mode: the live stack set aside, the
   recorded prompt as a list, each row's bar its share of the budget then */
.context .window.sawing #fold { display: none; }
.context .saw[hidden] { display: none; }
.context .saw { flex: none; display: flex; flex-direction: column; gap: var(--spacing-sm); }
.context .saw-head { display: flex; align-items: center; justify-content: space-between; gap: 1em; padding: 0 var(--spacing-sm); }
.context .saw-head .back { font-size: 0.82em; padding: 0.5em 1em; }
.context .saw-meta { padding: 0 var(--spacing-sm); color: var(--muted-color); font-size: 0.72em; }
.context .saw-stack { display: flex; flex-direction: column; gap: 2px; }
.context .saw-stack .seg {
  position: relative;
  padding: 0.35em 0.55em;
  font-size: 0.72em;
  line-height: 1.35;
  border-radius: 0.4em;
  background-color: transparent;
  box-shadow: inset 0 0 0 1px var(--hairline-color);
  overflow: hidden;
}
.context .saw-stack .seg .bar {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--t) / var(--budget) * 100%);
  min-width: 3px;
  z-index: -1;
  background-color: color-mix(in oklch, var(--c, var(--text-color)) 18%, var(--app-background));
}
/* not the live stack's proportional, size-contained segments */
.context .saw-stack .seg { isolation: isolate; container-type: normal; flex: none; transition: none; }
.context .saw-stack .seg.system .bar { background-color: var(--wash-color); }
.context .saw-stack .seg.summary .bar { background: repeating-linear-gradient(-45deg, var(--wash-color) 0 6px, color-mix(in oklch, var(--wash-color), var(--app-background)) 6px 12px); }
.context .saw-stack .seg .text { display: block; color: var(--secondary-text-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.context .saw-stack .seg.changed { opacity: 0.7; }
.context .saw-stack .since { color: var(--danger); }
.context .saw-stack .without, .context .saw-stack .asked { float: right; margin-left: 0.5em; }
.context .saw-stack .without button { font-size: 0.9em; padding: 0.2em 0.7em; }
.context .saw-stack .asked { color: var(--muted-color); }

/* attention: Jev's latest judgments, one quiet line each */
.context .attention { padding: 0.25em var(--spacing-sm); font-size: 0.72em; line-height: 1.6; color: var(--muted-color); }
.context .attention .label { display: block; margin-bottom: 0.2em; }
.context .attention li + li { margin-top: 0.15em; }
.context .attention .label { color: var(--secondary-text-color); }
.context .attention li { display: flex; gap: 0.5em; white-space: nowrap; cursor: help; }
.context .attention li a { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; text-decoration: none; }
.context .attention li .p { font-variant-numeric: tabular-nums; }
.context .attention li.yes, .context .attention li.yes a { color: var(--text-color); }

@keyframes context-squeeze { 50% { opacity: 0.45; } }
@keyframes context-caret { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) {
  .context .seg, .context .msg { transition: none; }
  .context .seg.squeeze, .context .msg.squeeze, .context .fresh, .context .typing .body::after, .context .judged.pending { animation: none; }
}

/* phones: the window is a band under the top rail, folded to its gauge
   and a toggle; opened, it drops over the room */
@media (width <= 760px) {
  .context { --window: auto; --side: 0px; }
  .context .log { padding: calc(var(--top-overlay-height) + 6.5em) var(--spacing-lg) var(--bottom-overlay-height); }
  .context .window {
    z-index: 9;
    top: var(--top-overlay-height);
    left: 16px;
    right: 16px;
    bottom: auto;
    width: auto;
    padding: 0 0 var(--spacing-sm);
    border-left: none;
    max-height: calc(100dvh - var(--top-overlay-height) - var(--bottom-overlay-height));
  }
  /* one rail across the top, solid, with the folded window right under it */
  .context #top-container::before { bottom: 0; background: var(--app-overlay-solid); }
  .context .mover { display: none; }
  .context #fold { display: flex; flex-direction: column; gap: var(--spacing-sm); }
  .context #fold > summary {
    display: block;
    align-self: flex-start;
    padding: 0.5em 1em;
    border-radius: var(--border-radius);
    background-color: var(--wash-color);
    font-size: 0.82em;
    cursor: pointer;
    list-style: none;
  }
  .context #fold > summary::-webkit-details-marker { display: none; }
  .context #fold[open] > summary { background-color: var(--active-background); color: var(--active-text); }
  .context .stack { height: 45dvh; }
}

/* the map rotation: the clock, the board's tabs, the match's end and the
   vote, and the move to the next match */
.blocks #clock { font-variant-numeric: tabular-nums; }
.blocks #clock[hidden] { display: none; }
.blocks #scoreboard .tabs { display: flex; flex-wrap: wrap; gap: 0.2em var(--spacing-md, 1em); margin-bottom: var(--spacing-xs, 0.25em); font-size: 0.85em; color: var(--muted-color); }
.blocks #scoreboard .tabs span { white-space: nowrap; }
.blocks #scoreboard .tabs .on { color: var(--text-color); font-weight: 600; }
.blocks #scoreboard table[hidden], .blocks #career[hidden] { display: none; }
.blocks #career h3 { margin-top: var(--spacing-sm); font-size: 0.85em; color: var(--muted-color); font-weight: 500; }
.blocks #career ol { font-size: 0.85em; padding-left: 1.4em; }
.blocks:not(:has(canvas[data-level])) #scoreboard .tabs [data-tab="career"] { display: none; }

/* seasons: the Season tab (TAB, 5), badges on the board */
.blocks #season[hidden] { display: none; }
.blocks #season { --season: var(--text-color); }
.blocks #season h3 { margin: var(--spacing-sm, 0.5em) 0 var(--spacing-xs, 0.25em); font-size: 0.85em; color: var(--muted-color); font-weight: 500; }
.blocks #season .season-name { color: var(--season); font-size: 1.05em; font-weight: 600; margin-top: 0; }
.blocks #season .season-level { font-size: 1.4em; margin: 0.1em 0; font-variant-numeric: tabular-nums; }
.blocks #season .season-level span { font-size: 0.6em; color: var(--muted-color); }
.blocks #season .season-bar { height: 0.55em; border-radius: 0.3em; background: var(--hairline-color); overflow: hidden; }
.blocks #season .season-bar span { display: block; height: 100%; background: var(--season); }
.blocks #season .season-warn { font-size: 0.8em; border-left: 2px solid var(--season); padding-left: 0.5em; }
.blocks #season .season-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: var(--spacing-md, 1em); }
@media (max-width: 700px) { .blocks #season .season-grid { grid-template-columns: 1fr; } }
.blocks #season table { width: 100%; font-size: 0.85em; }
.blocks #season tr.you { font-weight: 600; }
.blocks #season .season-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25em; margin: 0.2em 0; }
.blocks #season .season-kind { min-width: 7.5em; font-size: 0.8em; color: var(--muted-color); }
.blocks #season .season-pick { font-size: 0.78em; padding: 0.1em 0.55em; }
.blocks #season .season-pick.on { background: var(--active-background); color: var(--active-text); }
.blocks #season .season-pick.locked { opacity: 0.45; }
.blocks #season .season-lv { font-size: 0.85em; opacity: 0.8; }
.blocks #season .season-track { font-size: 0.82em; padding-left: 1.2em; margin: 0; }
.blocks #season .season-track .soon { font-style: italic; }
.blocks #season .season-soon { color: var(--muted-color); }
.blocks #scoreboard .badge { display: inline-block; width: 0.6em; height: 0.6em; margin-right: 0.45em; transform: rotate(45deg); vertical-align: 0.05em; }
.blocks #match {
  position: fixed;
  left: 50%;
  top: calc(var(--top-overlay-height) + var(--spacing-md, 1em));
  translate: -50% 0;
  z-index: 14;
  width: min(44em, calc(100% - 2 * var(--spacing-md, 1em)));
  max-height: calc(100% - var(--top-overlay-height) - var(--dock-bottom) - var(--chat-band) - var(--spacing-md, 1em));
  overflow: auto;
  padding: var(--spacing-md, 1em) var(--spacing-lg);
  border-radius: var(--border-radius);
  background-color: var(--app-overlay-solid);
  box-shadow: 0 0 0 1px var(--hairline-color);
  font-size: 0.9em;
  color: var(--secondary-text-color);
}
.blocks #match[hidden] { display: none; }
.blocks #match .why { text-align: center; font-size: 0.85em; color: var(--muted-color); }
.blocks #match .title { text-align: center; font-size: 1.5em; color: var(--text-color); margin: 0 0 var(--spacing-sm); }
.blocks #match table { margin: 0 auto; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.blocks #match th, .blocks #match td { padding: 0.2em 0.7em; text-align: right; white-space: nowrap; }
.blocks #match th:first-child, .blocks #match td:first-child { text-align: left; }
.blocks #match th { color: var(--muted-color); font-weight: 500; }
.blocks #match tr.you td { font-weight: 700; background-color: var(--wash-color); }
.blocks #match .swatch { display: inline-block; width: 0.8em; height: 0.8em; margin-right: 0.5em; border-radius: 2px; vertical-align: -0.05em; }
.blocks #match .honours { list-style: none; padding: 0; margin: var(--spacing-sm) 0; text-align: center; font-size: 0.9em; }
.blocks #match h3 { text-align: center; font-size: 1em; color: var(--text-color); margin: var(--spacing-md, 1em) 0 var(--spacing-sm); }
.blocks #match .vote { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5em, 1fr)); gap: var(--spacing-sm); }
.blocks #match .vote button {
  width: 100%;
  display: grid;
  justify-items: center;
  gap: 0.15em;
  padding: var(--spacing-sm);
  border-radius: var(--border-radius);
  border: 1px solid var(--hairline-color);
  background: var(--app-background);
  color: var(--text-color);
  font: inherit;
  cursor: pointer;
}
.blocks #match .vote button.mine { border-color: var(--text-color); box-shadow: 0 0 0 1px var(--text-color); }
.blocks #match .vote canvas { width: 5.5em; height: 5.5em; image-rendering: pixelated; border-radius: 4px; background: var(--wash-color); }
.blocks #match .vote .name { font-weight: 600; font-size: 0.9em; }
.blocks #match .vote .count { font-variant-numeric: tabular-nums; font-size: 0.85em; }
.blocks #match .vote .note { font-size: 0.75em; color: var(--muted-color); min-height: 1.2em; }
.blocks #match .left { text-align: center; margin-top: var(--spacing-sm); font-variant-numeric: tabular-nums; }
.blocks #next {
  position: fixed;
  inset: 0;
  z-index: 15;
  display: grid;
  place-items: center;
  background-color: color-mix(in oklch, var(--app-background) 70%, transparent);
  backdrop-filter: blur(4px);
}
.blocks #next[hidden] { display: none; }
.blocks #next p { font-size: 2em; color: var(--text-color); }
/* the vote over everything: the weapon bar and the help step aside */
.blocks:has(#match:not([hidden])) #hotbar { visibility: hidden; }
.blocks main:has(#match:not([hidden])) #intro { display: none; }

/* carrying (loadout.rs): a card for each hand, the drawn one ringed and
   larger; the support item G throws; build mode while it's on */
.blocks #hotbar #hand1, .blocks #hotbar #hand2 { min-width: 9.5em; }
.blocks #hotbar #hand1[aria-checked="true"], .blocks #hotbar #hand2[aria-checked="true"] { font-size: 1em; }
.blocks #hotbar .slot.empty .name { font-style: italic; }
/* crafting (craft.rs): the HUD's materials counter (filling as a craft
   goes), B's quick-craft menu, and the TAB screen's Craft tab */
.blocks #hotbar #materials { font-size: 0.86em; padding: 0; background-image: linear-gradient(to right, color-mix(in oklch, var(--text-color) 22%, transparent) var(--fill, 0%), transparent var(--fill, 0%)); font-variant-numeric: tabular-nums; }
.blocks #hotbar #materials .label { display: flex; align-items: center; gap: 0.55em; padding: var(--spacing-md) var(--control-inline-padding); }
.blocks #hotbar #materials .key { opacity: 0.7; }
.blocks #hotbar #materials.open, .blocks #hotbar #materials.crafting { outline: 2px solid var(--text-color); outline-offset: 2px; }
.blocks .mat, .blocks #craftmenu .c, .blocks #crafting .c { display: inline-flex; align-items: center; gap: 0.25em; }
.blocks .mat i, .blocks #craftmenu .c i, .blocks #crafting .c i { display: inline-block; width: 0.72em; height: 0.72em; border-radius: 2px; background: var(--swatch); box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.25); }
.blocks .mat b { font-weight: 600; min-width: 1.2em; }
.blocks #craftmenu {
  position: fixed;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  z-index: 14;
  width: min(34em, calc(100% - 2 * var(--spacing-md, 1em)));
  max-height: calc(100% - var(--top-overlay-height) - 7em);
  overflow: auto;
  padding: var(--spacing-sm) var(--spacing-md, 1em);
  border-radius: var(--border-radius);
  background-color: color-mix(in oklch, var(--app-background) 88%, transparent);
  backdrop-filter: blur(6px);
}
.blocks #craftmenu[hidden], .blocks #crafting[hidden] { display: none; }
.blocks #craftmenu .title { margin: 0 0 var(--spacing-xs, 0.25em); font-weight: 600; display: flex; justify-content: space-between; align-items: baseline; gap: 1em; }
.blocks #craftmenu .title .keys { font-weight: 400; font-size: 0.75em; color: var(--muted-color); }
.blocks #craftmenu .have { margin: var(--spacing-xs, 0.25em) 0 0; display: flex; flex-wrap: wrap; gap: 0.4em 1em; font-size: 0.82em; color: var(--secondary-text-color); font-variant-numeric: tabular-nums; }
.blocks .recipes { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.3em; }
.blocks .recipes .recipe { width: 100%; display: grid; grid-template-columns: 1.1em 1fr auto; grid-template-rows: auto auto; gap: 0 0.45em; align-items: center; padding: 0.35em 0.55em; border: 0; border-radius: calc(var(--border-radius) * 0.7); background-color: var(--wash-color); color: inherit; font: inherit; font-size: 0.85em; text-align: left; cursor: pointer; position: relative; overflow: hidden; }
.blocks .recipes .recipe:hover, .blocks .recipes .recipe:focus-visible { box-shadow: inset 0 0 0 2px var(--text-color); }
.blocks .recipes .recipe .n { grid-column: 1; grid-row: 1 / 3; font-size: 0.85em; color: var(--muted-color); font-variant-numeric: tabular-nums; }
.blocks .recipes .recipe .what { grid-column: 2; grid-row: 1; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.blocks .recipes .recipe .t { grid-column: 3; grid-row: 1; font-size: 0.8em; color: var(--muted-color); white-space: nowrap; }
.blocks .recipes .recipe .cost { grid-column: 2 / 4; grid-row: 2; display: flex; gap: 0.7em; font-size: 0.85em; font-variant-numeric: tabular-nums; }
.blocks .recipes .recipe .cost .short { color: var(--danger, oklch(55% 0.2 25)); }
.blocks .recipes .recipe.poor, .blocks .recipes .recipe.shut { opacity: 0.42; }
.blocks .recipes .recipe.busy::after { content: ""; position: absolute; inset: auto 0 0 0; height: 3px; background: var(--text-color); transform-origin: left; animation: craft-fill var(--left, 1s) linear forwards; }
@keyframes craft-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.blocks #scoreboard.crafts { pointer-events: auto; }
@media (max-width: 40em) { .blocks .recipes { grid-template-columns: minmax(0, 1fr); } }
.blocks.touch #craftmenu { top: calc(var(--top-overlay-height) + var(--spacing-sm)); translate: -50% 0; max-height: calc(100% - var(--top-overlay-height) - 2em); font-size: 0.92em; }
.blocks.touch #craftmenu .recipes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.blocks.touch #craftmenu .recipe { padding: 0.55em 0.6em; }
.blocks.touch #craftmenu .recipe { grid-template-columns: 0 1fr auto; }
.blocks.touch #craftmenu .recipe .n { display: none; }
.blocks.touch #craftmenu .title .keys { display: none; }
/* E's prompt: in the prompt band over the cards (see the overlay
   regions), where neither the chat nor the inspector reaches */
.blocks #hint { font-size: 0.86em; color: var(--text-color); }
/* the Beam drawn: a violet vignette that deepens the longer it's held,
   and the health pill pulsing violet */
.blocks #corrupt {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.8s ease;
  background: radial-gradient(ellipse at center, transparent calc(62% - var(--k, 0) * 38%), oklch(32% 0.2 300 / calc(var(--k, 0) * 0.95)) 100%);
}
.blocks #corrupt.on { opacity: 1; animation: blocks-corrupt 1.1s ease-in-out infinite; }
@keyframes blocks-corrupt { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.25); } }
.blocks #hp.corrupt { --pen: oklch(58% 0.22 300) !important; animation: blocks-soul 1.1s ease-in-out infinite; }
@keyframes blocks-soul { 0%, 100% { box-shadow: 0 0 0 0 oklch(62% 0.24 300 / 0); } 50% { box-shadow: 0 0 0 4px oklch(62% 0.24 300 / 0.55); } }
/* quad damage: the pill violet with its seconds left (pulsing its last
   five), and the weapon drawn glowing violet */
.blocks #boosts.quad { background: oklch(52% 0.22 300); color: white; font-weight: 600; box-shadow: 0 0 12px 2px oklch(62% 0.24 300 / 0.6); }
.blocks #boosts.quad.ending { animation: blocks-soul 0.5s ease-in-out infinite; }
.blocks #hotbar .slot.quad { box-shadow: 0 0 14px 3px oklch(62% 0.24 300 / 0.75); outline-color: oklch(62% 0.24 300); }
/* the TAB screen's inventory: it takes the pointer (the scoreboard
   doesn't); a row of two hands, a row of four for the backpack */
/* Settings: a column of controls, each a label, the control and its value */
.blocks #scoreboard.settings { pointer-events: auto; }
.blocks #settings[hidden] { display: none; }
/* the TAB screen's Map tab: the heatmap from above (heatmap.js) */
.blocks #heatmap[hidden] { display: none; }
.blocks #heatmap { margin-top: var(--spacing-sm); }
.blocks #heatmap canvas { display: block; width: min(56vh, 26em, calc(100vw - 4em)); aspect-ratio: 1; border-radius: var(--border-radius); }
.blocks #scoreboard.map { min-width: 0; }
.blocks #settings { display: grid; gap: var(--spacing-sm); margin-top: var(--spacing-sm); font-size: 0.9em; }
.blocks #settings .set { display: grid; grid-template-columns: 11em minmax(0, 1fr) 3.5em; align-items: center; gap: var(--spacing-sm); }
.blocks #settings .set.check { grid-template-columns: auto 1fr; justify-content: start; }
.blocks #settings .set output { text-align: right; font-variant-numeric: tabular-nums; color: var(--secondary-text-color); }
.blocks #settings fieldset.set { display: block; border: 0; padding: 0; margin: 0; }
.blocks #settings fieldset.set legend { padding: 0; margin-bottom: var(--spacing-xs, 0.25em); }
.blocks #settings fieldset.set label { margin-right: var(--spacing-md, 1em); }
.blocks #settings input[type=range] { width: 100%; accent-color: var(--text-color); }
.blocks #scoreboard.inv { pointer-events: auto; }
.blocks #scoreboard .tabs span { cursor: pointer; }
.blocks #inventory[hidden] { display: none; }
.blocks #inventory h3 { margin: var(--spacing-sm) 0 var(--spacing-xs, 0.25em); font-size: 0.85em; color: var(--muted-color); font-weight: 500; }
.blocks #inventory .inv-slots { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--spacing-sm); }
.blocks #inventory .hands { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.blocks #inventory .pack { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.blocks #inventory li { min-height: 6.4em; border-radius: var(--border-radius); background-color: var(--wash-color); display: flex; align-items: stretch; }
.blocks #inventory li.empty { outline: 1px dashed var(--hairline-color); outline-offset: -1px; }
.blocks #inventory li.drawn { box-shadow: inset 0 0 0 2px var(--text-color); }
.blocks #inventory li .none { margin: auto; font-size: 0.8em; color: var(--muted-color); }
.blocks #inventory .inv-arm { flex: 1; display: grid; grid-template-columns: 1fr auto; grid-template-rows: 1fr auto; align-items: end; gap: 0 0.4em; padding: 0.3em 0.5em; background: none; border: 0; text-align: left; cursor: grab; font: inherit; color: inherit; }
.blocks #inventory .inv-arm img { grid-column: 1 / -1; width: 100%; max-height: 3.6em; object-fit: contain; pointer-events: none; }
.blocks #inventory .inv-arm .name { font-weight: 600; font-size: 0.85em; }
.blocks #inventory .inv-arm .ammo { font-variant-numeric: tabular-nums; font-size: 0.8em; color: var(--secondary-text-color); }
.blocks #inventory .inv-arm .tag { position: absolute; font-size: 0.7em; color: var(--muted-color); }
.blocks #inventory li { position: relative; }
.blocks #inventory .inv-arm .tag { top: 0.3em; right: 0.5em; }
.blocks #inventory .inv-arm.picked { box-shadow: inset 0 0 0 2px var(--accent-color, var(--text-color)); border-radius: var(--border-radius); }
.blocks #inventory .inv-drop { margin: var(--spacing-sm) 0 0; padding: 0.5em; border-radius: var(--border-radius); outline: 1px dashed var(--hairline-color); outline-offset: -1px; text-align: center; font-size: 0.8em; color: var(--muted-color); }
.blocks #inventory .inv-also { margin-top: var(--spacing-sm); font-size: 0.8em; }
.blocks #inventory .inv-also p { margin: 0.15em 0; }
.blocks #inventory .inv-help { margin: var(--spacing-xs, 0.25em) 0 0; font-size: 0.75em; color: var(--muted-color); }
@media (max-width: 40em) { .blocks #inventory .pack { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Blocks' teams (blocks/teams.rs): a team match's two sides. Each side's
   colour (--side, from the server's data-teams) edges what's theirs, never
   replacing a player's own colour: the sides' frags in the top rail, the
   board's header and rows, the kill feed's lines, team chat, the vote's
   team card and the end's title. */
.blocks #teams { display: inline-flex; gap: 0.6em; font-variant-numeric: tabular-nums; }
.blocks #teams[hidden] { display: none; }
.blocks #teams .side { font-weight: 600; color: var(--side); }
.blocks #teams .side::before { content: ""; display: inline-block; width: 0.55em; height: 0.55em; margin-right: 0.35em; border-radius: 50%; background: var(--side); vertical-align: 0.05em; }
.blocks #teams .side.mine { text-decoration: underline; text-underline-offset: 0.2em; }
.blocks #scoreboard tr.side td { padding-top: 0.6em; border-bottom: 2px solid var(--side); color: var(--side); font-size: 0.95em; text-align: left; }
.blocks #scoreboard tr.side strong { font-weight: 700; }
.blocks #scoreboard tr[data-side] td:first-child, .blocks #match tr[data-side] td:first-child { box-shadow: inset 3px 0 0 var(--side); }
.blocks #feed li[data-side] { border-left: 3px solid var(--side); padding-left: 0.5em; }
.blocks #said li.team { border-left-color: var(--side); background-color: color-mix(in oklch, var(--side) 14%, var(--wash-color)); }
.blocks #said li.team .tag { margin-right: 0.45em; padding: 0 0.35em; border-radius: var(--border-radius); background: var(--side); color: var(--wash-color); font-size: 0.8em; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.blocks #sayform { display: flex; align-items: stretch; gap: 0.3em; }
.blocks #sayform[hidden] { display: none; }
.blocks #sayform #say { flex: 1; min-width: 0; }
.blocks #saychannel { flex: none; font-size: 0.8em; font-weight: 600; padding: 0 0.7em; border-radius: var(--border-radius); border: 1px solid var(--hairline-color); background: var(--wash-color); color: var(--text-color); cursor: pointer; }
.blocks #saychannel[hidden] { display: none; }
.blocks #saychannel[aria-pressed="true"] { background: var(--side); border-color: var(--side); color: var(--wash-color); }
.blocks #sayform.team #say { box-shadow: 0 0 0 2px var(--side); }
.blocks #match .vote button.teams { box-shadow: inset 0 0 0 2px light-dark(#d9620c, #ffa24a), inset 0 0 0 4px light-dark(#1f63d8, #6aa6ff); }
.blocks #match .vote .badge { font-size: 0.75em; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-color); }
.blocks #match.teamed .title { color: var(--side, var(--text-color)); }
.blocks #scoreboard tr.side .sum { color: var(--muted-color); font-weight: 400; }

/* Fivefold's structures (blocks/totems.rs, totems.js): the Totem status
   panel in the top rail (each side's heart, phase, what stands of its
   Totem and a pip a tower), the feed's structure lines, and the end's
   breach maps. Each side's colour is --side, as teams' is. */
.blocks #totem { display: inline-flex; gap: 0.7em; align-items: center; font-variant-numeric: tabular-nums; }
.blocks #totem[hidden] { display: none; }
.blocks #totem .tside { display: inline-flex; align-items: center; gap: 0.4em; color: var(--side); font-weight: 600; }
.blocks #totem .tside.mine .heart { text-decoration: underline; text-underline-offset: 0.2em; }
.blocks #totem .tside .phase { font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-color); opacity: 0.8; }
.blocks #totem .tside[data-phase="vulnerable"] .phase { color: var(--side); opacity: 1; }
.blocks #totem .tside[data-phase="exposed"] .phase, .blocks #totem .tside[data-phase="broken"] .phase { color: light-dark(oklch(50% 0.21 25), oklch(72% 0.18 25)); opacity: 1; font-weight: 800; }
.blocks #totem .tside .bar { display: inline-block; width: 1.6em; height: 0.42em; border-radius: 0.21em; background: color-mix(in oklch, var(--side) 22%, var(--wash-color)); overflow: hidden; }
.blocks #totem .tside .bar .fill { display: block; height: 100%; background: var(--side); }
.blocks #totem { font-size: 0.9em; }
.blocks #totem .tside .pips { display: inline-flex; gap: 1px; }
.blocks #totem .tside .pips i { display: inline-block; width: 0.34em; height: 0.62em; border-radius: 1px; border: 1px solid var(--side); background: linear-gradient(to top, var(--side) var(--k, 100%), transparent var(--k, 100%)); }
.blocks #totem .tside .pips i.ward { height: 0.8em; }
.blocks #totem .tside .pips i.felled { background: none; opacity: 0.45; }
.blocks #feed li.totem { font-weight: 600; }
.blocks #feed li.totem.big { font-weight: 800; color: var(--side, var(--text-color)); }
.blocks #match .breach .maps { display: flex; justify-content: center; gap: var(--spacing-md, 1em); flex-wrap: wrap; }
.blocks #match .breach figure { margin: 0; text-align: center; }
.blocks #match .breach canvas { width: 9em; image-rendering: pixelated; border-radius: 4px; background: var(--wash-color); box-shadow: inset 0 -3px 0 var(--side); }
.blocks #match .breach figcaption { font-size: 0.75em; color: var(--muted-color); margin-top: 0.2em; }
.blocks #match .breach .key { text-align: center; font-size: 0.72em; color: var(--muted-color); margin: 0.3em 0 0; }

/* Passport: one identity across the gallery. */
.passport-main { max-width: 1120px; margin: 110px auto 64px; padding: 0 28px; }
.passport-intro { margin-bottom: 38px; }
.passport-intro h1 { font-size: clamp(40px,6vw,76px); font-weight: 450; letter-spacing: -.045em; margin: 12px 0 20px; line-height: 1; }
.passport-intro p { font-size: 20px; line-height: 1.5; }
.passport-main .eyebrow { font-size: 11px; letter-spacing: .15em; font-weight: 600; }
.passport-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 54px; align-items: start; }
.passport-card { background: light-dark(oklch(93% .055 var(--hue)),oklch(30% .065 var(--hue))); color: light-dark(oklch(28% .07 var(--hue)),oklch(91% .04 var(--hue))); border: 1px solid light-dark(#0002,#fff2); border-radius: 26px; padding: 32px; min-height: 320px; position: sticky; top: 100px; overflow-wrap: anywhere; }
.passport-mark { font-family: sans-serif; font-size: 105px; line-height: 1; margin-bottom: 32px; color: oklch(60% .18 var(--hue)); }
.passport-card h2 { font-size: clamp(25px,3vw,38px); font-weight: 450; margin: 8px 0 14px; }
.card-bottom { display: flex; justify-content: space-between; gap: 12px; margin-top: 56px; padding-top: 18px; border-top: 1px solid #8885; font-size: 11px; letter-spacing: .04em; }
.passport-editor h2,.passport-secondary h2 { font-weight: 500; font-size: 23px; margin: 0 0 14px; }
.passport-editor form { display: flex; flex-direction: column; gap: 10px; }
.passport-editor label { margin-top: 16px; font-size: 14px; }
.passport-editor input:not([type=hidden]),.passport-editor textarea { width: 100%; box-sizing: border-box; padding: 13px 15px; border: 1px solid #8886; border-radius: 10px; background: var(--app-background); color: inherit; font: inherit; }
.passport-editor input[type=range] { padding: 0; accent-color: oklch(58% .15 var(--hue,220)); }
.passport-editor button[type=submit] { margin-top: 18px; padding: 14px; }
.passport-main button { cursor: pointer; }
.passport-main small,.passport-main .quiet { opacity: .7; font-size: 13px; line-height: 1.5; }
.swatches { display: flex; gap: 12px; padding: 6px 0; }
.swatches button { width: 27px; height: 27px; padding: 0; border: 2px solid #8884; border-radius: 50%; background: oklch(65% .17 var(--hue)); }
.passport-main details { margin-top: 30px; padding-top: 20px; border-top: 1px solid #8884; }
.passport-main summary { cursor: pointer; }
.passport-main details p { font-size: 14px; line-height: 1.5; }
.passport-secondary { display: grid; grid-template-columns: 1fr 1fr; gap: 54px; margin-top: 60px; }
.passport-secondary p { opacity: .75; font-size: 14px; line-height: 1.5; }
.device-list,.history-list { list-style: none; padding: 0; }
.device-list li,.history-list li { display: flex; align-items: center; gap: 12px; padding: 16px 0; border-bottom: 1px solid #8884; }
.device-list li>div,.history-list li>div { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.passport-secondary small { display: block; font-size: 11px; margin-top: 5px; }
.passport-secondary button { font-size: 12px; }
.current-device { font-size: 10px; opacity: .6; display: inline-block; }
.color-dot { display: inline-block; width: 18px; height: 18px; border-radius: 50%; background: oklch(65% .17 var(--hue)); flex-shrink: 0; }
.passport-footnote { margin-top: 54px; max-width: 540px; font-size: 14px; line-height: 1.6; opacity: .7; }
.passport-footnote strong { display: block; }
#notice:not(:empty) { background: light-dark(#f3eee0,#3a3429); border-radius: 10px; padding: 16px; margin-bottom: 24px; }
.key-save { border: 1px solid #b99b4c; background: light-dark(#fff9e7,#302c20); border-radius: 14px; padding: 20px; margin-bottom: 32px; }
.key-save p { font-size: 14px; line-height: 1.5; }
.key-save textarea { min-height: 90px; font-family: monospace; font-size: 13px; overflow-wrap: anywhere; }
.passport-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
[data-passport-badge] { border: 1px solid #8885; }
@media(max-width:700px) {
 .passport-main { margin-top: 100px; padding: 0 20px; }
 .passport-grid,.passport-secondary { grid-template-columns: 1fr; gap: 32px; }
 .passport-card { position: static; min-height: 220px; padding: 24px; }
 .passport-mark { font-size: 72px; margin-bottom: 20px; }
 .card-bottom { margin-top: 32px; }
 .passport-intro p { font-size: 17px; }
 .passport-secondary { margin-top: 40px; }
}
@media(max-width:700px) {
 .passport #top-container { position: relative; }
 .passport #top-container .pills { flex-wrap: wrap; overflow: visible; }
 .passport .passport-main { margin-top: 30px; }
}

/* One stable TAB window; only its contents scroll between pages. */
.blocks #scoreboard {
  --menu-gap: 0.75rem;
  --menu-top: calc(var(--top-overlay-height) + var(--menu-gap));
  box-sizing: border-box;
  top: var(--menu-top);
  width: min(60rem, calc(100% - 2 * var(--menu-gap) - env(safe-area-inset-left) - env(safe-area-inset-right)));
  height: min(44rem, calc(100dvh - var(--menu-top) - var(--bottom-overlay-height, 6rem) - var(--menu-gap)));
  min-width: 0;
  max-width: none;
  max-height: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  pointer-events: auto;
  container: blocks-menu / inline-size;
}
.blocks.touch #scoreboard {
  top: var(--menu-top);
  max-width: none;
  max-height: none;
}
.blocks #scoreboard .tabs {
  flex: none;
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 0.35em;
  margin: 0 0 0.65em;
}
.blocks #scoreboard .tabs span { padding: 0.4em 0; }
.blocks #scoreboard .tab-pages {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow: auto;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
}
.blocks #scoreboard #classes { max-height: none; overflow: visible; }
.blocks #scoreboard #heatmap canvas { width: min(100%, 26rem); height: auto; margin-inline: auto; }
.blocks #scoreboard #heatmap .inv-help { max-width: 32rem; margin-inline: auto; }
@container blocks-menu (max-width: 50rem) {
  .blocks #scoreboard .tabs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@container blocks-menu (max-width: 38rem) {
  .blocks #season .season-grid { grid-template-columns: minmax(0, 1fr); }
  .blocks #inventory .pack { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .blocks #scoreboard .recipes { grid-template-columns: minmax(0, 1fr); }
  .blocks #settings label.set:not(.check) { grid-template-columns: minmax(0, 1fr) 3.5em; }
  .blocks #settings label.set:not(.check) > span:first-child { grid-column: 1 / -1; }
}
@container blocks-menu (max-width: 26rem) {
  .blocks #scoreboard .tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* One footer: vitals on the left, carried equipment on the right. */
.blocks .footer-controls {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem; width: 100%;
  position: relative; z-index: 1;
}
.blocks #vitals { display: flex; flex: none; }
.blocks .footer-controls #hotbar { justify-content: flex-end; min-width: 0; }
.blocks.touch #input-container {
  padding-inline: max(0.6em, env(safe-area-inset-left)) max(0.6em, env(safe-area-inset-right));
}
/* Empty footer space passes through; every control remains tappable. */
.blocks #input-container > .footer-controls { pointer-events: none; }
.blocks .footer-controls .pill { pointer-events: auto; }

/* In-world HUD: silhouettes, ammunition and instrument-like vital strips. */
.blocks .footer-controls {
  --hud-text-size: 12px;
  --hud-row-height: 40px;
  --hud-row-gap: 6px;
  --hud-side-width: clamp(160px, 26vw, 260px);
  --hud-weapon-cols: 1ch minmax(0, 1fr) 3ch 5ch 64px;
  --hud-cell-gap: 6px;
  font-weight: 400;
  color: #f4f8f3;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: var(--hud-text-size);
  text-shadow: none;
}
.blocks .footer-controls .pill,
.blocks .footer-controls .tank,
.blocks .footer-controls #materials {
  border: 0;
  border-radius: 0;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
  color: inherit;
}
.blocks .footer-controls .tank .label { color: inherit; }
.blocks .footer-controls #vitals,
.blocks .footer-controls #hotbar {
  display: grid; grid-template-rows: repeat(3, var(--hud-row-height)); gap: var(--hud-row-gap);
  align-items: stretch; padding: 0;
}
.blocks .footer-controls #vitals { width: var(--hud-side-width); }
.blocks .footer-controls .hud-row {
  box-sizing: border-box; height: var(--hud-row-height); min-width: 0; padding: 0;
  display: grid; align-items: center; line-height: 1.2;
  border: 0; border-bottom: 1px solid #ffffff60;
}
.blocks .footer-controls #vitals .tank {
  display: grid; position: relative; overflow: visible; width: 100%; min-width: 0;
  grid-template-columns: minmax(0, 1fr) 4ch; gap: 8px;
}
.blocks .footer-controls #vitals .tank .label { display: contents; padding: 0; }
.blocks #vitals .count { grid-column: 2; grid-row: 1; text-align: right; font-size: var(--hud-text-size); }
.blocks #vitals .tank::after {
  content: ''; grid-column: 1; grid-row: 1; width: 100%; height: 6px;
  background: linear-gradient(90deg, var(--vital-color) var(--fill), #ffffff30 0);
  mask-image: repeating-linear-gradient(90deg, #000 0 calc(25% - 2px), transparent 0 25%);
  box-shadow: none;
}
.blocks #hp { --vital-color: #a2f5cb; }
.blocks #armor { --vital-color: #b8dfff; }
.blocks #hp.critical { --vital-color: #ff7665; color: #ffaaa0; }
.blocks #hp.corrupt { --vital-color: #d09aff; }
.blocks .footer-controls #hotbar {
  grid-template-columns: minmax(0, 1fr);
  width: var(--hud-side-width);
}
.blocks .footer-controls #hotbar .slot[data-hand] {
  grid-column: 1 / -1; min-width: 0; height: var(--hud-row-height);
  display: grid; grid-template-columns: var(--hud-weapon-cols); align-items: center; gap: var(--hud-cell-gap);
  font-size: var(--hud-text-size); position: relative; padding: 0;
  border-bottom: 1px solid #ffffff60; text-align: left; outline: none;
}
/* Hand order stays fixed when switching; only the accent marks the drawn one. */
.blocks .footer-controls #hotbar #hand1 { grid-row: 1; }
.blocks .footer-controls #hotbar #hand2 { grid-row: 2; }
.blocks .footer-controls #hotbar .slot[data-hand][aria-checked="true"] { border-bottom-color: #b8f5d7; }
.blocks .footer-controls #hotbar .slot[data-hand][aria-checked="true"]::before {
  content: ''; position: absolute; left: -7px; top: 50%; transform: translateY(-50%); width: 3px; height: 10px; background: #b8f5d7;
}
.blocks #hotbar .slot .label { padding: 0; background: none; display: flex; align-items: baseline; gap: 8px; }
.blocks .footer-controls #hotbar .slot[data-hand] .label { display: contents; }
.blocks .footer-controls #hotbar .slot[data-hand] .key { display: block; grid-column: 1; grid-row: 1; }
.blocks .footer-controls #hotbar .slot[data-hand] .name {
  grid-column: 2; grid-row: 1; display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-size: var(--hud-text-size); font-style: normal;
}
.blocks #hotbar .slot .key { font-size: var(--hud-text-size); color: #c4d2ce; }
.blocks .footer-controls #hotbar .slot[data-hand] .count { grid-column: 3; grid-row: 1; margin: 0; text-align: right; font-size: var(--hud-text-size); font-weight: 400; }
.blocks .footer-controls #hotbar .weapon-model {
  grid-column: 5; grid-row: 1; display: block; width: 64px; height: 28px; object-fit: contain;
  filter: brightness(0) invert(1);
}
.blocks .footer-controls #hotbar .weapon-model[hidden] { display: none; }
.blocks .weapon-reserve { grid-column: 4; grid-row: 1; text-align: right; font-size: var(--hud-text-size); color: #cfdfd9; }
.blocks .weapon-state { display: none; position: absolute; top: 50%; transform: translateY(-50%); left: calc(1ch + var(--hud-cell-gap)); max-width: 35%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--hud-text-size); }
.blocks .slot.reloading .weapon-state { color: #ffe2a0; }
.blocks .footer-controls #hotbar #support,
.blocks .footer-controls #materials {
  grid-column: 1 / -1; justify-self: stretch; height: var(--hud-row-height); min-width: 0; padding: 0;
  display: flex; align-items: center; font-size: var(--hud-text-size);
  border: 0; border-bottom: 1px solid #ffffff60; text-align: left;
}
.blocks .footer-controls #hotbar #support { grid-row: 3; }
.blocks .footer-controls #materials { grid-row: 3; }
.blocks .footer-controls #hotbar #support .label {
  display: grid; grid-template-columns: var(--hud-weapon-cols); gap: var(--hud-cell-gap); align-items: center; width: 100%;
}
.blocks .footer-controls #hotbar #support .name { display: block; grid-column: 2; grid-row: 1; min-width: 0; }
.blocks .footer-controls #hotbar #support .count { grid-column: 3; grid-row: 1; text-align: right; }
.blocks .footer-controls #hotbar #support .key { grid-column: 1; grid-row: 1; justify-self: start; }
.blocks .footer-controls #hotbar #support .support-action { grid-column: 4 / 6; grid-row: 1; justify-self: end; white-space: nowrap; }
.blocks .footer-controls #materials .label { display: flex; align-items: center; width: 100%; padding: 0; gap: 8px; }
.blocks .footer-controls #materials .key { margin-right: auto; }
.blocks #hotbar .slot:focus-visible, .blocks #materials:focus-visible { outline: 2px solid #b8f5d7; outline-offset: 4px; }
.blocks #hotbar .slot:hover { border-bottom-color: #b8f5d7; }
.blocks.touch #vitals .label { padding: 0; }
@media (max-width: 600px) {
  .blocks .footer-controls { --hud-row-height: 36px; --hud-row-gap: 5px; --hud-side-width: calc((100% - 14px) / 2); --hud-weapon-cols: 1ch minmax(0, 1fr) 2ch 4ch 40px; --hud-cell-gap: 3px; }
  .blocks .footer-controls { gap: 14px; }
  .blocks .footer-controls #hotbar #support, .blocks .footer-controls #materials { height: var(--hud-row-height); }
  .blocks .footer-controls #hotbar #support .label { grid-template-columns: var(--hud-weapon-cols); gap: var(--hud-cell-gap); }
  .blocks .footer-controls #hotbar .slot[data-hand] { height: var(--hud-row-height); grid-template-columns: var(--hud-weapon-cols); gap: var(--hud-cell-gap); }
  .blocks .footer-controls #hotbar .weapon-model { width: 40px; height: 24px; }
}
@media (orientation: landscape) {
  .blocks.touch .footer-controls { --hud-row-height: 36px; }
  .blocks.touch #touch { --lift: 8rem; --u: calc(var(--tb, 1) * 0.7px); }
  .blocks.touch .footer-controls #hotbar .slot[data-hand],
  .blocks.touch .footer-controls #hotbar #support,
  .blocks.touch .footer-controls #materials { height: var(--hud-row-height); }
  .blocks.touch .footer-controls #hotbar .weapon-model { height: 26px; }
}
.blocks #input-container::before {
  content: ''; display: block; position: absolute; inset: -3rem 0 0; z-index: 0; pointer-events: none;
  background: linear-gradient(transparent, #0b151b99);
}
.blocks.touch #hotbar .slot .label { padding: 0; }
@media (orientation: portrait) {
  .blocks.touch #touch { --lift: 9rem; }
}
.blocks .footer-controls button, .blocks .footer-controls .label { text-shadow: inherit; }


.blocks .footer-controls #hotbar .slot.text-count[data-hand] .count {
  font-size: var(--hud-text-size); line-height: 1.2; grid-column: 3 / 5; white-space: normal; text-align: right;
}

/* Keep the footer to silhouettes, bars, numbers and key hints. */
.blocks .footer-controls #hotbar .weapon-state { display: none; }
.blocks .footer-controls #hotbar .reloading .weapon-state { display: block; }
.blocks .footer-controls #hotbar .reloading .name { visibility: hidden; }
.blocks .footer-controls #vitals,
.blocks .footer-controls #vitals .label { text-shadow: none; }
.blocks .footer-controls #hotbar .support-action,
.blocks .footer-controls .mat b {
  font-size: var(--hud-text-size); font-weight: 400;
}

@media (max-width: 600px) {
  .blocks .footer-controls #materials .label { gap: 4px; }
  .blocks .footer-controls #materials .key { display: none; }
  .blocks .footer-controls #materials .mat { flex: 1; min-width: 0; }
}

/* Class abilities share the footer's row rhythm and unshadowed typography. */
.blocks .footer-controls #abilities {
  position: absolute; left: 50%; top: 0; bottom: auto; translate: -50% 0;
  width: min(220px, calc(100% - 2 * var(--hud-side-width) - 48px));
  display: grid; grid-template-rows: repeat(2, var(--hud-row-height)); gap: var(--hud-row-gap);
}
.blocks .footer-controls #abilities[hidden] { display: none; }
.blocks .footer-controls .abicon {
  width: 100%; height: var(--hud-row-height); border-radius: 0; background: none;
  grid-template-columns: 1ch minmax(0, 1fr) 3ch; gap: 8px; padding: 0;
  color: inherit; font: inherit; text-shadow: none; box-shadow: none;
  place-items: center stretch; pointer-events: auto;
}
.blocks .footer-controls .abicon .key,
.blocks .footer-controls .abicon .name,
.blocks .footer-controls .abicon .left {
  position: static; width: auto; font: inherit; text-shadow: none; text-align: left;
}
.blocks .footer-controls .abicon .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.blocks .footer-controls .abicon .left { text-align: right; }
.blocks .footer-controls .abicon .ring {
  inset: auto 0 -1px; height: 1px; border-radius: 0; mask: none;
  background: linear-gradient(90deg, #b8f5d7 calc(var(--k, 1) * 100%), #ffffff60 0);
}
.blocks .footer-controls .abicon .charge { left: 0; right: 0; bottom: 3px; height: 2px; border-radius: 0; }
@media (max-width: 700px) {
  .blocks .footer-controls #abilities {
    top: auto; bottom: calc(100% + var(--hud-row-gap)); width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: var(--hud-row-height); gap: 14px;
  }
}

/* Keyboard bindings stay inside the existing scrolling Tab page. */
.blocks #key-bindings { min-width: 0; border: 0; padding: 0; margin: 0; }
.blocks #key-bindings h3 { font-size: 1em; margin: 0.8em 0; }
.blocks .binding-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(6em, auto); align-items: center; gap: 1em; margin: 0.4em 0; }
.blocks .binding-row button { min-height: 2.5em; overflow-wrap: anywhere; }
.blocks #key-bindings > button { margin: 0.3em 0.5em 0.3em 0; }
/* Keep the menu's last controls clickable above the fixed combat HUD. */
.blocks #scoreboard { z-index: 31; }
html.titling body.blocks > #scoreboard { display: none !important; }

/* Title lettering stays clean against the moving sky. */
html.titling .blocks #title, html.titling .blocks #title * { text-shadow: none; filter: none; }
