/* Bike or Die 2026 — the shell.
 *
 * Every colour in this file is a var() from tokens.css. If a literal hex ever
 * appears here, the palette has been forked and the theme switch is broken.
 * The sheet has hairlines and right angles: nothing is rounded above 3px and
 * there are no shadows on anything.
 */

*, *::before, *::after { box-sizing: border-box; }

/* Our own display rules outrank the UA sheet, so say it once and mean it. */
[hidden] { display: none !important; }

/* `color-scheme: light dark` in tokens.css lets native form controls pick
   their own text colour from the OS/browser preference -- which is *not*
   the same switch as our own `data-theme`. A `<button>` or `<select>` that
   never gets an explicit `color` here falls back to that UA default: white
   on a system set to dark, even while `data-theme="light"` has the card
   around it sitting on a light --panel. Every card/row is a bare <button>
   (ccard, card--sm, row, dfcard--start, ...), so this is the one place that
   has to hold for all of them at once, rather than chasing it class by
   class. */
button, select { color: inherit; font: inherit; }

body {
  min-height: 100dvh;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}
body[data-screen="play"] { user-select: none; -webkit-user-select: none; }

/* A control is not a document. `user-select` alone does not stop a long press
   on a button's glyph from raising the OS text menu -- iOS reads
   `-webkit-touch-callout` for the callout and Android Chrome starts a
   selection from the text node before any `preventDefault` on the pointer
   event lands -- so the pads, the canvas and the HUD say all three, and say it
   outside `[data-screen]` so it holds through a screen change too. */
.pad, .deck, .hud, #cv, #edov {
  -webkit-touch-callout: none;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* --- a touch device is an instrument, not a document ----------------------
 *
 * On a coarse pointer nothing zooms and nothing selects. This is a game with
 * a one-viewport contract (8.2): a pinch that leaves the canvas half off the
 * screen mid-run, or a long press that raises copy/translate over the
 * controls, is a lost run either way.
 *
 * It takes all four of these, because each engine ignores a different one:
 *
 *   `touch-action`     kills the pinch on Android Chrome and the double-tap
 *                      zoom everywhere, including iOS 9.3+. iOS does not
 *                      honour it for *page* zoom -- `lockZoom()` in dom.js
 *                      covers that with the WebKit gesture events.
 *   `user-scalable=no` honoured everywhere except iOS, which has ignored it
 *                      since 10. Kept for the engines that do read it.
 *   `user-select`      the selection itself.
 *   `touch-callout`    the iOS copy/translate menu, which appears from a long
 *                      press even where a selection cannot be made.
 *
 * All of it is gated on `pointer: coarse`, so a desktop browser keeps its
 * zoom and its selection -- including a touch laptop driven by a mouse, whose
 * *primary* pointer is fine. That is the same gate the deck and the 56 px
 * targets already use.
 */
@media (pointer: coarse) {
  html { touch-action: pan-x pan-y; }
  body {
    -webkit-touch-callout: none;
    -webkit-user-select: none; user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  /* Two exceptions, both of them so something stays *possible*:
     a field you cannot select text in is a field you cannot edit, and a toast
     is where a share link lands when `navigator.clipboard` is not there to
     take it (app.js `shareDraft`). */
  input, textarea, [contenteditable="true"], .toast {
    -webkit-touch-callout: default;
    -webkit-user-select: text; user-select: text;
  }
  /* iOS zooms the page when a field under 16px takes focus, which is a zoom
     none of the above can reach -- it is the browser, not a gesture, and it
     fires for `select` as well as for a text field. `.search` and `.select`
     are 14px and `.ednum` inherits 12px from `.edfield`, so this has to
     out-specify a class: `:root` + the element is 0-1-1 and clears 0-1-0
     without reaching for `!important`. */
  :root input, :root select, :root textarea { font-size: 16px; }
}

.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: 8px; top: -60px; z-index: 60;
  background: var(--panel); color: var(--ink); border: 1px solid var(--rule-2);
  padding: 8px 12px; border-radius: var(--radius); text-decoration: none;
}
.skip:focus { top: 8px; }

.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.meta { color: var(--muted); font-size: 13px; }
/* A disclosed engine limitation, not decoration: portal levels report the
   wrong outcome (KNOWN-DEFECTS D14), so the sheet says so. Same colour as
   the clock running out and a warning toast. */
.meta.is-warn { color: var(--spark); }
.note { color: var(--ink-2); font-size: 14px; margin: 12px 0; }

/* --- buttons -------------------------------------------------------------- */
.btn {
  font-family: var(--font-ui); font-size: 14px; font-weight: 500;
  min-height: 44px; padding: 8px 14px;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--rule-2); border-radius: var(--radius);
  cursor: pointer; transition: border-color 120ms ease-out, color 120ms ease-out;
}
.btn:hover { border-color: var(--annot); color: var(--annot); }
.btn--primary {
  background: var(--signal); border-color: var(--signal); color: var(--sheet);
  font-weight: 700;
}
.btn--primary:hover { color: var(--sheet); border-color: var(--signal); filter: brightness(1.08); }
.btn--ghost { background: transparent; border-color: var(--rule); }
.btn--danger { color: var(--fail); border-color: var(--fail); }
/* Only ever on screen when there is a newer build waiting, so it is allowed to
   be the loudest thing in the top bar -- and the signal colour is what the
   rest of the page already means by "act on this". */
.btn--update {
  background: var(--signal); border-color: var(--signal); color: var(--sheet);
  font-weight: 700;
}
.btn--update:hover { color: var(--sheet); border-color: var(--signal); filter: brightness(1.08); }
/* Five words of navigation already do not fit a 320 px bar (see the media
   query at the foot of this section), so the sixth control cannot be a word
   there. `aria-label` carries the meaning either way. */
.upd-short { display: none; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: default; }

kbd {
  font-family: var(--font-mono); font-size: 11px; line-height: 1;
  padding: 4px 6px; min-width: 24px; display: inline-block; text-align: center;
  border: 1px solid var(--rule-2); border-bottom-width: 2px;
  border-radius: 3px; background: var(--panel); color: var(--ink-2);
}

