/*
 * White base, grey chrome, black text. The only saturated colour on screen
 * belongs to the data — anything else competing for attention would make the
 * map harder to read, which is the whole point of the thing.
 *
 * Light only, deliberately: a choropleth's colour scale is calibrated against
 * its surface, and a dark surface needs its own validated ramps rather than an
 * inverted copy of these.
 */

:root {
  --page: #ffffff;
  --surface: #fbfbfb;
  --hairline: #e9e9e9;
  --rule: #dcdcdc;
  --muted: #8a8a8a;
  --secondary: #5c5c5c;
  --primary: #1a1a1a;
  --accent: #12497f;
  --found: #2f8f5b;
  --danger: #b33f31;

  --topbar-height: 52px;

  --font: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

* { box-sizing: border-box; }

/* An author `display` rule beats the user-agent [hidden] rule, so anything
   given a display value here would ignore its hidden attribute. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--page);
  color: var(--primary);
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* `.topbar` and `.shell` are the two children here, stacked so the shell can
   claim whatever height the topbar didn't take — see `.shell`'s `flex: 1`. */
body { display: flex; flex-direction: column; }

button { font: inherit; color: inherit; }

/* ------------------------------------------------------------- top bar */

.topbar {
  height: var(--topbar-height);
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--page);
  position: relative;
  z-index: 30;
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex: none;
}
.brand-mark {
  font-weight: 600;
  letter-spacing: -0.015em;
  font-size: 15px;
}

.search-wrap { position: relative; flex: 1; max-width: 460px; }

#search {
  width: 100%;
  height: 32px;
  padding: 0 11px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--page);
  font-size: 13px;
  color: var(--primary);
  outline: none;
}
#search::placeholder { color: var(--muted); }
#search:focus { border-color: var(--accent); }
#search::-webkit-search-cancel-button { cursor: pointer; }

.results {
  position: absolute;
  top: 36px;
  left: 0;
  right: 0;
  background: var(--page);
  border: 1px solid var(--rule);
  border-radius: 4px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
  overflow: hidden;
  z-index: 40;
}

.result {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 7px 11px;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}
.result:hover, .result.is-active { background: var(--surface); }
.result-name { color: var(--primary); }
.result-kind { color: var(--muted); font-size: 11px; flex: none; }
.result-empty { color: var(--muted); cursor: default; }

/* Says why these rows are being offered rather than navigated to — a suggestion
   the reader confirms, not an answer. */
.result-heading {
  padding: 7px 11px 5px;
  border-bottom: 1px solid var(--rule);
  color: var(--muted);
  font-size: 11px;
  line-height: 1.35;
}

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }

/* The same badge the level control is, in the same row, because it answers the
   same kind of question — what is on screen. Named rather than iconic, and
   allowed to grow to a readable width before it truncates: a variable label
   ("Householders with a bachelor's degree or higher") is a sentence where a
   level id is a word. */
.var-badge {
  display: flex;
  align-items: center;
  gap: 5px;
  max-width: 260px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: none;
  color: var(--secondary);
  font-size: 12px;
  cursor: pointer;
}
.var-badge:hover { border-color: var(--muted); color: var(--primary); }
#variable-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.level-control { position: relative; }

/* Muted and borderless while zoom is deciding — the badge it has always been,
   plus a chevron. Filled once something is pinned, so held reads differently
   from observed. The level is named in both states: hiding it while automatic
   would throw away the one thing this was already good at. */
