/* ============================================================
   Raw States — light cartographic product UI
   Flat surfaces, 1px lines, one type family. Category colour is
   reserved for map data; UI selection is ink-on-light.

   Every control lives in one panel. Nothing floats over the map
   except search and the detail card, so there is nothing to
   collide with at any width.
   ============================================================ */

:root {
  /* Neutrals carry a faint warm bias rather than the usual cool grey. The
     basemap is pale green land and blue water; a slightly warm surround reads
     as paper against it and stops the chrome competing with the data. */
  --bg: #edeeea;
  --surface: #ffffff;
  --surface-2: #f4f5f1;
  --line: #dcddd7;
  --line-strong: #c2c4bc;
  --text: #1e211d;
  --text-dim: #565b53;
  /* 4.7:1 on white. The faint tone is used at 11px for metadata, where the
     previous value sat at 3.7:1 and failed AA — darkened until it clears,
     while staying clearly recessive against --text-dim. */
  --text-faint: #71756a;

  --sel-bg: #1e211d;
  --sel-text: #ffffff;

  --danger: #b04a3e;

  /* One easing and three durations for the whole interface, so nothing moves
     at a speed nothing else moves at. Everything is under a fifth of a second:
     the point is to soften a state change, not to perform one. */
  --ease: cubic-bezier(0.32, 0.08, 0.24, 1);
  --t-fast: 110ms;
  --t-med: 160ms;
  --t-slow: 220ms;

  --radius: 6px;
  --sidebar-w: 268px;
  --topbar-h: 48px;
  --search-h: 56px;

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  --z-map: 1;
  --z-panel: 20;
  --z-scrim: 28;
  --z-detail: 30;
  --z-drawer: 34;
  --z-topbar: 40;
  --z-search: 42;

  font-size: 16px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "Public Sans", -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 0.8125rem;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
  /* This is an instrument, not a document. Dragging across it should move the
     map or the sheet, never paint a blue selection over a paragraph — which is
     exactly what the second half of a double-tap-drag zoom used to do when the
     first tap had opened a card under the finger. Selection is handed back
     below to the two places where copying text is a real thing to want. */
  -webkit-user-select: none;
  user-select: none;
  /* Stops the long-press magnifier and the "copy / share" bubble from
     interrupting a slow pan. */
  -webkit-touch-callout: none;
  /* iOS Safari otherwise flashes a grey box over every button that is held
     for more than an instant. */
  -webkit-tap-highlight-color: transparent;
}

/* Read-and-copy surfaces get selection back: the blurb on a card is the one
   thing here somebody might reasonably want to quote, and the privacy page is
   a document in the ordinary sense. */
.detail-blurb,
.detail-credit,
.doc,
/* Fields must keep it or you cannot place a caret, select a typo, or paste
   into them on some engines — which would have traded a zoom bug for a
   worse one in the search box and both password fields. */
input,
textarea {
  -webkit-user-select: text;
  user-select: text;
}

#map {
  position: absolute;
  inset: var(--topbar-h) 0 0 var(--sidebar-w);
  z-index: var(--z-map);
  /* The canvas sets this on itself, but the container did not, so a gesture
     that began a few pixels off the canvas could still be claimed by the
     browser for scrolling. */
  touch-action: none;
}
body:not(.nav-open) #map { left: 0; }

a { color: inherit; }
.hidden { display: none !important; }
button { font-family: inherit; }

/* ============ Top bar ============
   Three zones on a grid with matching side columns, so the mode
   toggle is centred on the window rather than pushed off by
   whatever happens to sit beside it. */
.topbar {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: var(--topbar-h);
  z-index: var(--z-topbar);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  padding-left: max(14px, var(--safe-l));
  padding-right: max(14px, var(--safe-r));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.top-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.top-center { justify-self: center; }
.top-right { justify-self: end; display: flex; align-items: center; gap: 10px; }

/* The mark is the wordmark. Nothing sits beside it. */
.brand-mark { flex: 0 0 auto; display: block; }
.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0 10px;
  height: 34px;
  cursor: pointer;
  flex: 0 0 auto;
}
.nav-toggle:hover { color: var(--text); background: var(--surface-2); }
.nav-toggle[aria-expanded="true"] { background: var(--sel-bg); border-color: var(--sel-bg); color: var(--sel-text); }
/* The three CSS-drawn bars that used to live here are gone: the button now
   carries a Lucide sliders icon, which says "filters" rather than "menu". */

.seg {
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  flex: 0 0 auto;
}
.seg button {
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 0 16px;
  height: 34px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.seg button + button { border-left: 1px solid var(--line-strong); }
.seg button:hover { color: var(--text); background: var(--surface-2); }
.seg button.active { background: var(--sel-bg); color: var(--sel-text); }
.seg button:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -2px; }
.seg-small button { padding: 0 11px; font-size: 0.75rem; }

/* History / Upcoming: a pill with a thumb that slides under the active half.
   The thumb is the ::after; buttons sit above it and only change colour. */
/* History / Upcoming. A recessed track with a raised white pill riding under
   the active half: the pill is the ::after so the labels never move, and the
   icon takes the accent only on the selected side. */
