/* 4D Chicago — walkthrough chrome.
   The 3D scene is always daylight; the theme switch only reskins the interface.
   Mobile (390x780) is a release gate, so every panel is sized from the visual
   viewport and nothing relies on hover. */

:root {
  --ink: #f4f1ea;
  --ink-dim: #b9b3a6;
  --panel: rgba(20, 19, 17, 0.86);
  --panel-solid: #16150f;
  --edge: rgba(244, 241, 234, 0.16);
  --accent: #d8a24a;
  --doc: #cfd8c5;
  --inf: #e2a63f;
  --con: #9aa3ad;
  --shadow: 0 10px 34px rgba(0, 0, 0, 0.5);
  /* Behind the canvas. It should never be visible — but headless Chromium's
     compositor occasionally paints the page ground behind a fixed, rounded,
     scrollable overlay, and a hard black rectangle over a daylight scene reads
     as a rendering bug. Themed, it reads as nothing at all. */
  --page: #0d0f12;
  --r: 12px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
  --vh: 100vh;
}

[data-theme="light"] {
  --ink: #1d1a14;
  --ink-dim: #5c564a;
  --panel: rgba(250, 248, 243, 0.92);
  --panel-solid: #faf8f3;
  --edge: rgba(29, 26, 20, 0.16);
  --accent: #8a5a12;
  --shadow: 0 10px 30px rgba(40, 34, 20, 0.24);
  --page: #dfe7ec;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: var(--page);
  color: var(--ink);
  font: 15px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
}

#view {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
}

.noscript { position: fixed; inset: 0; display: grid; place-items: center; }

/* ---------------------------------------------------------------- gate --- */

.gate {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 20px;
  background: radial-gradient(120% 90% at 50% 0%, rgba(8, 10, 14, 0.35), rgba(8, 10, 14, 0.86));
  z-index: 40;
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.gate[hidden] { display: none; }

.gate-card {
  width: min(460px, 100%);
  padding: 26px 24px 20px;
  border: 1px solid var(--edge);
  border-radius: 16px;
  background: var(--panel);
  box-shadow: var(--shadow);
  text-align: center;
}
.gate-eyebrow {
  margin: 0 0 6px;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
.gate-card h1 {
  margin: 0 0 6px;
  font-size: 25px;
  font-weight: 620;
  letter-spacing: -0.01em;
}
.gate-sub { margin: 0 0 18px; color: var(--ink-dim); font-size: 14px; }

/* The town's own two numbers, counted from the committed data (T-0036). Two
   figures side by side rather than a sentence: the visitor is being told how
   much town there is, and a numeral read at a glance is the whole of that. The
   denominator sits under each one because 322 alone reads as a total, and this
   is a progress report on a town still being built. Tabular numerals so the
   pair does not jitter as the count grows. */
.gate-census {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 0 0 18px;
  padding: 12px 6px;
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
}
.gate-census[hidden] { display: none; }
.gate-census .gc-fig { margin: 0; display: grid; gap: 1px; }
.gate-census .gc-n {
  font-size: 26px;
  font-weight: 640;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.gate-census .gc-l {
  font-size: 12px;
  color: var(--ink);
  letter-spacing: .01em;
}
.gate-census .gc-of {
  font-size: 11px;
  color: var(--ink-dim);
  opacity: .85;
}

/* Loading progress. A bar rather than a spinner because the question a visitor
   actually has is "is this stuck?", and only a bar that MOVES answers it. It is
   driven from the real boot stages in main.js, never from a timer. */
.gate-bar {
  height: 4px; margin: 0 0 18px; border-radius: 2px;
  background: rgba(255, 255, 255, .10); overflow: hidden;
}
.gate-bar > i {
  display: block; height: 100%; width: 0%;
  background: var(--inf); border-radius: 2px;
  transition: width .25s ease;
}
.gate-bar.done { opacity: 0; transition: opacity .4s ease .2s; }

/* Which build you are looking at. Deliberately quiet, and deliberately the last
   thing on the card: it is for the moment something has gone wrong. */
.gate-build {
  margin: 14px 0 0; font-size: 11px; letter-spacing: .04em;
  color: var(--ink-dim); opacity: .65; font-variant-numeric: tabular-nums;
}

.gate-btn {
  width: 100%;
  padding: 13px 18px;
  font: inherit;
  font-weight: 600;
  color: #17140c;
  background: var(--accent);
  border: 0;
  border-radius: 10px;
  cursor: pointer;
  transition: filter .15s ease, opacity .15s ease;
}
.gate-btn:disabled { opacity: .45; cursor: progress; }
.gate-btn:not(:disabled):hover { filter: brightness(1.08); }

.gate-keys { margin: 14px 0 0; font-size: 12px; color: var(--ink-dim); }
.gate-keys .k {
  display: inline-block;
  padding: 1px 6px;
  margin: 0 2px;
  border: 1px solid var(--edge);
  border-radius: 5px;
  font-size: 11px;
  color: var(--ink);
}
.gate-touch { display: none; }
body.is-touch .gate-keys:not(.gate-touch) { display: none; }
body.is-touch .gate-touch { display: block; }

.gate-fine {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--edge);
  font-size: 11.5px;
  color: var(--ink-dim);
}
.gate-fine code { font-size: 11px; }

/* ----------------------------------------------------------------- hud --- */

.hud {
  position: fixed;
  inset: 0;
  z-index: 20;
  pointer-events: none;
  padding: calc(10px + var(--safe-t)) 10px calc(10px + var(--safe-b));
  display: flex;
  flex-direction: column;
}
.hud[hidden] { display: none; }

.hud-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }

.badge {
  pointer-events: auto;
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 7px 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--panel);
  box-shadow: var(--shadow);
}
.badge-year { font-size: 17px; font-weight: 640; letter-spacing: 0.01em; }
.badge-sub { font-size: 11px; color: var(--ink-dim); }
/* The altitude readout rides in the year badge. Without nowrap it wraps the
   badge to three stacked lines at 390 px — "1835 / 1 July / 72 ft up" — which
   is how it first shipped to a screenshot. */