/* --- top bar -------------------------------------------------------------- */
.topbar {
  height: 48px; display: flex; align-items: center; gap: 16px;
  padding: 0 16px; border-bottom: 1px solid var(--rule);
  background: var(--sheet-sunk);
}
.wordmark {
  font-family: var(--font-ui); font-weight: 700; font-size: 13px;
  letter-spacing: .02em; white-space: nowrap;
}
.wordmark b { color: var(--signal); }
.crumb { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.15; }
.crumb-t {
  font-size: var(--fs-name); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.crumb-s { font-size: var(--fs-read-sm); color: var(--muted); }
.topnav { display: flex; gap: 8px; }
.topnav .btn { min-height: 34px; padding: 5px 10px; font-size: 13px; }

/* --- the stage ------------------------------------------------------------ */
/* On S1 the page is exactly one viewport tall: top bar, canvas, legend strip,
   all on screen at once and nothing to scroll to. The canvas keeps its 16:10
   (§8.2) and gives up width when the window is too short for it, so the frame
   is sized off the stage's height rather than the other way round -- hence the
   size container. `--deck-h` is whatever the touch deck takes out of that
   height; it is 0 wherever the deck is not in the flow. */
.screen { display: block; }
.stage {
  position: relative; background: var(--sheet-sunk);
  --deck-h: 0px;
}
.frame { position: relative; }
body[data-screen="play"] {
  height: 100dvh; overflow: hidden;
  display: flex; flex-direction: column;
}
body[data-screen="play"] main,
body[data-screen="play"] .screen--play,
body[data-screen="play"] .stage {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}
body[data-screen="play"] .stage { container-type: size; justify-content: center; }
body[data-screen="play"] .frame {
  width: min(100%, calc((100cqh - var(--deck-h)) * 1.6));
  margin-inline: auto;
}
body[data-screen="play"] .strip { flex: none; }
#cv {
  display: block; width: 100%; aspect-ratio: 16 / 10; height: auto;
  background: var(--sky); touch-action: none;
}
#cv:focus-visible { outline: 2px solid var(--annot); outline-offset: -4px; }

.hud {
  position: absolute; left: 16px; right: 16px; top: 16px; height: 28px;
  display: flex; align-items: center; gap: 16px; z-index: 3;
  pointer-events: none; transition: opacity 200ms ease-out;
}
.hud.is-dim { opacity: .35; }
body.reduce-motion .hud.is-dim { opacity: 1; }
body.hud-large .hud { height: 35px; font-size: 125%; }

.hud-clock {
  font-size: var(--fs-read); font-weight: 500; color: var(--ink);
  text-shadow: 0 0 2px var(--sky);
}
.hud-clock.is-warn { color: var(--spark); }
.hud-bar {
  width: 120px; height: 2px; background: var(--rule); display: inline-block;
}
.hud-bar > span { display: block; height: 100%; background: var(--ink); }
.hud-bar.is-warn > span {
  background: repeating-linear-gradient(45deg,
    var(--spark) 0 3px, transparent 3px 6px), var(--spark);
}
.hud-flags { display: flex; align-items: center; gap: 8px; margin-inline: auto; }
.hud-flags .label { color: var(--muted); }
.pips { display: inline-flex; gap: 3px; }
.pip {
  width: 7px; height: 7px; border: 1px solid var(--flag); display: inline-block;
}
.pip.is-on { background: var(--flag); }
.hud-finish {
  font-size: var(--fs-read-sm); color: var(--muted); letter-spacing: .06em;
}
.hud-finish.is-open { color: var(--pass); font-weight: 700; }
.hud-finish.is-static { opacity: .7; }
.hud-hof {
  pointer-events: auto; min-width: 32px; min-height: 28px;
  font-family: var(--font-mono); font-size: 13px; line-height: 1;
  background: transparent; color: var(--muted);
  border: 1px solid var(--rule); border-radius: var(--radius); cursor: pointer;
}
.hud-pause {
  pointer-events: auto; min-width: 32px; min-height: 28px;
  font-family: var(--font-mono); font-size: 11px;
  background: transparent; color: var(--muted);
  border: 1px solid var(--rule); border-radius: var(--radius); cursor: pointer;
}

/* --- the strip under the canvas ------------------------------------------- */
.strip {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 8px 16px; background: var(--sheet-sunk);
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  min-height: 24px;
}
.legend { display: flex; gap: 14px; flex-wrap: wrap; }
body.no-legend .legend { display: none; }
.cap { display: inline-flex; align-items: center; gap: 6px; }
.cap-l { font-size: 12px; color: var(--muted); }
.cap.is-on kbd { background: var(--signal); border-color: var(--signal); color: var(--sheet); }
.dev { margin-left: auto; font-size: var(--fs-read-sm); color: var(--muted); }

/* --- touch deck ----------------------------------------------------------- */
.deck { display: none; }
.pad {
  font-family: var(--font-mono); font-size: 16px;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--rule-2); border-radius: var(--radius);
  touch-action: none; cursor: pointer;
}
.pad.is-pressed { background: var(--signal); border-color: var(--signal); color: var(--sheet); }

@media (pointer: coarse) and (orientation: portrait) {
  /* Controls sit below the canvas. Nothing overlaps the game. */
  .deck {
    display: grid; height: 184px; padding: 8px 16px 16px; touch-action: none;
    gap: 12px; background: var(--sheet-sunk);
    grid-template-columns: 72px 72px 1fr 88px;
    grid-template-rows: 88px 72px;
    grid-template-areas: "flip .    . fwd" "leanl leanr . brake";
    align-items: end;
  }
  .pad--flip  { grid-area: flip;  width: 64px; height: 64px; }
  .pad--lean-l { grid-area: leanl; width: 72px; height: 72px; }
  .pad--lean-r { grid-area: leanr; width: 72px; height: 72px; }
  .pad--fwd   { grid-area: fwd;   width: 88px; height: 88px; }
  .pad--brake { grid-area: brake; width: 72px; height: 72px; }
  body.left-handed .deck { direction: rtl; }
  .stage { --deck-h: 184px; }
  .deck { flex: none; }
  /* The pads stay on the bottom edge where the thumbs are; the canvas takes
     the middle of what is left. */
  body[data-screen="play"] .frame { margin-block: auto; }
  .strip { display: none; }
}

