/* ══════════════════════════════════════════════════════════════════════════
   Chrome, 2026-09-21 — the wireframe pass
   ──────────────────────────────────────────────────────────────────────────
   What moved, and why it is a file of its own rather than more lines in
   style.css: every rule here is an OVERRIDE of something already settled in
   style.css, and keeping them together means the old rules can be read as
   the build they describe rather than as a trail of half-superseded state.
   Loaded last, so equal specificity resolves this way.

     · the drawer   left, always open   →   right, opened by the top bar
     · the controls right               →   left
     · search       a pane in the drawer→   the top bar, with predictions
     · the report   a page of its own   →   a panel over the map
     · new          municipality chips, map labels, clock + stats cards

   Selectors are written `body #x` rather than `#x` wherever the rule they
   replace could otherwise win on specificity.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --chrome-inset: 12px;        /* every floating box sits on this margin */
  --chrome-disc: 42px;         /* the round buttons in the top bar */
  --chrome-card: #0d1b20;      /* the wordmark's ground, dark in every look */
  --panel-w: 360px;            /* the drawer, and what must clear it */
}

/* ── 1. The top bar ────────────────────────────────────────────────────────
   Three floating pills rather than one bar: who this is on the left, where
   you are in the middle, what you can open on the right. The bar itself is
   only a layout row and lets clicks through to the map between the pills. */

body #sitehead {
  position: fixed; inset: var(--chrome-inset) var(--chrome-inset) auto;
  left: var(--chrome-inset); right: var(--chrome-inset);
  width: auto; max-width: none; transform: none;
  height: auto; border-radius: 0;
  background: none; border: 0; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  pointer-events: none;                /* the gaps belong to the map */
  z-index: 70;
}
body #sitehead .shin {
  display: flex; flex-direction: row; align-items: flex-start; gap: 12px;
  height: auto; max-width: none; margin: 0; padding: 0;
}
body #sitehead .shin > * { pointer-events: auto; }
#sitehead #mainnav { display: none; }   /* its two links are buttons now */
/* The Cambium logo anchor stays in the DOM for style.css and account.js to
   find; `hidden` alone loses to its own display:flex. */
#sitehead .sbrand[hidden] { display: none !important; }

.eik-pill {
  display: flex; align-items: center;
  /* border-box, so the stated height IS the height: as content-box the pill
     was 52 + 7 + 7 = 66px tall, which put 14px above and below the buttons
     against 6px at the end of the row. */
  box-sizing: border-box;
  /* Glass, like the cards on the other side of the map: 80% opaque over a
     blur, so the city reads through the bar without the words losing their
     ground. */
  background: rgba(255, 255, 255, .8);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
          backdrop-filter: blur(16px) saturate(1.2);
  border-radius: 999px;
  box-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
}

/* The wordmark, and who is signed in, in one pill. */
/* Even insets when it is just the wordmark. The 6px on the right was sized
   for the avatar disc, which is only there once someone is signed in — so it
   comes back only then, where a disc that close to the edge is right. */
/* The wordmark stands on the ink the cards use rather than on glass: it is
   the one thing in the bar that is not about the map. */
#eik-brandpill {
  gap: 9px; padding: 6px 18px; height: 48px;
  background: color-mix(in srgb, var(--chrome-card) 80%, transparent);
  box-shadow: 0 2px 14px rgba(5, 28, 35, .22);
}
#eik-brandpill:has(#eik-avatar:not([hidden])) { padding-right: 6px; }
#eik-brandpill .eik-word {
  font: 700 18px/1 var(--font-display, sans-serif);
  letter-spacing: .01em; color: var(--on-dark);
}
#eik-brandpill .eik-by {
  display: flex; align-items: center; gap: 6px;
  font: 400 14px/1 var(--font-sans, sans-serif); color: #ffffffa8;
}
/* The beta tag: quiet beside the wordmark, but a real link to a person. */
#eik-brandpill .eik-beta {
  font: 600 11px/1 var(--font-sans, sans-serif);
  letter-spacing: .06em; text-transform: uppercase; text-decoration: none;
  color: var(--on-dark); padding: 4px 8px; border-radius: 999px;
  border: 1px solid #ffffff59; white-space: nowrap;
}
#eik-brandpill .eik-beta:hover { background: #ffffff26; }
#eik-brandpill .eik-beta:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }
/* The wordmark's own letters are drawn in currentColor; the leaf beside them
   carries its own brand fills. */
#eik-brandpill .eik-logo {
  height: 15px; width: auto; display: block; color: var(--on-dark);
}
/* account.js paints the initials disc; it only needs a home. */
#eik-brandpill #eik-avatar { margin-left: 4px; }

/* Where you are, and the two things you can do from here. */
/* One inset all round: 6px above, below and after the buttons, which is what
   `height - button height` has to divide into — 48 and 36. */
#eik-searchpill {
  position: relative; flex: 0 1 620px; min-width: 260px;
  margin-inline: auto; padding: 6px; height: 48px;
}
#eik-searchpill .eik-mag {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 30px; margin-left: 4px;
  color: var(--text-muted, #051c239e); cursor: text;
}
#eik-searchpill .eik-mag svg { width: 18px; height: 18px; }
/* The two faces of the field: the crumb you can click, and the input you
   type in. Only one is in the layout at a time, so the pill cannot jump
   width between them. */
#eik-searchpill .eik-crumb {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 2px;
  overflow: hidden; white-space: nowrap; cursor: text;
  font: 500 15px/1 var(--font-sans, sans-serif); color: var(--text, #051c23);
}
#eik-searchpill.typing .eik-crumb { display: none; }
/* Out of sight but not display: none, so Tab can still reach the field. */
#eik-searchpill:not(.typing) #eik-searchfield {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
#eik-searchpill:has(#eik-searchfield:focus-visible) { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }
/* The way back: grey, like a path already walked, and underlined on hover so
   it reads as somewhere to click rather than as a label. */
.eik-crumb-place {
  flex: none; border: 0; padding: 4px 7px; border-radius: 8px;
  background: none; cursor: pointer;
  color: var(--text-muted, #051c239e);
  font: inherit; text-decoration: none;
  transition: background-color .14s, color .14s;
}
.eik-crumb-place:hover {
  background: var(--surface-stone, #f1f2ed); color: var(--text, #051c23);
  text-decoration: underline; text-underline-offset: 3px;
}
.eik-crumb-place:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 1px; }
.eik-crumb-sep { flex: none; padding: 0 1px; color: #051c2352; }
/* Where you are: ink, and not a control. */
.eik-crumb-current {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  padding: 4px 7px 4px 6px; color: var(--text, #051c23); font-weight: 600;
}

#eik-searchfield {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  appearance: none; -webkit-appearance: none;
  padding: 0 10px 0 2px;
  font: 500 15px/1 var(--font-sans, sans-serif); color: var(--text, #051c23);
}
#eik-searchfield::-webkit-search-decoration,
#eik-searchfield::-webkit-search-cancel-button { display: none; }
#eik-searchfield::placeholder { color: var(--text-muted, #051c239e); font-weight: 400; }
.eik-act {
  flex: none; border: 0; cursor: pointer;
  height: 36px; padding: 0 18px; border-radius: 999px;
  background: var(--text, #051c23); color: var(--on-dark);
  font: 500 14px/1 var(--font-sans, sans-serif);
  transition: background-color .15s, transform .12s;
}
.eik-act + .eik-act { margin-left: 6px; }
.eik-act:hover:not(:disabled) { background: #17323b; }
.eik-act:active:not(:disabled) { transform: scale(.97); }
/* Off at street level: there is no street report. Grey rather than gone, so
   the bar does not reflow and the control stays where you left it. */
.eik-act:disabled {
  background: #051c2314; color: var(--text-muted, #051c239e);
  cursor: default;
}
.eik-act:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }

/* The predictions, hanging under the pill. */
#eik-suggest {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  max-height: min(58vh, 520px); overflow-y: auto;
  padding: 6px; margin: 0; list-style: none;
  background: var(--surface); border-radius: 18px;
  box-shadow: 0 18px 44px rgba(5, 28, 35, .22), 0 0 0 1px rgba(5, 28, 35, .05);
}
#eik-suggest[hidden] { display: none; }
#eik-suggest .eik-sg-head {
  padding: 10px 14px 6px;
  font: 600 11px/1 var(--font-mono, monospace); letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-muted, #051c239e);
}
#eik-suggest li.eik-sg {
  display: flex; align-items: baseline; gap: 10px;
  padding: 10px 14px; border-radius: 12px; cursor: pointer;
  font: 500 15px/1.2 var(--font-sans, sans-serif); color: var(--text, #051c23);
}
#eik-suggest li.eik-sg .sub {
  margin-left: auto; font-weight: 400; font-size: 12.5px;
  color: var(--text-muted, #051c239e); white-space: nowrap;
}
#eik-suggest li.eik-sg:hover,
#eik-suggest li.eik-sg.active { background: var(--surface-stone, #f1f2ed); }
#eik-suggest li.eik-sg.on { background: var(--brand-soft, #cdeeff); }

/* The three ways into the drawer. */
#eik-tools { display: flex; gap: 10px; margin-left: auto; }
#eik-tools button {
  width: var(--chrome-disc); height: var(--chrome-disc); padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--surface); color: var(--text, #051c23);
  box-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
  transition: background-color .15s, color .15s, transform .12s;
}
#eik-tools button svg { width: 21px; height: 21px; }
#eik-tools button:hover { background: var(--surface-stone, #f1f2ed); }
#eik-tools button:active { transform: scale(.94); }
#eik-tools button.on { background: var(--text, #051c23); color: var(--on-dark); }
#eik-tools button:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }

/* The map owns the whole window now — the bar floats over it rather than
   sitting above it, so there is no header height to subtract. */
body #map-wrap { height: 100vh; height: 100dvh; }
body[data-view="report"] #map-wrap,
body[data-view="feedback"] #map-wrap { height: 100vh; height: 100dvh; }

/* ── 2. The controls, on the left ──────────────────────────────────────────
   MapLibre pins its control container to the right; the whole column —
   our play/speed/theme circles and MapLibre's own zoom group — moves over
   in one move by flipping that container. */
.maplibregl-ctrl-top-right { left: 0; right: auto; }
.maplibregl-ctrl-top-right .maplibregl-ctrl { float: left; clear: left; }
#mapstack { margin: 0 0 24px 10px; align-items: flex-start; }
.maplibregl-ctrl-top-right .maplibregl-ctrl-group { margin-left: 10px; }

/* Both halves of the speed control were built to hang off the LEFT of their
   button, which was clear of everything while the stack sat on the right and
   is off the window now: the badge measured out at x = -4 and the slider
   opened to x = -160. Mirrored, so they open into the map. */
#mapstack .ms-badge { left: auto; right: 0; transform: translate(50%, -50%); }
#ms-speed-panel { left: 0; right: auto; }
/* The wide padding is the room the circle and its badge sit over, so it
   swaps sides with them; the slider still starts 16px from the far end. */
#mapstack.speed-open #ms-speed-panel { padding: 0 16px 0 58px; }

/* ── 3. The drawer, on the right ───────────────────────────────────────────
   Same panel, same contents, same listeners — it is where it sits and when
   it is there that changed. Closed it is offset right and inert; open it
   slides back under `body.dock-open`, which index.html now toggles. */
/* style.css states the drawer's geometry on `#panel[data-pane]` as well as
   `#panel`, which outruns a plain `body #panel` — so this one matches the
   same way. */
body #panel, body #panel[data-pane] {
  position: fixed; left: auto; right: 0; top: 0; bottom: 0;
  width: var(--panel-w); max-height: none; height: auto;
  border-radius: 0; padding: 0 16px 16px;
  box-shadow: -18px 0 44px rgba(5, 28, 35, .22);
  /* Closed, it waits off the right edge; open, it is home. chrome.js drives
     the move itself with an inline transform so it can time it against the
     icons' flight.
     The transform is stated in both states on purpose — style.css centres
     the drawer with `translate(26px, -50%)` for the floating card it used to
     be, and leaving transform out of these rules lets that one back in and
     hangs the panel half its own height above the window. */
  opacity: 1; transform: translateX(100%); pointer-events: none;
  visibility: hidden;
  z-index: 72;                     /* over the top bar it comes out of */
  /* NOTHING transitions here. chrome.js drives the move with an inline
     transition, and this shuts the door on style.css's two — a .34s
     cubic-bezier(.22,.61,.36,1) from the floating card the panel used to be,
     and a .38s cubic-bezier(.4,0,.2,1) with a .06s delay from the left-hand
     drawer after that. Both still matched, so the property had three
     transitions over it: the inline one ran the slide, and the moment
     chrome.js cleared it the surviving CSS one picked the transform up again
     and eased it a second time. That second, slower, delayed pass over an
     element that had already arrived is what read as a bounce. */
  transition: none;
}
body.dock-open #panel, body.dock-open #panel[data-pane] {
  opacity: 1; transform: none; pointer-events: auto; visibility: visible;
  /* style.css adds `transition-delay: .06s` here for the hand-off it used to
     do with the dock; with no transition to delay it is just a trap for the
     next person. */
  transition: none; transition-delay: 0s;
}
/* Visible for the whole move, including the moment it spends off-screen: the
   content is drawn before it slides, never in front of the reader. */
