/* R0 — skeleton. Palette tracks plan.html so the two read as one project.
   Colour tokens live in theme.css (linked from index.php's <head>, before this file) — see there,
   not here, to change a hex. */

*{box-sizing:border-box}
html{height:100%}
body{
  /* dvh, not vh — mobile browser chrome shrinks the viewport as you scroll, and vh doesn't
     notice, which is what leaves a canvas cropped under the URL bar. */
  height:100dvh; min-height:100%;
  margin:0; background:var(--ground); color:var(--ink);
  font-family:var(--mono); font-size:13px; line-height:1.5;
  display:grid; grid-template-columns:1fr; grid-template-rows:1fr;
  overflow:hidden; -webkit-font-smoothing:antialiased;
}

.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0,0,0,0); white-space:nowrap; border:0;
}

/* ---- debug (`?debug`) only — the plot shares the screen with `#panel` again, same split as
   before the settings/menu rework. A normal visitor never triggers any of this: `#panel` isn't
   even in the layout (display:none, below) unless `data-debug` is on `<html>`. */
html[data-debug] body{grid-template-columns:1fr clamp(17rem,26vw,23rem)}

@media (max-width:760px) and (orientation:portrait){
  html[data-debug] body{grid-template-columns:1fr; grid-template-rows:minmax(0,1fr) minmax(9rem,34dvh)}
  html[data-debug] #panel{border-left:0; border-top:1px solid var(--hair); padding:.75rem .75rem 0}
  html[data-debug] .counts{grid-template-columns:repeat(auto-fit,minmax(3.9rem,1fr))}
}

/* Landscape phone: stacking would leave the plot a letterbox. Stay side-by-side and narrow the
   panel instead — the plot is height-bound here, so width given back is width wasted. */
@media (max-height:520px) and (orientation:landscape){
  html[data-debug] body{grid-template-columns:1fr minmax(13rem,17rem)}
  html[data-debug] #panel{padding:.6rem .7rem 0}
  html[data-debug] h1{font-size:.66rem}
  html[data-debug] .sub{display:none}
  html[data-debug] .counts{grid-template-columns:repeat(auto-fit,minmax(3.6rem,1fr))}
}

/* Very small: the log is the first thing that can go. */
@media (max-width:400px) and (orientation:portrait){
  html[data-debug] body{grid-template-rows:minmax(0,1fr) minmax(8rem,30dvh)}
}

/* ---- stage ------------------------------------------------------------- */
#stage{position:relative; min-width:0; min-height:0; display:block; overflow:hidden}
#cv{
  display:block; width:100%; height:100%;
  /* Pointer Events only — the browser must not steal drags (§4). */
  touch-action:none; -webkit-user-select:none; user-select:none;
  cursor:crosshair;
}

/* ---- map (§49) — real UI (§49.1 step 3): `.map-active` alone shows it, set by `setMapActive`
   (main.js) from the inventory button. `?testmap`, the dev flag this was originally built behind,
   was retired 2026-08-31 once the map became a real, permanent feature. `#mapcv` sits absolutely,
   right on top of `#cv` in the same box, rather than replacing it in the flow — `#cv` must keep a
   real, measurable size at all times so
   `view.layout()` (main.js's only source for where the map's own window rect goes, per mapview.js)
   never sees a collapsed, `display:none` canvas. */
#mapcv{
  display:none; position:absolute; inset:0; width:100%; height:100%;
  touch-action:none; cursor:pointer;
}
html.map-active #mapcv{display:block}

/* ---- attract mode (docs/ATTRACT-MODE.md) — the game's own idle/start screen, shown on every load
   by default (a settings toggle, on by default, lets a player turn it off) — not a first-load-only
   splash. Set by `attract.js`'s `start()`/`stop()` (called from main.js). Same sits-on-top-of-`#cv`-
   without-replacing-it reasoning as `#mapcv` above — no `pointer-events:none` either: it should
   physically block a stray tap from reaching the real `#cv` underneath while shown. */
#attractcv{
  display:none; position:absolute; inset:0; width:100%; height:100%;
  cursor:pointer;   /* the whole screen dismisses attract mode (docs/ATTRACT-MODE.md) */
}
html.attract-active #attractcv{display:block}
/* Offline fallback (PLAN.md §58) plays through this same canvas and the same replay-engine.js
   machinery every real segment uses — a canned single-plot "segment" (attract.js's
   `enterOfflineFallback`), not a separate look-alike screen. No extra CSS needed here: same block-
   tile frame, same sign, same camera fit. (A first cut used a second canvas with offline.js's own
   bare-10×10 look; Adam's report — it read as offline mode, not attract mode, and he couldn't get
   back to the real attract screen — is why that was replaced rather than kept.) */