@media (pointer: coarse) and (orientation: landscape) {
  /* Full-bleed canvas, pads only in the corners — never in the action. */
  body[data-screen="play"] .frame { width: 100%; flex: 1; min-height: 0; }
  #cv { width: 100%; height: 100%; aspect-ratio: auto; }
  .deck { display: block; }
  .deck .pad {
    position: absolute; opacity: .4; z-index: 4;
    display: grid; place-items: center;
  }
  .deck .pad.is-pressed { opacity: .9; }
  .pad--lean-l { left: 16px;  bottom: 16px; width: 72px; height: 72px; }
  .pad--lean-r { left: 100px; bottom: 16px; width: 72px; height: 72px; }
  .pad--flip   { left: 50%; margin-left: -32px; bottom: 16px; width: 64px; height: 64px; }
  .pad--fwd    { right: 16px;  bottom: 16px; width: 96px; height: 96px; }
  .pad--brake  { right: 124px; bottom: 16px; width: 72px; height: 72px; }
  body.left-handed .pad--lean-l { left: auto; right: 16px; }
  body.left-handed .pad--lean-r { left: auto; right: 100px; }
  body.left-handed .pad--fwd    { right: auto; left: 16px; }
  body.left-handed .pad--brake  { right: auto; left: 124px; }
  .strip { display: none; }
}

@media (min-width: 1440px) {
  /* A definite width, not `max-width` + auto margins. The stage is a size
     container on S1, and `margin-inline: auto` stops a flex item stretching to
     its parent -- which leaves a size-contained box nothing to measure, so it
     collapses to zero and takes the canvas with it. */
  .stage { width: min(100%, 1280px); margin-inline: auto; }
  .screen--levels { max-width: 1200px; margin: 0 auto; }
}

/* --- browser -------------------------------------------------------------- */
.screen--levels { padding: 0 16px 64px; }
.lanes {
  display: flex; gap: 0; border-bottom: 1px solid var(--rule);
  position: sticky; top: 0; background: var(--sheet); z-index: 5;
}
.lane-tab {
  font-family: var(--font-ui); font-size: 14px; font-weight: 500;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); padding: 14px 16px; min-height: 44px; cursor: pointer;
}
.lane-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--signal); }
.lane { padding-top: 20px; }

.sec {
  display: flex; align-items: center; gap: 12px;
  margin: 32px 0 12px; padding-bottom: 9px; border-bottom: 1px solid var(--rule);
}
.sec:first-child { margin-top: 0; }
.sec-h { font-size: var(--fs-head); font-weight: 500; margin: 0; letter-spacing: .005em; }
.sec-r { font-size: var(--fs-read-sm); color: var(--muted); margin-left: auto; }
/* One line of prose under a section rule: what the section is, where a count
   alone would not say it (ui-spec 2.3). Reading measure, not full width. */
.secnote { margin: -4px 0 12px; max-width: var(--measure); }
.bar { width: 90px; height: 4px; background: var(--rule); display: inline-block; }
.bar-fill { display: block; height: 100%; background: var(--pass); }