body #panel[style*="translateX"] { visibility: visible; }
body #panel-head { border-radius: 0; }

/* The three buttons ARE the tabs: the drawer opens over the corner they sit
   in, and the same three icons carry on inside it as the tab strip. Fading
   them out under it keeps the handover clean at any width — the panel is
   narrower than the bar on a small window. */
/* The dissolve, and the order of it. Going in, the buttons leave at once and
   the drawer waits for them (chrome.js holds it for the same .18s). Coming
   back, they wait for the drawer to clear the corner instead, so the two are
   never both in it. */
body.dock-open #eik-tools {
  opacity: 0; pointer-events: none; visibility: hidden;
  transition: opacity .18s ease, visibility 0s linear .18s;
}
#eik-tools { transition: opacity .2s ease .2s; }

/* ── The tab strip ─────────────────────────────────────────────────────────
   The three round buttons in the bar dissolve and the drawer appears with
   the same three icons carried on as tabs — one control at two moments, not
   two controls. The tab keeps the disc's dimensions where it can: 42px tall,
   left padding half of that, so the icon sits exactly where it sat in the
   bar and the eye has somewhere to land. chrome.js owns the ordering (see
   `handover`); nothing here animates on its own. */

/* The head is a row — the tabs, then the close button — with the API key
   strip wrapping underneath it when the Ask pane is open. */
/* White behind the tabs: they are the discs off the map, and a disc reads
   as lifted off its ground, not laid on a grey band. */
body #panel-head { background: var(--surface); }
/* The Display sections are told apart by their spacing, not by rules. */
body #panel[data-pane="filters"] > #panel-body > .psec { border-top-color: transparent; }
/* Date, Display and Suggestions share one ground, from the tabs to the foot of the panel. */
body #panel:is([data-pane="time"], [data-pane="filters"], [data-pane="ideas"]),
body #panel:is([data-pane="time"], [data-pane="filters"], [data-pane="ideas"]) #panel-head { background: #f5f6f8; }

/* The head is a column — the tabs' row, then the API-key strip when the Ask
   pane is open. 12px down from the top is exactly where the discs sit on the
   bar, so the icons never change height and the flight is level; 16px in
   from each side is the drawer's own content column, so the first pill
   starts on the same line as the big numbers below it and the close button
   ends on the line they end on. */
body #panel-head { padding: 12px 16px; }
/* One row, and it never wraps: if the labels ever outgrow the width the
   strip gives way, rather than the close button dropping to a line of its
   own. */
body #panel-head .eik-head-row {
  display: flex; flex-direction: row; flex-wrap: nowrap;
  align-items: center; gap: 10px; width: 100%;
}
body #panel-head #ptabs {
  flex: 0 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 0; background: none; border-radius: 0;
  /* NOT overflow:hidden — a pill's shadow falls outside its box, and the
     strip would cut it off, leaving the tabs without the lifted edge the
     discs in the bar have. */
  overflow: visible;
}
body #panel-head #key-bar { margin: 8px 0 0; }

/* A tab: the disc, grown. White with the bar's own shadow when it is not the
   one you are on, brand tint and ink when it is. */
/* Words, not icons: "Date", "Display" and "Suggestions" say what each pane is, and with the icons gone the
   three tabs, two gaps and the close disc fit the drawer's 328px of content width with about 20px to spare.
   A tab may still shrink, ellipsis and all, rather than slide under the close button. */
body #panel #ptabs .ptab {
  height: 42px; flex: 0 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 7px;
  padding: 0 16px;
  border-radius: 999px;
  background: var(--surface); color: var(--text, #051c23);
  box-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
  font: 600 12px/1 var(--body, sans-serif);
  transition: background-color .2s ease, color .2s ease, box-shadow .2s ease;
}
body #panel #ptabs .ptab svg { display: none; }
body #panel #ptabs .ptab:hover { background: var(--surface-stone, #f1f2ed); }
body #panel #ptabs .ptab.on {
  background: var(--brand-soft, #cdeeff); color: var(--text, #051c23);
  box-shadow: none;
}
body #panel #ptabs .ptab span { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A tab's pane arrives as a cascade, in the Suggestions pane's own motion (views.js marks the blocks). */
body #panel.pane-in .pane-block {
  animation: pane-rise .5s cubic-bezier(.16, 1, .3, 1) backwards;
  animation-delay: calc(var(--pi, 0) * 40ms);
}
@keyframes pane-rise { from { opacity: 0; transform: translateY(18px); } }
@media (prefers-reduced-motion: reduce) { body #panel.pane-in .pane-block { animation: none; } }
/* The way out: a disc the size of a tab, keylined rather than filled, at the
   end of the row. */
#panel-x {
  position: static; flex: none;
  width: 42px; height: 42px; padding: 0;
  /* After the shorthand, not before it: `margin: 0` later in the same rule
     would reset the auto and leave the button short of the edge. */
  margin: 0 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--surface); color: var(--text, #051c23);
  font-size: 19px; line-height: 1;
  box-shadow: 0 0 0 1px var(--border, #0000001a);
  transition: background-color .15s, opacity .16s ease;
}
#panel-x:hover { background: var(--surface-stone, #f1f2ed); }
#panel-x:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }
/* It used to arrive at 60% and grow into place, which is the other half of
   the bounce: a disc springing out at the end of the sequence. It fades in
   where it belongs now. */
#panel-x.eik-late { opacity: 0; pointer-events: none; }

/* The drawer is the height of the window now, and its panes were written for
   a box a third shorter: each control grew to fill the extra, which left the
   last one's contents floating at the top of a stretched box and a band of
   white under it. The controls keep their own heights and the slack goes
   BETWEEN them instead, so the pane starts at the top and ends at the
   bottom. */
body #panel-body > .psec { padding-bottom: 4px; }
/* Chrome wraps a <details>' contents in an anonymous ::details-content slot,
   and THAT is the flex container its children sit in — anything said about
   how the children are spaced has to be said here, not on the <details>.

   The spacing is FIXED: one gap, the same on every window. Distributing the
   leftover room instead (space-between, or letting each slider grow into a
   share of it) makes the pane breathe differently at every height, so the
   same panel reads as a different design on a laptop and on a large screen.
   The slack collects at the foot of the pane, where it is just the drawer
   being taller than what it holds. */
body #panel-body > details.psec::details-content {
  /* Was 22px. The Date pane is the tallest of the three and its last control
     — the time of day, the one the whole pane is for — fell off the bottom of
     a laptop-height window. Seven gaps at 22px were 154px of the ~900px the
     pane needed; at 12px they are 84px, and that plus the trims below brings
     the stack under 780px, so the pane ends above the fold on a 820px window
     instead of 80px below it. */
  justify-content: flex-start; row-gap: 12px;
}
body #panel-body > details.psec > * { flex: 0 0 auto; }
/* style.css states this one on `#panel[data-pane="time"] .slider-ctrl`, which
   outruns the line above: the four sliders were each told to grow into a
   share of the leftover and centre their contents in it. In a drawer the
   height of the window that share is large, so every slider carried 40px of
   air and the last one left a white band under the pane. They keep their own
   heights now and the slack goes between them. */
body #panel[data-pane="time"] .slider-ctrl { flex: 0 0 auto; }
body #panel, body #panel[data-pane] { padding-bottom: 6px; }

/* The rest of the room for that last slider, taken from padding that was
   only ever air. style.css keeps its own values; these sit over them.

   Deliberately untouched: #histogram's 56px, which style.css explains is the
   point of the chart (48px flattened the difference between the 6am ramp and
   the midday plateau), and the slider groups' own heights, which carry the
   tick labels. */
body #panel #readout {
  padding-top: 16px; padding-bottom: 16px;   /* was 24 / 26 */
  margin-bottom: 0;                          /* the row-gap already separates */
}
body #panel #readout label { margin-bottom: 10px; }     /* was 16 */

/* ...but only where the room is actually short. The compact values above are
   the FALLBACK — what a laptop needs to see the last slider — and a taller
   window gets the rhythm back rather than the same tight stack with a band of
   dead grey under it. The pane is ~787px compact and ~900px generous, so the
   first step waits until 940px of window, where the generous stack still
   leaves a margin. */
@media (min-height: 940px) {
  body #panel-body > details.psec::details-content { row-gap: 22px; }
  body #panel #readout {
    padding-top: 24px; padding-bottom: 26px; margin-bottom: 16px;
  }
  body #panel #readout label { margin-bottom: 16px; }
}
/* Taller again, and the slack is worth spending rather than pooling at the
   foot: the four sliders are the pane's spine and they read better apart. */
@media (min-height: 1100px) {
  body #panel-body > details.psec::details-content { row-gap: 32px; }
}

/* ── 4. The cards: what is true right now ──────────────────────────────────
   The stats and the clock read the same numbers the Date pane does, so they
   step aside when the drawer that holds it opens. */
/* Centred on the window's own vertical middle rather than parked above the
   chip row: the cards are a readout, not a footer, and at the foot of a tall
   window they sat in the corner with the chips crowding them. The centring
   lives on the same transform as the exit, so the two are written together —
   a second `transform` would replace the first. */