/* `#map-toggle` (z-index:2) and `#shelf` (z-index:1) both sit above `#attractcv`, which has none —
   an explicit z-index wins over DOM order regardless of value. Without this they float over the
   attract screen, clickable/visible, whenever the saved session's inventory has a Map or held items
   (main.js restores `inventory` and the `.show`/meter state from it on load, independent of whether
   attract mode is showing) — game chrome with no business appearing before the game is even
   entered. Same set offline mode already hides below. */
html.attract-active #map-toggle,
html.attract-active #shelf{display:none}

/* ---- offline mode (docs/OFFLINE-MODE.md) — a bare local 10×10 sandbox, entirely separate from
   `#cv`: painting here never touches the shared plot or the network. Same sits-on-top-of-`#cv`
   treatment as `#mapcv`/`#attractcv` above. */
#offlinecv{
  display:none; position:absolute; inset:0; width:100%; height:100%;
  touch-action:none; -webkit-user-select:none; user-select:none; cursor:crosshair;
}
html.offline-active #offlinecv{display:block}
html.offline-active #map-toggle,
html.offline-active #shelf,
html.offline-active #msg,
html.offline-active #attract-exit{display:none}

/* `#attract-done` is its own button (not `#help-done`) — dismissing it reveals the game, and only
   then, for a genuinely new player, does the help card open as a separate follow-up step (main.js).
   The visible "Click to Play"/"Continue" sign is canvas art now (attract.js's regenerateFrame(),
   docs/ATTRACT-MODE.md) — this element stays real and focusable for keyboard/AT users, but visually
   transparent, sized and positioned over the drawn sign's exact rect by JS (left/top/width/height set
   directly, not by this stylesheet) rather than duplicating the look in CSS. */
#attract-done{
  display:none; position:absolute; z-index:2;
  background:transparent; border:0; color:transparent; padding:0; cursor:pointer;
}
html.attract-active #attract-done{display:block}
/* Offline mode's own canvas visually covers this (DOM-order stacking, both `z-index:auto`), but
   `#attract-done`'s explicit `z-index:2` still puts it above `#offlinecv` in *hit-testing* order —
   invisible but real and focusable, so a drag over it could select its text or, worse, land a
   click that silently dismisses attract mode underneath (docs/OFFLINE-MODE.md). Higher-specificity
   selector so it wins over the `display:block` rule above regardless of cascade order. */
html.attract-active.offline-active #attract-done{display:none}

/* ---- panel (debug only) -------------------------------------------------
   Not part of the normal layout at all — `display:none` until `?debug` puts `data-debug` on
   `<html>` (main.js), at which point the grid rule above gives it a real column/row to sit in. */
#panel{
  display:none;
  min-width:0; min-height:0; flex-direction:column;
  background:var(--surface); border-left:1px solid var(--hair);
  padding:1rem 1rem 0;
}
html[data-debug] #panel{display:flex}

#debug{min-width:0; min-height:0; display:flex; flex-direction:column; flex:1 1 auto}

#panel header{flex:none}
h1{
  font-size:.72rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--accent); margin:0;
}
.sub{margin:.2rem 0 0; color:var(--muted); font-size:.72rem; letter-spacing:.04em}

.row{flex:none; display:flex; flex-wrap:wrap; gap:.35rem; margin:.9rem 0 .75rem}
button{
  font:inherit; font-size:.68rem; font-weight:600; letter-spacing:.09em; text-transform:uppercase;
  padding:.4rem .6rem; border:1px solid var(--hair); border-radius:4px;
  background:var(--surface-2); color:var(--ink-2); cursor:pointer;
}
/* Grayscale is the default hover/focus for every button in the file (docs/STYLE.md §1/§5.1) — teal
   is a debug-only exception below, not a general button state. Nothing new needs an override to stay
   neutral; only the debug panel needs to opt IN to the accent. */
