/* Migration Space
   A dark ground, one accent for arrivals and one for departures, and as little
   furniture as the interface can survive on. Everything floats over the canvas;
   nothing gets a box unless it needs one to stay readable. */

:root {
  --ink:    #e9effb;
  --ink-2:  #94a3be;
  --ink-3:  #5e6c86;
  --ink-4:  #3d4859;
  --warm:   #ffb866;
  --cool:   #7cacff;
  --line:   rgba(140, 172, 224, 0.14);
  --ground: #05070d;
  --serif:  'Instrument Serif', 'Iowan Old Style', Georgia, serif;
  --sans:   'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --edge:   clamp(20px, 3.4vw, 46px);
  --ease:   cubic-bezier(0.22, 0.61, 0.29, 1);
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

body { overflow: hidden; }

#sky {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: manipulation;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ── shared idioms ─────────────────────────────────────────────────────── */

.k, .mark, .switch button, .back, .result em, .tie em {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

a { color: var(--ink-2); text-decoration-color: var(--ink-4); text-underline-offset: 2px; }
a:hover { color: var(--ink); }

/* ── top left ──────────────────────────────────────────────────────────── */

/* One column from top edge to bottom edge. Normal flow keeps the name, the back
   link and the numbers from ever landing on top of each other, whatever the
   length of the county name. */
.rail {
  position: fixed;
  top: var(--edge);
  bottom: var(--edge);
  left: var(--edge);
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: min(320px, 76vw);
  pointer-events: none;
}

.rail > *, .rail-foot > * { pointer-events: auto; }
.rail-foot { margin-top: auto; width: 100%; }

/* The same column, mirrored: switches at the top, the ranking taking the slack
   in the middle, the source note pinned to the bottom. */
.rail-right {
  left: auto;
  right: var(--edge);
  align-items: stretch;
  width: min(310px, 76vw);
  z-index: 4;
}

/* A soft ground under the ranking rather than a panel: the country is drawn
   right up to this edge, and small type over points of light is unreadable. */
.rail-right::before {
  content: '';
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: calc(min(310px, 76vw) + var(--edge) * 2.4);
  background: linear-gradient(to right, transparent, rgba(4, 7, 14, 0.78) 42%,
                              rgba(4, 7, 14, 0.9));
  pointer-events: none;
  z-index: -1;
}

.brand { pointer-events: none; }
.brand .mark { color: var(--ink-2); letter-spacing: 0.22em; }

.brand .sub {
  margin-top: 6px;
  font-size: 12px;
  color: var(--ink-3);
  transition: opacity 0.5s var(--ease);
}

body[data-view="system"] .brand .sub { opacity: 0; }

.finder { position: relative; margin-top: 26px; width: min(280px, 62vw); }

#search {
  width: 100%;
  padding: 7px 0 8px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font: 400 15px/1.2 var(--sans);
  outline: none;
  transition: border-color 0.35s var(--ease);
  -webkit-appearance: none;
}

#search::placeholder { color: var(--ink-4); }
#search:focus { border-bottom-color: rgba(160, 195, 250, 0.4); }
#search::-webkit-search-cancel-button { display: none; }

.results {
  margin-top: 6px;
  padding: 4px 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(9, 13, 23, 0.94);
  backdrop-filter: blur(14px);
  overflow: hidden;
}

.result {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 7px 12px;
  border: 0;
  background: none;
  color: var(--ink-2);
  font: 400 13px/1.3 var(--sans);
  text-align: left;
  cursor: pointer;
}

.result em { font-style: normal; color: var(--ink-4); }
.result.on, .result:hover { background: rgba(124, 172, 255, 0.09); color: var(--ink); }
.result.on em, .result:hover em { color: var(--ink-3); }

/* ── the selected place ────────────────────────────────────────────────── */

.title {
  margin-top: 30px;
  width: 100%;
  pointer-events: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}

body[data-view="system"] .title { opacity: 1; transform: none; transition-delay: 0.25s; }

.title h1 {
  margin: 0;
  font: 400 clamp(30px, 4vw, 46px)/1.02 var(--serif);
  letter-spacing: -0.005em;
  color: var(--ink);
}

.title p {
  margin: 7px 0 0;
  font-size: 11px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.back {
  display: block;
  margin-bottom: 18px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease), color 0.25s var(--ease);
}

.back::before { content: '\2190\00a0\00a0'; }
.back:hover { color: var(--ink); }
body[data-view="system"] .back { opacity: 1; pointer-events: auto; }

/* ── summary ───────────────────────────────────────────────────────────── */

.summary {
  width: 100%;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}

body[data-view="system"] .summary {
  opacity: 1;
  transform: none;
  transition-delay: 0.35s;
}

.k { display: block; color: var(--ink-4); margin-bottom: 7px; }

/* A dotted underline marks a label as having a definition on hover, without
   spending any space on the definition itself. */
.stat .k[title] {
  width: fit-content;
  border-bottom: 1px dotted var(--ink-4);
  cursor: help;
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  padding-bottom: 15px;
  border-bottom: 1px solid var(--line);
}

.stat b {
  display: block;
  font: 400 20px/1 var(--serif);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.stat em {
  display: block;
  margin-top: 5px;
  font: 500 11px/1 var(--sans);
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}

.stat.in b { color: var(--warm); }
.stat.out b { color: var(--cool); }
.stat.net.up b, .stat.net.up em { color: var(--warm); }
.stat.net.down b, .stat.net.down em { color: var(--cool); }
.stat.net em { opacity: 0.72; }

.ties {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
}

.ties i { font-size: 11px; color: var(--ink-4); font-style: normal; }

.tie {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  margin-left: -6px;
  padding: 3px 6px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--ink-2);
  font: 400 12px/1.35 var(--sans);
  text-align: left;
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}

.tie span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tie em { font-style: normal; color: var(--ink-4); flex: none; font-variant-numeric: tabular-nums; }
.tie:hover { background: rgba(124, 172, 255, 0.1); color: var(--ink); }

.fine {
  margin: 13px 0 0;
  font-size: 11px;
  line-height: 1.65;
  color: var(--ink-4);
}

/* ── switches ──────────────────────────────────────────────────────────── */

/* Now a block inside .rail-right rather than fixed on its own, so the ranking
   between it and the footer can never be overlapped by either. */
.controls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  flex: none;
}