#eik-hud {
  position: fixed; right: var(--chrome-inset); top: 50%;
  transform: translateY(-50%);
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  z-index: 20; pointer-events: none;
  transition: opacity .24s ease, transform .3s cubic-bezier(.22, .61, .36, 1);
  /* The cards take the look of the map, as the panels do: light glass on a light map, dark on a dark one.
     rgba() and not color-mix(): a change of look then fades them in sRGB, as the map and the panels fade. */
  --hud-ground: rgba(255, 255, 255, .8);
  --hud-figure: var(--brand);
  --hud-date: var(--brand);
  --hud-quiet: var(--text-muted);
  --hud-faint: #051c233d;
  --hud-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
}
[data-ui="dark"] [data-view="map"] #eik-hud {
  --hud-ground: rgba(16, 35, 43, .8);
  --hud-figure: var(--brand-accent);
  --hud-date: #9edfffd6;
  --hud-quiet: #9edfff8f;
  --hud-faint: #ffffff3d;
  --hud-shadow: 0 10px 30px rgba(5, 28, 35, .34);
}

/* They open the Date pane, so they say so on approach. The lift is small on
   purpose: these are a readout first and a way in second, and a card that
   jumps under the pointer reads as the primary control on the map, which it
   is not. */
/* The wrapper is pointer-events:none so the gap between the two cards stays
   map. A child that wants clicks has to ask for them back — the same shape
   as `#sitehead .shin > *` above. Without this the cards took no pointer at
   all, while a synthetic .click() worked perfectly well, because that skips
   hit-testing: a test can pass on an element nobody can actually press. */
#eik-stats, #eik-clock {
  pointer-events: auto;
  cursor: pointer; transition: transform .16s ease, box-shadow .16s ease;
}
#eik-stats:hover, #eik-clock:hover { transform: translateY(-1px); }
#eik-stats:focus-visible, #eik-clock:focus-visible {
  outline: 2px solid var(--brand-accent); outline-offset: 3px;
}
body.dock-open #eik-hud,
body[data-view="report"] #eik-hud,
body[data-view="feedback"] #eik-hud {
  opacity: 0; transform: translate(14px, -50%); pointer-events: none; visibility: hidden;
  transition: opacity .24s ease, transform .3s cubic-bezier(.22, .61, .36, 1), visibility 0s linear .24s;
}
/* ...and the cards with it. `pointer-events: none` on a parent does NOT
   cover a child that sets `auto`, so every state that hides the HUD has to
   put the cards away by name as well, or an invisible card stays clickable —
   which is the bug the note below already records for the chips. */
body.dock-open #eik-hud > *,
body[data-view="report"] #eik-hud > *,
body[data-view="feedback"] #eik-hud > *,
body.orienting #eik-hud > * { pointer-events: none; }
/* pointer-events too: invisible chips and cards were still clickable over
   the landing card, and a fly-to fired from there is cancelled frame by
   frame by the intro's own bearing spin — a click that silently does
   nothing. */
body.orienting #eik-hud { opacity: 0; pointer-events: none; visibility: hidden; }

/* Named for what it is and whose it is. `.eik-card` was the first name and
   the wrong one: account.css already owns that class for the sign-in, the
   cookie sheet and the profile modal, so this rule — loaded after it — was
   painting the profile dialogue in the clock's ink and leaving its labels
   dark on dark. */
.eik-hud-card {
  /* A fifth of the way transparent, over a blur: enough for the city to
     carry through the card as colour and movement without any of it being
     read as content. */
  background: var(--hud-ground);
  -webkit-backdrop-filter: blur(16px) saturate(1.25);
          backdrop-filter: blur(16px) saturate(1.25);
  color: var(--text); border-radius: 18px;
  box-shadow: var(--hud-shadow);
}
#eik-stats {
  display: flex; flex-direction: column; gap: 14px;
  padding: 14px 18px 16px; min-width: 224px;
}
#eik-stats .eik-statrow { display: flex; gap: 20px; }
#eik-stats .eik-stat { display: flex; flex-direction: column; gap: 3px; }

/* The chart, on the card's own ground. The bars are map.js's own elements
   copied across, so only their colour changes here. */
#eik-stats .eik-histbox { display: flex; flex-direction: column; gap: 7px; }
#eik-stats .eik-histbox[hidden] { display: none; }
#eik-stats .eik-histlabel {
  font: 500 10.5px/1 var(--font-sans, sans-serif); color: var(--text-muted);
}
#eik-hist {
  display: flex; align-items: flex-end; gap: 2px; height: 34px;
  padding-bottom: 2px; border-bottom: 1px solid var(--border);
}
#eik-hist .hbar {
  flex: 1; min-height: 2px; border-radius: 2px 2px 0 0;
  background: var(--hud-faint);
  transition: background .2s;
}
#eik-hist .hbar.active { background: var(--hud-figure); }
#eik-stats .eik-stat b {
  font: 600 19px/1 var(--font-sans, sans-serif);
  font-variant-numeric: tabular-nums; color: var(--text);
}
#eik-stats .eik-stat span {
  font: 400 10.5px/1.25 var(--font-sans, sans-serif); color: var(--text-muted);
}
#eik-clock { padding: 0 0 14px; min-width: 224px; }
#eik-clock .eik-date {
  display: flex; align-items: stretch;
  /* Twice the size it was: the date is the other half of what this card
     says, not a caption under the hour. */
  font: 600 24px/1 var(--font-mono, monospace); letter-spacing: .04em;
  text-transform: uppercase; color: var(--hud-date);
}
#eik-clock .eik-date span {
  flex: 1; text-align: center; padding: 16px 4px 14px;
  border-bottom: 1px solid var(--border);
}
#eik-clock .eik-date span + span { border-left: 1px solid var(--border); }
/* The weekday, lighter: the month and the date are what you set, the day of
   the week is what follows from them. */
#eik-clock .eik-date .eik-dow { font-weight: 400; color: var(--hud-quiet); }
#eik-clock .eik-time {
  /* Centred, not on the baseline: AM over PM is a two-line block, and it
     belongs on the middle of the hour rather than hanging off its feet. */
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 16px 18px 2px;
}
#eik-clock .eik-hhmm {
  font: 300 46px/1 var(--font-sans, sans-serif);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
#eik-clock .eik-mer {
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
}
#eik-clock .eik-mer span {
  font: 600 13px/1 var(--font-mono, monospace); letter-spacing: .08em; color: var(--hud-faint);
}
#eik-clock .eik-mer span.on { color: var(--text); }

/* The Ask pane asks for a key until it has one. Once it does, the ask has
   been answered, so the blue strip stands down to a quiet line that says so
   and keeps the way back — chat.js already writes both the words ("Claude
   API key saved") and the button's verb ("Change"), so this is the same row
   at two weights rather than a second control to keep in step. */
body.has-chat-key #panel[data-pane="ask"] #key-bar {
  justify-content: flex-start; gap: 4px;
  margin: 8px 0 2px; padding: 0 2px;
  background: none; border-radius: 0;
}
body.has-chat-key #key-bar-copy {
  display: flex; align-items: center; gap: 6px;
  font: 500 11.5px/1.3 var(--font-sans, sans-serif);
  color: var(--text-muted, #051c239e);
}
/* A tick, drawn rather than typed: the state is "done", and a checkbox glyph
   in body text sits on the wrong baseline. */
body.has-chat-key #key-bar-copy::before {
  content: ""; flex: none; width: 13px; height: 13px; border-radius: 50%;
  background: var(--brand, #064d90);
  -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='11'/%3E%3Cpath d='M7 12.4l3.4 3.4L17 9'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='11'/%3E%3Cpath d='M7 12.4l3.4 3.4L17 9'/%3E%3C/svg%3E") center / contain no-repeat;
}
body.has-chat-key #panel[data-pane="ask"] #key-cta {
  padding: 2px 6px; background: none; border: 0; border-radius: 6px;
  font: 500 11.5px/1.3 var(--font-sans, sans-serif);
  color: var(--brand-line, #008cff); text-decoration: underline;
  text-underline-offset: 2px;
}
body.has-chat-key #panel[data-pane="ask"] #key-cta:hover {
  background: var(--surface-stone, #f1f2ed); color: var(--brand, #064d90);
}

/* The old caption said the same thing in the same breath; the cards say it
   better, so it goes. */
body #mapcaption { display: none !important; }

/* ── 5. The municipalities ─────────────────────────────────────────────────
   Along the foot of the map: every municipality in the city's extent, the
   one you are in filled. */
#eik-chips {
  /* Clear of the controls on the left and the Feedback button on the right. */
  position: fixed; left: 78px; right: 190px; bottom: var(--chrome-inset);
  display: flex; align-items: center; gap: 10px; padding: 2px 4px;
  z-index: 20;
  transition: opacity .24s ease;
}
/* Only the run of municipalities scrolls; the city and the two arrows hold
   their places on either side of it. */
#eik-chips .eik-chip-scroll {
  flex: 1 1 auto; min-width: 0;
  display: flex; overflow-x: auto; scrollbar-width: none;
  /* the run fades at both ends rather than being cut off square */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 20px,
                                      #000 calc(100% - 20px), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 20px,
                                      #000 calc(100% - 20px), transparent);
}
#eik-chips .eik-chip-scroll::-webkit-scrollbar { display: none; }
/* …and only where there is more to reach: a chip with nothing beyond it is
   not half a chip. */
#eik-chips .eik-chip-scroll.at-start {
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent);
}
#eik-chips .eik-chip-scroll.at-end {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 20px, #000 100%);
          mask-image: linear-gradient(90deg, transparent, #000 20px, #000 100%);
}
#eik-chips .eik-chip-scroll.at-start.at-end {
  -webkit-mask-image: none; mask-image: none;
}
#eik-chips .eik-chip-track { display: flex; gap: 10px; margin: 0 auto; padding: 0 4px; }

/* The city, pinned. Deselected it is not one of the twenty — it is where
   they all are — so it wears the brand tint rather than the plain white the
   others use, and only goes to ink when it is the one selected. */
/* `#eik-chips button` sets the white, so the city has to be stated at least
   as specifically to take it back. */
#eik-chips #eik-chip-city {
  flex: none; background: var(--brand-soft, #cdeeff); color: var(--text, #051c23);
}
#eik-chips #eik-chip-city:hover { background: #bbe6ff; }
#eik-chips #eik-chip-city.on { background: var(--text, #051c23); color: var(--on-dark); }

#eik-chips .eik-chip-arrow {
  flex: none; width: 36px; height: 36px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--surface); color: var(--text, #051c23);
  box-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
  transition: background-color .15s, opacity .18s, transform .12s;
}
#eik-chips .eik-chip-arrow svg { width: 17px; height: 17px; }
#eik-chips .eik-chip-arrow:hover:not(:disabled) { background: var(--surface-stone, #f1f2ed); }
#eik-chips .eik-chip-arrow:active:not(:disabled) { transform: scale(.92); }
#eik-chips .eik-chip-arrow:disabled { opacity: .35; cursor: default; }
#eik-chips .eik-chip-arrow:focus-visible {
  outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px;
}
/* Everything fits: the arrows have nothing to offer and go. */
#eik-chips.no-scroll .eik-chip-arrow { display: none; }
body[data-view="report"] #eik-chips, body[data-view="feedback"] #eik-chips, body.orienting #eik-chips { opacity: 0; pointer-events: none; }
#eik-chips button {
  flex: none; height: 44px; padding: 0 26px; border: 0; border-radius: 999px;
  background: var(--surface); color: var(--text, #051c23); cursor: pointer;
  font: 500 15px/1 var(--font-sans, sans-serif);
  box-shadow: 0 2px 14px rgba(5, 28, 35, .16), 0 0 0 1px rgba(5, 28, 35, .05);
  transition: background-color .15s, color .15s, transform .12s;
}
#eik-chips button:hover { background: var(--surface-stone, #f1f2ed); }
#eik-chips button:active { transform: scale(.97); }
#eik-chips button.on { background: var(--text, #051c23); color: var(--on-dark); }
#eik-chips button:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }

/* With the drawer open the row keeps its distance rather than running
   under it — the chips are still usable while the panes are. */
body.dock-open #eik-chips { right: calc(var(--panel-w) + var(--chrome-inset)); }
#eik-chips { transition: opacity .24s ease, right .3s cubic-bezier(.22,.61,.36,1); }

/* The Feedback button sits in the drawer's bottom corner, where the Ask
   pane keeps its composer. It steps aside for the same reason — out from
   under the drawer, and up out of the chip row it would otherwise land on. */
body.dock-open #fbbtn, body.dock-open #fbpop {
  right: calc(var(--panel-w) + var(--chrome-inset) + 6px);
}
/* MapLibre's attribution disc owns the very corner — 38px, 10px in from each
   edge — and the feedback pill was landing on top of it, hiding the one
   control on the map that has to stay reachable.

   The two sit side by side on the corner's own line, matched to the disc:
   the same 38px, the same 10px off the bottom, so they read as a pair rather
   than as one control that happens to be near another. The pill slots into
   the 142px of clear map between the chip row's last arrow (which ends at
   1250 on a 1440 window) and the disc. */
#fbbtn {
  right: 60px; bottom: 10px;
  height: 38px; padding: 0 18px;
  display: inline-flex; align-items: center;
}
#fbpop { right: 60px; bottom: 58px; }   /* clear of the pill's new top */

body.dock-open #fbbtn { bottom: 70px; }
body.dock-open #fbpop { bottom: 114px; }
#fbbtn { transition: right .3s cubic-bezier(.22,.61,.36,1),
                     bottom .3s cubic-bezier(.22,.61,.36,1); }

/* The same places, named where they are. They arrive with the area rather
   than sitting there waiting: fade and rise, one after another. */
/* The wrapper is MapLibre's to position; nothing of ours belongs on it. */
.eik-label-wrap { will-change: transform; }
.eik-label {
  padding: 8px 18px; border-radius: 999px;
  background: var(--text, #051c23); color: var(--on-dark); cursor: pointer;
  font: 500 14px/1 var(--font-sans, sans-serif); white-space: nowrap;
  box-shadow: 0 4px 16px rgba(5, 28, 35, .28);
  /* Up from below, gently: a long ease-out with a touch of scale, so the
     label settles into the map rather than snapping onto it. */
  opacity: 0; transform: translateY(18px) scale(.96);
  transition: opacity .62s cubic-bezier(.16, .84, .32, 1),
              transform .68s cubic-bezier(.16, .84, .32, 1),
              background-color .15s, color .15s;
}
.eik-label.in { opacity: 1; transform: none; }
.eik-label:hover { background: #17323b; }
.eik-label.on { background: var(--surface); color: var(--text, #051c23); }
body.dock-open .eik-label, body[data-view="report"] .eik-label, body[data-view="feedback"] .eik-label { opacity: 0 !important; }
@media (prefers-reduced-motion: reduce) {
  .eik-label { transition: opacity .2s ease; transform: none; }
  .eik-label.in { transform: none; }
}

/* ── 6. The report, as a panel ─────────────────────────────────────────────
   Same document, same rail, same share menu — it opens over the map now
   instead of replacing it, leaving the controls showing down the left. */
body[data-view="report"] #report,
body[data-view="feedback"] #board {
  position: fixed; top: 0; right: 0; bottom: 0; left: 92px;
  overflow-y: auto; overscroll-behavior: contain;
  padding-top: 0; margin: 0; z-index: 68;
  box-shadow: -18px 0 60px rgba(5, 28, 35, .34);
  animation: eik-report-in .32s cubic-bezier(.22, .61, .36, 1) both;
}
/* Opacity only — no transform. A transform anywhere in these keyframes makes
   the panel a containing block for its `position: fixed` children, and the
   report's rail and share button are fixed: they scrolled away with the text
   instead of staying pinned to the window. */
@keyframes eik-report-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  body[data-view="report"] #report, body[data-view="feedback"] #board { animation: none; }
}
/* The focus lands on the panel itself, which is not a control. */
#report:focus, #board:focus { outline: none; }
/* Behind the report, over everything else: the city is still there, but it
   is not what you are reading. Clicking it is the way back. */
#report-scrim {
  position: fixed; inset: 0; z-index: 67;
  background: rgba(5, 12, 15, .3);
  opacity: 0; pointer-events: none;
  transition: opacity .28s ease;
}
body[data-view="report"] #report-scrim, body[data-view="feedback"] #report-scrim { opacity: 1; pointer-events: auto; }

/* The place is what this report IS, so it carries the title; the line about
   the ten analyses says what a report of that kind contains, which is a
   subtitle's job. The two swap weights rather than positions. */
#report .eik-report-title {
  margin: 12px 0 0;
  font-family: var(--display, var(--font-display, sans-serif));
  font-weight: 400; font-size: 44px; line-height: 1.08;
  letter-spacing: -.025em; color: var(--on-dark); text-wrap: balance;
}
body #report .rintro .rtitle {
  margin: 10px 0 0;
  font-size: 19px; font-weight: 400; line-height: 1.35;
  letter-spacing: -.005em; color: var(--brand-accent, #9edfff);
  max-width: 46ch;
}
body #report .rintro .rtitle em { color: #ffffffb8; }

/* The three things pinned to the report's right edge — close, share, rail —
   stand on one vertical axis: the close button is 42px at a 22px inset, so
   its centre is 43px in, and the 38px discs below it sit at 24px to match.

   planning.js hangs the share button off the RAIL (46px above whatever height
   the rail happens to be) and writes that as an inline `top`, so this has to
   be stated `!important` to take it back. It belongs under the way out, not
   over the dots: the rail is for moving through the report, and the two
   buttons are for what you do with it. */
#report .repsave {
  top: 74px !important;                      /* 22 + 42 + 10 */
  right: 24px;
}
#report .repsave-pop {
  top: 74px !important; bottom: auto !important;
  right: 70px;                               /* clear of the 38px disc */
}
/* Centred on the window, whatever it is scrolled to. */
#report .secnav { right: 24px; top: 50%; transform: translateY(-50%); }

#report-x, #board-x {
  position: fixed; top: 22px; right: 22px; z-index: 69;
  width: var(--chrome-disc); height: var(--chrome-disc); padding: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--surface); color: var(--text, #051c23); font-size: 20px; line-height: 1;
  box-shadow: 0 2px 14px rgba(5, 28, 35, .22);
  transition: background-color .15s, transform .12s;
}
#report-x:hover, #board-x:hover { background: var(--surface-stone, #f1f2ed); }
#report-x:active, #board-x:active { transform: scale(.94); }
#report-x:focus-visible, #board-x:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; }
body:not([data-view="report"]) #report-x { display: none; }
body:not([data-view="feedback"]) #board-x { display: none; }
/* The pill leads to the board, so it steps aside there. */
body[data-view="feedback"] #fbbtn { display: none !important; }
/* The note pop-up and its pill belong to the map, not to the report. */
body[data-view="report"] #fbbtn[aria-controls="fbpop"],
body[data-view="report"] #fbpop { display: none !important; }
/* "Back to top" answered a page that scrolled: the map filled part of the
   window and the analyses ran on below it. Nothing scrolls the window any
   more — the map is the whole of it and the report scrolls inside its own
   panel — so the button has nothing to return to. map.js only re-syncs it on
   a scroll event, which now never fires, and it was hanging over the Ask
   pane's composer. */
#backtotop { display: none !important; }
/* The report is the thing you are reading now: the top bar, the chips and
   the cards all step out of its way, and the close button is how you get
   back. What stays is the strip of controls down the left, so the
   simulation is still running in plain sight behind it. */
body[data-view="report"] #sitehead, body[data-view="feedback"] #sitehead { display: none; }

@media (max-width: 900px) {
  body[data-view="report"] #report, body[data-view="feedback"] #board { left: 0; }
  #report-x { top: 14px; right: 14px; }
}

/* ── 7. Narrow windows ─────────────────────────────────────────────────────
   The pills stack rather than shrink to nothing, and the drawer takes the
   width it needs. */
@media (max-width: 1080px) {
  #eik-searchpill { flex-basis: 380px; }
  .eik-act { padding: 0 14px; }
}
/* The bar has a floor of about 1,125px wide: the search pill, the report
   button and the brand pill each keep a minimum width. Below that, the pill
   gives up width first, and the brand pill drops its "by" line, so the
   account button stays on screen. */
@media (max-width: 1180px) {
  #eik-searchpill { min-width: 120px; }
  #eik-brandpill .eik-by { display: none; }
}
@media (max-width: 820px) {
  body #sitehead .shin { flex-wrap: wrap; }
  #eik-searchpill { order: 3; flex: 1 1 100%; margin-inline: 0; }
  #eik-tools { margin-left: auto; }
  body #panel, body #panel[data-pane] { left: 0; width: auto; }
  #eik-hud { display: none; }
  #eik-chips { left: 12px; right: 12px; justify-content: flex-start; }
}

/* ── The layer toggles ─────────────────────────────────────────────────────
   style.css draws these as a bare mark — a blue tick when on, a black ✕ when
   off, no box. Two marks with no frame read as a status column rather than a
   control: nothing says "you can change this", and the ✕ in particular reads
   as "broken" rather than "off" on a row like Shadows & sun.

   A box, then. Empty when off, filled when on, with the tick in the surface
   colour so it is cut out of the fill rather than sitting on it. The palette
   has no success green and none is invented: --brand-line is the colour this
   app already uses for "this is on". */
body #panel .chk-label input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 17px; height: 17px; flex: none; margin: 0; padding: 0;
  box-sizing: border-box;
  border: 1.5px solid color-mix(in srgb, var(--text) 32%, transparent);
  border-radius: 4px;
  background: transparent none;
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
/* The ✕ has to be turned off by name. style.css paints it with
   `#panel .chk-label input[type="checkbox"]:not(:checked)` — one id, TWO
   class-level selectors (the class and the :not pseudo-class) — which outranks
   a plain `body #panel .chk-label input[...]` however late this file loads.
   Matching its shape, plus the element, is what wins. Without this the off
   rows kept a cross inside the new box. */
body #panel .chk-label input[type="checkbox"]:not(:checked) {
  background-image: none;
}
body #panel .chk-label input[type="checkbox"]:hover {
  border-color: color-mix(in srgb, var(--text) 55%, transparent);
}
body #panel .chk-label input[type="checkbox"]:checked {
  background: var(--brand-line) center / 12px 12px no-repeat;
  border-color: var(--brand-line);
  /* the tick, in the surface colour, cut out of the fill */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 9.5L7 13l7.5-8'/%3E%3C/svg%3E");
}
body #panel .chk-label input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--brand-line); outline-offset: 2px;
}

/* ══════════════════════════════════════════════════════════════════════════
   v7 — the plan, mirrored                                        2026-09-22
   ──────────────────────────────────────────────────────────────────────────
   Everything that was on the left is on the right and everything that was on
   the right is on the left. Not a whim: v6 put the brand in the corner the
   eye starts from and the working controls in the corner it ends at, which
   is the wrong way round for a tool. Here the panes, their buttons and the
   readouts take the left — where the reading starts — and the wordmark and
   the map's own furniture go to the right, where they can be found but are
   not in the way.

   Written as one block at the end rather than edited in place above, so the
   original build can still be read as the thing this departs from. Every
   selector matches the specificity of the rule it overturns; equal
   specificity, later wins.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The top bar: tools ← → brand ─────────────────────────────────────────
   `order` rather than row-reverse: the search group keeps its auto margins
   and stays centred, and the DOM order — which is the tab order — is left
   alone. */