button:hover:not(:disabled){border-color:var(--muted); color:var(--ink-2)}
button:disabled{opacity:.4; cursor:default}
button[aria-pressed="true"]{background:var(--warn); border-color:var(--warn); color:var(--ground)}
button:focus-visible{outline:2px solid var(--muted); outline-offset:2px}
html[data-debug] button:hover:not(:disabled){border-color:var(--accent); color:var(--accent)}
html[data-debug] button:focus-visible{outline-color:var(--accent)}

.counts{
  flex:none; display:grid; grid-template-columns:repeat(auto-fit,minmax(4.6rem,1fr)); gap:1px;
  margin:0 0 .75rem; background:var(--hair); border:1px solid var(--hair); border-radius:4px;
  overflow:hidden;
}
.counts>div{background:var(--surface); padding:.45rem .55rem}
.counts dt{font-size:.6rem; letter-spacing:.1em; text-transform:uppercase; color:var(--muted)}
.counts dd{margin:.15rem 0 0; font-size:1rem; font-weight:600; font-variant-numeric:tabular-nums}

/* ---- log --------------------------------------------------------------- */
#log{
  flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  list-style:none; margin:0; padding:0 0 1rem; border-top:1px solid var(--hair);
}
#log li{
  display:grid; grid-template-columns:3.4rem 1fr; gap:.5rem;
  padding:.35rem 0; border-bottom:1px solid var(--hair); font-size:.7rem;
}
#log li:first-child{padding-top:.5rem}
#log .dt{color:var(--muted); font-variant-numeric:tabular-nums; text-align:right}
#log .nm{color:var(--accent); font-weight:600}
#log .pl{color:var(--ink-2); word-break:break-word}
#log li.first .nm{color:var(--warn)}
#log .empty{grid-column:1/-1; color:var(--muted); padding:.75rem 0; text-align:center}

/* ---- selection + inventory (R5) ---------------------------------------- */
.counts.inv{grid-template-columns:1fr auto; margin-bottom:.5rem}
.counts.inv .wide{grid-column:1}
.counts dd{font-size:.9rem}
.counts.inv dd{font-size:.82rem; letter-spacing:.02em}
.counts dt sup{color:var(--muted); font-size:1em}

/* A transient line over the canvas. Never a number — §28.3. */
#msg{
  position:absolute; left:50%; bottom:1.1rem; transform:translateX(-50%);
  margin:0; padding:.3rem .7rem; border-radius:4px; pointer-events:none;
  background:var(--surface); border:1px solid var(--hair); color:var(--ink-2);
  font-size:.7rem; letter-spacing:.09em; text-transform:uppercase;
  opacity:0; transition:opacity .18s;
}
#msg:not(:empty){opacity:1}

/* ---- shelf life (§47) — an unplaced item spoils; the bar is the only signal, never a number.
   `left`/`width` are set from JS (`sizeShelf()`, main.js) to track the canvas's own 12×12 window
   frame (§47.1) exactly, including across a resize — nothing here fixes the block's size. */
#shelf{
  position:absolute; bottom:.75rem; z-index:1;
  display:flex; flex-direction:column; gap:.3rem; pointer-events:none;
}
.shelf-row{
  display:none; align-items:center; gap:.45rem;
  background:var(--surface); border:1px solid var(--hair); border-radius:4px;
  padding:.28rem .55rem; opacity:.92;
}
.shelf-row.show{display:flex}
/* §48 — `#shelf`'s own `pointer-events:none` lets clicks pass through to the canvas beneath every
   ordinary row; a dismissable one (a non-placeable item's early-end tap) opts back in. */
.shelf-row.dismissable{pointer-events:auto; cursor:pointer}
.shelf-name{
  font-size:.62rem; letter-spacing:.09em; text-transform:uppercase; color:var(--ink-2);
  white-space:nowrap; flex:none;
}
/* §47.2 — the artwork's palette is white/grey/black and nothing else (§13.1); the bar borrows the
   exact ART_RGB values (cell.js) rather than the UI's teal/amber vars, and — matching ART_RGB's own
   "these never invert for theme" rule — stays these literal hexes in dark mode too. Urgency under
   25% is a pulse, not a colour swap: still no hue added. */
.shelf-bar{
  position:relative; flex:1 1 auto; min-width:0; height:.35rem; border-radius:3px;
  background:#E0E0E0; overflow:hidden;
}
.shelf-fill{
  position:absolute; inset:0 auto 0 0; height:100%;
  background:#000;
}
.shelf-row.low .shelf-fill{animation:shelf-pulse 1s ease-in-out infinite}
@keyframes shelf-pulse{50%{opacity:.35}}