.level-badge {
  display: flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
}
.level-badge:hover:not(:disabled) { border-color: var(--rule); color: var(--secondary); }
.level-badge[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
.level-badge:disabled { cursor: default; }
.level-chevron { font-size: 8px; opacity: 0.7; }
.level-badge:disabled .level-chevron { display: none; }

.level-menu {
  position: absolute;
  top: 34px;
  right: 0;
  min-width: 176px;
  background: var(--page);
  border: 1px solid var(--rule);
  border-radius: 4px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
  overflow: hidden;
  z-index: 40;
}
.level-option {
  display: block;
  width: 100%;
  padding: 7px 11px;
  border: 0;
  background: none;
  text-align: left;
  color: var(--primary);
  cursor: pointer;
}
.level-option:hover:not(:disabled), .level-option.is-active { background: var(--surface); }
.level-option.is-active { font-weight: 600; }
.level-option:disabled { color: var(--muted); cursor: default; }

.toggle {
  height: 30px;
  padding: 0 11px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--page);
  color: var(--secondary);
  cursor: pointer;
}
.toggle:hover:not(:disabled) { border-color: var(--muted); color: var(--primary); }
.toggle[aria-pressed="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
.toggle:disabled { opacity: 0.4; cursor: default; }

/* --------------------------------------------------------------- shell */

/* `flex: 1` rather than `height: calc(100% - var(--topbar-height))`: a
   calc() against the topbar's *declared* height drifts the moment the topbar
   wraps to two rows (see the narrow-topbar block below) or otherwise renders
   taller than that constant says. `body` is the flex column now, so this
   simply takes whatever height the topbar's real, rendered box left behind —
   correct at any width, wrapped or not, with nothing to keep in sync.
   `min-height: 0` stops a flex child from refusing to shrink below its
   content's natural height, which would otherwise let the map push the
   legend bar off the bottom of a short viewport. */
.shell {
  display: flex;
  flex: 1;
  min-height: 0;
}

.badge {
  flex: none;
  font-size: 9.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 0 4px;
}

/* ------------------------------------------------------------- picker */

.legend-btn {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 0; border: 0; background: none; cursor: pointer; text-align: left;
}
.legend-btn:hover .legend-title { color: var(--accent); }
.legend-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.legend-caret { color: var(--muted); font-size: 8px; flex: none; }

.scrim { position: absolute; inset: 0; z-index: 20; background: rgb(20 22 26 / 0.24); }

.picker {
  position: absolute; z-index: 21; left: 50%; top: 64px; transform: translateX(-50%);
  width: min(560px, calc(100% - 48px)); max-height: calc(100% - 120px);
  display: flex; flex-direction: column;
  background: var(--page); border: 1px solid var(--rule); border-radius: 6px;
  box-shadow: 0 18px 56px rgb(0 0 0 / 0.22), 0 2px 6px rgb(0 0 0 / 0.08);
  overflow: hidden;
}

.pick-search { flex: none; border-bottom: 1px solid var(--hairline); }
.pick-search input {
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 42px; padding: 0 12px; border: 0; outline: none;
  background: none; font: inherit; font-size: 14px; color: var(--primary);
}
.pick-search input::placeholder { color: var(--muted); }
.pick-search input::-webkit-search-cancel-button { cursor: pointer; }

.pick-list { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 4px 0 6px; }
.pick-group {
  display: flex; align-items: baseline; gap: 8px;
  padding: 11px 14px 4px;
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--muted);
}
.pick-count { font-weight: 400; letter-spacing: 0; text-transform: none; }