.card {
  display: flex; gap: 14px; align-items: center;
  background: var(--panel); border: 1px solid var(--rule);
  border-radius: var(--radius-panel); padding: 14px;
}
.card--wide .card-body { flex: 1; min-width: 0; }
.cardrow {
  display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px;
  scroll-snap-type: x proximity;
}
body.reduce-motion .cardrow { scroll-behavior: auto; }
.card--sm {
  flex-direction: column; align-items: stretch; gap: 6px; min-width: 128px;
  text-align: left; cursor: pointer; scroll-snap-align: start;
  font-family: var(--font-ui);
}
.card--sm:hover { border-color: var(--annot); }
.card--sm.is-complete { border-color: var(--pass); }
.card-sm-name {
  font-size: 13px; font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-sm-time { font-size: var(--fs-read-sm); color: var(--muted); }
.name {
  font-size: var(--fs-name); font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.map { background: var(--sheet-sunk); border: 1px solid var(--rule); display: block; }
.map--big { width: 192px; height: 120px; }
.map--sheet { width: 100%; height: auto; aspect-ratio: 17 / 10; }

.actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0; }
.intro { color: var(--ink-2); max-width: var(--measure); }

.grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
.ccard {
  height: 116px; text-align: left; cursor: pointer; font-family: var(--font-ui);
  background: var(--panel); border: 1px solid var(--rule);
  border-radius: var(--radius-panel); padding: 12px;
  display: flex; flex-direction: column; gap: 8px;
}
.ccard:hover { border-color: var(--annot); }
.ccard.is-complete { border-color: var(--pass); }
/* The game's own card, above the 197. One per row and drawn in the signal
   colour, so the difference between the game and a level pack is visible
   before the badge is read (ui-spec 2.4). */
.grid--sig { grid-template-columns: 1fr; }
.ccard--sig { border-color: var(--signal); background: var(--signal-soft); }
.ccard--sig:hover { border-color: var(--signal); }
.ccard-top { display: flex; gap: 8px; align-items: baseline; }
.ccard-name {
  font-size: var(--fs-name); font-weight: 500; flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ccard-n { font-size: var(--fs-read-sm); color: var(--muted); }
.ccard .bar { width: 100%; }
.ccard-foot { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-top: auto; }
.ccard-k { font-size: var(--fs-read-sm); color: var(--muted); }
.crumbs { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: var(--fs-read-sm);
  border: 1px solid var(--rule); background: var(--panel); color: var(--ink-2);
  border-radius: var(--radius); padding: 3px 7px;
}
.chip--era { color: var(--muted); }
.chip--sig { color: var(--signal); background: var(--signal-soft); border-color: var(--signal); }
.chip--best { color: var(--signal); background: var(--signal-soft); border-color: var(--signal); }
.chip--facet { cursor: pointer; min-height: 32px; }
.chip--facet[aria-checked="true"] {
  border-color: var(--annot); background: var(--annot-soft); color: var(--annot);
}
.chip--facet.is-zero { opacity: .4; }
.chip-check { width: 10px; display: inline-block; }
.chip-n { color: var(--muted); }

.searchbar { display: flex; gap: 10px; margin-bottom: 16px; }
.search, .select {
  font-family: var(--font-ui); font-size: 14px; min-height: 44px;
  padding: 8px 12px; background: var(--panel); color: var(--ink);
  border: 1px solid var(--rule-2); border-radius: var(--radius);
}
.search { flex: 1; min-width: 0; }
.facets { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.facet { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
.facet > .label { min-width: 84px; }
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.sortbar { display: flex; gap: 10px; align-items: center; margin-bottom: 8px; }
#showing { margin-bottom: 8px; }

.vl {
  position: relative; height: 60vh; min-height: 320px; overflow-y: auto;
  border: 1px solid var(--rule); border-radius: var(--radius-panel);
  background: var(--panel);
}
.vl--group { height: 68vh; }
.vl-win { position: absolute; top: 0; left: 0; right: 0; }
.vl-spacer { width: 1px; }

.row {
  display: flex; align-items: center; gap: 12px; padding: 0 12px;
  border-bottom: 1px solid var(--rule); cursor: pointer; overflow: hidden;
}
.row:hover { background: var(--sheet-sunk); }
.row.is-current { box-shadow: inset 3px 0 0 var(--signal); }
.row.is-complete .row-main .name::after { content: " ✓"; color: var(--pass); }
.row.is-attempted .row-main .name::after { content: " ◐"; color: var(--muted); }
.row.is-absent { opacity: .45; }
.row-main { flex: 1; min-width: 0; }
.row-side { text-align: right; display: flex; flex-direction: column; gap: 2px; }
.row-side .meta { font-size: var(--fs-read-sm); white-space: nowrap; }
.badge { margin-right: 6px; }
.peek {
  background: none; border: 1px solid var(--rule); border-radius: var(--radius);
  color: var(--muted); min-width: 44px; min-height: 44px; cursor: pointer;
}
.empty { padding: 32px 8px; color: var(--ink-2); }

/* --- overlays ------------------------------------------------------------- */
.modalhost { position: fixed; inset: 0; z-index: 40; }
.scrim { position: absolute; inset: 0; background: var(--sheet-sunk); opacity: .82; }
.dialog {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(420px, calc(100vw - 32px)); max-height: 86vh; overflow: auto;
  background: var(--panel); border: 1px solid var(--rule-2);
  border-radius: var(--radius-panel); padding: var(--pad-panel);
}
.dialog--sheet { width: min(380px, calc(100vw - 32px)); left: auto; right: 16px;
                 top: 64px; transform: none; max-height: calc(100vh - 96px); }
.dialog-h {
  font-family: var(--font-ui); font-size: var(--fs-head); font-weight: 500;
  letter-spacing: .06em; margin: 0 0 12px; padding-bottom: 9px;
  border-bottom: 1px solid var(--rule); text-transform: uppercase;
}
.dialog-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; }
.res-name { font-size: 20px; font-weight: 500; }
.res-time {
  font-size: 34px; font-weight: 500; margin: 14px 0 4px;
  font-variant-numeric: tabular-nums;
}
.res-2 { font-size: 15px; color: var(--ink-2); display: flex; gap: 8px; align-items: center; }
.res-2.is-pass { color: var(--pass); }
.res-2.is-fail { color: var(--fail); }
.res-3 { font-size: 13px; color: var(--muted); margin-top: 4px; }

.kv { display: grid; grid-template-columns: 96px 1fr; gap: 6px 12px; margin: 14px 0; }
.kv dt { margin: 0; }
.kv dd { margin: 0; font-size: 14px; color: var(--ink-2); }
.sheet-body .map--sheet { margin: 12px 0; }

.settings { display: flex; flex-direction: column; gap: 8px; }
.set-g { margin-top: 14px; }
.set-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 40px; font-size: 14px;
}
/* The update check answers in place rather than in a toast: the player is
   looking at the dialog they pressed the button in. Empty until it has
   something to say, and it reserves no height while it is. */
.set-status { margin: 4px 0 0; min-height: 20px; }
.set-status:empty { min-height: 0; }
.help p { font-size: 15px; color: var(--ink-2); max-width: var(--measure); }
.legend-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 14px 0; }
.legend-item { display: flex; gap: 10px; align-items: center; font-size: 13px; }
.legend-mark { width: 40px; height: 40px; flex: none; }
table.keys { border-collapse: collapse; margin-top: 8px; }
table.keys td { padding: 3px 10px 3px 0; font-size: 13px; }

.toasts { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%);
          z-index: 50; display: flex; flex-direction: column; gap: 8px; }
.toast {
  background: var(--panel); border: 1px solid var(--rule-2);
  border-radius: var(--radius); padding: 10px 14px; font-size: 14px;
  color: var(--ink);
}
.toast--warn { border-color: var(--spark); }
.toast--error { border-color: var(--fail); color: var(--fail); }

.boot { position: fixed; inset: 0; background: var(--sheet); z-index: 70;
        display: grid; place-items: center; }
.bootcard { text-align: center; }
.bootcard h2 { font-family: var(--font-ui); font-weight: 700; font-size: var(--fs-masthead);
               letter-spacing: var(--ls-masthead); margin: 0 0 8px; }