.badge { flex-wrap: nowrap; white-space: nowrap; }
.badge-alt {
  padding-left: 9px;
  margin-left: 2px;
  border-left: 1px solid var(--edge);
  font-size: 11px;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.badge-alt[hidden] { display: none; }
.fly-glyph { font-size: 10px; opacity: .85; }

.hud-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

/* Navigation sits below the date badge, away from the settings panel and from
   both touch-control corners. It never captures a pointer: the map is a readout,
   not a second way to steer. */
.navigation-stack {
  position: absolute;
  top: calc(58px + var(--safe-t));
  left: 10px;
  display: grid;
  justify-items: start;
  gap: 7px;
  pointer-events: none;
}
.compass {
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 4px 9px 4px 5px;
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: var(--panel);
  box-shadow: var(--shadow);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.compass[hidden], .overview-map[hidden], .street-readout[hidden] { display: none; }
.compass strong { min-width: 29px; color: var(--ink); font-size: 11.5px; }
.compass > span:last-child { color: var(--ink-dim); }
.compass-dial {
  position: relative;
  width: 26px;
  height: 26px;
  flex: none;
  border: 1px solid var(--edge);
  border-radius: 50%;
  color: var(--ink-dim);
  font-size: 6px;
  line-height: 1;
}
.compass-n, .compass-e, .compass-s, .compass-w { position: absolute; }
.compass-n { top: 2px; left: 50%; transform: translateX(-50%); color: var(--accent); }
.compass-e { top: 50%; right: 2px; transform: translateY(-50%); }
.compass-s { bottom: 2px; left: 50%; transform: translateX(-50%); }
.compass-w { top: 50%; left: 2px; transform: translateY(-50%); }
.compass-needle {
  position: absolute;
  top: 50%; left: 50%;
  color: var(--accent);
  font-size: 17px;
  line-height: 1;
  transform-origin: 50% 88%;
  transform: translate(-50%, -88%);
}
.overview-map {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--edge);
  border-radius: 10px;
  background: #696546;
  box-shadow: var(--shadow);
}
.overview-map canvas { display: block; opacity: .94; }
.overview-north, .overview-caption {
  position: absolute;
  padding: 1px 5px;
  border-radius: 5px;
  background: rgb(18 19 18 / 72%);
  color: #f4f1ea;
  font-size: 8px;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.overview-north { top: 4px; right: 4px; color: #f4b54f; font-weight: 700; }
.overview-caption { right: 4px; bottom: 4px; color: #d5d0c4; }
.street-readout {
  display: grid;
  width: 248px;
  gap: 2px;
  padding: 8px 10px 9px;
  border: 1px solid var(--edge);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: var(--shadow);
  line-height: 1.25;
}
.street-readout strong {
  overflow: hidden;
  color: var(--ink);
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.street-mode {
  color: var(--accent);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.street-modern, .street-approach {
  overflow: hidden;
  color: var(--ink-dim);
  font-size: 10px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.street-approach { color: var(--ink); }

.chip {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 11px;
  font: inherit;
  font-size: 13px;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  cursor: pointer;
  min-height: 38px;
}
.chip.icon { width: 38px; justify-content: center; padding: 8px; font-size: 15px; }
.chip[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }

/* Unread marker on the help chip. `position: relative` has to sit on the chip
   itself or the dot anchors to the nearest positioned ancestor, which is the
   HUD, and it lands in the corner of the screen. */
.chip { position: relative; }
.chip-dot {
  position: absolute;
  top: 5px; right: 5px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.chip-dot[hidden] { display: none; }
.chip .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: linear-gradient(135deg, var(--doc) 0 33%, var(--inf) 33% 66%, var(--con) 66%);
}

.hud-legend {
  pointer-events: auto;
  align-self: flex-end;
  width: min(320px, calc(100% - 4px));
  margin-top: 8px;
  padding: 12px 14px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--panel);
  box-shadow: var(--shadow);
  font-size: 12.5px;
}
.hud-legend[hidden] { display: none; }
.legend-title { margin: 0 0 8px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); }
.hud-legend ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
.hud-legend li { display: grid; grid-template-columns: 14px 1fr; gap: 8px; align-items: start; color: var(--ink-dim); }
.sw { width: 12px; height: 12px; border-radius: 3px; margin-top: 3px; display: block; }
.sw-doc { background: var(--doc); }
.sw-inf { background: var(--inf); }
.sw-rec { background: repeating-linear-gradient(45deg, var(--con) 0 2px, transparent 2px 4px); border: 1px solid var(--con); }
.legend-note { margin: 10px 0 0; color: var(--ink-dim); font-size: 11.5px; }
.legend-note code { font-size: 11px; }

/* ----------------------------------------------------------- liberties --- */
/* One <details> per recorded liberty. Collapsed by default because eighteen
   confessions opened at once is a wall rather than a disclosure — the titles
   are the index, and the reasoning is one tap away. */

.legend-h {
  margin: 16px 0 0;
  padding-top: 13px;
  border-top: 1px solid var(--edge);
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent);
}
/* `minmax(0, 1fr)` and not the default `auto`, because an `auto` grid track
   resolves toward MAX-CONTENT: a card holding one long unbreakable run widens
   the whole column and the panel's text is then clipped at the right edge
   instead of wrapping. Every section that mounts here had fitted anyway, so the
   fault was latent until T-0281's plant vocabularies — thirty words in one row —
   pushed a 390 px panel's content to 419 px against its 338 px box. This says
   the column may shrink below its content, which is what makes text wrap; the
   sections that already fitted are unchanged by it. */
.liberties { margin-top: 10px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; }

.lib { border: 1px solid var(--edge); border-radius: 9px; background: rgb(255 255 255 / 3%); }
.lib summary {
  display: flex; align-items: baseline; gap: 7px;
  padding: 7px 9px;
  cursor: pointer;
  list-style: none;
  font-size: 12px;
  color: var(--ink);
}
.lib summary::-webkit-details-marker { display: none; }
.lib summary:hover { background: rgb(255 255 255 / 5%); border-radius: 8px; }
.lib[open] summary { border-bottom: 1px solid var(--edge); border-radius: 8px 8px 0 0; }
.lib-id {
  flex: 0 0 auto;
  color: var(--accent);
  font-size: 11px; font-variant-numeric: tabular-nums; letter-spacing: .04em;
}
.lib-title { flex: 1 1 auto; }
.lib-scope {
  flex: 0 0 auto;
  padding: 1px 6px;
  border: 1px solid var(--edge); border-radius: 999px;
  color: var(--ink-dim); font-size: 10px; white-space: nowrap;
}

.lib-body { margin: 0; padding: 9px 10px 2px; display: grid; gap: 3px; }
.lib-body dt {
  color: var(--accent);
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
}
.lib-body dd { margin: 0 0 8px; color: var(--ink-dim); font-size: 11.5px; line-height: 1.5; }
.lib-body code { font-size: 11px; }
.lib-body b { color: var(--ink); font-weight: 600; }

.lib-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px;
  margin: 0; padding: 0 10px 9px;
}
.lib-subject {
  padding: 1px 6px;
  border: 1px solid var(--edge); border-radius: 999px;
  color: var(--ink); font-size: 10px;
}
/* An admission, not a label: this entry says it invented that building's
   footprint or position, and the commit gate holds it to the claim. Tinted to
   read as the conjectural swatch does, since that is what it is describing. */
.lib-covers {
  padding: 1px 6px;
  border: 1px dashed var(--con); border-radius: 999px;
  color: var(--ink); font-size: 10px; white-space: nowrap;
}
.lib-covers em { font-style: normal; color: var(--ink-dim); }
.lib-date { color: var(--ink-dim); font-size: 10px; font-variant-numeric: tabular-nums; }

/* An excluded structure reuses the liberty entry's box wholesale: it is the same
   kind of disclosure, and a visitor should not have to learn a second interface
   to read a second confession. Only the citation list is new — the entry ends in
   the evidence, because "we left this out" is a claim like any other here. */
.excl-cites { padding: 2px 10px 10px 28px; font-size: 11.5px; gap: 7px; }

/* The wildlife section (ROADMAP K51) reuses the same box for the third time, and
   for the third time the reason is that a visitor should not have to learn a new
   interface to read a new kind of record. A habitat is a liberty-shaped entry
   with its animals nested inside it, which is the one structural difference: the
   nested list is indented and loses the outer border so ten habitats holding a
   hundred and thirty-nine species still read as a list and not as a stack of
   boxes inside boxes. */
.fauna-species { display: grid; gap: 4px; padding: 2px 10px 10px 14px; }
/* The legend's swatch is a block because it heads a list item there. Inside a
   graded claim it sits in front of the value it grades, and a block would put
   the grade on a line of its own above the answer. */
.fauna-sp .lib-body .sw, .fauna-sp summary .sw { display: inline-block; vertical-align: 0; margin: 0 4px 0 0; }
.fauna-sp { background: rgb(255 255 255 / 2%); }
.fauna-binomial { font-style: italic; color: var(--ink-dim); font-size: 11px; margin-left: 6px; }
/* The reasoning behind a graded claim, under the value it grades. Dimmed and
   small because the value is the answer and this is why we believe it — the same
   ranking the provenance card gives the two. */
.fauna-why { display: block; margin-top: 3px; color: var(--ink-dim); font-size: 11px; line-height: 1.45; }
/* A value outside its own closed set. It cannot happen while tools/validate.py
   holds the vocabulary, which is exactly why it is worth drawing: a card that
   silently prints an unknown word is a card that hides a broken gate. */
.fauna-unknown { border-color: var(--con); color: var(--con); }

/* The plants section (T-0281) reuses the wildlife's box for the fifth time, and
   deliberately reuses its CLASSES too rather than cloning them under a new
   prefix: a community with its species nested inside it is structurally a
   habitat with its animals nested inside it, and a second set of rules saying
   the same thing is a second place for the two to drift apart. Only what is
   genuinely new to this section is named here. */
/* THE GRID COLUMN HAS TO BE TOLD IT MAY BE NARROW, and this is the one rule here
   that is not cosmetic. `.lib-body` is a one-column grid with the default
   `auto` track, and an `auto` track resolves toward MAX-CONTENT — so a `dd`
   holding a long unbroken run widens the whole card instead of wrapping inside
   it. The liberties, the wildlife and the people never showed it because their
   longest line is short enough to fit anyway; this section's vocabulary lists
   are not (`inflorescence_shapes` alone is thirty words), and at 390 px the
   first render pushed the panel's content to 419 px against a 338 px box and
   clipped every line of reasoning at the right edge. `minmax(0, 1fr)` is the
   column saying it may shrink below its content, which is what makes the text
   wrap. Scoped to this section deliberately: the same latent fault sits under
   every other card here and correcting it globally is a layout change to four
   sections this ticket did not measure. */
.plant-vocab .lib-body, .plant-zone > .lib-body, .plant-sp .lib-body {
  grid-template-columns: minmax(0, 1fr);
}
/* A community's summary carries FOUR things where a habitat's carries three —
   the id, the name, the species count and, on eight of the ten, the chip saying
   the sward lattice cannot draw what this community's records ask for. `.lib
   summary` is a flex row that does not wrap, so the fourth chip ran the line
   past the panel's right edge at 390 px. Wrapping is the honest answer rather
   than dropping the chip: the chip is the finding. */
.plant-zone > summary { flex-wrap: wrap; }
.plant-zone > summary .lib-title { min-width: 0; }
.plant-clamp { margin: 4px 0 0; padding-left: 16px; display: grid; gap: 4px; }
.plant-clamp li { line-height: 1.45; }
/* The chip that says a community is drawing less than its records ask for. It
   takes the conjectural colour on purpose — not because the RECORD is weak, but
   because what a visitor sees there is the lattice's answer and not the
   evidence's, and this panel's colours already mean exactly that. */
.plant-clamped { border-color: var(--con); color: var(--con); }

/* The people section (ROADMAP K52) reuses the same box for the fourth time, and
   the nesting is the wildlife section's exactly: a household is a liberty-shaped
   entry with its people inside it, indented and unboxed so 173 households do not
   read as boxes inside boxes. */
.res-people { display: grid; gap: 4px; padding: 2px 10px 10px 14px; }
.res-person { background: rgb(255 255 255 / 2%); }
.res-person .lib-body .sw, .res-person summary .sw,
.res-hh summary .sw, .res-fields .sw {
  display: inline-block; vertical-align: 0; margin: 0 4px 0 0;
}
/* The reasoning behind a graded claim, under the value it grades — the same
   ranking, and the same styling, the wildlife cards use. On this layer it
   carries more weight than anywhere else in the panel: a household's note
   routinely explains that the figure above it is NOT evidence. */
.res-why { display: block; margin-top: 3px; color: var(--ink-dim); font-size: 11px; line-height: 1.45; }
.res-role { color: var(--ink-dim); font-size: 11px; margin-left: 6px; }
.res-chips { display: inline-flex; flex-wrap: wrap; gap: 4px; margin-left: 6px; }
.res-chip {
  font-size: 10.5px; color: var(--ink-dim); border: 1px solid var(--edge);
  border-radius: 999px; padding: 0 6px; white-space: nowrap;
}
/* The seventeen households this project could not put on a building card. The
   chip is the finding, so it is the one thing in the list drawn in the
   conjectural colour rather than the dim one. It is NOT drawn inside the
   letter-list group, where having no building is the definition rather than a
   finding — see residents.js § letterListGroupHtml. */
.res-orphan { border-color: var(--con); color: var(--con); }
/* The letter-list cohort (T-0379), which is most of the town's people and none of
   its evidence. One closed group, marked in the conjectural colour the same way a
   single thin claim is, so the split is visible before it is opened. */
.res-ll-group { border-left: 2px solid var(--con); padding-left: 9px; }
.res-ll-group > summary .lib-title { color: var(--ink-dim); }
.res-hh summary { display: flex; flex-wrap: wrap; align-items: baseline; }
.res-hh .lib-title { text-transform: capitalize; }

/* A claim the ground makes about itself, in the same box for the same reason.
   The confidence chip sits in the summary because the grade IS the headline
   here: a visitor scanning the list should be able to see, without opening
   anything, that the water is documented and the bank face is not. */
.ground-cites { padding: 2px 10px 10px 28px; font-size: 11.5px; gap: 7px; }
.ground summary .conf { flex: 0 0 auto; }
.ground .lib-body dd.ground-note { color: var(--ink-dim); }
.ground-standard { border-left: 2px solid var(--edge); padding-left: 9px; }

.crosshair {
  position: absolute;
  left: 50%; top: 50%;
  width: 6px; height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.75);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
  opacity: 0;
  transition: opacity .2s ease;
}
body.is-locked .crosshair { opacity: 1; }
body.is-touch .crosshair { display: none; }

.hud-bottom { margin-top: auto; display: flex; justify-content: center; }
.hint {
  margin: 0;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--ink-dim);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 999px;
  opacity: 0;
  transition: opacity .25s ease;
  max-width: 100%;
  text-align: center;
}
.hint.on { opacity: 1; }

/* --------------------------------------------------------- touch stick --- */

.touch-layer { position: absolute; inset: 0; pointer-events: none; }
.touch-layer[hidden] { display: none; }
.stick {
  position: absolute;
  width: 116px; height: 116px;
  margin: -58px 0 0 -58px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(12, 12, 12, 0.28);
  opacity: 0;
  transition: opacity .12s ease;
}
.stick.on { opacity: 1; }
.stick-knob {
  position: absolute;
  left: 50%; top: 50%;
  width: 46px; height: 46px;
  margin: -23px 0 0 -23px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.6);
}