.switch { display: flex; gap: 2px; }

.switch button {
  padding: 7px 13px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ink-4);
  cursor: pointer;
  transition: color 0.28s var(--ease), background 0.28s var(--ease),
              border-color 0.28s var(--ease);
}

.switch button:hover { color: var(--ink-2); }

.switch button.on {
  color: var(--ink);
  border-color: var(--line);
  background: rgba(140, 172, 224, 0.08);
}

.switch.quiet {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s var(--ease);
}

body[data-view="system"] .switch.quiet { opacity: 1; pointer-events: auto; }

/* Compass against Graph is the one decision this rail is built around, so it
   gets the width of the rail and a real segmented control rather than two
   small chips easily read as labels. */
#layout {
  align-self: stretch;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(8, 11, 20, 0.6);
}

#layout button {
  padding: 9px 0;
  font-size: 10px;
  letter-spacing: 0.18em;
  border-radius: 999px;
  border-color: transparent;
}

#layout button.on {
  color: var(--ink);
  border-color: rgba(255, 184, 102, 0.45);
  background: rgba(255, 184, 102, 0.14);
}

/* The two axis boxes and the reading under them travel with the Graph choice:
   only in the solar system, and only once Graph is picked. Display, not
   opacity, so they take no room in the rail the rest of the time. */
#axis-x, #axis-y, #reading { display: none; }
body[data-view="system"].graph-mode #axis-x,
body[data-view="system"].graph-mode #axis-y,
body[data-view="system"].graph-mode #reading { display: flex; }

/* The attribute needs restating above that rule's specificity, or an empty
   reading box stays on screen. Same trap as .metric-extra further down. */
body[data-view="system"].graph-mode #reading[hidden] { display: none; }

/* Graph mode is for choosing what to plot, so the country-wide ranking leaves
   entirely rather than sitting collapsed under the boxes. */
body[data-view="system"].graph-mode #ranking { display: none; }

/* With the ranking gone nothing in the rail grows, so the footer needs telling
   to stay at the bottom. Everywhere else the ranking still fills the gap and an
   auto margin here would swallow the space before flex-grow saw it. */
body[data-view="system"].graph-mode .foot { margin-top: auto; }

/* The same card as the ranking, so the rail reads as a stack of aligned panels
   rather than a pile of loose chips. No backdrop-filter, for the same reason
   the ranking has none: the canvas behind animates every frame. Sized to its
   contents, shrinking with its own scrollbar when the rail runs short. */