/* §49.1 step 3 — Map doesn't get a shelf-row meter like every other item (main.js skips it in that
   loop): instead, while an unspoiled instance sits in inventory, this button sits *inline*, inside
   the 12×12 window's own right edge — `right` set from JS (`sizeMapToggle()`) to `view.cssW -
   (view.winX + view.winSize)`, so its right edge lands flush with the frame's, never past it.
   `visibility` (not `display`) hides it so `offsetWidth` stays readable even while hidden —
   `sizeShelf()` needs that number to shrink the shelf block and leave it room, Adam's own ask: any
   bar on screen reads slightly shorter while the button is there, not covered by it. `aria-pressed`
   mirrors `mapActive` so it reads as a real on/off switch, not a one-shot button. */
#map-toggle{
  position:absolute; bottom:.75rem; z-index:2;
  visibility:hidden;
}
#map-toggle.show{visibility:visible}
/* docs/STYLE.md §2 — Adam's call, 2026-08-31: the general button[aria-pressed="true"] rule's amber
   (above) no longer fits this button once toggled on; grayscale instead, same opt-out shape §5.1
   already uses for a button that shouldn't inherit a shared default. `#play` (player.html) keeps the
   general rule's amber — this override is scoped to `#map-toggle` alone. */
#map-toggle[aria-pressed="true"]{background:var(--surface-2); border-color:var(--muted); color:var(--ink-2)}

/* ---- attract re-entry ---------------------------------------------------
   docs/ATTRACT-MODE.md, "A way back in" — its own fixed corner, not a menu item, so returning to
   the idle screen doesn't require opening Settings first. Mirrors `#menu-toggle`'s corner treatment
   (same size, same safe-area handling) on the opposite side. Hidden while attract mode is already
   showing — there's nothing to "return to" from inside it. */
#attract-exit{
  position:absolute; z-index:3;
  top:calc(.75rem + env(safe-area-inset-top)); left:calc(.75rem + env(safe-area-inset-left));
  width:2.1rem; height:2.1rem; padding:0; display:grid; place-items:center; font-size:1.1rem;
  border-radius:6px;
}
html.attract-active #attract-exit{display:none}

/* ---- offline re-entry -----------------------------------------------------
   Same corner as `#attract-exit` (top-left) — the two are never shown at once, so there's no
   clash. Always reachable regardless of what's painted; no menu required to leave. */
#offline-exit{
  display:none; position:absolute; z-index:3;
  top:calc(.75rem + env(safe-area-inset-top)); left:calc(.75rem + env(safe-area-inset-left));
  width:2.1rem; height:2.1rem; padding:0; place-items:center; font-size:1.1rem;
  border-radius:6px;
}
html.offline-active #offline-exit{display:grid}

/* ---- offline clear --------------------------------------------------------
   Bottom-centre, on-canvas — resets every cell to white (offline.js's `clear()`). Plain button
   sizing/shape (not the icon-square treatment `#offline-exit` uses) since it carries a label. */
#offline-clear{
  display:none; position:absolute; z-index:3;
  bottom:calc(.75rem + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%);
  padding:.5rem 1.1rem; border-radius:6px;
}
html.offline-active #offline-clear{display:block}

/* ---- offline sandbox, "back online" (PLAN.md §58) — shown once connectivity actually returns
   (net.state, source:'net' — not the faster-but-unreliable navigator.onLine signal) while the
   sandbox is open; a way back into the real live game from here. Stacked above #offline-clear,
   same bottom-centre slot, since both can be visible at once — Adam to confirm/adjust on a real
   device, this offset is a first guess, not eyeballed. */
#offline-online{
  display:none; position:absolute; z-index:3;
  bottom:calc(3.4rem + env(safe-area-inset-bottom)); left:50%; transform:translateX(-50%);
  padding:.5rem 1.1rem; border-radius:6px;
}
html.offline-active.offline-online-ready #offline-online{display:block}

/* ---- settings gear + drawer ---------------------------------------------
   `#menu-toggle` sits over the canvas like `#map-toggle`, but at a fixed corner rather than
   tracking the 12×12 window — it has to stay reachable regardless of what's in the shelf. The
   drawer itself is `position:fixed`, off the grid entirely, so opening it never resizes `#cv` —
   `view.layout()` (render.js) would otherwise see the canvas shrink and repaint mid-menu. */