.pick-row {
  display: block; width: 100%; padding: 6px 14px 7px;
  border: 0; border-left: 2px solid transparent; background: none;
  text-align: left; cursor: pointer; color: var(--secondary);
}
.pick-row:hover, .pick-row.is-cursor { background: var(--surface); color: var(--primary); }
.pick-row.is-cursor { border-left-color: var(--accent); }
.pick-row.is-active { color: var(--primary); font-weight: 500; background: var(--surface); }
.pick-row-top { display: flex; align-items: baseline; gap: 8px; }
.pick-label { flex: 1; }
.pick-label mark { background: #fdf3c3; color: inherit; border-radius: 2px; padding: 0 1px; }
.pick-figure {
  flex: none; font-weight: 600; font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em; color: var(--primary);
}
.pick-figure.is-empty { color: var(--muted); font-weight: 400; }
.pick-via { display: block; font-size: 10.5px; color: var(--muted); margin-top: 1px; }
.pick-via mark { background: #fdf3c3; border-radius: 2px; padding: 0 1px; }
.pick-row-meta {
  display: flex; justify-content: space-between; gap: 10px;
  color: var(--muted); font-size: 10.5px; font-variant-numeric: tabular-nums;
}
.pick-empty { padding: 22px 14px; color: var(--muted); text-align: center; }

/* ----------------------------------------------------------------- map */

.map-wrap { position: relative; flex: 1; min-width: 0; }
#map { position: absolute; inset: 0; }

.maplibregl-ctrl-attrib { font-size: 10px; }
.maplibregl-ctrl-group { box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08); }

.tooltip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 12;
  pointer-events: none;
  min-width: 150px;
  max-width: 260px;
  padding: 8px 10px;
  background: var(--page);
  border: 1px solid var(--rule);
  border-radius: 4px;
  box-shadow: 0 4px 14px rgb(0 0 0 / 0.10);
}
.tt-name { color: var(--secondary); font-size: 11.5px; margin-bottom: 3px; }
.tt-value { display: flex; align-items: baseline; gap: 7px; }
.tt-number { font-size: 19px; font-weight: 600; letter-spacing: -0.02em; }
.tt-number.tt-empty { font-size: 13px; font-weight: 400; color: var(--muted); }
.tt-margin { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.tt-level {
  margin-top: 4px;
  color: var(--muted);
  font-size: 10px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.tt-note {
  margin-top: 2px;
  color: var(--muted);
  font-size: 9px;
}

/* -------------------------------------------------------------- legend */

.legend {
  position: absolute;
  left: 14px;
  bottom: 14px;
  z-index: 11;
  width: 216px;
  padding: 10px 12px 11px;
  background: rgb(255 255 255 / 0.94);
  backdrop-filter: blur(3px);
  border: 1px solid var(--hairline);
  border-radius: 4px;
}
.legend-title { font-weight: 600; margin-bottom: 7px; letter-spacing: -0.01em; }
.legend-scale { display: flex; height: 9px; border-radius: 2px; overflow: hidden; }
.legend-swatch { flex: 1; display: block; }

/* Ticks sit at the boundaries between swatches, so they are placed by exact
   percentage rather than distributed by flex. Only alternating boundaries are
   labelled — six numbers in this width collide into a smear. */
.legend-ticks {
  position: relative;
  height: 13px;
  margin-top: 3px;
  color: var(--muted);
  font-size: 9.5px;
  font-variant-numeric: tabular-nums;
}
.legend-tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  white-space: nowrap;
}

.legend-note { margin-top: 6px; color: var(--muted); font-size: 11px; }

.legend-nodata {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 7px;
  padding-top: 7px;
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 11px;
}
.legend-nodata .legend-swatch {
  flex: none;
  width: 12px;
  height: 9px;
  border-radius: 2px;
  border: 1px solid var(--hairline);
}

/* -------------------------------------------------------- detail panel */

.detail {
  position: absolute;
  top: 14px;
  right: 14px;
  max-height: calc(100% - 50px);
  z-index: 13;
  width: 288px;
  display: flex;
  flex-direction: column;
  background: var(--page);
  border: 1px solid var(--rule);
  border-radius: 4px;
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.10);
}
.detail-body { overflow-y: auto; padding: 14px 14px 18px; }