.panel {
  flex: 0 1 auto;
  min-height: 0;
  flex-direction: column;
  margin-top: 20px;
  padding: 13px 14px 12px;
  border: 1px solid rgba(140, 172, 224, 0.11);
  border-radius: 14px;
  background: rgba(8, 11, 20, 0.55);
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}

body.ready .panel { opacity: 1; }

.panel-body {
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(140, 172, 224, 0.22) transparent;
}

.panel-body::-webkit-scrollbar { width: 3px; }
.panel-body::-webkit-scrollbar-thumb { background: rgba(140, 172, 224, 0.22); border-radius: 3px; }

.panel.collapsed { flex: none; }
.panel.collapsed .panel-body { display: none; }

/* One box per axis, every measure on show. The letter badge is what ties the
   box to the axis drawn on the canvas. */
.axis-tag {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 5px;
  background: rgba(255, 184, 102, 0.16);
  color: var(--warm);
  font: 600 9px/1 var(--sans);
}

.axis-panel .rank-title-label {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

.axis-where { flex: none; color: var(--ink-4); }

.axis-panel .rank-title-label b {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.axis-choices {
  gap: 7px;
  margin-top: 11px;
  /* Whatever is past the fold fades rather than being sliced through the
     middle of a pill, so a cut list reads as "more below". */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
}

.axis-choices .metric-group {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  align-items: baseline;
}

/* Tighter than the ranking's pills: there are 21 of them in each of two boxes,
   and every row saved is a row you do not have to scroll for. */
.axis-choices .metric-pill {
  padding: 5px 9px;
  font-size: 9px;
  letter-spacing: 0.06em;
}

/* Everything under the two boxes is a reading, not a control, so it carries no
   pills and sits quieter than the cards above it. It holds its height down to
   the three figures and scrolls past that, rather than cutting the answer off
   with nothing on screen to say it did. */
.reading {
  flex: 0 1 auto;
  min-height: 96px;
  padding-top: 12px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(140, 172, 224, 0.22) transparent;
}

.reading::-webkit-scrollbar { width: 3px; }
.reading::-webkit-scrollbar-thumb { background: rgba(140, 172, 224, 0.22); border-radius: 3px; }
.lens-ask {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.lens-ask[hidden] { display: none; }

/* The political exchange readout: where the movers came from and went, as
   places. Deliberately no warm/cool here. The app's gold and blue already sit
   close enough to partisan red and blue without putting them on a margin. */
.exchange { margin: 0; }

/* The rule between them belongs to the pair, so it only exists when both are
   on screen. */
.lens-ask:not([hidden]) + .exchange {
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}

.exchange-cells {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.exchange-cells b {
  display: block;
  font: 400 15px/1 var(--serif);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.exchange-cells .k { margin-bottom: 6px; }
.exchange-cells .none { color: var(--ink-4); font-size: 12px; }

.exchange-note {
  margin: 10px 0 0;
  font-size: 10.5px;
  line-height: 1.55;
  color: var(--ink-4);
}

.exchange-note b { color: var(--ink-2); font-weight: 500; }

/* Graph mode swaps the compass explainer for the axis one. */
body.graph-mode #legend { opacity: 0; }
body:not(.graph-mode) #graph-legend,
body:not([data-view="system"]) #graph-legend { opacity: 0; }

/* The left rail is empty in the swings view, so the county under the cursor
   reports there rather than in a tooltip that covers the chart. */
.dash-detail {
  margin-top: 34px;
  width: 100%;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
  pointer-events: none;
}

.dash-detail.on { opacity: 1; transform: none; }

/* Belongs to the swings view only. Out of it, it takes no room and cannot
   linger with a stale county in it. */
body:not([data-view="dash"]) .dash-detail { display: none; }

.dash-detail h2 {
  margin: 0 0 14px;
  font: 400 clamp(24px, 3vw, 34px)/1.05 var(--serif);
  color: var(--ink);
}

.dash-detail dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 7px 16px;
  margin: 0;
}

.dash-detail dt {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-4);
  align-self: center;
}

.dash-detail dd {
  margin: 0;
  font: 400 15px/1.2 var(--sans);
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

.dash-detail dd.swing { color: var(--warm); }
.dash-detail dd.swing.neg { color: var(--cool); }

/* ── views ─────────────────────────────────────────────────────────────── */

.views {
  display: flex;
  gap: 2px;
  margin: 18px 0 0 -11px;
}

.views button {
  padding: 6px 11px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ink-4);
  font: 500 9.5px/1 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.28s var(--ease), background 0.28s var(--ease),
              border-color 0.28s var(--ease);
}

.views button:hover { color: var(--ink-2); }

.views button.on {
  color: var(--ink);
  border-color: var(--line);
  background: rgba(140, 172, 224, 0.08);
}

/* Only one of the two rails is live at a time; the other is folded away so it
   cannot take height from the one that is showing. */
body[data-view="dash"] #ranking,
body:not([data-view="dash"]) #dash { display: none; }

/* The country's own furniture has nothing to say about a scatter plot. */
body[data-view="dash"] .controls .quiet,
body[data-view="dash"] .hint,
body[data-view="dash"] #map-legend { opacity: 0; pointer-events: none; }

.axis-picks {
  display: grid;
  gap: 5px;
  margin-top: 10px;
}

.axis {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.axis > span {
  font: 500 8.5px/1 var(--sans);
  letter-spacing: 0.16em;
  color: var(--ink-4);
}

.axis-select {
  width: 168px;
  padding: 3px 0 4px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink-2);
  font: 400 11.5px/1.2 var(--sans);
  text-align: right;
  text-align-last: right;
  outline: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.axis-select:focus { border-bottom-color: rgba(160, 195, 250, 0.4); }
.axis-select option, .axis-select optgroup { background: #0b1120; color: var(--ink); }

.dash-row {
  position: relative;
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 5px 7px 6px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--ink-2);
  font: 400 12px/1.35 var(--sans);
  text-align: left;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.dash-row:hover { background: rgba(124, 172, 255, 0.09); color: var(--ink); }
.dash-row.on { background: rgba(255, 184, 102, 0.11); color: var(--ink); }

/* ── the ranking ───────────────────────────────────────────────────────── */

/* A quiet control surface rather than text floating loose over the map: a
   hairline edge and a touch of fill, no blur (the canvas behind animates every
   frame, and re-sampling it through backdrop-filter is not free). */
.ranking {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;         /* take the slack between the switches and the footer */
  min-height: 0;          /* lets the list scroll instead of pushing the footer */
  margin-top: 20px;
  padding: 14px 14px 12px;
  border: 1px solid rgba(140, 172, 224, 0.11);
  border-radius: 14px;
  background: rgba(8, 11, 20, 0.55);
  opacity: 0;
  transition: opacity 0.6s var(--ease);
  /* A fallback, not the primary mechanism: normally the list alone scrolls.
     This only engages if the "More" panel, on a short window, still cannot
     fit everything above it, so the card scrolls rather than visibly bleeding
     into the footer below it. */
  overflow-y: auto;
}

body.ready .ranking { opacity: 1; }

/* Collapsing removes the card from the "grow to fill the rail" behaviour
   above, so it shrinks to just the title bar instead of leaving an empty
   card the height it used to be. */
.ranking.collapsed { flex: none; }

/* Basis zero, so it takes whatever the fixed panels leave and is the first to
   give it back. Scoped past .collapsed, which an id selector would otherwise
   outrank and leave a collapsed card still growing.

   The floor is the point past which giving up more stops helping: title and
   sort pills still work with no rows under them, and below that the settings
   panel starts scrolling instead. On a short screen neither card can be whole,
   which is what the collapse control on both of them is for. */
#ranking:not(.collapsed) { flex: 1 1 0; min-height: 120px; }

.rank-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-2);
  font: 500 9.5px/1 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
  flex: none;
  transition: color 0.22s var(--ease);
}

.rank-title:hover { color: var(--ink); }
.rank-title-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rank-title-label b { color: var(--ink); font-weight: 500; }

.rank-title .chev {
  flex: none;
  display: inline-block;
  color: var(--ink-4);
  transform: rotate(180deg);
  transition: transform 0.25s var(--ease);
}

.rank-title[aria-expanded="false"] .chev { transform: none; }

.rank-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  margin-top: 13px;
}

.ranking.collapsed .rank-body { display: none; }

.rank-head { flex: none; text-align: right; }

/* The picker reads as a toolbar, so its buttons line up from the left like any
   other control cluster; the numbers in the rows below stay right-aligned,
   which is the data-table convention and unaffected by this. */
#ranking .rank-head { text-align: left; }

.metric-pills, .metric-extra .metric-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.metric-pill {
  padding: 6px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(140, 172, 224, 0.05);
  color: var(--ink-3);
  font: 500 9.5px/1 var(--sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.22s var(--ease), background 0.22s var(--ease),
              border-color 0.22s var(--ease);
}

.metric-pill:hover { color: var(--ink-2); border-color: rgba(160, 195, 250, 0.32); }

/* On, not selected: the same warm the app already uses for "this is the lit
   one", so the active measure reads as active without a new colour. */
.metric-pill.on {
  color: var(--ink);
  background: rgba(255, 184, 102, 0.14);
  border-color: rgba(255, 184, 102, 0.5);
}

.metric-pill.more {
  background: none;
  border-style: dashed;
  color: var(--ink-4);
}

.metric-pill.more:hover { color: var(--ink-2); border-color: rgba(160, 195, 250, 0.32); }

.metric-pill.more .chev {
  display: inline-block;
  margin-left: 2px;
  transition: transform 0.25s var(--ease);
}

.metric-pill.more[aria-expanded="true"] .chev { transform: rotate(180deg); }

/* Bounded and scrollable in its own right, the way a native dropdown would be:
   with 21 measures across four groups, an unbounded panel pushes the card's
   height past the space the rail actually has and spills into the footer. */
.metric-extra {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 220px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(140, 172, 224, 0.22) transparent;
}

.metric-extra::-webkit-scrollbar { width: 3px; }
.metric-extra::-webkit-scrollbar-thumb { background: rgba(140, 172, 224, 0.22); border-radius: 3px; }

/* An author rule setting `display` beats the UA stylesheet's `[hidden]` rule
   at equal specificity, so the class above would otherwise force this open
   regardless of the attribute. Restate it with higher specificity. */
.metric-extra[hidden] { display: none; }

.metric-extra .metric-group { align-items: baseline; }

.metric-group-label {
  flex: none;
  margin-right: 2px;
  font-size: 8.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-4);
}

#ranking .rank-opts {
  justify-content: flex-start;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.rank-opts {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  margin-top: 9px;
}

.chipbtn {
  padding: 4px 9px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ink-4);
  font: 500 9px/1 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}

.chipbtn:hover { color: var(--ink-2); }
.chipbtn.on { color: var(--ink-2); border-color: var(--line); }

.rank-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  margin-top: 12px;
  scrollbar-width: thin;
  scrollbar-color: rgba(140, 172, 224, 0.22) transparent;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 14px,
                                      #000 calc(100% - 18px), transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 14px,
                              #000 calc(100% - 18px), transparent);
}