/* Rise / descend, free-fly only. Bottom RIGHT, above the look half: the left
   thumb is already the stick, and the right thumb is the one that drags to
   look — reaching down to a corner it can hit without crossing the screen. */
.rise-pad {
  position: absolute;
  right: 14px;
  bottom: calc(20px + var(--safe-b));
  display: grid;
  gap: 10px;
  pointer-events: auto;
}
.rise-pad[hidden] { display: none; }
.rise-btn {
  width: 58px; height: 58px;        /* comfortably past the 44px touch minimum */
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.34);
  background: rgba(12, 12, 12, 0.42);
  color: #fff;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.rise-btn.on { background: var(--accent); color: #17140c; }

/* --------------------------------------------------------------- popup --- */

.popup {
  position: fixed;
  z-index: 30;
  right: 12px;
  top: calc(58px + var(--safe-t));
  width: min(392px, calc(100vw - 24px));
  max-height: calc(var(--vh) - 80px - var(--safe-t) - var(--safe-b));
  overflow: auto;
  padding: 16px 16px 18px;
  border: 1px solid var(--edge);
  border-radius: 14px;
  background: var(--panel-solid);
  box-shadow: var(--shadow);
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.popup[hidden] { display: none; }

.pop-head { display: flex; align-items: flex-start; gap: 10px; }
.pop-head h2 { margin: 0; font-size: 18px; font-weight: 620; letter-spacing: -0.01em; }
.pop-aka { margin: 3px 0 0; font-size: 12px; color: var(--ink-dim); }
/* The production identity, demoted under the title it used to BE (T-0076): small,
   dimmed and still selectable, because a reference line nobody can copy is decoration. */
.pop-spec { margin: 3px 0 0; font-size: 11px; color: var(--ink-dim); }
.pop-spec code { font-size: 11px; }
.pop-close {
  margin-left: auto;
  width: 30px; height: 30px;
  flex: 0 0 30px;
  font: inherit; font-size: 16px; line-height: 1;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: 8px;
  cursor: pointer;
}

.pop-meta {
  margin: 12px 0 0;
  padding: 9px 11px;
  border-radius: 9px;
  background: rgba(127, 127, 127, 0.1);
  font-size: 12.5px;
  color: var(--ink-dim);
}
.pop-meta strong { color: var(--ink); font-weight: 600; }
.pop-flag {
  display: block;
  margin-top: 6px;
  color: var(--accent);
  font-size: 11.5px;
}

.pop-sec { margin: 16px 0 0; }
.pop-sec > h3 {
  margin: 0 0 8px;
  font-size: 10.5px;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--accent);
}