.seg.top-center {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  padding: 4px;
  overflow: visible;
  box-shadow: inset 0 1px 2px rgba(30, 33, 40, 0.06);
}
.seg.top-center::after {
  content: "";
  position: absolute;
  top: 4px; bottom: 4px; left: 4px;
  width: calc(50% - 4px);
  background: #fff;
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(30, 33, 40, 0.16), 0 2px 8px rgba(30, 33, 40, 0.10);
  transition: transform 0.22s cubic-bezier(.32, .72, 0, 1);
}
.seg.top-center.mode-now::after { transform: translateX(100%); }
.seg.top-center button {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  flex: 1 1 0;
  min-width: 112px;
  height: 32px;
  padding: 0 14px;
  border-radius: 999px;
  background: transparent;
  color: var(--text-faint);
  font-weight: 600;
  transition: color 0.18s var(--ease);
}
.seg.top-center button + button { border-left: 0; }
.seg.top-center button:hover { background: transparent; color: var(--text-dim); }
.seg.top-center button.active { background: transparent; color: var(--text); }
.seg-icon { width: 15px; height: 15px; flex: 0 0 auto; opacity: .55; transition: opacity 0.18s var(--ease), color 0.18s var(--ease); }
.seg.top-center button.active .seg-icon { opacity: 1; color: var(--accent, #b8860b); }
@media (prefers-reduced-motion: reduce) {
  .seg.top-center::after { transition: none; }
}
/* In the sidebar a segmented control has a column to fill, so its halves split
   the width evenly instead of sizing to their labels. */
.seg-wide { width: 100%; }
.seg-wide button { flex: 1; padding: 0; }

/* ============ Paper ============ */
/* Fractal noise at low alpha, baked into a data URI: the grain is what stops
   a large flat panel looking like an empty div. Tiled at 180px so no repeat
   is visible, and kept under 4% so it never reads as dirt on a screenshot. */
:root {
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
  --paper-warm: #fcfbf9;
  --paper-edge: #f2efea;
}

.topbar {
  background:
    var(--grain),
    linear-gradient(180deg, var(--paper-warm) 0%, var(--paper-edge) 100%);
  border-bottom: 1px solid #ded9d1;
  box-shadow: inset 0 1px 0 #ffffff, 0 1px 0 rgba(255, 255, 255, 0.7);
}

/* The sidebar's own rule further down sets a flat background, so the paper
   is applied there instead of here where it would be overwritten. */

/* The search field and the panels sit on the paper, so they read as objects
   placed on it rather than holes cut through it. */
.search-wrap { background: transparent; border-right: 0; }
.search-input { background: #fff; box-shadow: inset 0 1px 2px rgba(80, 66, 44, 0.06); }
.state-select { background-color: #fff; }
.sidebar-collapse {
  background: linear-gradient(180deg, #fdfcfa 0%, var(--paper-edge) 100%);
  border-top: 1px solid #ded9d1;
  box-shadow: inset 0 1px 0 #ffffff;
}

@media (prefers-reduced-transparency: reduce) {
  .topbar, .sidebar { background-image: none; }
}

/* ============ Search ============ */
.search-wrap {
  position: absolute;
  top: var(--topbar-h);
  left: 0;
  width: var(--sidebar-w);
  height: var(--search-h);
  z-index: var(--z-search);
  padding: 10px 12px;
  background: var(--surface);
  border-right: 1px solid var(--line);
  /* No bottom border. The search field carries its own, 10px above where this
     one landed, and two parallel rules with white between them read as a stray
     line rather than as a divider. The sidebar below is the same surface and
     supplies its own section headings. */
}
/* With the panel collapsed there is no column to sit in, so search floats. */
body:not(.nav-open) .search-wrap {
  width: min(340px, calc(100vw - 24px));
  height: auto;
  padding: 0;
  top: calc(var(--topbar-h) + 10px);
  left: 12px;
  background: transparent;
  border: 0;
}
body:not(.nav-open) .search-input { box-shadow: 0 2px 12px rgba(27, 30, 35, 0.18); }
body:not(.nav-open) .search-results { left: 0; right: 0; top: calc(100% + 6px); }

.search-input {
  width: 100%;
  height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.8125rem;
  padding: 0 32px 0 10px;
}
.search-input:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -1px; }
.search-input::-webkit-search-cancel-button { display: none; }

.search-clear {
  position: absolute;
  top: 50%;
  right: 18px;
  transform: translateY(-50%);
  width: 26px; height: 26px;
  border: 0;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
body:not(.nav-open) .search-clear { right: 6px; }
.search-clear:hover { color: var(--text); }

.search-results {
  position: absolute;
  left: 12px; right: 12px;
  top: calc(100% - 8px);
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 320px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 6px 20px rgba(27, 30, 35, 0.16);
}
.search-results li { border-bottom: 1px solid var(--line); }
.search-results li:last-child { border-bottom: 0; }
.search-hit {
  display: block;
  width: 100%;
  min-height: 44px;
  text-align: left;
  border: 0;
  background: transparent;
  padding: 8px 10px;
  cursor: pointer;
  font: inherit;
}
.search-hit:hover, .search-hit:focus-visible { background: var(--surface-2); }
.search-hit strong { display: block; font-size: 0.8125rem; font-weight: 600; }
.search-hit span { display: block; font-size: 0.6875rem; color: var(--text-faint); }
.search-empty { padding: 10px; font-size: 0.75rem; color: var(--text-faint); }

/* ============ Drawer backdrop (phones) ============ */
.scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-scrim);
  background: rgba(27, 30, 35, 0.38);
  border: 0;
  padding: 0;
  display: none;
}

/* ============ Sidebar ============ */
.sidebar {
  position: absolute;
  top: calc(var(--topbar-h) + var(--search-h));
  bottom: 0;
  left: 0;
  width: var(--sidebar-w);
  z-index: var(--z-panel);
  background:
    var(--grain),
    linear-gradient(180deg, var(--paper-warm) 0%, #faf8f5 55%, var(--paper-edge) 100%);
  border-right: 1px solid #ded9d1;
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}
body:not(.nav-open) .sidebar { display: none; }

.sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 14px 16px 8px;
  padding-left: max(16px, var(--safe-l));
}
.side-group + .side-group {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
/* A divider needs something on both sides of it. A hidden section still
   counts as a previous sibling in CSS, which is how a rule ended up drawn
   above the first visible group with nothing over it. */
.side-group.hidden + .side-group,
.side-group:first-of-type,
#state-group + .side-group:not(#panel-list) { border-top: 0; padding-top: 0; margin-top: 14px; }
#state-group { border-top: 0; padding-top: 0; margin-top: 0; }
.side-group h3 {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--text-faint);
}
.side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 24px;
  margin: 0 0 6px;
}
.side-head + .cat-list + .side-head { margin-top: 10px; }
.clear-btn {
  border: 0;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 4px 6px;
  margin-right: -6px;
  border-radius: 3px;
  cursor: pointer;
  line-height: 1.4;
  white-space: nowrap;
}
.clear-btn:hover { color: var(--text); background: var(--surface-2); }
.clear-btn:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -2px; }

.side-note {
  margin: 8px 0 0;
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--text-faint);
}
/* Divides the curated layers from the bulk ones inside "Also show", so the
   off-by-default block reads as a deliberate group rather than as items
   somebody forgot to tick. */