.rank-list::-webkit-scrollbar { width: 3px; }
.rank-list::-webkit-scrollbar-thumb { background: rgba(140, 172, 224, 0.22); border-radius: 3px; }

.rank-row {
  position: relative;
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 5px 7px 6px;
  border: 0;
  border-radius: 5px;
  background: none;
  color: var(--ink-2);
  font: 400 12px/1.35 var(--sans);
  text-align: left;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.rank-n {
  color: var(--ink-4);
  font-size: 9.5px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
}

.rank-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rank-v {
  font-style: normal;
  font-variant-numeric: tabular-nums;
  color: var(--warm);
}

.rank-v.neg { color: var(--cool); }

/* The hairline under each row: the value's size relative to the leader, so the
   column reads as a shape before it reads as numbers. */
.rank-bar {
  position: absolute;
  right: 7px;
  bottom: 2px;
  height: 1px;
  background: linear-gradient(to right, transparent, var(--warm));
  opacity: 0.35;
  border-radius: 1px;
}

.rank-bar.neg { background: linear-gradient(to right, transparent, var(--cool)); }

.rank-row:hover { background: rgba(124, 172, 255, 0.09); color: var(--ink); }
.rank-row.on { background: rgba(255, 184, 102, 0.11); color: var(--ink); }
.rank-row.on .rank-n { color: var(--ink-2); }

.rank-note {
  flex: none;
  margin: 10px 0 0;
  font-size: 10px;
  line-height: 1.55;
  color: var(--ink-4);
  text-align: right;
}

#ranking .rank-note { text-align: left; }

/* ── bottom right ──────────────────────────────────────────────────────── */

/* No auto margin here: the ranking above grows to fill instead, and an auto
   margin would swallow that free space before flex-grow ever saw it. */
.foot {
  width: 100%;
  padding-top: 14px;
  flex: none;
  text-align: right;
}

/* The two legends are mutually exclusive, so they share one grid cell. Left in
   normal flow they would both hold space and the hidden one shows up as a gap
   the size of a paragraph. */
.legends {
  display: grid;
  margin-bottom: 12px;
}

.legends > * { grid-area: 1 / 1; }

.legend {
  opacity: 0;
  transition: opacity 0.6s var(--ease);
}

body[data-view="system"] .legend { opacity: 1; transition-delay: 0.4s; }

/* The country's colours only mean something while the country is on screen. */

body[data-view="map"] .map-legend { opacity: 1; }
body[data-view="system"] .map-legend { opacity: 0; transition-delay: 0s; }
.legend p { margin: 0 0 5px; font-size: 11px; line-height: 1.6; color: var(--ink-3); }
.legend .muted { color: var(--ink-4); }

.credit {
  margin: 11px 0 0;
  font-size: 10.5px;
  line-height: 1.6;
  color: var(--ink-4);
}

/* The shared freshness bar, dressed down for a dark room: the module's default
   is a lit box with a status-coloured edge, which reads as an alert here. The
   colour survives on the dots, where it still does its job. */
/* Selectors are by id: mountFreshnessBar assigns className outright, which
   wipes any class put on the element, so a class hook here never matches. */
#freshness {
  --df-border: transparent;
  --df-text: var(--ink-3);
  display: inline-flex;
  justify-content: flex-end;
  gap: 14px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: 10px;
}