#menu-toggle{
  position:absolute; z-index:3;
  top:calc(.75rem + env(safe-area-inset-top)); right:calc(.75rem + env(safe-area-inset-right));
  width:2.1rem; height:2.1rem; padding:0; display:grid; place-items:center; font-size:1rem;
  border-radius:6px;
}

/* ---- connectivity status (PLAN.md §58) — left of #menu-toggle, same top offset and size so it
   reads as a matched pair. Always visible, not gated to any mode — main.js's net.state listener
   toggles data-online. The wifi glyph itself (index.php) is unclassed and always shown; only the
   strike-through line is gated here, shape only, no new hue (docs/STYLE.md tier 3). */
#net-status{
  position:absolute; z-index:3;
  top:calc(.75rem + env(safe-area-inset-top));
  right:calc(.75rem + 2.1rem + .4rem + env(safe-area-inset-right));
  width:2.1rem; height:2.1rem; display:grid; place-items:center;
  color:var(--muted);
}
#net-status .net-offline-glyph{display:none}
#net-status[data-online="false"] .net-offline-glyph{display:block}

/* ---- cheat coordinate readout (?pumpkineater, index.php/main.js) — bottom-right, the one on-canvas
   corner nothing else occupies (top-left is #attract-exit/#offline-exit, top-right #net-status/
   #menu-toggle, bottom-centre #map-toggle/#offline-clear). Plain text over the canvas, same as
   #net-status accepts — grayscale only, no new hue. `hidden` by default; main.js's checkbox toggles it. */
#cheat-coords{
  position:absolute; z-index:3;
  bottom:calc(.75rem + env(safe-area-inset-bottom)); right:calc(.75rem + env(safe-area-inset-right));
  color:var(--muted); font-size:.7rem; letter-spacing:.03em; font-variant-numeric:tabular-nums;
  pointer-events:none;
}
/* No accent anywhere in the menu/help chrome — this now falls out of the grayscale default above
   (docs/STYLE.md §5.1) rather than needing its own override; `html[data-debug]` never applies here
   since `#menu-toggle`/`#menu`/`#help` render regardless of `?debug`. */

#menu-scrim{
  position:fixed; inset:0; z-index:4; background:rgba(0,0,0,.35);
  opacity:0; pointer-events:none; transition:opacity .18s;
}
#menu-scrim.show{opacity:1; pointer-events:auto}

#menu{
  position:fixed; top:0; right:0; z-index:5; width:min(20rem,86vw); height:100dvh;
  flex-direction:column; gap:.9rem;
  background:var(--surface); border-left:1px solid var(--hair);
  padding:calc(1rem + env(safe-area-inset-top)) 1rem calc(1rem + env(safe-area-inset-bottom));
  overflow-y:auto;
}
#menu:not([hidden]){display:flex}

#menu header{flex:none; display:flex; align-items:center; justify-content:space-between}
#menu h2{
  font-size:.72rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink); margin:0;
}
#menu-close{padding:.3rem .5rem}

.menu-item{
  flex:none; width:100%; text-align:left; padding:.6rem .7rem;
}

#settings, #cheats{flex:none; padding-top:.9rem; border-top:1px solid var(--hair)}
#settings h3, #cheats h3{
  font-size:.68rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase;
  color:var(--muted); margin:0 0 .6rem;
}
#cheats .menu-item{margin-bottom:.7rem}

/* `.field` — label + input row, same convention player.css already uses for its own transport
   controls (docs/STYLE.md §3); index.php never needed a number input before the cheat warp row. */
.field{display:flex; align-items:center; gap:.4rem; flex-wrap:wrap}
.field > span:first-child{
  color:var(--muted); font-size:.64rem; text-transform:uppercase; letter-spacing:.07em;
}
.field input[type=number]{
  font:inherit; font-size:.78rem; border:1px solid var(--hair); border-radius:4px;
  padding:.3rem .45rem; background:var(--surface-2); color:var(--ink); width:5rem;
}

/* after-action-audit.md Tier 0 — the live service-worker version, always shown regardless of
   ?debug (unlike .sub, which html[data-debug] hides in one narrow-landscape breakpoint — the wrong
   behaviour for a label meant to be visible while testing). Grayscale only, no new hue. */
#build-version{flex:none; margin:0; color:var(--muted); font-size:.62rem; letter-spacing:.04em}