@media (max-width: 599px) {
  /* The top bar has to *fit* now.
     `Levels Make Creators ? Settings` is 331 px of nav, and with the 110 px
     wordmark in front of it the bar wants 475 px on a 390 px phone. It never
     fitted; what hid it was the viewport meta, which let Chrome widen the
     layout viewport to 476 px and scale the whole page down to suit. Pinning
     the viewport (§4.4) took that away, and on the play screen -- where the
     body is `overflow: hidden` -- Settings was simply clipped off the right
     edge and could not be reached at all.
     The wordmark goes first because it is the only thing here that does
     nothing: it is not a link and it is not focusable (§9.1), and the crumb
     already says where you are. */
  .topbar { gap: 10px; padding: 0 10px; }
  .wordmark { display: none; }
  .topnav { gap: 6px; }
  .topnav .btn { padding: 5px 8px; }
  .upd-long { display: none; }
  .upd-short { display: inline; font-size: 15px; line-height: 1; }
  .crumb-s { display: none; }
  .hud { left: 10px; right: 10px; gap: 10px; }
  .hud-flags .label, .hud-flags #hud-frac { display: none; }
  .dialog { left: 0; right: 0; bottom: 0; top: auto; transform: none;
            width: 100%; max-width: none; border-radius: 0; max-height: 88vh; }
  .dialog--sheet { top: auto; height: 60vh; }
  .legend-grid { grid-template-columns: 1fr; }
  .row-side .meta:first-child { display: none; }
}

/* 320 px is a real phone (SE, and most of the small Androids). Five words of
   navigation do not fit one at 13px however tight the padding, so below 380 px
   the nav drops to the small type the chips already use -- which is what buys
   the crumb back the room to say which level you are on. */
@media (max-width: 379px) {
  .topnav { gap: 4px; }
  .topnav .btn { padding: 5px 6px; font-size: 12px; }
}

/* ==========================================================================
   The editor.  #/edit is the drafts list, #/edit/<id> the canvas.

   No new tokens: --annot, --annot-soft, --fail, --spark, --rule, --panel and
   .mono cover all of it, which is the standard the rest of this file keeps
   (there is not one literal hex above this line and there is not one below).
   ========================================================================== */

.screen--drafts { padding: 24px 16px 64px; max-width: 1100px; margin-inline: auto; }
/* The shell's headings are all `--fs-head` 13px/500 (see `.sec-h`); these two
   were UA-styled at 24px/700 and 18.7px/700, sizes the scale does not have. */
.dfhead h2 { margin: 0 0 4px; font-size: var(--fs-head); font-weight: 500;
             letter-spacing: .005em; }
.screen--drafts > h3 { font-size: var(--fs-head); font-weight: 500;
                       margin: 0 0 10px; letter-spacing: .005em;
                       border-bottom: 1px solid var(--rule); padding-bottom: 6px; }
.dfhead p { margin: 0 0 20px; max-width: 62ch; }
.dfstart {
  display: grid; gap: 12px; margin-bottom: 32px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.dfcard {
  display: flex; flex-direction: column; gap: 4px; text-align: left;
  padding: 14px; border: 1px solid var(--rule); background: var(--panel);
  border-radius: var(--radius-panel); color: var(--ink);
}
.dfcard--start { cursor: pointer; font: inherit; }
.dfcard--start:hover { border-color: var(--annot); }
.dfcard--start:focus-visible { outline: 2px solid var(--annot); outline-offset: 2px; }
.dfgrid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.dfthumb {
  width: 100%; height: auto; aspect-ratio: 8 / 5; background: var(--sheet-sunk);
  border: 1px solid var(--rule); border-radius: var(--radius-panel);
}
/* A name a human wrote is set in the grotesque and truncated, never wrapped
   -- `--fs-read` is the mono readout token and made these 19px/700. */
.dfname {
  font-size: var(--fs-name); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.dfacts { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.dfexport { align-items: center; margin: 0 0 16px; }

/* Used five times by the editor and defined nowhere before now, so every one
   of them rendered as a full 44 px .btn: four per draft card, wrapping. */
/* Used five times by the editor and defined nowhere before now, so every one
   of them rendered as a full 44 px .btn: four per draft card, wrapping. It is
   a *narrower* button, not a smaller target -- ui-spec 7.5's floor is 44 px on
   a fine pointer and 56 on a coarse one, and a Delete that cannot be missed
   is not the place to economise. */
.btn--tiny {
  min-height: 44px; padding: 4px 9px; font-size: var(--fs-read-sm);
}

/* --- the editor screen ---------------------------------------------------- */

body[data-screen="edit"] { height: 100dvh; overflow: hidden;
                           display: flex; flex-direction: column; }
body[data-screen="edit"] main,
body[data-screen="edit"] .screen--edit { flex: 1; min-height: 0;
                                         display: flex; flex-direction: column; }
.screen--edit .edmain { flex: 1; min-height: 0; display: flex; }
.screen--edit .strip { flex: none; }

.edbar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 8px 16px; background: var(--sheet-sunk);
  border-bottom: 1px solid var(--rule);
}
.edtools { display: flex; gap: 4px; }
.edtool {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  min-height: 44px; padding: 5px 9px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); color: var(--ink-2); font: inherit;
}
.edtool:hover { border-color: var(--annot); color: var(--annot); }
.edtool[aria-pressed="true"] {
  border-color: var(--annot); background: var(--annot-soft); color: var(--annot);
}
.edtool-cap { font-size: 11px; opacity: .8; }
.edmeta { display: flex; align-items: center; gap: 10px; margin-inline: auto; }
.edname {
  font: inherit; padding: 5px 8px; min-width: 22ch;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); color: var(--ink);
}
.edname:focus-visible { outline: 2px solid var(--annot); outline-offset: -1px; }
.edfp { color: var(--muted); font-size: var(--fs-read-sm); }
.edacts { display: flex; gap: 6px; }

.edstage { flex: 1; min-width: 0; background: var(--sheet-sunk);
           display: flex; flex-direction: column; }