.side-sub {
  margin: 12px 0 6px;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.sidebar-collapse {
  border: 0;
  border-top: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  padding: 12px 9px;
  padding-bottom: max(12px, var(--safe-b));
  cursor: pointer;
  flex: 0 0 auto;
}
.sidebar-collapse:hover { background: var(--surface-2); color: var(--text); }

/* Show list <-> Show filters. In list mode only the list section (and the
   state picker, which applies to it) stays; everything else steps aside. */
.sidebar.list-mode .sidebar-scroll .side-group:not(#panel-list):not(#state-group),
.sidebar.list-mode .sidebar-scroll .map-page { display: none; }
.sidebar.list-mode #panel-list { display: block; }
.side-group, .map-page { transition: opacity 0.16s var(--ease), transform 0.2s var(--ease); }
.panel-leaving { opacity: 0; transform: translateY(10px); }
.panel-entering { opacity: 0; transform: translateY(14px); }
@media (prefers-reduced-motion: reduce) {
  .side-group, .map-page { transition: none; }
}

/* The state picker: the same quiet line-and-surface language as everything
   else in the column, with a chevron drawn in so it isn't the OS default. */
.state-select {
  width: 100%;
  height: 34px;
  padding: 0 30px 0 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="%2358606b" stroke-width="2.4"><path d="m6 9 6 6 6-6"/></svg>') no-repeat right 10px center;
  color: var(--text);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  appearance: none;
  cursor: pointer;
}
.state-select:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -2px; }

.cat-list { list-style: none; margin: 0; padding: 0; }
.cat-row {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 30px;
  padding: 4px 0;
  cursor: pointer;
  font-size: 0.8125rem;
}
.cat-row:hover .cat-name { color: var(--text); }
.cat-row input {
  accent-color: var(--sel-bg);
  margin: 0;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
}
.cat-swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--c);
  flex: 0 0 auto;
}
.cat-name { flex: 1; color: var(--text-dim); font-weight: 500; min-width: 0; }
.cat-count {
  font-size: 0.6875rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

/* ---- Dates, in the panel with every other filter ---- */
.range-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.range-dash { color: var(--text-faint); flex: 0 0 auto; }
.range-row input {
  flex: 1 1 0;
  min-width: 0;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
  padding: 0 4px;
  height: 30px;
}
.range-row input:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -1px; }
.range-row input.bad { border-color: var(--danger); }

/* Kept for the historical Years panel, which still uses number fields. */
.date-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.date-field { display: flex; flex-direction: column; gap: 3px; font-size: 0.6875rem; font-weight: 600; color: var(--text-faint); min-width: 0; }
.date-field input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
  padding: 0 6px;
  height: 30px;
  min-width: 0;
}
.date-field input:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -1px; }

/* War chips sit under the War layer row, indented to read as its children. */
/* War chips live under the War row and only exist while War is on. Collapsed
   height animates so the list does not jump. */
.sub-row {
  margin: 0 0 0 26px;
  gap: 4px;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 180ms ease, opacity 140ms ease, margin 180ms ease;
}
.sub-row.open {
  max-height: 80px;
  opacity: 1;
  margin: 5px 0 3px 26px;
}
.date-field input:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -1px; }

#range-readout { font-variant-numeric: tabular-nums; }

/* ---- Periods ---- */
/* Filter options are words, not buttons. Bordered pills next to a checkbox
   list read as a second, competing control. */
.era-row { display: flex; flex-wrap: wrap; gap: 14px; }
/* The date shortcuts sit under two inputs that span the column, so they space
   themselves across the same width rather than clustering at one end. */
#date-quick {
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
  flex-wrap: nowrap;
}
#date-quick button { flex: 0 1 auto; }
.era-row button {
  border: 0;
  background: none;
  padding: 0;
  height: auto;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--text-faint);
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.12s;
}
.era-row button:hover { color: var(--text); }
.era-row button.active { color: var(--text); font-weight: 700; }
.era-row button:focus-visible { outline: 2px solid var(--text-dim); outline-offset: 2px; border-radius: 2px; }

/* War options stack under their parent row, indented to read as children. */
.sub-row {
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  margin: 0 0 0 27px;
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition: max-height 180ms ease, opacity 140ms ease, margin 180ms ease;
}
.sub-row.open { max-height: 90px; opacity: 1; margin: 3px 0 4px 27px; }
.sub-row button { font-size: 0.6875rem; }

/* ---- Layer groups ----
   A heading, a tri-state box that takes the whole group, and a caret. The
   subs are indented under it and collapse away, so the panel reads as six
   headings until you open one. */
.lgroup { border-top: 1px solid var(--line); }
.lgroup:first-child { border-top: 0; }
.lgroup-head { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.lgroup-box { width: 15px; height: 15px; accent-color: var(--sel-bg); cursor: pointer; flex: 0 0 auto; }
.lgroup-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: 7px 0;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  text-align: left;
}
.lgroup-name { font-size: 0.8125rem; font-weight: 600; }
.lgroup-tally {
  margin-left: auto;
  font-size: 0.6875rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
/* The caret rotates rather than swapping glyphs, so the state change reads. */
.lgroup-caret {
  width: 0; height: 0;
  border-left: 4.5px solid transparent;
  border-right: 4.5px solid transparent;
  border-top: 5.5px solid var(--text-faint);
  transition: transform 160ms var(--ease);
  flex: 0 0 auto;
}
.lgroup.collapsed .lgroup-caret { transform: rotate(-90deg); }
.lgroup-toggle:hover .lgroup-caret { border-top-color: var(--text); }
.lgroup-toggle:focus-visible { outline: 2px solid var(--text-dim); outline-offset: 2px; border-radius: 3px; }

.lgroup-subs {
  margin: 0 0 6px 23px;
  overflow: hidden;
  max-height: 400px;
  opacity: 1;
  transition: max-height 180ms var(--ease), opacity 140ms var(--ease), margin 180ms var(--ease);
}
.lgroup.collapsed .lgroup-subs { max-height: 0; opacity: 0; margin-bottom: 0; }

/* ---- The list of what is showing ----
   Appears once the filters have cut the map to something countable. Names are
   the point, so they get the weight; the place is support. */
.list-count {
  font-size: 0.6875rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.pin-list { list-style: none; margin: 0; padding: 0; }
.pin-list-item {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  padding: 7px 8px;
  margin: 0 -8px;
  border-radius: 6px;
  cursor: pointer;
  font: inherit;
}
.pin-list-item:hover { background: var(--surface-2); }
.pin-list-item:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -2px; }
.pin-list-name { font-size: 0.8125rem; font-weight: 600; color: var(--text); line-height: 1.3; }
.pin-list-sub { font-size: 0.6875rem; color: var(--text-faint); line-height: 1.3; }
.pin-list-more {
  display: block;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.pin-list-more:hover { background: var(--surface-2); color: var(--text); }

/* The wars are checkboxes now, not single-select chips: each one shows or
   hides that war's battlefields independently. */
.war-check {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 0.6875rem;
  color: var(--text-dim);
  cursor: pointer;
  padding: 2px 0;
}
.war-check input { width: 13px; height: 13px; accent-color: var(--sel-bg); cursor: pointer; }
.war-check:hover { color: var(--text); }

/* ============ Panels ============ */
.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 4px 18px rgba(27, 30, 35, 0.14);
}
.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-dim);
}
.panel-body { padding: 10px 12px 12px; }