#eik-tools { order: 1; margin-left: 0; }

/* With the drawer up, the bar's world starts at the drawer's edge. Moving
   #sitehead's left there — and collapsing the tool buttons, which are already
   faded out and standing in the drawer's place — leaves the search group's
   own auto margins to centre it in exactly what is left between the drawer
   and the wordmark. No offset to keep in step with anything: the centring is
   still the browser's, it is just being given the right box to centre in. */
/* Less the inset, not plus it. The bar keeps a 12px gap from the window's
   right edge but sits flush to the drawer on the left, and that asymmetry
   lands on the centred group as half of it — a constant 6px, at every width.
   Pulling the left edge back by the whole inset restores the balance the
   auto margins are splitting. */
body.dock-open #sitehead { left: calc(var(--panel-w) - var(--chrome-inset)); }
body.dock-open #eik-tools { width: 0; margin: 0; padding: 0; overflow: hidden; }
#eik-searchgroup { order: 2; }
#eik-brandpill { order: 3; }

/* The pill and the report button as one travelling unit. The auto margins
   move up here from the pill, or the button would be pushed to the far end
   of the row while the pill sat in the middle. */
#eik-searchgroup {
  display: flex; align-items: center; gap: 10px;
  min-width: 0;
  flex: 0 1 auto; margin-inline: auto;
}
/* 620px. It went to 827 (a third wider than the original 620) and has come
   back a quarter from there, which lands on 620.25 — the width it started
   at. Kept stated here rather than reverted to the base rule, because the
   sizing mechanism below is the part that matters and still has to hold.

   Stated as a WIDTH, not a flex-basis. A flex-basis does not feed the
   group's intrinsic size: the group sized itself from the pill's content,
   which is an input with no width of its own, so it fell back to the 260px
   min-width and the group came out 385px wide while its auto margins ate the
   other 1256. A definite width does contribute, so the group measures 952
   and centres that. flex-shrink is still 1, so the pill gives the width back
   on a narrow window, down to the same 260px floor. */
#eik-searchpill { margin-inline: 0; flex: 0 1 auto; width: 620px; }
#eik-reportcta { flex: none; height: 48px; padding: 0 20px; }

/* ── The map's own controls: left → right ─────────────────────────────── */
.maplibregl-ctrl-top-right { left: auto; right: 0; }
.maplibregl-ctrl-top-right .maplibregl-ctrl { float: right; clear: right; }
.maplibregl-ctrl-top-right .maplibregl-ctrl-group { margin-left: 0; margin-right: 10px; }
#mapstack { margin: 0 10px 24px 0; align-items: flex-end; }
#mapstack .ms-badge { right: auto; left: 0; transform: translate(-50%, -50%); }
#ms-speed-panel { left: auto; right: 0; }
#mapstack.speed-open #ms-speed-panel { padding: 0 58px 0 16px; }

/* ── The drawer: right → left ─────────────────────────────────────────────
   The shadow turns with it — it falls away from the panel, into the map. */
body #panel, body #panel[data-pane] {
  left: 0; right: auto;
  transform: translateX(-100%);
  /* 44px of blur threw a soft grey field a long way across the map, which
     read as haze over the city rather than as an edge. Tighter and a touch
     lighter: enough to lift the panel off the map, not enough to tint it. */
  box-shadow: 10px 0 16px rgba(5, 28, 35, .16);
}
body.dock-open #panel, body.dock-open #panel[data-pane] { transform: none; }

/* ── The readouts: right → left ─────────────────────────────────────────── */
#eik-hud { right: auto; left: var(--chrome-inset); }
body.dock-open #eik-hud,
body[data-view="report"] #eik-hud,
body[data-view="feedback"] #eik-hud { transform: translate(-14px, -50%); }

/* ── The chip row ─────────────────────────────────────────────────────────
   Its gap for the drawer swaps sides too, and the row keeps clear of the map
   controls that have just moved to the right. */
/* A sixth shorter than the span it would otherwise take. Stated as a
   fraction of the run rather than a fixed inset, so the row keeps the same
   proportion on a laptop and on a 27in screen instead of being a sixth
   shorter on one and a third on the other.

   The reduction comes off the RIGHT: the left edge is pinned clear of the
   drawer and the readouts beside it, and the right is the end that runs long
   and crowds the feedback pill. */
/* The row sits BETWEEN two things — the drawer on its left and the feedback
   pill on its right — so it is measured from both and centred in what is
   left, rather than anchored to one edge and allowed to run at the other.

   It still gives up a sixth of that run, as before; the difference is that
   the sixth is now split evenly, a twelfth to each side, so the gap to the
   drawer and the gap to the pill are the same number at every width. */
:root {
  --road-gap: 177px;      /* the feedback pill: 60px inset + its own 117px */
}
#eik-chips {
  --run: calc(100vw - var(--road-gap));
  left: calc(var(--run) / 12);
  right: calc(var(--road-gap) + var(--run) / 12);
}
body.dock-open #eik-chips {
  --run: calc(100vw - var(--panel-w) - var(--road-gap));
  left: calc(var(--panel-w) + var(--run) / 12);
  right: calc(var(--road-gap) + var(--run) / 12);
}

/* The feedback pill no longer has to dodge the drawer — that corner is the
   map's now — so it stays beside the attribution disc in every state. */
body.dock-open #fbbtn { right: 60px; bottom: 10px; }
body.dock-open #fbpop { right: 60px; bottom: 58px; }

/* ── Narrow windows ───────────────────────────────────────────────────────
   The small-screen rules above collapse the drawer to full width and pin the
   chips to both edges; only the pieces that name a side need restating. */
@media (max-width: 900px) {
  #eik-tools { margin-left: 0; margin-right: auto; }
  body #panel, body #panel[data-pane] { left: 0; right: 0; width: auto; }
  #eik-chips, body.dock-open #eik-chips { left: 12px; right: 12px; }
}

body #panel.building-open {
  display: flex; flex-direction: column; padding: 0; overflow: hidden;
  transform: none; pointer-events: auto; visibility: visible;
}
#panel.building-open > :not(#building-inspector):not(#panel-body),
#panel.building-open > #panel-body > :not([data-pane="time"]) { display: none !important; }
/* The Date pane's own sliders, in their usual order and size, under the building. */
body #panel.building-open > #panel-body {
  display: block; flex: none; order: 2;
  max-height: 42%; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain;
  border-top: 1px solid #c8dce8; background: #fff;
}
body #panel.building-open > #panel-body > details.psec[data-pane="time"] { display: block !important; margin: 0; padding-top: 12px; }
body #panel.building-open > #panel-body > details.psec[data-pane="time"]::details-content { display: flex; flex-direction: column; }
#panel.building-open > #panel-body > details[data-pane="time"] > :not(.slider-ctrl) { display: none !important; }
#panel.building-open .slider-ctrl { display: flex; flex-direction: column; margin-bottom: 0; }
@media (max-height: 700px) and (min-width: 641px) {
  body #panel.building-open > #panel-body { max-height: 25%; }
  #panel.building-open .bi-head { padding-top: 8px; padding-bottom: 4px; }
}
@media (max-width: 640px) {
  body #panel.building-open {
    display: flex !important; top: auto; bottom: 58px;
    left: 12px; right: 12px; width: auto; height: calc(100dvh - 130px); max-height: calc(100dvh - 130px);
    border-radius: 14px;
  }
  body #panel.building-open > #panel-body { max-height: 30%; }
}

/* ── The municipality navigator, smaller ──────────────────────────────────
   It was drawn at the weight of a primary control — 44px pills in 15px type,
   running the full width of the window — for a row that is navigation, not
   the subject. At that size it competed with the map it sits on and with the
   panel beside it, and it still could not show the whole region without
   scrolling.

   Taken down to a navigator's weight: 34px tall in 13px type, with the
   padding pulled in to match. Nothing is lost — the same twenty-one places,
   the same pinned city, the same arrows — and more of them are in view at
   once, which is the point of a row you scan. */
#eik-chips { gap: 8px; padding: 2px; }
#eik-chips .eik-chip-track { gap: 6px; padding: 0 3px; }

/* One button, drawn once. The chips and the feedback pill sit on the same
   line of the same map and were two different white pills — 34px in 13px
   medium against 38px in 13.5px semibold, and two different shadows. They
   take the pill's measurements now, so the row reads as the same family of
   control rather than as a smaller imitation of the one beside it.
   The dropped keyline goes with it: #fbbtn never had one. */
#eik-chips button {
  height: 38px; padding: 0 18px;
  color: var(--dim);
  font: 600 13.5px/1 var(--body);
  box-shadow: 0 3px 14px rgba(5, 28, 35, .18);
}
#eik-chips button:hover { background: var(--surface); color: var(--ink); }
/* Selected stays ink-filled — that is the row's own job and the pill has no
   equivalent. */
#eik-chips button.on { background: var(--text, #051c23); color: var(--on-dark); }
#eik-chips #eik-chip-city { color: var(--text, #051c23); }
#eik-chips .eik-chip-arrow {
  width: 38px; height: 38px;
  color: var(--dim);
  box-shadow: 0 3px 14px rgba(5, 28, 35, .18);
}
#eik-chips .eik-chip-arrow svg { width: 16px; height: 16px; }

/* ── The activity legend, as checkboxes ───────────────────────────────────
   The same change the layer toggles got, for the same reason: these rows are
   chosen and unchosen, and a bare tick beside a bare ✕ reads as a status
   column rather than as something you can press. Both levels — the category
   head and the tier-2 list under it — take the box.

   `!important` throughout, and not for convenience: style.css paints these
   marks with `!important` of its own, because each swatch carries an inline
   `background` with the activity's colour that the marks had to beat. That
   inline style is still there, so anything that sets the background here has
   to outrank it the same way. The colour has not been visible since the
   marks arrived, so nothing is lost by covering it again. */
body #activity-legend .swatch {
  width: 15px; height: 15px; box-sizing: border-box;
  border: 1.5px solid color-mix(in srgb, var(--text) 32%, transparent);
  border-radius: 4px;
  background: transparent none !important;
  transition: background-color .12s ease, border-color .12s ease;
}
body #activity-legend .key-cat-head.sel > .key-main > .swatch,
body #activity-legend .key-kids li.sel > .key-main > .swatch {
  border-color: var(--brand-line);
  background: var(--brand-line)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 9.5L7 13l7.5-8'/%3E%3C/svg%3E")
    center / 11px 11px no-repeat !important;
}

/* ── The report, mirrored ─────────────────────────────────────────────────
   The last thing still built the old way round. It filled from a 92px strip
   of map on the LEFT across to the right edge — which in this build meant it
   came out from under the drawer, on the side the drawer is on, and its rail
   and close sat at the far right beside the map's own controls.

   Turned over: the strip of map is on the right now, the report comes from
   the left, and its furniture goes with it to the left edge.

   NO transform, deliberately. The note above `eik-report-in` is not a style
   preference — a transform anywhere on this element makes it the containing
   block for its `position: fixed` children, and the rail and the share button
   are fixed; they scroll away with the text instead of staying put. The
   entrance stays a fade for the same reason. */