.detail-close {
  position: absolute;
  top: 6px;
  right: 8px;
  z-index: 1;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 3px;
  background: var(--page);
  color: var(--muted);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.detail-close:hover { background: var(--surface); color: var(--primary); }

.detail-header { padding-right: 22px; margin-bottom: 6px; }
.detail-name { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
.detail-sub { color: var(--muted); font-size: 10.5px; font-family: var(--mono); margin-top: 2px; }

.detail-group {
  margin: 15px 0 5px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}

.pct-track {
  position: relative;
  height: 3px;
  margin-top: 5px;
  border-radius: 2px;
  background: var(--hairline);
}
.pct-marker {
  position: absolute;
  top: -1px;
  width: 2px;
  height: 5px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--primary);
}

/* Seven rows in a 650px box reads as broken, so the panel is as tall as what
   it holds and the expanded list scrolls inside it. */
.detail-more {
  display: block; width: 100%; margin-top: 10px; padding: 7px 0;
  border: 0; border-top: 1px solid var(--hairline); background: none;
  color: var(--accent); text-align: left; cursor: pointer;
}
.detail-more:hover { color: var(--primary); }
.detail-all { margin: 0 -14px; display: flex; flex-direction: column; }
/* Cancels .pick-row's own 14px horizontal padding against detail-body's,
   the same trick .detail-all uses, so a summary row's left edge lines up
   with the panel title rather than sitting 14px further right. */
.detail-rows { margin: 0 -14px; }
.detail-all .pick-list { max-height: 52vh; }
.pick-figure.is-soft { color: var(--muted); font-weight: 500; }

/* -------------------------------------------------------------- status */

.status {
  position: absolute;
  left: 50%;
  top: 14px;
  transform: translateX(-50%);
  z-index: 14;
  max-width: 460px;
  padding: 7px 12px;
  background: var(--primary);
  color: #fff;
  border-radius: 4px;
  font-size: 12px;
  box-shadow: 0 4px 14px rgb(0 0 0 / 0.14);
}
.status.is-error { background: var(--danger); }

/* A brief ring where search landed — the one place green appears. */
.flash {
  position: absolute;
  z-index: 12;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid var(--found);
  border-radius: 50%;
  pointer-events: none;
  animation: flash 1.2s ease-out forwards;
}
@keyframes flash {
  0%   { transform: scale(0.4); opacity: 0; }
  25%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(2.6); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .flash { animation-duration: 1ms; }
}

/* -------------------------------------------------------------- narrow */

@media (max-width: 820px) {
  .brand { width: auto; }
  .shell { flex-direction: column; }
  .search-wrap { max-width: none; }
  .toggle { padding: 0 8px; font-size: 11px; }

  /* Both overlays become the same bottom sheet. A sheet is thumb-reachable and
     a centred dialog is not. */
  .picker, .detail {
    position: absolute; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-width: none; transform: none;
    height: 78%; max-height: 78%;
    border-radius: 14px 14px 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    box-shadow: 0 -8px 40px rgb(0 0 0 / 0.18);
  }
  .picker::before, .detail::before {
    content: ""; display: block; flex: none;
    width: 34px; height: 4px; margin: 8px auto 4px;
    border-radius: 2px; background: var(--rule);
  }
  /* 16px exactly. Anything smaller makes iOS zoom the viewport on focus, and
     the map is then the wrong size behind the sheet that caused it. */
  .pick-search input { height: 46px; font-size: 16px; }
  .pick-row { padding: 10px 16px 11px; }
  .detail-body { padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }

  /* The legend was display:none here, so a phone painted a choropleth with no
     key at all. It is a full-width bar now, and it is also what opens the
     picker — the only variable control a phone has. */
  .legend {
    left: 0; right: 0; bottom: 0; width: auto;
    border-radius: 0; border-left: 0; border-right: 0; border-bottom: 0;
    padding: 9px 14px calc(9px + env(safe-area-inset-bottom, 0px));
  }
  .legend-title { font-size: 14px; }
  .legend-caret { font-size: 10px; }
  /* Two of the four notes are for a reading surface that is not a phone. */
  .legend .legend-note:not(:first-of-type) { display: none; }

  /* The legend is a full-width bottom bar at this width and would otherwise
     completely occlude MapLibre's bottom-right attribution — OpenStreetMap
     and Protomaps attribution is a licensing obligation, not decoration.
     Moved to the top-right corner, which the bottom-sheet layout leaves
     clear at every width in this block, rather than nudged up by the
     legend's own height — that height already varies with which notes are
     showing and would make the offset one more thing to keep in sync. */
  .maplibregl-ctrl-bottom-right {
    top: 8px;
    right: 8px;
    bottom: auto;
  }
}

/* -------------------------------------------------------- narrow topbar */

/* A second, narrower breakpoint than the sheet one above — the topbar alone.
   Search is the most-used control on a map, so below 560px it gets a full
   width row of its own rather than folding behind an icon; brand and the
   level badge stay on the first row with Compare. The wrapped bar is taller
   than `--topbar-height` says, and that no longer needs reconciling: `.shell`
   reads `flex: 1`, not the variable, so it simply takes what the topbar's
   real rendered height left behind. */
@media (max-width: 560px) {
  .topbar {
    height: auto;
    flex-wrap: wrap;
    row-gap: 8px;
    padding-block: 8px;
  }
  .search-wrap { order: 3; flex-basis: 100%; max-width: none; }
  /* Three controls and a wordmark on one row: the variable is the longest of
     them and the only one that can give ground, so it shrinks first and
     truncates rather than pushing Compare onto a row of its own. */
  .var-badge { min-width: 0; flex: 0 1 auto; }
}