.edstage .frame { flex: 1; min-height: 0; }
#edcv, #edov { position: absolute; inset: 0; width: 100%; height: 100%; }
#edcv { background: var(--sky); }
/* The base rule gives every canvas an opaque `--sky`, which is right for the
   one the game draws on and wrong for a canvas stacked on top of it: without
   this the overlay hides the level completely, and the scene underneath is
   still there in the backing store, so only a screenshot shows it. */
#edov { background: transparent; touch-action: none; cursor: crosshair; }
#edov:focus-visible { outline: 2px solid var(--annot); outline-offset: -4px; }

.edside {
  width: 300px; flex: none; overflow-y: auto;
  border-left: 1px solid var(--rule); background: var(--sheet);
}
.edpanel { padding: 12px 14px; border-bottom: 1px solid var(--rule); }
.edpanel h3 { margin: 0 0 8px; font-size: var(--fs-read-sm);
              letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.edfield { display: grid; grid-template-columns: 1fr auto; align-items: center;
           gap: 10px; margin: 4px 0; font-size: var(--fs-read-sm); }
.edpick { max-width: 16ch; }
.edswatch { display: inline-flex; align-items: center; gap: 6px; }
.edswatch input[type="color"] { width: 44px; height: 28px; padding: 0;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); }
.ednum {
  font: inherit; font-family: var(--font-mono); width: 10ch; padding: 3px 6px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); color: var(--ink);
}
.edlist { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.edissue { display: grid; grid-template-columns: auto 1fr; gap: 4px 8px;
           font-size: var(--fs-read-sm); }
.edissue .btn { grid-column: 2; justify-self: start; }
/* `--spark` measures 4.39:1 and `--muted` 3.40:1 on `--sheet`, both under the
   4.5:1 floor ui-spec 7.5 sets for text. The word already carries the state
   (7.4: never hue alone), so the colour can afford to be the readable one. */
.edsev { font-size: var(--fs-label); letter-spacing: .12em; padding-top: 2px; }
.edissue--error .edsev { color: var(--fail); }
.edissue--warn .edsev { color: var(--fail); }
.edissue--note .edsev { color: var(--ink-2); }
.edissue--warn { border-left: 2px solid var(--spark); padding-left: 6px; }
.edissue--error { border-left: 2px solid var(--fail); padding-left: 6px; }
.edok { color: var(--muted); font-size: var(--fs-read-sm); margin: 0; }
.edstat { color: var(--muted); font-size: var(--fs-read-sm); }
.edhint { color: var(--muted); font-size: var(--fs-read-sm); margin-left: auto; }
.edprops { display: grid; gap: 6px; min-width: 320px; }

/* --- the menu row --------------------------------------------------------
   The four menus the 2004 editor had, plus the switches that are modes rather
   than actions. A mode belongs where its state is visible: burying "snap" in a
   menu means opening the menu to find out whether it is on. */
.edrow {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 2px 12px; background: var(--sheet);
  border-bottom: 1px solid var(--rule);
}
.edmenu { display: flex; position: relative; }
.edmenu-title {
  font: inherit; cursor: pointer; padding: 6px 10px; min-height: 32px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: none; color: var(--ink-2);
}
.edmenu-title:hover { color: var(--annot); }
.edmenu-title[aria-expanded="true"] {
  background: var(--annot-soft); color: var(--annot); border-color: var(--annot);
}
.edmenu-title:focus-visible { outline: 2px solid var(--annot); outline-offset: -1px; }
.edmenu-pop {
  position: absolute; top: 100%; left: 0; z-index: 40; min-width: 232px;
  padding: 4px; margin-top: 2px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); box-shadow: 0 6px 24px rgba(0, 0, 0, .22);
  /* The Edit menu carries nineteen items, which is taller than a 900 px
     viewport once the two bars above it are counted -- "Nudge down" fell off
     the bottom of the screen. The menu scrolls; the page still does not.
     `EdMenu.show` sets the cap, because it depends on where the popup lands. */
  overflow-y: auto;
}
.edmenu-item {
  display: grid; grid-template-columns: 1.2em 1fr auto; align-items: center;
  gap: 8px; width: 100%; cursor: pointer; text-align: left;
  font: inherit; padding: 6px 8px; min-height: 32px;
  border: 0; border-radius: var(--radius); background: none; color: var(--ink);
}
.edmenu-item:hover:not([disabled]), .edmenu-item:focus-visible {
  background: var(--annot-soft); color: var(--annot); outline: none;
}
.edmenu-item[disabled] { color: var(--muted); cursor: default; }
.edmenu-key { color: var(--muted); font-size: var(--fs-label); }
.edmenu-tick { color: var(--annot); }
.edmenu-sep { height: 1px; margin: 4px 6px; background: var(--rule); }
.edrow-right { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.edtoggle {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font: inherit; padding: 4px 9px; min-height: 32px;
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); color: var(--ink-2);
}
.edtoggle:hover { border-color: var(--annot); color: var(--annot); }
.edtoggle[aria-pressed="true"] {
  border-color: var(--annot); background: var(--annot-soft); color: var(--annot);
}
.edzoom { display: flex; gap: 2px; }

/* --- the palette rail ----------------------------------------------------
   The original's "type selector": a standing answer to "what am I about to
   place?". Each swatch keeps the digit that picks it, so the keyboard
   shortcuts become discoverable instead of being replaced. */
.edpal {
  width: 132px; flex: none; overflow-y: auto; padding: 8px;
  border-right: 1px solid var(--rule); background: var(--sheet);
}
.edpal-h { margin: 0 0 6px; font-size: var(--fs-label); letter-spacing: .06em;
           text-transform: uppercase; color: var(--muted); }
.edpal-item {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 6px; width: 100%; cursor: pointer; text-align: left;
  font: inherit; font-size: var(--fs-read-sm); padding: 3px 5px; margin-bottom: 2px;
  min-height: 34px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: none; color: var(--ink-2);
}
.edpal-item:hover { border-color: var(--annot); color: var(--annot); }
.edpal-item[aria-pressed="true"] {
  border-color: var(--annot); background: var(--annot-soft); color: var(--annot);
}
.edpal-item:focus-visible { outline: 2px solid var(--annot); outline-offset: 1px; }
.edpal-cv { width: 26px; height: 26px; flex: none; }
.edpal-label { overflow: hidden; text-overflow: ellipsis; }
.edpal-note { margin: 6px 0 0; font-size: var(--fs-label); line-height: 1.35; }