body[data-view="report"] #report,
body[data-view="feedback"] #board {
  left: 0; right: var(--report-strip);
  box-shadow: 18px 0 60px rgba(5, 28, 35, .34);
}
/* The rail goes back to the right — but to the REPORT's right edge, not the
   window's. The report stops short of the window by the strip of city it
   leaves showing, so the rail is inset by that plus its own margin; without
   the first term it would sit out on the map, detached from the text it
   indexes. Both rules read the same variable, so the strip can change in one
   place and the rail follows. */
:root { --report-strip: 92px; }
#report .secnav {
  left: auto; right: calc(var(--report-strip) + 24px);
}
/* Top right, on the REPORT's corner rather than the window's — the same
   relationship it had before this build turned the layout over, when the
   report ran to the window's edge and the two corners were the same point.
   Inset by the strip of city the report leaves showing, or the close would
   float out on the map, belonging to neither. */
#report-x, #board-x { left: auto; right: calc(var(--report-strip) + 22px); }

/* The drawer is on this side too, and it outranks the report (z 72 against
   68), so it would sit on top of the rail and the close. The report IS the
   view while it is up — the drawer steps aside for it, the same way the top
   bar already does. */
body[data-view="report"] #panel,
body[data-view="report"] #panel[data-pane],
body[data-view="feedback"] #panel,
body[data-view="feedback"] #panel[data-pane] {
  transform: translateX(-100%); pointer-events: none;
}

@media (max-width: 900px) {
  /* Full-bleed: no strip, so the rail sits on its own margin alone. */
  :root { --report-strip: 0px; }
  body[data-view="report"] #report, body[data-view="feedback"] #board { left: 0; right: 0; }
  #report-x, #board-x { top: 14px; left: auto; right: 14px; }
}

/* ── The note pop-up's own two controls ─────────────────────────────────
   The markup for these went in without any CSS to go with it, which is why
   the link came out as a default purple visited-link and its inline arrow —
   an <svg> with a viewBox, no width, and `display: inline` — stretched to
   the full 288px of the panel. The arrow is gone; a link at the end of a row
   does not need one to say it leads away.

   The link and Send sit at opposite ends of the same row: what is coming
   next on the left, what you have to say about now on the right. */
#fbpop .fbcta {
  font: 600 12.5px/1 var(--body); color: var(--brand);
  text-decoration: none; white-space: nowrap;
}
#fbpop .fbcta:hover { text-decoration: underline; }
#fbpop .fbcta:focus-visible { outline: 2px solid var(--brand-line); outline-offset: 3px; border-radius: 4px; }
/* Waiting on a URL: stated, not silently dead. */
#fbpop .fbcta[aria-disabled="true"] { color: var(--mute); cursor: default; pointer-events: none; }
#fbpop .fbnote { flex: 1 1 auto; text-align: right; }
#fbpop .fbsoon {
  margin: 8px 0 0; text-align: left;
  font: 400 11px/1.4 var(--body); color: var(--mute);
}

/* ── "View on map", and the layer it turns on ─────────────────────────────
   The button sits beside the figure's download control: both answer "what
   else can I do with this drawing". It is drawn as a quiet pill rather than
   an icon because, unlike download, it needs a word to be understood. */
/* All three in the idiom of "Share and save" at the foot of each section:
   plain text buttons, no disc and no pill, 37px tall, mono 12.5/500, muted
   until you point at them. They were three different shapes — a bordered
   circle, another bordered circle, and a tinted pill — for three things that
   are all just "something you can do with this figure". The page already had
   a settled way of saying that; this borrows it rather than inventing a
   fourth. */
/* These selectors are long because the rules they overturn are. style.css
   sizes the two discs with `#report .secmap:has(.mapwrap) .cardhead .capq` —
   one id and FOUR class-level selectors, since `:has()` carries the weight of
   its argument. Anything shorter loses, however late it loads, which is what
   left the ? white and the download at 27px on the first attempt. Copied
   verbatim and relying on load order, rather than retyped shorter. */
#report .secmap:has(.mapwrap) .cardhead .capq,
#report .secmap:has(.mapwrap) .cardhead .mapdl,
#report .secmap:has(.mapwrap) .cardhead .mapview,
#report .cardhead .mapview {
  height: 37px; margin-top: 0; box-sizing: border-box; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  /* The pill the share menu wears, copied from .sharebg: the surface, its
     hairline, and 18px rather than 999px — on a 37px control the browser
     clamps 999 to 18.5 anyway, and the note there explains why a fixed
     corner is the one that survives the shape changing. */
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: 18px;
  color: var(--dim); font: 500 12.5px var(--mono);
  cursor: pointer;
  transition: color .15s, box-shadow .26s ease;
}
/* The two icon-only ones stay square; the width comes from the same 10px
   that sets the height, so they read as one rhythm with the words. */
#report .secmap:has(.mapwrap) .cardhead .capq,
#report .secmap:has(.mapwrap) .cardhead .mapdl { width: 37px; padding: 10px; }
#report .cardhead .mapview { margin-left: 0; padding: 10px 18px; }
/* The same lift the share pill takes on hover, so they answer the pointer
   the way it does rather than only changing colour. */
#report .secmap:has(.mapwrap) .cardhead .capq:hover,
#report .secmap:has(.mapwrap) .cardhead .mapdl:hover,
#report .cardhead .mapview:hover {
  color: var(--ink); background: var(--surface);
  box-shadow: 0 2px 8px rgba(5, 28, 35, .07);
}
#report .cardhead .capq:focus-visible,
#report .cardhead .mapdl:focus-visible,
#report .cardhead .mapview:focus-visible {
  outline: 2px solid var(--brand-line); outline-offset: 2px; border-radius: 999px;
}
#report .secmap:has(.mapwrap) .cardhead .mapdl svg { width: 17px; height: 17px; }
#report .cardhead .mapview:hover { background: #bbe6ff; }
#report .cardhead .mapview:focus-visible {
  outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px;
}

/* The legend, which carries the caveat. It is deliberately not a floating
   chip that can be missed: the accuracy note is part of the key, in the same
   box, so the colours cannot be read without it. */
#eik-ej-key {
  position: fixed; left: var(--chrome-inset); bottom: 74px; z-index: 40;
  width: 250px; box-sizing: border-box; padding: 12px 13px 11px;
  background: var(--surface, #fff); border-radius: var(--radius, 10px);
  box-shadow: 0 3px 18px rgba(5, 28, 35, .2);
  font-family: var(--font-sans, sans-serif);
}
#eik-ej-key .ej-head { display: flex; align-items: flex-start; gap: 8px; }
#eik-ej-key .ej-head b { font-size: 12.5px; line-height: 1.3; color: var(--text); }
#eik-ej-key #eik-ej-x {
  margin-left: auto; flex: none; width: 22px; height: 22px; border: 0;
  border-radius: 50%; background: transparent; color: var(--text-muted);
  font-size: 17px; line-height: 1; cursor: pointer;
}
#eik-ej-key #eik-ej-x:hover { background: var(--surface-grey, #ededed); color: var(--text); }
#eik-ej-key .ej-bands { display: flex; flex-direction: column; gap: 5px; margin-top: 9px; }
#eik-ej-key .ej-band {
  display: flex; align-items: center; gap: 7px;
  font-size: 11.5px; color: var(--text-body);
}
#eik-ej-key .ej-band i {
  width: 14px; height: 14px; flex: none; border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(5, 28, 35, .12);
}
#eik-ej-key .ej-warn {
  margin: 10px 0 0; padding-top: 9px;
  border-top: 1px solid var(--border-soft, #00000010);
  font-size: 10.5px; line-height: 1.45; color: var(--text-muted);
}

/* ── The Environmental Justice view ───────────────────────────────────────
   Arriving from the report's figure is not arriving at the simulation. The
   subject is one dataset laid over the city, so everything that belongs to
   the simulation steps off: the top bar, the drawer and its buttons, the
   chips, the live readouts, the playback controls, the feedback pill.

   What stays is the map's own navigation — zoom and the compass — because
   moving around the city IS the task here, and the two ways back, which are
   stated at the bottom because nothing else on screen offers one. */
body.eik-ej-view #sitehead,
body.eik-ej-view #panel,
body.eik-ej-view #eik-tools,
body.eik-ej-view #eik-chips,
body.eik-ej-view #eik-hud,
body.eik-ej-view #mapstack,
body.eik-ej-view #fbbtn,
body.eik-ej-view #fbpop,
body.eik-ej-view #orient {
  display: none !important;
}
/* Explicitly NOT hidden: .maplibregl-ctrl-group — the +, − and compass. */

#eik-ej-acts {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: var(--chrome-inset); z-index: 41;
  display: flex; align-items: center; gap: 10px;
}
#eik-ej-acts .ej-act {
  height: 38px; padding: 0 18px; border: 0; border-radius: 999px;
  background: var(--surface); color: var(--dim); cursor: pointer;
  font: 600 13.5px/1 var(--body);
  box-shadow: 0 3px 14px rgba(5, 28, 35, .18);
  transition: background-color .15s, color .15s, transform .12s;
}
#eik-ej-acts .ej-act:hover { color: var(--ink); }
#eik-ej-acts .ej-act:active { transform: scale(.97); }
#eik-ej-acts .ej-act:focus-visible {
  outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px;
}
/* The simulation is where the app lives, so that one is the solid pill. */
#eik-ej-acts .ej-act-main { background: var(--text, #051c23); color: var(--on-dark); }
#eik-ej-acts .ej-act-main:hover { background: #17323b; color: var(--on-dark); }

/* The city goes grey under a report map so its colours are the only ones on
   screen, and comes back over the same 1.1s the camera takes to tilt back.
   Only the WebGL canvas: the data sits on #eik-mv-overlay above it. */
#map .maplibregl-canvas { transition: filter 1.1s ease; }
body.eik-mv-grey #map .maplibregl-canvas { filter: grayscale(1) contrast(.75) brightness(1.08); transition-duration: .7s; }
body.eik-mv-grey.eik-parcels-colour #map .maplibregl-canvas { filter: none; }
#map .parcel-lens-map canvas.maplibregl-canvas { filter: none; transition: none; }
/* Inside the edge: the map box clips anything drawn outside the canvas.
   !important: deck.gl sets an inline `outline: none` on the canvas. */
#map .maplibregl-canvas:focus-visible { outline: 3px solid var(--brand-line, #008cff) !important; outline-offset: -3px; }
#eik-mv-overlay {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; opacity: 0; transition: opacity .45s ease;
  mix-blend-mode: multiply;
}
#eik-mv-overlay.on { opacity: 1; transition-delay: .15s; }

/* The key moves up off the new row. */
body.eik-ej-view #eik-ej-key { bottom: 66px; }

/* ── Municipality names ───────────────────────────────────────────────────
   Off unless asked for. `!important` is doing real work here: chrome.js's
   label loop writes `display` straight onto each marker's wrapper as it
   enters and leaves the view, and an inline style is only beaten by an
   important declaration. Without it the switch would hold for one frame. */
body:not(.show-munis) .eik-label-wrap { display: none !important; }

/* ── The chip row, retired ────────────────────────────────────────────────
   Search is the way to a place in this build. The row is HIDDEN rather than
   removed: chrome.js still keeps the selection on it (`button.on` carries
   the name) and still builds the map's labels in the same module, so taking
   it out of the DOM would cost both for the sake of a row nobody sees. */
body #eik-chips, body.dock-open #eik-chips { display: none !important; }