.icon-btn {
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font-size: 1.125rem;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
  border-radius: 4px;
  flex: 0 0 auto;
}
.icon-btn:hover { color: var(--text); background: var(--surface-2); }

/* ============ Detail ============ */
/* Isolated search result: a plain bar stating what the map is filtered to,
   with the way out. Hidden unless focus mode is active. */
.focus-bar {
  position: absolute;
  top: calc(var(--topbar-h) + 56px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  display: none;
  align-items: center;
  gap: 8px;
  max-width: min(92vw, 520px);
  padding: 7px 8px 7px 12px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  box-shadow: 0 2px 10px rgba(27, 30, 35, 0.12);
  font-size: 0.8125rem;
}
body.focus-mode .focus-bar { display: flex; }
.focus-label { color: var(--text-dim); flex: 0 0 auto; }
.focus-name {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.focus-clear {
  flex: 0 0 auto;
  padding: 5px 9px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}
.focus-clear:hover { background: var(--surface); border-color: var(--line-strong); }

.sheet-grip { display: none; }

.detail {
  position: absolute;
  top: calc(var(--topbar-h) + 12px);
  right: 12px;
  z-index: var(--z-detail);
  width: 300px;
  max-height: calc(100vh - var(--topbar-h) - 40px);
  display: flex;
  flex-direction: column;
}
.detail .panel-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

.detail-cat { font-size: 0.6875rem; font-weight: 700; color: var(--text-dim); }
.detail-title {
  margin: 0 0 4px;
  font-size: 1.0625rem;
  line-height: 1.25;
  font-weight: 700;
  text-wrap: balance;
}
.detail-when { font-size: 0.8125rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.detail-when .live { color: var(--danger); }
.detail-where { font-size: 0.75rem; color: var(--text-dim); margin-top: 2px; }
/* The image is scaled, never modified — Commons serves the file whole at a
   requested width and object-fit only trims overflow for layout. Nothing here
   crops, overlays or filters, so CC BY-SA's share-alike clause, which binds
   derivative works, is never engaged. A fixed aspect ratio reserves the space
   before the image loads so the card does not jump. */
/* The ratio moves to the figure and the image fits inside it rather than
   filling it. object-fit: cover on a 4:3 box crops a portrait from the top,
   which is how the photograph of Steve Prefontaine lost his head. A bar of
   background beside a tall picture costs nothing; a decapitated subject is a
   bug. The box is still reserved, so the card does not jump on load. */
.detail-figure {
  margin: 10px 0 0;
  display: block;
}
.detail-figure img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 4px;
  background: var(--surface-2);
  opacity: 1;
  transition: opacity var(--t-med) var(--ease);
}
/* Held invisible until the file decodes, so the previous card's photograph
   never shows through for a frame. The figure's background fills the reserved
   box in the meantime. */
.detail-figure img.loading { opacity: 0; }
.detail-credit {
  display: block;
  font-size: 0.625rem;
  line-height: 1.45;
  color: var(--text-faint);
  padding: 5px 1px 0;
  overflow-wrap: anywhere;
}
.detail-credit a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.detail-credit a:hover { color: var(--text-dim); }

.detail-blurb {
  font-size: 0.8125rem;
  line-height: 1.55;
  color: #3a4048;
  margin: 10px 0;
  max-width: 60ch;
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.detail-meta {
  font-size: 0.6875rem;
  color: var(--text-faint);
  border-top: 1px solid var(--line);
  padding-top: 8px;
}
.detail-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.detail-link {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  text-decoration: none;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  padding: 7px 11px;
  color: var(--text-dim);
  transition: background 0.15s, color 0.15s;
}
.detail-link:hover { background: var(--surface-2); color: var(--text); }
.detail-link.primary { color: var(--text); border-color: var(--text-dim); }

/* ============ Attribution & map controls ============ */
.attrib {
  position: absolute;
  bottom: calc(2px + var(--safe-b));
  right: calc(6px + var(--safe-r));
  z-index: var(--z-panel);
  font-size: 0.625rem;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.78);
  padding: 2px 6px;
  border-radius: 3px;
  max-width: min(70vw, 900px);
}
.attrib a { text-decoration: none; }
.maplibregl-ctrl-attrib { display: none; }

.maplibregl-ctrl-group {
  background: var(--surface) !important;
  border: 1px solid var(--line);
  border-radius: var(--radius) !important;
  box-shadow: 0 2px 10px rgba(27, 30, 35, 0.14) !important;
}
.maplibregl-ctrl-group button + button { border-top: 1px solid var(--line) !important; }
.maplibregl-ctrl-bottom-right { bottom: calc(22px + var(--safe-b)); }

/* ============ Motion ============
   Only three things move: something arriving, something being pointed at,
   and the drawer. Each is a short settle rather than a gesture — the card
   rises 4px, not 40, because the map behind it must not appear to shift.

   The panels are toggled with a display:none class, which cannot be
   transitioned. An animation on the visible state re-runs every time display
   flips back, so the entrances need no JavaScript and cannot fall out of sync
   with the logic that opens them. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.detail:not(.hidden) { animation: rise-in var(--t-med) var(--ease); }
.search-results:not(.hidden) { animation: rise-in var(--t-fast) var(--ease); }
body.focus-mode .focus-bar { animation: rise-in var(--t-med) var(--ease); }
.scrim:not(.hidden) { animation: fade-in var(--t-med) var(--ease); }

/* The horizontal centring transform on the focus bar would be wiped out by the
   keyframe's transform, so it animates from a translated start instead. */
@media (min-width: 761px) {
  @keyframes rise-in-centred {
    from { opacity: 0; transform: translateX(-50%) translateY(4px); }
    to   { opacity: 1; transform: translateX(-50%); }
  }
  body.focus-mode .focus-bar { animation-name: rise-in-centred; }
}

/* Hover and press. A control should acknowledge the cursor quickly and settle
   the colour slowly — the reverse feels sluggish. */
.seg button,
.nav-toggle,
.icon-btn,
.detail-link,
.focus-clear,
.search-hit {
  transition:
    background var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease);
}
.seg button:active,
.nav-toggle:active,
.icon-btn:active,
.focus-clear:active { transition-duration: 40ms; }

/* One focus treatment everywhere, visible on both light and dark chrome. */
:where(button, a, input, [tabindex]):focus-visible {
  outline: 2px solid var(--text-dim);
  outline-offset: 2px;
  border-radius: 3px;
}

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

/* ============================================================
   Phones and small tablets
   ============================================================ */
@media (max-width: 760px) {
  :root {
    /* 40 + 6 + 36 + 6 of bottom padding. Everything that hangs off the header
       — the drawer, the sheet, the focus bar — measures from this. */
    --topbar-h: 88px;
    --sidebar-w: 0px;
  }

  #map, body:not(.nav-open) #map { left: 0; }

  /* Two rows, because one row could not hold a wordmark, a mode toggle and an
     account without all three degrading — the single-row version had two
     emergency breakpoints shrinking padding and blanking labels, and still
     dropped the site's name entirely.

     Row one is identity: what this is, and who you are on it.
     Row two is the two controls that answer "what am I looking at" — the
     filters drawer and the era toggle — sitting together because they do the
     same job.

     The brand spans the first two columns so column one is sized by the
     filters button alone; the toggle spans the last two so it runs to the
     right edge. That keeps both rows independent inside one grid. */
  .topbar {
    grid-template-columns: auto 1fr auto;
    grid-template-rows: 40px 36px;
    grid-template-areas:
      "brand brand account"
      "nav   seg   seg";
    align-items: center;
    padding: 0 10px 6px;
    padding-left: max(10px, var(--safe-l));
    padding-right: max(10px, var(--safe-r));
    padding-top: var(--safe-t);
    height: calc(var(--topbar-h) + var(--safe-t));
    column-gap: 8px;
    row-gap: 6px;
  }
  /* The wrapper stops being a box so its two children can take their own
     places in the grid — one per row. */
  .top-left { display: contents; }

  /* min-width:0 lets the grid shrink it. Without that, a grid item refuses to
     go below its content and a longer name would shove the account buttons
     off the right edge rather than ellipsising. At 320px the current name
     fits with nothing to spare, so the guard is doing real work. */
  .brand {
    grid-area: brand;
    display: block;
    min-width: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    align-self: center;
  }
  .top-right { grid-area: account; }

  .nav-toggle {
    grid-area: nav;
    height: 36px;
    width: 40px;
    padding: 0;
    justify-content: center;
  }
  .nav-toggle-text { display: none; }

  /* Full width on its own row, halves splitting it evenly: the toggle is the
     most-used control on the map and now reads as a real segmented control
     rather than something squeezed between two other things. */
  .top-center {
    grid-area: seg;
    justify-self: stretch;
    width: 100%;
  }
  .top-center button { flex: 1; padding: 0; }
  .seg button { height: 36px; font-size: 0.8125rem; }
  .seg-small button { padding: 0 10px; font-size: 0.75rem; }

  /* Search floats over the map, always reachable. */
  .search-wrap,
  body:not(.nav-open) .search-wrap {
    display: block;
    position: absolute;
    top: calc(var(--topbar-h) + var(--safe-t) + 8px);
    left: max(10px, var(--safe-l));
    right: max(10px, var(--safe-r));
    width: auto;
    height: auto;
    padding: 0;
    background: transparent;
    border: 0;
  }
  .search-input {
    height: 42px;
    font-size: 1rem; /* 16px keeps iOS from zooming the page on focus */
    border-radius: 999px;
    padding: 0 44px 0 16px;
    box-shadow: 0 2px 12px rgba(27, 30, 35, 0.18);
  }
  .search-clear, body:not(.nav-open) .search-clear { right: 8px; width: 30px; height: 30px; }
  .search-results,
  body:not(.nav-open) .search-results {
    left: 0; right: 0;
    top: calc(100% + 8px);
    max-height: 50dvh;
    border-radius: 10px;
  }

  /* Sidebar becomes an off-canvas drawer. */
  .sidebar,
  body:not(.nav-open) .sidebar {
    display: flex;
    position: fixed;
    top: calc(var(--topbar-h) + var(--safe-t));
    bottom: 0;
    left: 0;
    width: min(88vw, 340px);
    z-index: var(--z-drawer);
    box-shadow: 6px 0 28px rgba(27, 30, 35, 0.22);
    transform: translateX(-102%);
    transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1);
  }
  body.nav-open .sidebar { transform: translateX(0); }
  body.nav-open .scrim { display: block; }

  .sidebar-scroll { padding: 14px 16px 16px; }
  .cat-row { min-height: 44px; padding: 6px 0; font-size: 0.875rem; }
  .cat-row input { width: 20px; height: 20px; }
  .cat-swatch { width: 12px; height: 12px; }
  .clear-btn { font-size: 0.75rem; padding: 8px 10px; min-height: 36px; }
  .side-group h3 { font-size: 0.75rem; }
  .side-note { font-size: 0.75rem; }
  .sidebar-collapse { padding: 14px 9px; padding-bottom: max(14px, var(--safe-b)); font-size: 0.8125rem; }
  .date-field input, .range-row input { height: 40px; font-size: 0.875rem; }
  .era-row button { font-size: 0.8125rem; }

  /* Detail becomes a bottom sheet. */
  .detail {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-height: min(66dvh, 620px);
    border-radius: 14px 14px 0 0;
    border-bottom: 0;
    box-shadow: 0 -6px 28px rgba(27, 30, 35, 0.24);
    padding-bottom: var(--safe-b);
  }
  .sheet-grip {
    display: block;
    width: 100%;
    height: 26px;
    border: 0;
    background: transparent;
    cursor: grab;
    position: relative;
    flex: 0 0 auto;
    touch-action: none;
  }
  .sheet-grip:active { cursor: grabbing; }
  /* The header is a drag handle too — people pull cards by their title. The
     browser must not claim these touches for scrolling, or it fires
     pointercancel mid-drag. The scrollable body keeps its default so long
     entries still scroll; when collapsed there is no scrollable content and
     the whole sheet becomes a handle. */
  .detail .panel-head { touch-action: none; }
  body.sheet-min .detail { touch-action: none; }
  .sheet-grip::before {
    content: "";
    position: absolute;
    left: 50%; top: 10px;
    transform: translateX(-50%);
    width: 40px; height: 4px;
    border-radius: 2px;
    background: var(--line-strong);
  }
  .detail .panel-head { padding: 2px 14px 8px; }
  .detail .panel-body { padding: 10px 14px 16px; }
  .detail-title { font-size: 1.125rem; }
  .detail-blurb { font-size: 0.875rem; }
  .detail-link { padding: 9px 13px; font-size: 0.8125rem; }
  .icon-btn { padding: 8px 10px; font-size: 1.25rem; }

  /* Collapsed sheet: the card keeps its full height and is translated down to
     leave the grip and title showing, so pulling it back up carries the whole
     card with the finger. The rules that did this by hiding the children and
     collapsing max-height lived here and were overridden further down; they
     are gone rather than left to fight, since a card that changes height while
     a drag is reading that height is the bug this replaced.
     See the sheet-min block near the end of this file. */
  body.sheet-min .sheet-grip::before { background: var(--text-dim); }

  /* Kept at the top on phones. The sheet owns the bottom of the screen, and
     this has to stay visible while a result is open — it is the only thing
     telling the user why the rest of the map is empty. */
  /* The 58px this used to add cleared a search field that floated over the
     map; the field lives in the filters drawer now, so the bar sits directly
     under the header. */
  .focus-bar {
    top: calc(var(--topbar-h) + var(--safe-t) + 8px);
    left: max(8px, var(--safe-l));
    right: max(8px, var(--safe-r));
    transform: none;
    max-width: none;
    width: auto;
    font-size: 0.75rem;
    padding: 6px 7px 6px 10px;
  }

  body.sheet-open .attrib { display: none; }
  /* No zoom buttons on a phone: pinch and double-tap-drag both zoom, and a
     control that sits on top of the map to duplicate a gesture the map
     already has is just something covering the map. Desktop keeps them —
     a mouse has no pinch. */
  .maplibregl-ctrl-bottom-right { display: none; }

  /* Everything tappable in the chrome clears the same floor. The checkbox rows
     in the filter panel were the worst of it: a 13px box with a label beside
     it, which is a target for a stylus rather than a thumb. */
  .cat-row { min-height: 44px; }
  .cat-row input[type="checkbox"] { width: 20px; height: 20px; }
  .clear-btn { min-height: 40px; }
  .era-row button, .date-quick button { min-height: 40px; }
  .search-hit { min-height: 48px; }
  .account-tool { min-height: 42px; }
  .account-saved-item { min-height: 44px; }
  .detail-link { min-height: 42px; display: inline-flex; align-items: center; }
  .like-btn { min-height: 40px; padding: 6px 12px; }
  .icon-btn { width: 42px; height: 42px; }

  .attrib {
    left: max(8px, var(--safe-l));
    right: auto;
    font-size: 0.5625rem;
    max-width: 64vw;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* Short landscape phones. */
@media (max-width: 900px) and (max-height: 480px) {
  .detail { max-height: 82dvh; }
}

/* ============ Accounts & likes ============
   Same flat language as everything else: ink on light, 1px lines, selection
   is inversion. The heart is the one place the danger red appears outside an
   alert, because a saved place is the one warm mark the UI makes. */
.account { position: relative; }
.account-out { display: flex; gap: 6px; }
.account-btn {
  height: 34px;
  padding: 0 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.account-btn:hover { background: var(--surface-2); }
.account-btn:focus-visible { outline: 2px solid var(--text-dim); outline-offset: -2px; }
/* Sign up is the one filled button in the chrome. Creating an account is the
   only thing here a first-time visitor cannot already do. */
.account-btn.primary {
  background: var(--sel-bg);
  border-color: var(--sel-bg);
  color: var(--sel-text);
}
.account-btn.primary:hover { background: #2c302a; border-color: #2c302a; }

.account-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 260px;
  max-height: min(60vh, 480px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(30, 33, 29, 0.14);
  z-index: var(--z-search);
  animation: rise-in var(--t-med) var(--ease);
}
.account-menu-head {
  padding: 10px 12px;
  font-weight: 700;
  font-size: 0.8125rem;
  border-bottom: 1px solid var(--line);
}
.account-saved { padding: 4px 0; }
.account-empty {
  margin: 0;
  padding: 10px 12px;
  color: var(--text-dim);
  font-size: 0.75rem;
}
.account-saved-item {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  background: none;
  color: var(--text);
  font-size: 0.8125rem;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-saved-item:hover { background: var(--surface-2); }
.account-stats {
  display: flex;
  gap: 14px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
}
.account-stat { font-size: 0.75rem; color: var(--text-dim); }
.account-stat b { color: var(--text); font-size: 0.875rem; }
.account-saved-list { list-style: none; margin: 0; padding: 0; }
.account-saved-row { display: flex; align-items: stretch; }
.account-saved-row .account-saved-item {
  flex: 1;
  min-width: 0;
  white-space: normal;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.account-saved-x {
  flex: 0 0 32px;
  border: 0;
  background: none;
  color: var(--text-faint);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.account-saved-x:hover { color: var(--text); background: var(--surface-2); }

/* Editor's pick: the card wears a gold edge and a star. Kept to a border, a
   soft ring and one glyph so it reads as chosen, not decorated. */
#detail.pick {
  border: 3px solid #b8860b;
  box-shadow: 0 0 0 4px rgba(184, 134, 11, 0.13), 0 8px 28px rgba(30, 33, 29, 0.14);
}
.pick-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  padding: 4px 10px 4px 8px;
  border: 1px solid rgba(184, 134, 11, 0.45);
  border-radius: 999px;
  background: rgba(184, 134, 11, 0.10);
  color: #8a6408;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.pick-badge .icon { width: 13px; height: 13px; fill: #b8860b; stroke: #b8860b; }

/* One link: the correction control sits beside it rather than under. */
.detail-links.one-link {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.detail-links.one-link .suggest-btn { margin-top: 0; }

/* Suggest an edit */
.suggest-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 14px;
  padding: 8px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}
.suggest-btn:hover { background: var(--surface-2); color: var(--text); }
.suggest-card { max-height: min(86vh, 720px); overflow-y: auto; }
.sg-textarea { height: auto; padding: 8px 10px; line-height: 1.45; resize: vertical; }
.sg-opt { color: var(--text-faint); font-weight: 400; }
.hp-field { position: absolute; left: -9999px; height: 0; width: 0; opacity: 0; }
.sg-map-toggle {
  display: block;
  width: 100%;
  margin: 12px 0 0;
  padding: 8px 10px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  background: none;
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.sg-map-toggle:hover { color: var(--text); background: var(--surface-2); }
.sg-map-wrap { position: relative; margin-top: 8px; }
#sg-map { height: 240px; border: 1px solid var(--line-strong); border-radius: var(--radius); }
.sg-crosshair {
  position: absolute;
  left: 50%; top: 50%;
  width: 2px; height: 26px;
  transform: translate(-50%, -100%);
  pointer-events: none;
}
.sg-crosshair::before {
  content: "";
  position: absolute;
  left: 50%; top: 0;
  width: 14px; height: 14px;
  transform: translateX(-50%);
  border-radius: 50% 50% 50% 0;
  background: #22262c;
  border: 2px solid #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,.3);
  rotate: -45deg;
}
.sg-crosshair::after {
  content: "";
  position: absolute;
  left: 50%; bottom: -2px;
  width: 6px; height: 3px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: rgba(0,0,0,.35);
}
.sg-map-hint { margin: 6px 0 0; font-size: 0.6875rem; color: var(--text-faint); }
.sg-remove-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0 4px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
}
.account-signout {
  display: block;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.account-signout:hover { background: var(--surface-2); color: var(--text); }

/* Heart on the detail card */
.like-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  margin-right: 2px;
  padding: 4px 9px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.like-btn:hover { color: var(--text); }
.like-btn:active { transform: scale(0.94); }
.like-btn.liked { color: var(--danger); border-color: var(--danger); }
.like-heart { transition: fill var(--t-fast) var(--ease), transform var(--t-med) var(--ease); }
/* One path, filled or not — rather than swapping ♥ for ♡, whose two glyphs are
   different widths in most fonts and made the button twitch on every click. */
.like-btn.liked .like-heart { fill: currentColor; }
.like-count { font-variant-numeric: tabular-nums; }
/* An empty count still occupied a flex slot, so its 5px gap sat to the right of
   the heart and pushed the heart 2.5px left of centre on every unliked card.
   Removing it from layout is what actually centres the icon. */
.like-count:empty { display: none; }
/* .panel-head is already flex (space-between); the heart's margin-left:auto
   absorbs the free space, so it sits with the ×, not centred. */

/* Auth modal */
.auth-overlay {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-search) + 2);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(30, 33, 29, 0.38);
  animation: fade-in var(--t-med) var(--ease);
}
.auth-card {
  width: min(360px, 100%);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 14px 44px rgba(30, 33, 29, 0.22);
  padding: 16px;
  animation: rise-in var(--t-med) var(--ease);
}
.auth-head { display: flex; align-items: center; justify-content: space-between; }
.auth-head h2 { margin: 0; font-size: 1rem; }
/* Says what the visitor was trying to do when this appeared, so the form is
   an answer to their action rather than an interruption of it. */
.auth-why {
  margin: 10px 0 0;
  padding: 8px 10px;
  background: var(--surface-2);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-size: 0.75rem;
}
.auth-seg { margin: 12px 0; display: flex; }
.auth-perks {
  display: none;
  list-style: none;
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.auth-card.mode-up .auth-perks { display: block; }
.auth-perks li {
  font-size: 0.75rem;
  color: var(--text-dim);
  padding: 3px 0 3px 18px;
  position: relative;
}
.auth-perks li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--text);
  font-weight: 700;
}
.auth-seg button { flex: 1; }
.auth-label {
  display: block;
  margin: 10px 0 4px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.auth-input {
  width: 100%;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}
/* 16px exactly. Any smaller and iOS Safari zooms the page when the field
   takes focus, which is what made the sign-up card lurch. */
.auth-input { font-size: 16px; }
.auth-input:focus { outline: 2px solid var(--sel-bg); outline-offset: -1px; }
.auth-error {
  margin-top: 10px;
  padding: 8px 10px;
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  color: var(--danger);
  font-size: 0.75rem;
}
.auth-submit {
  width: 100%;
  margin-top: 14px;
  height: 38px;
  border: 1px solid var(--sel-bg);
  border-radius: var(--radius);
  background: var(--sel-bg);
  color: var(--sel-text);
  font-size: 0.8125rem;
  font-weight: 700;
  cursor: pointer;
}
.auth-submit:disabled { opacity: 0.6; cursor: default; }
.auth-note {
  margin: 10px 0 0;
  color: var(--text-faint);
  font-size: 0.6875rem;
  line-height: 1.5;
}

@media (max-width: 760px) {
  .account-btn { height: 34px; padding: 0 11px; font-size: 0.75rem; }
  .account-menu { position: fixed; top: calc(var(--topbar-h) + 6px); right: 8px; left: 8px; width: auto; }
}

/* The rule that shrank the mode toggle's padding at 380px is gone with the
   single-row header that needed it: the toggle has a row to itself now and
   shares it only with a 40px button, so it has room at every width down to
   320px. The account cluster still tightens, since it shares row one with the
   wordmark. */
@media (max-width: 380px) {
  .brand { font-size: 0.9375rem; }
  .account-out { gap: 4px; }
  .account-btn { padding: 0 9px; }
}

/* Only on the narrowest phones does a label have to go. "Sign in" becomes an
   icon and "Sign up" keeps its word, because the one a first-time visitor has
   never used is the one that must stay legible. */
@media (max-width: 350px) {
  #signin-btn {
    font-size: 0; /* label replaced by the ::before glyph */
    padding: 0 10px;
  }
  #signin-btn::before {
    content: "\2192\5D";       /* → into a bracket: the sign-in convention */
    font-size: 0.8125rem;
    letter-spacing: -0.08em;
  }
}

/* ============ Icons ============
   Lucide, inlined as a sprite. One base class does the work: icons inherit
   the text colour, scale with the type, and never introduce a baseline gap
   the way an inline glyph does. */
.icon {
  width: 1.25em;
  height: 1.25em;
  flex: 0 0 auto;
  display: block;          /* kills the inline descender gap that made icon
                              buttons look a pixel low */
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: middle;
}
/* Trailing marks inside a run of text sit on the baseline instead. */
.icon-inline {
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: 0.4em;
  opacity: 0.75;
}

/* Buttons that are nothing but an icon get a square, centred target rather
   than padding tuned to a character's height. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
}
.icon-btn .icon { width: 18px; height: 18px; }

.account-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.account-btn .icon { width: 15px; height: 15px; }
.nav-toggle-icon { width: 16px; height: 16px; }

/* The search field carries its own magnifier, so the placeholder no longer
   has to explain what the box is. */
.search-icon {
  position: absolute;
  left: 22px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--text-faint);
  pointer-events: none;
  z-index: 1;
}
.search-input { padding-left: 46px; }
.search-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.search-clear .icon { width: 15px; height: 15px; }

@media (max-width: 350px) {
  /* The icon replaces the label here, so the label goes and the ::before
     hack that used to draw a text arrow is no longer needed. */
  #signin-btn .btn-label { display: none; }
  #signin-btn::before { content: none; }
}

/* Account tools: export, delete, privacy. Separated from the saved list by a
   rule because they act on the account rather than on the map. */
.account-tools {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--line);
}
.account-tool {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  background: none;
  color: var(--text-dim);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.account-tool:hover { background: var(--surface-2); color: var(--text); }
.account-tool.danger { color: var(--danger); }
.account-tool.danger:hover { background: #fbf0ee; color: var(--danger); }

/* The destructive confirm reads as destructive. */
.auth-submit.danger { background: var(--danger); border-color: var(--danger); }
.auth-submit.danger:hover { background: #9c3f34; border-color: #9c3f34; }

/* ============================================================
   Mobile: search moves into the filter panel, and the sheet
   becomes a real bottom sheet rather than two layouts.
   ============================================================ */
@media (max-width: 760px) {
  /* Search stops floating over the map. It was a pill sitting directly under
     the top bar covering whatever was beneath it, on a screen that has none to
     spare. It lives in the filter panel now, which is where every other
     control already is, and appears only when that panel is open. */
  .search-wrap,
  body:not(.nav-open) .search-wrap { display: none; }

  body.nav-open .search-wrap {
    display: block;
    position: fixed;
    top: calc(var(--topbar-h) + var(--safe-t));
    left: 0;
    width: min(88vw, 340px);
    right: auto;
    height: auto;
    z-index: calc(var(--z-drawer) + 1);
    padding: 12px 16px 10px;
    background: var(--surface);
    border: 0;
    border-bottom: 1px solid var(--line);
  }
  body.nav-open .search-input {
    height: 42px;
    font-size: 16px;      /* below this iOS zooms the page on focus */
    border-radius: var(--radius);
    padding: 0 40px 0 38px;
    box-shadow: none;
    border: 1px solid var(--line-strong);
  }
  body.nav-open .search-icon { left: 28px; }
  body.nav-open .search-clear { right: 24px; }
  body.nav-open .search-results {
    left: 16px; right: 16px;
    top: calc(100% - 4px);
    max-height: 60dvh;
  }
  /* The drawer's own content starts below the search field. */
  body.nav-open .sidebar-scroll { padding-top: 78px; }

  /* ---- the sheet ----
     Previously "minimised" hid the body with display:none, so dragging the
     bar back up moved a bar and then the rest of the card appeared all at
     once. The sheet is now always its full height and is simply translated
     down, leaving a peek. Collapsing and expanding are then the same
     transform the drag is already writing, so the whole card follows the
     finger the entire way. */
  .detail {
    transition: transform var(--t-slow) var(--ease);
    will-change: transform;
  }
  body.sheet-min .detail {
    transform: translateY(calc(100% - 62px));
    max-height: min(66dvh, 620px);   /* unchanged: it is moved, not resized */
  }
  /* The children stay in the document and keep their normal type and padding.
     Nothing here restates them: hiding or resizing them is what made the
     expansion jump, so those rules are simply absent.

     Nothing below the sheet's peek should take a touch while it is down. */
  body.sheet-min .detail .panel-body { pointer-events: none; }
}

/* ---- /map/ page content ----
   Served into the sidebar on deep-linked layer pages. It is the answer an
   engine reads and the context a reader wants, in the same markup — no hidden
   copy, no second version of the page. */
.map-page {
  padding: 4px 0 16px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
}
.map-page h1 { font-size: 1rem; font-weight: 700; line-height: 1.3; margin: 0 0 8px; }
.map-page h2 {
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  margin: 16px 0 6px;
}
.map-page p { font-size: 0.8125rem; line-height: 1.55; margin: 0 0 8px; color: var(--text-dim); }
.mp-tldr { color: var(--text) !important; }
.mp-note { font-size: 0.6875rem !important; color: var(--text-faint) !important; }
.map-page ul, .map-page dl { margin: 0; padding: 0; list-style: none; }
.mp-list li, .mp-states li, .mp-siblings li {
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 3px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.75rem;
}
.mp-list li:last-child, .mp-states li:last-child, .mp-siblings li:last-child { border-bottom: 0; }
.mp-name { font-weight: 600; color: var(--text); }
.mp-place { color: var(--text-faint); }
.mp-year { margin-left: auto; color: var(--text-faint); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.map-page a { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.map-page a:hover { color: var(--sel-bg); }
.mp-faq dt { font-size: 0.75rem; font-weight: 600; margin: 10px 0 2px; }
.mp-faq dd { font-size: 0.75rem; line-height: 1.5; margin: 0; color: var(--text-dim); }