/* ---- how-to-play modal --------------------------------------------------- */
#help{
  position:fixed; inset:0; z-index:6; place-items:center;
  background:rgba(0,0,0,.45); padding:1.25rem;
}
#help:not([hidden]){display:grid}
.help-card{
  position:relative; width:min(28rem,100%); max-height:min(34rem,86dvh); overflow-y:auto;
  background:var(--surface); border:1px solid var(--hair); border-radius:8px;
  padding:1.5rem 1.5rem 1.25rem;
}
.help-card h2{
  font-size:.85rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink); margin:0 0 .75rem;
}
.help-card h3{
  font-size:.7rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted); margin:1.1rem 0 .4rem;
}
.help-card p{margin:0 0 .6rem; color:var(--ink-2); font-size:.82rem; line-height:1.6}
.help-card strong{color:var(--ink)}

/* The road/wall previews are the real recipe patterns (`RECIPES`, recipes.js), rendered cell-by-
   cell in the exact three art colours (`ART_RGB`, cell.js) — never CSS vars — same rule §47.2 pins
   for the shelf bar: a thing standing in for the artwork matches the artwork, not the UI chrome. If
   a pattern is ever re-tuned this redraws with it automatically; nothing here is hand-copied. */
.help-patterns{display:flex; gap:1.5rem; flex-wrap:wrap; margin:.2rem 0 .7rem}
.pattern-block{display:flex; flex-direction:column; align-items:center; gap:.3rem}
.pattern-grid{
  display:grid; gap:1px; background:var(--hair); border:1px solid var(--hair); border-radius:2px;
  overflow:hidden;
}
.pattern-cell{width:.9rem; height:.9rem}
.pattern-label{
  font-size:.6rem; letter-spacing:.09em; text-transform:uppercase; color:var(--muted);
}
#help-close{position:absolute; top:.75rem; right:.75rem; padding:.3rem .5rem}
.help-close-line{margin-top:1.1rem; text-align:center}

/* ---- world unlock (docs/WORLDS.md §6) — same overlay/card pattern as #help, grayscale only ------ */
#unlock{
  position:fixed; inset:0; z-index:6; place-items:center;
  background:rgba(0,0,0,.45); padding:1.25rem;
}
#unlock:not([hidden]){display:grid}
#unlock-form{display:flex; flex-direction:column; gap:.6rem; margin-top:1rem}
#unlock-password{
  font:inherit; font-size:.85rem; padding:.5rem .6rem; border:1px solid var(--hair); border-radius:4px;
  background:var(--ground); color:var(--ink);
}
#unlock-password:focus-visible{outline:2px solid var(--muted); outline-offset:2px}
#unlock-error{margin:0; color:var(--ink-2); font-size:.78rem}
#unlock-submit{align-self:flex-start}

/* ---- the dial — a labelled range/checkbox row, shared by two surfaces: the debug panel's tuning
   sliders (`#panel`, `?debug`-gated) and the real `#settings` drawer's volume/start-screen controls,
   which every player sees. Grayscale by default, same pattern as button:hover above (docs/STYLE.md
   §5.1) — the debug panel opts in to `--warn` on its own sliders; the real settings drawer stays
   neutral like the rest of the menu (docs/STYLE.md §5.4). */
.dial{
  flex:none; display:grid; grid-template-columns:auto 1fr auto; gap:.5rem; align-items:center;
  margin:0 0 .7rem; font-size:.62rem; letter-spacing:.11em; text-transform:uppercase; color:var(--muted);
}
.dial input[type=range]{width:100%; accent-color:var(--muted); min-width:0}
.dial input[type=checkbox]{accent-color:var(--muted); justify-self:start}
/* #theme-select — the one .dial row that isn't a range/checkbox (PLAN.md §56 follow-up, 2026-08-31);
   default OS select chrome would stand out against the drawer otherwise. */
.dial select{
  font:inherit; font-size:.68rem; text-transform:none; letter-spacing:normal;
  border:1px solid var(--hair); border-radius:4px; padding:.25rem .4rem;
  background:var(--surface-2); color:var(--ink); justify-self:start;
}
.dial output{font-weight:600; color:var(--ink-2); font-variant-numeric:tabular-nums; letter-spacing:.04em}
.dial span{cursor:pointer; user-select:none}
.dial span.muted{opacity:.5; text-decoration:line-through}
html[data-debug] .dial input[type=range],
html[data-debug] .dial input[type=checkbox]{accent-color:var(--warn)}