/* ── Somewhere other than Boston ──────────────────────────────────────────
   A statement and a way to act on it, on one line where the chips used to
   be. The sentence comes first: the button only makes sense once you know
   the simulation is not the city you are looking at. */
#eik-city-reload {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: var(--chrome-inset); z-index: 41;
  display: flex; align-items: center; gap: 14px;
  padding: 8px 8px 8px 16px; border-radius: 999px;
  background: var(--surface, #fff);
  box-shadow: 0 3px 18px rgba(5, 28, 35, .2);
  font-family: var(--font-sans, sans-serif);
  max-width: calc(100vw - 2 * var(--chrome-inset)); box-sizing: border-box;
}
#eik-city-reload .cr-said {
  font-size: 12.5px; color: var(--text-body); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
#eik-city-reload .cr-said b { color: var(--text); font-weight: 600; }
#eik-city-reload .cr-go {
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  height: 34px; padding: 0 16px; border: 0; border-radius: 999px;
  background: var(--text, #051c23); color: var(--on-dark); cursor: pointer;
  font: 600 12.5px/1 var(--font-sans, sans-serif);
  transition: background-color .15s, transform .12s;
}
#eik-city-reload .cr-go:hover { background: #17323b; }
#eik-city-reload .cr-go:active { transform: scale(.97); }
#eik-city-reload .cr-go:focus-visible {
  outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px;
}
#eik-city-reload .cr-go svg { width: 15px; height: 15px; flex: none; }

/* Not while the report or the EJ view is up — neither is the simulation. */
body[data-view="report"] #eik-city-reload,
body[data-view="feedback"] #eik-city-reload,
body.eik-ej-view #eik-city-reload { display: none !important; }

/* ── The search list: the twenty under the city ───────────────────────────
   Boston is the region these places are in, not the first of a flat list of
   twenty-one, so the list is drawn that way: the city flush, the rest
   stepped in under it with a hairline running down the indent to carry the
   eye. Typed results are not indented — see `under` in chrome.js. */
/* `li` is not decoration in these selectors: the rule being overridden is
   `#eik-suggest li.eik-sg`, an id, a class AND an element, so a selector of
   an id and a class loses to it however late it is written. */
#eik-suggest li.eik-sg-under { padding-left: 34px; position: relative; }
/* A house on each row rather than a rule down the group. The line said only
   "these belong to the one above"; the house says what they are — places
   people live — which is also what the number on the right is counting.

   A mask rather than an inline image, so the glyph takes its colour from a
   token and follows the row: muted normally, and the row's own ink when it
   is the selected one. */
#eik-suggest li.eik-sg-under::before {
  content: ""; position: absolute; left: 13px; top: 50%;
  width: 14px; height: 14px; margin-top: -7px;
  background-color: var(--text-muted, #051c239e);
  -webkit-mask: var(--eik-house) center / 14px 14px no-repeat;
          mask: var(--eik-house) center / 14px 14px no-repeat;
}
#eik-suggest li.eik-sg-under.on::before,
#eik-suggest li.eik-sg-under.active::before {
  background-color: var(--text, #051c23);
}
:root {
  --eik-house: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.2 7.4 8 2.6l5.8 4.8'/%3E%3Cpath d='M3.9 6.9V13h8.2V6.9'/%3E%3C/svg%3E");
}

/* Population, where there is one: a figure, so it is set as one. */
#eik-suggest .sub.num {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted, #051c239e);
}

/* ── The date card ────────────────────────────────────────────────────────
   Without the time under it the card is one row, so the padding that held
   the two apart goes with it. Same box, same left edge as the stats above —
   it reads as the second of a pair, not as a card that lost something. */
#eik-clock { padding: 0; min-width: 224px; }
#eik-clock .eik-date { border-radius: var(--radius, 10px); }
/* The running hour is for a presentation tour only: there no one sets the clock by hand. */
#eik-clock .eik-time { display: none; }
body.presenting #eik-clock .eik-time { display: flex; padding: 14px 18px; }

/* The month reads as light as the year. Both are context — the month barely
   moves and the year moves less — and the day between them is the one you
   actually set, so it keeps the weight and they give theirs up. Matches
   .eik-dow exactly, which is what the year already uses. */
#eik-clock .eik-date span:first-child {
  font-weight: 400; color: var(--hud-quiet);
}

/* The three figures in the accent rather than in the ink: --brand-accent on
   the dark cards, --brand on the light ones. It is what the date beside them
   already uses — so the two cards read as one readout in one voice instead of
   a plain card and a blue one. The captions under them stay muted, so the
   numbers still lead. */
#eik-stats .eik-stat b { color: var(--hud-figure); }

/* ── The names, and the open drawer ───────────────────────────────────────
   `body.dock-open .eik-label { opacity: 0 !important }` above is why ticking
   the switch appeared to do nothing. It made sense in the build it was
   written for: the drawer was something you opened, looked at and closed, so
   suppressing the map's labels underneath it was a courtesy for a moment.
   Here the drawer is open from the start, so that "moment" is the whole
   session and the labels could never show.

   Now that the names are a switch, the drawer has no business overruling it.
   Only the report still does — there the map is context behind something you
   are reading, and its labels would be noise.

   Two classes on <body> plus `.in`, so this reaches past the `!important`
   above; and `.in` rather than the bare label, so the entrance still plays —
   a name that has not arrived yet stays at the opacity its own animation
   starts from. */
body.dock-open.show-munis:not([data-view="report"]):not([data-view="feedback"]) .eik-label.in {
  opacity: 1 !important;
}

/* ── A map card's head: title left, controls right ────────────────────────
   The three controls — explain, download, view on map — sat jammed against
   the end of the title, wherever that happened to fall, so they read as part
   of the heading rather than as things you can do to the drawing. They go to
   the right, over the map's own right edge, where a toolbar belongs.

   `margin-left: auto` on the FIRST of them rather than a justify-content on
   the head: the head is `justify-content: flex-start` for exactly these
   cards (style.css), and the h3 computes to `flex: 0 1 auto` so it will not
   grow into the gap. An auto margin opens the gap itself and carries the
   other two along, without touching either of those rules. */
#report .card:has(.mapwrap) .cardhead .capq { margin-left: auto; }
/* The trio sits 30px in from the map's right edge rather than on it. */
#report .card:has(.mapwrap) .cardhead { padding-right: 30px; }
/* The title sat on the head's flex-start baseline while the three controls
   are 37px tall, so it read a few pixels above their centre. Centring the
   row lines the title up with them instead. */
#report .card:has(.mapwrap) .cardhead { align-items: center; }
/* The key follows the head down by the same 10px, opening a bit of air
   under the now-taller row before the map starts. */
#report .card:has(.mapwrap) .cardhead { margin-bottom: 25px; }
/* A phone has no room for the title beside three controls, so they drop under it. */
@media (max-width: 600px) {
  #report .card:has(.mapwrap) .cardhead { flex-wrap: wrap; padding-right: 0; row-gap: 12px; }
  #report .card:has(.mapwrap) .cardhead h3 { flex: 1 0 100%; }
  #report .card:has(.mapwrap) .cardhead .capq { margin-left: 0; }
}

/* ── Share the whole report, removed ──────────────────────────────────────
   planning.js builds this button and its menu, and planning.js is generated,
   so it goes here rather than there. Hidden rather than deleted for the same
   reason: the file that makes it is regenerated from upstream, and a rule is
   the only edit that survives that.

   The per-figure download stays. Sharing a whole report is a document
   workflow — who it goes to, at what version, with what caveats — and a disc
   floating over the text was not that; taking one drawing away with you is. */
#report .repsave, #report .repsave-pop { display: none !important; }

/* ── The trial banner, on the feedback pill's line ─────────────────────────
   Centred on the screen. The 7px puts its 44px on the same centre as the
   38px pill, which is 10px off the bottom. The width cap keeps it clear of
   the pill (60px in, 117px wide) on narrow desktop windows. */
@media (min-width: 701px) {
  #eik-trial { top: auto; bottom: 7px; max-width: calc(100vw - 380px); }
}
/* A phone is too narrow for both, so the banner takes the pill's place. */
@media (max-width: 700px) {
  #fbbtn, #fbpop { display: none !important; }
  #eik-trial { bottom: 10px; }
}
/* It gives the bottom line to anything else that needs it there. */
body.eik-ej-view #eik-trial,
body:not([data-view="report"]):not([data-view="feedback"]):has(#eik-city-reload) #eik-trial { display: none !important; }

/* A locked report keeps the report page and its title; the dialog sits on the page. */
#report.report-locked .rintro .rtitle,
#report.report-locked .rintro .rlede,
#report.report-locked .rintro .eyebrow,
#report.report-locked .rintro .rstats { display: none; }
#report.report-locked .rintro { padding-bottom: 36px; }

/* ── The drawer's drag handle ─────────────────────────────────────────────
   A strip over the drawer's right edge. It shows only while the drawer is
   open, and not at all where the drawer fills the width. */
.panel-grip {
  position: fixed; top: 0; bottom: 0; left: calc(var(--panel-w) - 5px); width: 10px;
  z-index: 73; cursor: col-resize; touch-action: none;
  display: none;
}
body.dock-open .panel-grip { display: block; }
/* The drawer steps aside for these views, but the grip outranks them and would stay live over them. */
body[data-view="report"] .panel-grip,
body[data-view="feedback"] .panel-grip { display: none; }
.panel-grip::after {
  content: ""; position: absolute; top: 50%; left: 3px; width: 4px; height: 44px;
  transform: translateY(-50%); border-radius: 2px;
  background: var(--brand-line, #008cff); opacity: 0; transition: opacity .15s ease;
}
.panel-grip:hover::after, .panel-grip:focus-visible::after,
body.panel-resizing .panel-grip::after { opacity: .8; }
.panel-grip:focus-visible { outline: none; }
body.panel-resizing { user-select: none; cursor: col-resize; }
@media (max-width: 820px) { .panel-grip { display: none !important; } }

/* ── The edge of the data ──────────────────────────────────────────────── */
#eik-coverage {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  z-index: 60; box-sizing: border-box; padding: 10px 16px; border-radius: 18px;
  background: var(--brand-soft, #cdeeff); color: var(--brand, #064d90);
  box-shadow: 0 0 0 1px rgba(6, 77, 144, .18), 0 6px 20px rgba(6, 77, 144, .14);
  font: 500 13.5px/1.4 var(--font-sans, sans-serif); text-align: center;
  pointer-events: none;
}
#eik-coverage[hidden] { display: none; }
#eik-coverage span { display: block; }
#eik-coverage button {
  pointer-events: auto; margin: 4px 10px 0; padding: 0; border: 0; background: none; cursor: pointer;
  color: inherit; font: 600 13.5px/1.4 var(--font-sans, sans-serif);
  text-decoration: underline; text-underline-offset: 2px;
}
#eik-coverage button:focus-visible { outline: 2px solid var(--brand-line, #008cff); outline-offset: 2px; border-radius: 4px; }
body:has(#eik-suggest:not([hidden])) #eik-coverage { display: none; }
body[data-view="report"] #eik-coverage,
body[data-view="feedback"] #eik-coverage { display: none; }
#eik-suggest .eik-sg-ask { font-weight: 600; color: var(--brand, #008cff); cursor: pointer; }
#eik-coverage { padding-right: 38px; }
#eik-coverage .eik-cov-x {
  position: absolute; top: 6px; right: 8px; width: 24px; height: 24px; margin: 0;
  border-radius: 50%; font-size: 18px; line-height: 1; text-decoration: none; font-weight: 500;
}
#eik-coverage .eik-cov-x:hover { background: rgba(6, 77, 144, .12); }
#eik-suggest .eik-sg-note {
  text-transform: none; letter-spacing: 0; font: 500 13.5px/1.4 var(--font-sans, sans-serif);
}