/* The phase's own account of itself, in the record's words. Prose rather than a
   table row because it is an argument, not a value — it is where a record says a
   building that held the post office for three years is not the post office on
   the scene date, which no confidence chip can express. */
.pop-account {
  margin: 0 0 9px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-dim);
}

table.attrs { width: 100%; border-collapse: collapse; font-size: 13px; }
table.attrs th, table.attrs td { text-align: left; vertical-align: top; padding: 6px 6px 6px 0; border-top: 1px solid var(--edge); }
table.attrs tr:first-child th, table.attrs tr:first-child td { border-top: 0; }
table.attrs th { font-weight: 500; color: var(--ink-dim); width: 38%; }
table.attrs td .val { font-variant-numeric: tabular-nums; }

.conf {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
}
.conf-attested  { background: rgba(160, 200, 140, .2); color: #a9cf93; border: 1px solid rgba(160, 200, 140, .4); }
.conf-inferred     { background: rgba(226, 166, 63, .18); color: var(--inf); border: 1px solid rgba(226, 166, 63, .45); }
.conf-reconstructed    { background: rgba(154, 163, 173, .16); color: var(--con); border: 1px solid rgba(154, 163, 173, .4); }
[data-theme="light"] .conf-attested { color: #3f6a2c; }
[data-theme="light"] .conf-inferred { color: #8a5a12; }
[data-theme="light"] .conf-reconstructed    { color: #4d5560; }

/* WHO WAS HERE. A person's `grade` is a different axis from an attribute's
   confidence — it says how much of a PERSON is reconstructed, not how sure we
   are of a value — so it gets its own chip shape (square-ish, outlined) and
   never borrows the confidence palette's meaning. `derived` has no equivalent
   on the confidence scale at all, which is the clearest argument for keeping
   the two apart. */
.grade {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
  border: 1px solid currentColor;
  background: transparent;
}
.grade-attested { color: #a9cf93; }
.grade-inferred    { color: #8fb8d6; }
.grade-reconstructed   { color: var(--inf); }
[data-theme="light"] .grade-attested { color: #3f6a2c; }
[data-theme="light"] .grade-inferred { color: #2f5f80; }
[data-theme="light"] .grade-reconstructed { color: #8a5a12; }

/* The per-level summary, above every table on the card. It is read first and
   skimmed, so the three rows are separated by the residents' own hairline rather
   than boxed: a bordered panel at the top of a card that is already a stack of
   panels reads as a callout, and this is not an aside — it is the card's own
   contents page. The chip is the table's chip at the table's size, deliberately,
   because a bigger one here would look like a grade FOR THE BUILDING, which is
   the one thing this section must not be mistaken for. */
.pop-basis .basis-lead { margin: 0 0 10px; font-size: 12px; line-height: 1.5; color: var(--ink-dim); }
.pop-basis .basis-row { border-top: 1px solid var(--edge); padding-top: 8px; margin-top: 8px; }
.pop-basis .basis-row:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.pop-basis .basis-head { margin: 0 0 4px; font-size: 12px; line-height: 1.6; }
.pop-basis .basis-head .conf { margin-left: 0; }
.pop-basis .basis-count { margin-left: 6px; font-variant-numeric: tabular-nums; }
.pop-basis .basis-gloss { color: var(--ink-dim); }
.pop-basis .basis-what { margin: 0; font-size: 12.5px; line-height: 1.5; }
.pop-basis .basis-empty { color: var(--ink-dim); }
.pop-basis .basis-from { margin: 4px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--ink-dim); }
/* Not a caveat about the evidence — a caveat about the VIEW, which is why it is
   marked the way the rows' own `not built` chip is marked: outside the scale. */
.pop-basis .basis-absent {
  margin: 4px 0 0;
  font-size: 11.5px; line-height: 1.45; font-style: italic; color: var(--ink-dim);
}

.pop-residents .res-lead { margin: 0 0 8px; font-size: 12px; color: var(--ink-dim); line-height: 1.45; }
.pop-residents .res-hh { border-top: 1px solid var(--edge); padding-top: 8px; margin-top: 8px; }
.pop-residents .res-hh:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.pop-residents .res-head { margin: 0 0 4px; font-size: 13px; line-height: 1.45; }
.pop-residents .res-basis { margin: 0 0 6px; font-size: 11.5px; line-height: 1.45; color: var(--ink-dim); }
.pop-residents ul.res-people { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.pop-residents ul.res-people li { font-size: 12.5px; line-height: 1.4; }
.pop-residents .res-role { color: var(--ink-dim); margin-left: 6px; }

/* What the mesh does with a value nothing reads. Deliberately NOT a confidence
   chip: it answers a different question, so it is dashed rather than filled and
   sits outside the three-colour scale. A visitor should never read "not built"
   as a fourth grade of certainty. */
.geom {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
  color: var(--ink-dim);
  border: 1px dashed currentColor;
  background: transparent;
}
.geom-absent { font-weight: 600; }

.attr-note {
  display: block;
  margin-top: 4px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ink-dim);
}
/* An author `display` beats the UA rule for [hidden], so say it again here or
   every "why" note ships permanently expanded. */
.attr-note[hidden] { display: none; }
.attr-toggle {
  background: none; border: 0; padding: 0; margin-top: 3px;
  font: inherit; font-size: 11px; color: var(--accent); cursor: pointer; text-decoration: underline;
}

ol.cites { margin: 0; padding-left: 18px; font-size: 12.5px; display: grid; gap: 9px; }
ol.cites li { color: var(--ink-dim); line-height: 1.45; }
ol.cites .cite-text { color: var(--ink); }
ol.cites a { color: var(--accent); }
.tier { font-size: 10.5px; opacity: .8; }

/* What the page reprints, which is what the rung beside it is a judgement
   about. One line, never collapsed: a visitor who cannot see it reads a modern
   blog stamped "tier 2" and concludes the ladder is broken. The em rule marks
   it as the citation's own account of itself rather than more citation. */
.cite-reprints {
  margin: 3px 0 0;
  padding-left: 8px;
  border-left: 2px solid var(--edge);
  font-size: 11.5px; line-height: 1.45; color: var(--ink-dim);
}
.cite-reprints .reprints-work { color: var(--ink); }
.cite-reprints .reprints-date { font-variant-numeric: tabular-nums; opacity: .85; }

/* The limits collapse for the reason the record's own account does — several
   of these run to four clauses each and five open citations would push the rest
   of the card off a phone. */
.cite-more { margin-top: 4px; }
.cite-more > summary {
  cursor: pointer; list-style: none;
  font-size: 11px; color: var(--accent); text-decoration: underline;
}
.cite-more > summary::-webkit-details-marker { display: none; }
.cite-lim-h {
  margin: 6px 0 2px;
  font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim);
}
ol.cites ul.cite-lim { margin: 0; padding-left: 15px; }
ol.cites ul.cite-lim li { font-size: 11.5px; line-height: 1.45; }
ol.cites ul.cite-lim-not li { opacity: .95; }
.cite-reading { margin: 6px 0 0; font-size: 11.5px; line-height: 1.55; color: var(--ink-dim); }

/* The liberties for the inspected building reuse the Evidence panel's `.lib`
   entry wholesale — same markup, same renderer — so only the deltas live here:
   a count beside the heading, and the honest note when nothing is recorded. */
.pop-liberties .liberties { margin-top: 0; }
.pop-count {
  display: inline-block;
  margin-left: 4px;
  padding: 0 5px;
  border-radius: 999px;
  background: rgb(127 127 127 / 18%);
  color: var(--ink-dim);
  font-size: 10px; letter-spacing: 0; font-variant-numeric: tabular-nums;
}
.pop-lib-none { margin: 0; font-size: 12px; line-height: 1.5; color: var(--ink-dim); }

/* The open question about this building reuses the Evidence panel's entry the
   same way, so the only delta is the sentence introducing it. It sits directly
   under the two claims it qualifies and must not read as a fourth claim. */
.pop-question .liberties { margin-top: 0; }
.pop-question-lead {
  margin: 0 0 8px;
  font-size: 12px; line-height: 1.5;
  color: var(--ink-dim);
}

/* The record's own account: several hundred words of prose, folded away behind
   the same disclosure the liberties use, because it is the same kind of thing —
   an argument a visitor opens when they want it, not a value they skim. Shown
   verbatim, so the only styling decisions here are measure and rhythm. */
.research { border: 1px solid var(--edge); border-radius: 9px; background: rgb(255 255 255 / 3%); }
.research > summary {
  padding: 7px 9px;
  cursor: pointer;
  list-style: none;
  font-size: 12px;
  color: var(--ink);
}
.research > summary::-webkit-details-marker { display: none; }
.research > summary:hover { background: rgb(255 255 255 / 5%); border-radius: 8px; }
.research[open] > summary { border-bottom: 1px solid var(--edge); border-radius: 8px 8px 0 0; }
.research-body {
  margin: 0;
  padding: 9px 10px 10px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-dim);
}

.pop-foot { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--edge); font-size: 12px; color: var(--ink-dim); }
.pop-foot a { color: var(--accent); }

@media (max-width: 620px) {
  .popup {
    right: 8px; left: 8px;
    width: auto;
    top: auto;
    bottom: calc(8px + var(--safe-b));
    max-height: min(62vh, calc(var(--vh) - 120px));
  }
  .hud-legend { width: 100%; }
  .gate-card { padding: 22px 18px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ---- controls / settings panel ---------------------------------------- */
/* Replaces the old bare legend. Run already existed on Shift and nobody could
   discover it; a walkthrough that does not say how to walk is not finished. */

.hud-panel {
  pointer-events: auto;
  align-self: flex-end;
  /* 380, not 360: with a fifth tab the strip fits 360 exactly, and a layout
     with no slack in it is one font-metric difference away from wrapping on
     somebody else's machine. The extra twenty pixels are also twenty pixels of
     place name in the Go to list. */
  width: min(380px, calc(100% - 4px));
  margin-top: 8px;
  max-height: min(70vh, 560px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  font-size: 12.5px;
}
.hud-panel[hidden] { display: none; }

.control-help {
  pointer-events: auto;
  position: absolute;
  top: 54px;
  right: 0;
  width: min(330px, calc(100vw - 24px));
  padding: 14px 14px 12px;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: var(--panel-solid);
  color: var(--ink);
  box-shadow: var(--shadow);
  font-size: 12px;
}
.control-help[hidden], .control-help-grid[hidden] { display: none; }
.control-help > strong { display: block; margin: 0 28px 10px 0; font-size: 14px; }
.control-help-close {
  position: absolute; top: 7px; right: 8px;
  width: 28px; height: 28px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink-dim); font-size: 18px; cursor: pointer;
}
.control-help-close:hover { color: var(--ink); background: rgb(255 255 255 / 8%); }
.control-help-grid {
  display: grid;
  grid-template-columns: auto 1fr auto 1fr;
  align-items: center;
  gap: 7px 9px;
}
.control-help-grid kbd { margin: 0; white-space: nowrap; }
.control-help-gotit, .setting-button {
  border: 1px solid var(--edge); border-radius: 8px;
  background: rgb(255 255 255 / 8%); color: var(--ink);
  font: inherit; cursor: pointer;
}
.control-help-gotit { width: 100%; margin-top: 12px; padding: 7px 10px; }
.setting-button { padding: 5px 9px; }
.control-help-gotit:hover, .setting-button:hover { background: rgb(255 255 255 / 14%); }

@media (max-width: 520px) {
  .control-help { top: 92px; right: 0; width: min(310px, calc(100vw - 16px)); }
  .control-help-grid { grid-template-columns: auto 1fr; }
}

.panel-tabs {
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 8px 6px 0;
  border-bottom: 1px solid var(--edge);
}

.panel-tab {
  flex: 0 1 auto;
  /* 6px of side padding, not 9. Five tabs and a close button want 348 px of a
     342 px strip at the old figure, and flex-shrink pays the difference out of
     the labels rather than out of the row — which the release gate reads,
     correctly, as a squeezed tab. Measured rather than guessed: the desktop
     panel is 358 px wide and the strip now needs 334. */
  padding: 7px 6px;
  /* Five tabs plus the close button do not fit 360px if any of them may wrap;
     "What's new" broke onto a second line and made the whole strip two rows
     tall. Let the labels shrink instead — they are short enough to stay
     legible, and a ragged one-line strip beats a lumpy two-line one. The
     narrow-viewport block below buys the room the fifth tab needs. */
  white-space: nowrap;
  min-width: 0;
  border: 0;
  border-radius: 9px 9px 0 0;
  background: transparent;
  color: var(--ink-dim);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.panel-tab:hover { color: var(--ink); }
.panel-tab.is-on { color: var(--ink); background: rgb(255 255 255 / 7%); }

.panel-close {
  margin-left: auto;
  width: 26px; height: 26px;
  border: 0; border-radius: 8px;
  background: transparent; color: var(--ink-dim);
  font-size: 17px; line-height: 1; cursor: pointer;
}
.panel-close:hover { color: var(--ink); background: rgb(255 255 255 / 8%); }

.panel-body { padding: 13px 15px 15px; }

.keys { display: grid; grid-template-columns: auto 1fr; gap: 7px 13px; margin: 0; }
/* [hidden] is display:none at UA-stylesheet strength; any display rule on the
   element itself beats it. Both control lists rendered at once without this. */
.keys[hidden], .panel-body[hidden] { display: none; }
.keys dt { color: var(--ink-dim); font-size: 12px; white-space: nowrap; }
.keys dd { margin: 0; font-size: 12.5px; color: var(--ink); }

kbd {
  display: inline-block;
  min-width: 19px;
  padding: 1px 5px;
  margin-right: 2px;
  border: 1px solid var(--edge);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: rgb(255 255 255 / 7%);
  font: inherit; font-size: 11px; text-align: center;
}

.field { display: block; margin-bottom: 14px; font-size: 12.5px; color: var(--ink); }
.field > span { display: block; margin-bottom: 6px; color: var(--ink-dim); }
.field > span b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.field.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.field.row > span { margin: 0; }
.field input[type="range"] { width: 100%; accent-color: var(--accent); }
.field select {
  padding: 5px 8px; border-radius: 8px;
  border: 1px solid var(--edge);
  background: rgb(255 255 255 / 7%); color: var(--ink); font: inherit; font-size: 12px;
}
.check-field input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--accent);
}

.jump-field input[type="search"] {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--edge);
  border-radius: 8px;
  outline: none;
  background: rgb(255 255 255 / 7%);
  color: var(--ink);
  font: inherit;
}
.jump-field input[type="search"]:focus { border-color: var(--accent); }
.jump-results {
  /* The list is the whole of its own tab now rather than a field inside
     Settings, so it gets the height the panel can spare. */
  max-height: min(340px, 42vh);
  margin-top: 7px;
  overflow-y: auto;
  border: 1px solid var(--edge);
  border-radius: 8px;
}
.jump-group {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: 5px 9px;
  background: var(--panel-solid);
  color: var(--ink-dim);
  font-size: 9.5px;
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.jump-result {
  width: 100%;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 9px;
  border: 0;
  border-top: 1px solid var(--edge);
  background: rgb(255 255 255 / 3%);
  color: var(--ink);
  font: inherit;
  font-size: 11.5px;
  text-align: left;
  cursor: pointer;
}
.jump-result:hover, .jump-result:focus-visible { background: rgb(255 255 255 / 11%); }
/* `:not(.conf)`, because the confidence chip is also a <small> here and it
   carries the colour that IS its meaning — a bare `.jump-result small` rule
   outranks `.conf-inferred` on specificity and painted all three grades the
   same dim grey, which is a legend that lies. */
.jump-result small:not(.conf) { flex: none; color: var(--ink-dim); font-size: 9px; }
.jump-result .conf { flex: none; margin-left: 0; font-size: 9px; }
.jump-empty { margin: 0; padding: 10px; color: var(--ink-dim); font-size: 11.5px; }

.legend-list { margin: 0 0 10px; padding: 0; list-style: none; display: grid; gap: 8px; }
.legend-list li { font-size: 12.5px; color: var(--ink); line-height: 1.45; }

/* ---- What's new -------------------------------------------------------- */

.panel-tab { position: relative; }
.tab-dot {
  display: inline-block;
  width: 6px; height: 6px;
  margin-left: 5px;
  vertical-align: 1px;
  border-radius: 50%;
  background: var(--accent);
}
.tab-dot[hidden] { display: none; }

.wn-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 16px; }
.wn-entry { display: grid; gap: 5px; }
/* An unread release gets a rule down its left edge rather than a highlight
   fill: the panel already sits over a moving scene, and one more filled block
   competes with it. The marker should be findable, not loud. */
.wn-entry.is-new {
  padding-left: 9px;
  border-left: 2px solid var(--accent);
  margin-left: -11px;
}
.wn-head { display: flex; align-items: baseline; gap: 7px; }
.wn-title { font-size: 12.5px; font-weight: 600; color: var(--ink); line-height: 1.35; }
.wn-flag {
  flex: none;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #1d1a14;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.wn-meta { color: var(--ink-dim); font-size: 11px; }
.wn-items { margin: 3px 0 0; padding-left: 15px; display: grid; gap: 5px; }
.wn-items li { font-size: 12px; color: var(--ink); line-height: 1.5; }

@media (max-width: 560px) {
  .hud-panel { width: 100%; max-height: 58vh; }
  /* Chips go icon-only. Four labelled chips overflow 390 px by about forty
     pixels and the help button drops to a second row, which reads as broken
     rather than as a wrap. The tri-colour dot and the triangle carry the
     meaning; both keep their title and aria-label, and the Evidence tab
     explains the dot at length. */
  .chip-label { display: none; }
  #btn-fly, #btn-confidence { width: 38px; justify-content: center; padding: 8px; }
  /* And when an altitude is showing, it says more than a date that never
     changes — so the date yields rather than the badge growing. */
  .badge.has-alt .badge-sub { display: none; }
  /* The narrow panel cannot grow the way the desktop one just did, so the fifth
     tab's room comes out of the type instead. The release gate measures the
     result at 390×780 — one row, nothing squeezed — which is where a sixth tab
     would be caught. */
  .panel-tab { font-size: 11.5px; }
  .navigation-stack { top: calc(55px + var(--safe-t)); }
  .street-readout { width: 188px; }
  /* The mobile panel spans the same left edge as the navigation readouts. Hide
     them while it is open instead of letting its translucent surface mix the
     map into sliders and search results. Desktop keeps both columns visible. */
  .hud.panel-open .navigation-stack { visibility: hidden; }
}

/* ---- the confidence control's second half ------------------------------- */
/* Colouring by evidence and hiding a level are one enquiry at two depths, so
   they share a control: the chip toggles the colour, the caret opens this. The
   two buttons are welded into one pill so the pair reads as a single thing
   rather than as a chip that grew a neighbour. */
.chip-group { position: relative; display: flex; align-items: stretch; }
.chip-group > .chip:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.chip-group > .chip-more {
  border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: 0;
  width: 26px; min-width: 26px; padding: 8px 0; font-size: 11px;
}
.chip-group > .chip-more[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
/* A dot on the caret when a level is hidden. Hiding changes what the town IS,
   and a control that quietly removes two thirds of the buildings has to say so
   while the panel is shut. */
.chip-group.has-hidden > .chip-more::after {
  content: ''; position: absolute; top: 5px; right: 5px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--accent);
}

.confidence-menu {
  /* The HUD is pointer-events: none so the view stays live underneath, and
     every interactive piece re-enables it for itself. This panel rides in the
     chip group but is not a .chip, so without its own auto every checkbox
     click fell THROUGH to the canvas — re-locking the pointer and tripping
     the click-away handler at once (T-0108). */
  pointer-events: auto;
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 20;
  width: 268px; padding: 12px; border-radius: 12px;
  background: var(--panel-solid); border: 1px solid var(--edge);
  box-shadow: var(--shadow); color: var(--ink);
}
.confidence-menu[hidden] { display: none; }
.cm-title {
  margin: 0 0 8px; font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent);
}
.cm-row {
  display: grid; grid-template-columns: 16px 12px 1fr auto; gap: 8px;
  align-items: center; padding: 5px 0; cursor: pointer; font-size: 13px;
}
.cm-sw { width: 12px; height: 12px; border-radius: 3px; display: block; }
.sw-att { background: var(--doc); }
.cm-count { color: var(--ink-dim); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.cm-row input { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; }
/* An unticked row is what is NOT in the view, so it reads as absent. */
.cm-row:has(input:not(:checked)) .cm-label,
.cm-row:has(input:not(:checked)) .cm-count { color: var(--ink-dim); opacity: .55; }
.cm-row:has(input:not(:checked)) .cm-sw { opacity: .3; }
.cm-note { margin: 8px 0 0; font-size: 11.5px; line-height: 1.5; color: var(--ink-dim); }
.cm-reset {
  margin-top: 10px; width: 100%; padding: 7px 10px; border-radius: 8px;
  background: transparent; border: 1px solid var(--edge); color: var(--ink);
  font: inherit; font-size: 12.5px; cursor: pointer;
}
.cm-reset:hover { border-color: var(--accent); color: var(--accent); }

/* On a phone the panel is nearly as wide as the screen, so it anchors to the
   viewport rather than to a 26 px caret hanging off the right edge. */
@media (max-width: 520px) {
  .confidence-menu { width: min(280px, calc(100vw - 24px)); right: -8px; }
}