#freshness .df-dot { width: 5px; height: 5px; box-shadow: none; opacity: 0.7; }
#freshness .df-item { gap: 5px; }
#freshness .df-age { opacity: 0.85; }
#freshness .df-note { margin-left: 0; width: 100%; text-align: right; opacity: 1; }

/* ── incidentals ───────────────────────────────────────────────────────── */

.hint {
  position: fixed;
  left: 50%;
  bottom: var(--edge);
  z-index: 3;
  margin: 0;
  transform: translateX(-50%);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-4);
  pointer-events: none;
  transition: opacity 0.5s var(--ease);
  animation: breathe 5s ease-in-out infinite;
}

body[data-view="system"] .hint { opacity: 0; animation: none; }
body.zoomed .hint:not(.hint-top) { opacity: 0; animation: none; }

/* Its twin at the top: the same quiet line, shown only once a place is open,
   where clicking stops meaning "select" and starts meaning "travel". */
.hint-top {
  top: var(--edge);
  bottom: auto;
  opacity: 0;
  animation: none;
  transition: opacity 0.6s var(--ease);
}

body[data-view="system"] .hint-top { opacity: 1; transition-delay: 0.6s; }

/* Only present once the reader has actually moved the view. */
.zoomreset {
  position: fixed;
  left: 50%;
  bottom: var(--edge);
  z-index: 4;
  transform: translateX(-50%);
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(9, 13, 23, 0.6);
  color: var(--ink-3);
  font: 500 9.5px/1 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  backdrop-filter: blur(10px);
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}