/* ── The dark map's panels ───────────────────────────────────────────────
   With the Dark map, and with Time of day at night, the panels and the
   controls on the map are dark too: map.js sets data-ui on the root. The
   report and the account dialogs keep their light ground. The dark values go
   on the map's own containers only, so anything outside them stays light. */
[data-ui="dark"] [data-view="map"] :is(#map-wrap, #sitehead, #fbbtn, #fbpop, #eik-ob-card, #eik-hud) {
  color-scheme: dark;
  /* The body's colour is resolved on the body, with the light ink, and inherited as that colour. */
  color: var(--text);
  --text: #e4eef1;
  --text-body: #e4eef1cc;
  --text-muted: #e4eef19e;
  --surface: #10232b;
  --surface-grey: #1d343e;
  --surface-stone: #162b34;
  --surface-greyblue: #2c444e;
  --border: #ffffff21;
  --border-soft: #ffffff12;
  --brand: #8fd3ff;
  --brand-soft: #1b3d4c;
  /* The aliases take their value where tokens.css declares them, so they are declared again here. */
  --ink: var(--text); --dim: var(--text-body); --mute: var(--text-muted);
  --accent: var(--brand); --mint: var(--brand); --sand: var(--brand); --warn: var(--brand);
  --line: var(--border); --line-soft: var(--border-soft);
  --ground: var(--surface-stone); --raised: var(--surface-grey);
  --chat-them: #1d343e; --chat-you: #1b3d4c;
}
/* Only while the look changes, so a hover keeps its own speed. map.js (showUi) holds these transitions and moves
   them each frame to how far the map has come, so the duration only sets their scale. Grounds, lines and shadows
   mix as the map does. The text and the icons switch at six tenths, where the old ink and the new one read about
   as well on the ground; mixed half-way, the two would be the same grey. */
[data-ui-fade] [data-view="map"] :is(#map-wrap, #sitehead, #fbbtn, #fbpop, #eik-ob-card, #eik-trial, #eik-hud),
[data-ui-fade] [data-view="map"] :is(#map-wrap, #sitehead, #fbbtn, #fbpop, #eik-ob-card, #eik-trial, #eik-hud) * {
  transition-property: background-color, border-color, box-shadow, scrollbar-color, color, filter !important;
  transition-duration: 1s !important;
  transition-timing-function: linear, linear, linear, linear,
    linear(0, 0 60%, 1 60%, 1), linear(0, 0 60%, 1 60%, 1) !important;
  transition-delay: 0s !important;
}

/* Fills that were the ink colour turn to the light accent, with ink on them. */
[data-ui="dark"] [data-view="map"] :is(#map-wrap, #sitehead) :is(.eik-act:not(:disabled), .eik-label, #eik-city-reload .cr-go,
  #eik-tools button.on, #eik-chips button.on, #eik-chips #eik-chip-city.on, #eik-ej-acts .ej-act-main) {
  background: var(--brand-accent); color: var(--brand-deep);
}
[data-ui="dark"] [data-view="map"] :is(#map-wrap, #sitehead) :is(.eik-act:hover:not(:disabled), #eik-ej-acts .ej-act-main:hover) {
  background: #c4ebff; color: var(--brand-deep);
}
[data-ui="dark"] [data-view="map"] #sitehead .eik-act:disabled { background: var(--surface-grey); color: var(--text-muted); }
/* Fills in the brand colour carry ink, not white, now that the brand colour is light. Only in the map's own
   containers: the trial banner and the account dialogs keep their colours. */
[data-ui="dark"] [data-view="map"] :is(#map-wrap, #sitehead, #fbbtn, #fbpop, #eik-ob-card) :is(#chat-form button,
  #chat-key-save, #fbsend, #jump-now, #jump-8am, #scenario-note .note-cta, .eik-btn),
[data-ui="dark"] [data-view="map"] #panel[data-pane="ask"] :is(#chat-form button, #key-cta) {
  color: var(--brand-deep);
}
[data-ui="dark"] [data-view="map"] :is(#map-wrap, #sitehead, #fbbtn, #fbpop, #eik-ob-card) :is(#fbsend:hover, .eik-btn:hover) {
  background: #c4ebff;
}
/* The scenario box keeps its three states, as tints of the dark ground. */
[data-ui="dark"] [data-view="map"] #scenario-note { color: var(--text); }
[data-ui="dark"] [data-view="map"] :is(#scenario-note[data-state="present"], #scenario-note[data-state="election"]) {
  background: #1e4b5e;
}
[data-ui="dark"] [data-view="map"] #scenario-note[data-state="future"] { background: var(--brand-soft); }
[data-ui="dark"] [data-view="map"] #scenario-note[data-state="past"] { background: var(--surface-greyblue); }
[data-ui="dark"] [data-view="map"] #panel:is([data-pane="time"], [data-pane="filters"], [data-pane="ideas"]),
[data-ui="dark"] [data-view="map"] #panel:is([data-pane="time"], [data-pane="filters"], [data-pane="ideas"]) #panel-head { background-color: var(--surface-stone); }
/* The no-service note is a tint of the dark ground too, with light ink. */
[data-ui="dark"] [data-view="map"] #quiet-note { background: #4a3a1c; color: var(--text); }
[data-ui="dark"] [data-view="map"] #quiet-note .note-cta { background: #f6c887; color: #3a2105; }
/* The look button and its stage pill. */
@media (hover: hover) {
  [data-ui="dark"] [data-view="map"] #ms-theme-wrap:has(#ms-stages:not([hidden])) #ms-theme { background: var(--surface); }
  [data-ui="dark"] [data-view="map"] #ms-theme-wrap:has(#ms-stages:not([hidden])):is(:hover, :focus-within) #ms-theme {
    background: transparent;
  }
}
[data-ui="dark"] [data-view="map"] #mapstack #ms-stages button[aria-pressed="true"] {
  background: var(--brand-accent); color: var(--brand-deep);
}
[data-ui="dark"] [data-view="map"] #mapstack #ms-share[aria-expanded="true"],
[data-ui="dark"] [data-view="map"] #mapstack #ms-lens[aria-pressed="true"] {
  background: var(--brand-accent); color: var(--brand-deep);
}
/* MapLibre's zoom and compass buttons draw dark icons on white. */
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-group { background: var(--surface); }
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-group button + button { border-top-color: var(--border); }
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-group button:not(:disabled):hover { background-color: var(--surface-grey); }
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-group .maplibregl-ctrl-icon { filter: invert(.92); }
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button {
  background-color: color-mix(in srgb, var(--surface) 70%, transparent); color: var(--text);
}
[data-ui="dark"] [data-view="map"] .maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button:hover {
  background-color: var(--surface);
}

[data-ui="dark"] [data-view="map"] :is(#panel, #panel-head, #ptabs, #fbbtn, #fbpop, #fbpop select, #fbpop textarea,
  #mapstack button, #zoom-badge, #ms-speed-panel, #ms-stages, #keypop, #key-cta, .infotip, #ms-share-pop, #ms-share-pop .msp-link, #share-note, #sort-btn, #sort-menu,
  .browse-tabs button.on, .key-bulk-btn, .key-bulk-btn[disabled], .chat-eg, .chat-welcome .chat-eg,
  .chat-welcome .chat-eg:hover) {
  background-color: var(--surface);
}
[data-ui="dark"] [data-view="map"] :is(.psec[open], .psec[data-open], #readout, #brand-readout, .chat-assistant, #panel-collapse,
  .play-row button, #theme-toggle) {
  background-color: var(--surface-stone);
}
[data-ui="dark"] [data-view="map"] :is(#panel-collapse:hover, .play-row button:hover, #theme-toggle:hover,
  #mapstack button:hover, #mapstack #ms-stages button:hover, #sort-btn:hover, #sort-menu button:hover,
  #area-list li:hover, #ptabs .ptab:hover, .browse-tabs) {
  background-color: var(--surface-grey);
}
[data-ui="dark"] [data-view="map"] :is(.hbar, .slider-ctrl .ticks .tick.major) { background-color: var(--surface-greyblue); }
[data-ui="dark"] [data-view="map"] #panel::-webkit-scrollbar-thumb { background-color: var(--surface-greyblue); }
[data-ui="dark"] [data-view="map"] :is(#panel, #chat-log, #area-list, .infotip) { scrollbar-color: #3a535d transparent; }
/* The plain slider rails; the clock and the month keep their own day and season rails. */
[data-ui="dark"] [data-view="map"] input[type="range"]::-webkit-slider-runnable-track {
  background: linear-gradient(90deg, #2f7fc4, var(--brand-line, #008cff)) 0 / var(--pct) 100% no-repeat, var(--surface-greyblue);
}
[data-ui="dark"] [data-view="map"] input[type="range"]::-moz-range-track {
  background: linear-gradient(90deg, #2f7fc4, var(--brand-line, #008cff)) 0 / var(--pct) 100% no-repeat, var(--surface-greyblue);
}
/* The trial banner's pale days turn to a dark rose that keeps their ramp; its red days are dark already. */
[data-ui="dark"] [data-view="map"] #eik-trial[data-phase="calm"] {
  background: color-mix(in srgb, var(--eik-bar-bg) 22%, #3d0410); color: #fde8ec;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .35), inset 0 0 0 1px rgba(253, 232, 236, .16);
}
[data-ui="dark"] [data-view="map"] #eik-beta-pop { background: #10232b; color: #e4eef1; }
[data-ui="dark"] [data-view="map"] #eik-beta-pop a { color: #8fd3ff; }
[data-ui="dark"] [data-view="map"] .slider-ctrl .ticks .tick { background-color: var(--surface-grey); }
[data-ui="dark"] [data-view="map"] #readout .hbar:hover { background-color: #3d5862; }
[data-ui="dark"] [data-view="map"] .eik-pill { background-color: color-mix(in srgb, var(--surface) 86%, transparent); }
[data-ui="dark"] [data-view="map"] .chat-assistant::before { border-bottom-color: var(--surface-stone); }
[data-ui="dark"] [data-view="map"] :is(#panel-collapse, #theme-toggle, #mapstack button, #zoom-badge, .psec summary, .psec-head) {
  color: var(--text);
}
[data-ui="dark"] [data-view="map"] :is(.subtitle, #stats .stat span, .sub-note, #zoom-badge span,
  .slider-ctrl .ticks .tlab, .slider-ctrl label, #chat-key-clear, .chat-welcome-sub, .chat-egs-label,
  .browse-tabs button, #sort-btn, #area-list .ak, #ptabs .ptab, .info-i) {
  color: var(--text-muted);
}
[data-ui="dark"] [data-view="map"] :is(#panel-collapse, .play-row button, #theme-toggle, #street-search, #street-results,
  #chat-input, #chat-key, .key-bulk-btn, .chat-welcome .chat-eg, #keypop input) {
  border-color: var(--border);
}
[data-ui="dark"] [data-view="map"] :is(#histogram, #readout #histogram) { border-bottom-color: var(--border); }
[data-ui="dark"] [data-view="map"] .psec { border-top-color: var(--border); }