/* The 31 scenery sprites, drawn by `scenery.js` itself. */
.edscene { display: grid; grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
           gap: 2px; margin-top: 6px; }
.edscene-item {
  display: grid; place-items: center; cursor: pointer; padding: 1px;
  border: 1px solid transparent; border-radius: var(--radius); background: none;
}
.edscene-item:hover { border-color: var(--annot); }
.edscene-item.is-on { border-color: var(--annot); background: var(--annot-soft); }
.edscene-item:focus-visible { outline: 2px solid var(--annot); outline-offset: 1px; }
.edscene-cv { width: 100%; height: auto; aspect-ratio: 1; }

.edacts-row { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 10px; }
.edparam-note { margin: 4px 0 0; line-height: 1.4; }
.edparam-warn { margin: 6px 0 0; display: flex; align-items: center;
                gap: 6px; flex-wrap: wrap; line-height: 1.4; }
.edraw { margin-top: 10px; font-size: var(--fs-read-sm); }
.edraw summary { cursor: pointer; color: var(--muted); }
.edraw summary:focus-visible { outline: 2px solid var(--annot); outline-offset: 2px; }

/* A draft that is not being saved is not news that scrolls away. */
.edwarn {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 16px; background: var(--signal-soft);
  border-bottom: 1px solid var(--fail); color: var(--ink);
  font-size: var(--fs-read-sm);
}

/* On a phone the editor opens read-only: render, pan, zoom, the checks and a
   test ride -- the things a phone is genuinely good at. No drawing tools,
   because a 44 px thumb over a 32-unit grid cannot place a point where its
   author meant it to go, and a tool that quietly makes the wrong level is
   worse than no tool at all.
   The rule is the pointer AND the short side, not the width: at
   `max-width: 820px` a phone in landscape (844x390) got every drawing tool
   and a tablet in portrait (820x1180) got none of them, which is backwards
   in both directions.

   Hiding buttons is not the mechanism -- `Editor.isReadOnly()` is, and it
   reads the same two queries. This only takes away what the mode has already
   turned off. */
/* Every coarse pointer gets 56 px targets, not only the read-only ones:
   ui-spec 7.5 sets the floor by input device, and a tablet is a coarse
   pointer with the full tool set. */
@media (pointer: coarse) {
  .edtool { min-height: 56px; }
  .btn--tiny { min-height: 56px; }
  .edtoggle, .edmenu-title, .edmenu-item, .edpal-item { min-height: 56px; }
}

/* A narrow screen stacks: a 200 px aside beside a 390 px viewport leaves the
   canvas narrower than the panel next to it. */
@media (max-width: 1024px) {
  /* 132 px of rail plus 300 px of panel leaves too little canvas, so the rail
     lies down and scrolls sideways instead of being taken away: what you are
     about to place has to stay on screen at every width.

     Grid, not `flex-wrap`. A wrapped row does not put the rail on a line of its
     own -- `.edstage` carries `min-width: 0` so that a long level cannot push
     the panel off the screen, and a flex item that may shrink to nothing sits
     beside a full-width rail at zero width rather than wrapping past it. That
     is exactly what it did: an 820 px tablet got a 64 px canvas hanging over
     the right edge. Naming the two rows removes the question. */
  .screen--edit .edmain {
    display: grid; grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "pal pal" "stage side";
  }
  .edpal {
    grid-area: pal; width: auto; display: flex; gap: 6px; align-items: center;
    overflow-x: auto; overflow-y: hidden; padding: 6px 8px;
    border-right: 0; border-bottom: 1px solid var(--rule);
  }
  .edstage { grid-area: stage; }
  .edside { grid-area: side; }
  .edpal-h, .edpal-note { display: none; }
  .edpal-item { width: auto; margin: 0; flex: none; }
  .edscene { display: flex; gap: 2px; }
  .edscene-item { flex: none; width: 38px; }
}

@media (max-width: 700px) {
  .screen--edit .edmain {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas: "pal" "stage" "side";
  }
  .edside { width: auto; max-height: 34vh;
            border-left: 0; border-top: 1px solid var(--rule); }
  .edstage { min-height: 0; }
}

@media (pointer: coarse) and (max-width: 599px),
       (pointer: coarse) and (max-height: 500px) {
  .screen--edit.is-readonly .edtool[data-tool="select"],
  .screen--edit.is-readonly .edtool[data-tool="pen"],
  .screen--edit.is-readonly .edtool[data-tool="point"],
  .screen--edit.is-readonly .edtool[data-tool="material"],
  .screen--edit.is-readonly .edtool[data-tool="object"],
  .screen--edit.is-readonly .edtool[data-tool="sketch"],
  .screen--edit.is-readonly .edtool[data-tool="erase"] { display: none; }
  /* The palette and the splice switch have nothing to say when nothing can be
     placed. The *mode* still gates every one of them -- see `Editor.key` and
     `down()`; a stylesheet that hides a control cannot stop a drag. */
  .screen--edit.is-readonly .edpal,
  .screen--edit.is-readonly #edsplice,
  .screen--edit.is-readonly #edsnap { display: none; }
  /* The checks stay: they are one of the things a phone is promised, and the
     only part of the aside that is useless read-only is the selection. */
  .screen--edit.is-readonly #edsel { display: none; }
}
/* --- the import picker and the share preview (editpick.js) ---------------- */

/* A picker row is a 96 px minimap, a name, an id and a size; the 420 px a
   dialog is by default crushes the name to nothing. Above 600 px only --
   below it the dialog is already a full-width bottom sheet and a narrower
   `width` here would leave it stuck against the left edge, because the
   bottom-sheet rule sets `left: 0` and this one would not put it back. */