.zoomreset:hover { color: var(--ink); border-color: rgba(160, 195, 250, 0.34); }

@keyframes breathe { 0%, 100% { opacity: 0.45; } 50% { opacity: 0.9; } }

.chip {
  position: fixed;
  z-index: 6;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: rgba(9, 13, 23, 0.92);
  backdrop-filter: blur(10px);
  color: var(--ink-2);
  font-size: 11.5px;
  white-space: nowrap;
  pointer-events: none;
}

.loading {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 22px;
  background: var(--ground);
  transition: opacity 0.65s var(--ease);
}

.loading.gone { opacity: 0; pointer-events: none; }

.loading-mark {
  font-size: 10px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.loading-bar { width: 128px; height: 1px; background: rgba(140, 172, 224, 0.16); overflow: hidden; }
.loading-bar i { display: block; width: 40%; height: 100%; background: var(--warm); animation: sweep 1.15s var(--ease) infinite; }

@keyframes sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(320%); }
}

/* ── narrow screens ────────────────────────────────────────────────────── */

@media (max-width: 900px) {
  .foot { display: none; }
  .controls { gap: 7px; }
}

@media (max-width: 720px) {
  .brand .sub { display: none; }
  .rail { width: calc(100vw - 2 * var(--edge)); }
  .finder { margin-top: 16px; width: min(230px, 55vw); }
  .title { margin-top: 20px; }
  .back { margin-bottom: 12px; }
  .stats { gap: 10px; }
  .stat b { font-size: 17px; }
  .fine { display: none; }
  .hint { font-size: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .hint:not(.hint-top) { animation: none; opacity: 0.6; }
  .loading-bar i { animation: none; width: 100%; }
}