@media (min-width: 600px) {
  .dialog--pick { width: min(560px, calc(100vw - 32px)); }
}

/* A column, so the *list* is what scrolls and not the dialog. On a landscape
   phone (86vh of 500 px) the whole thing does not fit, and `Import` below a
   fold inside a dialog that also has a scrolling list is two scrollbars and
   one lost button. */
.dialog--pick { display: flex; flex-direction: column; }

.edpk { display: flex; flex-direction: column; gap: 10px; flex: 1 1 auto; min-height: 0; }
.edpk > p { margin: 0; }
.edpk-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.edpk-keys { margin-left: auto; }
.edpk-vl { flex: 1 1 auto; height: min(46vh, 340px); min-height: 132px; }
.edpk-vl:focus-visible { outline: 2px solid var(--annot); outline-offset: 2px; }
.edpk-count { margin: 0; }

.edpk-row { gap: 10px; }
/* Chosen reads as a mark and a rule before it reads as a colour -- the same
   order visual-identity 7.4 sets for the object marks. */
.edpk-row.is-picked { background: var(--sheet-sunk); box-shadow: inset 3px 0 0 var(--annot); }
.edpk-box { width: 14px; flex: none; text-align: center; color: var(--annot); }
.edpk-map { flex: none; width: 96px; height: 60px; }
/* A row is exactly 72 px and the reason a level cannot be opened is a whole
   sentence, so the second line truncates the way the name above it does. */
.edpk-row .meta { display: block; white-space: nowrap; overflow: hidden;
                  text-overflow: ellipsis; }

.edpk-checks { margin-top: 14px; }
.edpk-checks h3 { margin: 0 0 8px; font-size: var(--fs-read-sm);
                  letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.edsh .kv { margin: 12px 0; }

/* Under ~430 px the minimap and the right-hand column cannot both fit beside
   a name. The map is the identity, so the ids and the sizes go. */
@media (max-width: 430px) {
  .edpk-row .row-side { display: none; }
  .edpk-keys { display: none; }
}

/* --- Credit, and the creators screen (ui-spec 11.2, 11.4) ----------------- */

/* The one outbound link kind in the game. Underlined rather than coloured:
   the palette's accent is spent on the objective marks (visual-identity 7.4)
   and a second meaning for it would read as an object, not a link. */
.exlink { color: inherit; text-decoration: underline;
          text-underline-offset: 2px; text-decoration-thickness: 1px; }
.exlink:hover { color: var(--ink); }

/* A button that has to read as prose, inside a paragraph. */
.linkish { background: none; border: 0; padding: 0; font: inherit;
           color: inherit; text-decoration: underline;
           text-underline-offset: 2px; cursor: pointer; }

.credit { margin-top: 2px; }
/* Inside a collection card the credit is one line and must not push the
   progress bar around when a name is long. */
.ccard .credit { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.lede { max-width: 62ch; margin: 0 0 20px; }

.screen--creators { padding: 24px 16px 64px; max-width: 1100px;
                    margin-inline: auto; }

.crlist { display: flex; flex-direction: column; gap: 2px; }
/* Two rows of a four-cell grid: name and count on the first line, country and
   the collection summary under them. `min-width: 0` on the name is what lets
   the ellipsis work inside a grid cell. */
.crrow {
  display: grid; grid-template-columns: 1fr auto;
  gap: 2px 12px; align-items: baseline;
  padding: 12px 14px; min-height: 44px; text-align: left;
  background: var(--sheet); border: 0; border-radius: 4px;
  color: var(--ink); font: inherit; cursor: pointer;
}
.crrow:hover { background: var(--sheet-sunk); }
.crrow-name { grid-column: 1; min-width: 0; overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
.crrow-n { grid-column: 2; font-size: var(--fs-read-sm); color: var(--muted); }
.crrow-where { grid-column: 1; grid-row: 2; font-size: var(--fs-read-sm); }
.crrow-sub { grid-column: 2; grid-row: 2; font-size: var(--fs-read-sm);
             text-align: right; }

/* --- About (ui-spec 11.5) ------------------------------------------------ */

/* Prose, not a form: `.dialog`'s 420 px is a column for labelled rows and puts
   this at about 40 characters a line. `--measure` is the identity's reading
   width. */
.dialog--about { width: min(620px, calc(100vw - 32px)); }
.about h3 { margin: 18px 0 6px; font-size: var(--fs-read-sm);
            letter-spacing: .06em; text-transform: uppercase;
            color: var(--muted); }
.about h3:first-child { margin-top: 0; }
.about p { margin: 0 0 10px; max-width: 62ch; line-height: 1.5; }
/* The removal promise is the paragraph that has to be found, so it is the one
   with a rule against it. */
.about-contact { border-left: 2px solid var(--annot); padding-left: 12px; }
.crfoot { margin-top: 20px; }

/* --- The hall of fame (ui-spec 11.6) ------------------------------------- */

.hof-h { margin: 18px 0 6px; font-size: var(--fs-read-sm);
         letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.hof { list-style: none; margin: 0; padding: 0; }
/* Rank, name, time, action. The name column takes the slack so a long handle
   truncates rather than pushing the time off the sheet. */
.hof-row { display: grid; grid-template-columns: 2.4em 1fr auto auto;
           gap: 10px; align-items: baseline; padding: 3px 0;
           font-size: var(--fs-read-sm); }
.hof-row.is-medal { font-weight: 500; }
.hof-rank { color: var(--muted); }
.hof-who { min-width: 0; overflow: hidden; text-overflow: ellipsis;
           white-space: nowrap; }
.hof-where { margin-left: 6px; }
.hof-time { font-variant-numeric: tabular-nums; }
.hof-no { color: var(--muted); }
.hof-you { margin-top: 8px; }
.hof-note { margin-top: 6px; }
.res-watch, .hof-watch { margin-left: 8px; }

/* The replay badge sits where the finish state does; only one is ever on,
   because watching a record has no finish state of its own (ui-spec 11.7). */
.hud-replay { color: var(--annot); letter-spacing: .04em;
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
