/* ─── Theme tokens ─────────────────────────────────────────────────────── */
/* FALLBACKS, not the brand. The brand lives in the repo-root palette.json,
   which static/js/applyPalette.js fetches at startup and writes onto these same
   variables — see that file. These neutral grays are the graceful-degrade net:
   if /palette.json 404s or is malformed the app still renders, just in gray.
   Semantic colors (danger, ok) keep their real hue since the UI leans on it.

   Variable names are co2-tool's, deliberately: same names, same dark-override
   mechanism, so both apps read one palette and a brand change is one file.
   The four --app-* tokens at the bottom are the ones co2 has no equivalent for;
   they are derived from the palette rather than picked, so they follow it. */
:root {
  --brand-700: #37474f;
  --brand-600: #455a64;
  --brand-500: #546e7a;
  --brand-light: #eceff1;
  --brand-pale: #f5f7f8;

  --page: #f0f2f5;
  --surface: #ffffff;
  --line: #e0e0e0;
  --muted: #546e7a;
  --text: #333333;
  --text-soft: #555555;
  --text-faint: #666666;

  --danger: #c62828;
  --danger-bg: #fdecea;
  --ok: #2e7d32;
  --ok-bg: #e8f5e9;

  --accent-text: #0d47a1;
  --card-line: rgba(15, 40, 80, 0.1);
  --shadow-card: 0 1px 2px rgba(15, 40, 80, 0.04), 0 4px 16px rgba(15, 40, 80, 0.06);

  --chart-1: #37474f;
  --chart-2: #546e7a;
  --chart-3: #78909c;
  --chart-4: #90a4ae;
  --chart-5: #b0bec5;
  --chart-6: #cfd8dc;

  /* Same stack as co2-tool's index.css. Neither app ships the Inter webfont, so
     both currently render the first stack entry the OS has — matching by
     accident, but matching. Add the @font-face/link in both or neither. */
  --font-sans: Inter, system-ui, "Segoe UI", Roboto, sans-serif;

  /* ── App tokens ──────────────────────────────────────────────────────────
     Not in palette.json, because they are not brand decisions — each one is
     derived from a brand token, so they track it for free. */
  /* Second surface: insets and sub-headers that need to sit off --surface. */
  --surface-2: var(--brand-pale);
  /* Row/control hover. One step further from --surface than --surface-2. */
  --hover: var(--brand-light);
  /* Text and glyphs on a --brand-600 fill. White in both themes: the brand
     blues are shared between light and dark, so what sits on them is too. */
  --on-brand: #ffffff;
  /* Modal backdrop. Dark in both themes -- a light scrim over a light page
     reads as a render glitch rather than a blocked surface. */
  --scrim: rgba(15, 40, 80, 0.45);
}

/* Dark-mode fallbacks — mirrors palette.json's *_dark blocks so a theme flip
   repaints instantly (the pre-paint <script> in index.html sets the attribute)
   even before applyPalette's fetch resolves. Brand blues are shared with light
   and are NOT redeclared; neither are the --app-* tokens, which are derived. */
:root[data-theme="dark"] {
  --brand-light: #24344b;
  --brand-pale: #182234;

  --page: #0f172a;
  --surface: #1b2637;
  --line: #334155;
  --muted: #94a3b8;
  --text: #e5edf5;
  --text-soft: #c3cfdd;
  --text-faint: #9aa8ba;

  --danger: #ff6b6b;
  --danger-bg: rgba(198, 40, 40, 0.22);
  --ok: #66bb6a;
  --ok-bg: rgba(67, 160, 71, 0.18);

  --accent-text: #8ec5ff;
  --card-line: rgba(255, 255, 255, 0.1);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px rgba(0, 0, 0, 0.45);

  --chart-1: #5b9be8;
  --chart-2: #2fd39a;
  --chart-3: #f5c14b;
  --chart-4: #4cc94c;
  --chart-5: #8b7ce8;
  --chart-6: #f47272;

  --scrim: rgba(0, 0, 0, 0.7);
}

/* Match the OS/UA form-control + scrollbar rendering to the active theme. */
:root {
  color-scheme: light;
}
:root[data-theme="dark"] {
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

/* The `hidden` attribute is a UA rule at the lowest possible specificity, so any
   class that sets `display` silently defeats it. .am-item did: arming the demo
   reset showed the confirm buttons *and* the button that armed them. */
[hidden] {
  display: none !important;
}
html,
body {
  margin: 0;
  padding: 0;
  background: var(--page);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 13px;
  height: 100%;
}
body {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

button {
  font-family: inherit;
}
select {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 5px 10px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  min-width: 140px;
}
select:hover {
  border-color: var(--brand-600);
}
select:focus {
  outline: none;
  border-color: var(--brand-600);
}

/* ─── Brand header ─────────────────────────────────────────────────────── */
/* The gradient the other Boostlogix tools wear. Everything on it is white or a
   white alpha — there is no surface token that reads on a saturated brand fill,
   which is also why the controls live in the subnav below and not up here. */
/* Stacked above the maps, and not by a little. Leaflet's .leaflet-container is
   position:relative with z-index:auto, so it creates NO stacking context and its
   panes (400) and controls (800-1000) compete in the root context -- which means
   an account dropdown hanging over a map is painted under it and eats no clicks.
   Anything below 1001 here leaves part of the menu dead. The run overlay sits at
   2000 and still covers this. */
#topbar {
  position: relative;
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: linear-gradient(120deg, var(--brand-700), var(--brand-600) 60%, var(--brand-500));
  padding: 10px 20px;
  height: 56px;
  flex-shrink: 0;
}
.tb-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}
/* Height-driven with width:auto so the wide wordmark keeps its aspect. */
.tb-logo {
  height: 26px;
  width: auto;
  display: block;
  flex-shrink: 0;
}
.tb-divider {
  width: 1px;
  align-self: stretch;
  background: rgba(255, 255, 255, 0.28);
  flex-shrink: 0;
}
.tb-titles {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.tb-title {
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.tb-sub {
  color: rgba(255, 255, 255, 0.72);
  font-size: 11px;
}
.tb-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
/* ─── Account menu ─────────────────────────────────────────────────────── */
/* Port of co2-tool's AccountMenu. The trigger is the only shape that works on
   the gradient -- a translucent white pill -- and the dropdown drops off it onto
   ordinary surface colors, because a menu is a surface and not header chrome.
   Type is a step up from the 13px the dashboard runs at: this is a menu, and the
   things you click in it should not be the smallest text on the page. */
.am-wrap {
  position: relative;
}
.am-trigger {
  display: flex;
  align-items: center;
  gap: 9px;
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 999px;
  padding: 5px 12px 5px 5px;
  color: #fff;
  font-family: inherit;
  cursor: pointer;
}
.am-trigger:hover,
.am-trigger.open {
  background: rgba(255, 255, 255, 0.28);
}
.am-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  background: #fff;
  color: var(--brand-700);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}
.am-name {
  font-size: 14px;
  font-weight: 500;
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.am-chevron {
  font-size: 10px;
  opacity: 0.85;
}

.am-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 50;
  width: 272px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
  overflow: hidden;
  text-align: left;
}
.am-head {
  padding: 13px 15px;
  border-bottom: 1px solid var(--line);
}
.am-head-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}
.am-head-sub {
  font-size: 12px;
  color: var(--text-faint);
  margin-top: 2px;
  word-break: break-all;
}
.am-head-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.am-head-meta:empty {
  display: none;
}
.am-role {
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  /* --accent-text, not --brand-700: the brand navy vanishes on the dark chip. */
  background: var(--brand-light);
  color: var(--accent-text);
}
.am-company {
  font-size: 12px;
  color: var(--text-soft);
}
.am-note {
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-faint);
}

/* Theme picker: three segments in a well, the active one raised onto the
   surface. Reflects the stored preference — see js/account_menu.js. */
.am-theme {
  padding: 11px 15px 13px;
  border-bottom: 1px solid var(--line);
}
.am-theme-lbl {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 7px;
}
.am-theme-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3px;
  background: var(--brand-pale);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 3px;
}
.am-theme-opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 8px 2px;
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-soft);
}
.am-theme-opt:hover {
  color: var(--text);
}
.am-theme-opt.active {
  background: var(--surface);
  border-color: var(--line);
  color: var(--accent-text);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.am-section {
  padding: 6px;
}
.am-item {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 8px;
  padding: 10px 12px;
  cursor: pointer;
  font-family: inherit;
  font-size: 14px;
  color: var(--text);
  text-decoration: none;
}
.am-item:hover {
  background: var(--brand-pale);
}
/* Restarting throws the session away, which is destructive enough to say so. */
.am-item-danger {
  color: var(--danger);
  font-weight: 500;
}
.am-item-danger:hover {
  background: var(--danger-bg);
  color: var(--danger);
}
/* Only shown once the reset is armed, so the warning is not sitting in the menu
   as prose the other 99% of the time -- see js/account_menu.js. */
.am-confirm .am-note {
  padding: 4px 12px 8px;
}

/* ─── Subnav ───────────────────────────────────────────────────────────── */
/* Light bar under the gradient: this is where controls that need a surface of
   their own go. Same surface + hairline + soft shadow as the cards. */
#subnav {
  /* Same reason as #topbar, one layer down: its bottom shadow falls across the
     map, and the scenario picker sits here. */
  position: relative;
  z-index: 1050;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 2px 10px var(--card-line);
  padding: 8px 20px;
  flex-shrink: 0;
  /* The left slot is empty until the reveal fills it with the dataset totals, so
     hold a height that fits them and the bar does not jump when they arrive. */
  min-height: 48px;
}
.sn-left {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex-wrap: wrap;
}
/* auto margin, not space-between: the run button leaves the left slot after the
   reveal, and the picker must stay where it was rather than sliding across. */
.sn-right {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}
.sn-field {
  display: flex;
  align-items: center;
  gap: 8px;
}
.sn-lbl {
  color: var(--text-soft);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* ─── Headline ─────────────────────────────────────────────────────────── */
/* Lives inside the subnav's left slot, so it is a row of figures and not a bar
   of its own: no background, border or padding -- the subnav supplies those. */
#headline {
  display: flex;
  align-items: center;
  gap: 18px;
  font-size: 12px;
  min-width: 0;
}
.hl-item {
  display: flex;
  align-items: center;
  gap: 6px;
}
.hl-lbl {
  color: var(--text-soft);
}
.hl-val {
  font-weight: 700;
  font-size: 14px;
  color: var(--text);
}
.hl-val.good {
  color: var(--ok);
}
.hl-val.bad {
  color: var(--danger);
}
.hl-arrow {
  color: var(--text-faint);
  margin: 0 4px;
}
.hl-sep {
  width: 1px;
  height: 18px;
  background: var(--line);
}

/* ─── View container ───────────────────────────────────────────────────── */
/* Fills whatever the header, subnav and headline leave, and is the last thing
   that scrolls: overflow:hidden here is what keeps the header on screen. The
   panels inside are flex columns that give their spare height to the maps and
   tables, so "bigger" means the viewport rather than a fixed pixel count. */
.view-container {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
}
#day-map-panel,
#day-table-panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The table view's own title bar, carrying the second copy of the view switch.
   align-items:center rather than baseline, since the switch is a control and has
   no text baseline worth aligning to. */
.view-title {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  min-height: 32px;
}
.view-title .day-mode-toggle {
  margin-left: auto;
}
.view-title > span:first-child {
  font-size: 18px;
  font-weight: 700;
}
.view-sub {
  color: var(--text-soft);
  font-size: 12px;
}
.back-btn {
  margin-left: auto;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line);
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
  border-radius: 4px;
}
.back-btn:hover {
  background: var(--hover);
}

/* ─── Phase 1 layout ───────────────────────────────────────────────────── */
.p1-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  /* min-height:0 on both the grid and its panels, or the maps' content floors
     the track height and the whole column overflows instead of fitting. */
  flex: 1 1 auto;
  min-height: 0;
}
.p1-panel {
  background: var(--surface);
  border: 1px solid var(--card-line);
  box-shadow: var(--shadow-card);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}
/* A title bar, not just a title: the Kaart|Tabel switch sits on the right of it.
   The titles keep their own column so the switch centres against both lines. */
.p1-panel-header {
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
}
.p1-panel-titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.p1-panel-title {
  font-weight: 600;
  font-size: 14px;
}
.p1-panel-sub {
  color: var(--text-soft);
  font-size: 11px;
}
/* The wrap, not the map, is the panel's flex child -- growing the map itself
   does nothing while its parent still sizes to content. So the wrap takes every
   pixel the header, stats and route list do not, and the map fills the wrap. */
.p1-map-wrap {
  padding: 0;
  flex: 1 1 auto;
  min-height: 180px;
  display: flex;
}
.p1-map {
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  background: var(--page);
}
/* Five tracks for five stats. It was six, and because the last cell drops its
   divider the empty trailing track read as one very wide "Onhaalbaar" column --
   the row looked off-centre when it was simply a track short of content. */
.p1-stats {
  flex-shrink: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.p1-stat {
  padding: 12px 10px;
  border-right: 1px solid var(--line);
  /* The dividers sit on the track edges, so the label and value have to be
     centred within the track for the row to read as symmetrical. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  min-width: 0;
}
.p1-stat:last-child {
  border-right: none;
}
.p1-stat-val {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
}
.p1-stat-lbl {
  font-size: 10px;
  color: var(--text-soft);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-top: 2px;
}
.p1-stat-val.violation {
  color: var(--danger);
}
.p1-stat-val.good {
  color: var(--ok);
}

/* Capped in vh rather than px: now that the panel fills the viewport, a fixed
   140px showed five of sixteen routes on any screen. This scales with the space
   there actually is, and yields to the map when there is little of it. */
.p1-routes {
  flex: 0 1 auto;
  max-height: 24vh;
  overflow-y: auto;
  padding: 6px 0;
}
.p1-route-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 16px;
  cursor: pointer;
  border-left: 3px solid transparent;
}
.p1-route-row:hover {
  background: var(--hover);
  border-left-color: var(--brand-600);
}
.p1-route-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 2px solid var(--surface);
}
.p1-route-name {
  flex: 1;
  font-weight: 600;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.p1-route-meta {
  color: var(--text-soft);
  font-size: 11px;
}
.p1-route-row .badge-infeasible {
  background: var(--danger);
  color: var(--on-brand);
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 3px;
  text-transform: uppercase;
  font-weight: 700;
}
.p1-route-row .badge-merged {
  background: var(--chart-5);
  color: var(--on-brand);
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 3px;
  text-transform: uppercase;
  font-weight: 700;
}
.p1-route-row .badge-drivers {
  background: var(--muted);
  color: var(--on-brand);
  font-size: 9px;
  padding: 1px 5px;
  border-radius: 3px;
  text-transform: uppercase;
  font-weight: 700;
}

/* ─── Phase 1 banner ───────────────────────────────────────────────────── */
.p1-banner {
  flex-shrink: 0;
  margin-top: 16px;
  background: var(--ok-bg);
  border: 1px solid var(--ok);
  border-radius: 8px;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
}
.p1-banner-lbl {
  font-size: 13px;
  font-weight: 700;
  color: var(--ok);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.p1-banner-item {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.p1-banner-val {
  font-size: 22px;
  font-weight: 700;
  color: var(--ok);
}
.p1-banner-unit {
  font-size: 12px;
  color: var(--text-soft);
}
.p1-banner-before {
  font-size: 18px;
  font-weight: 600;
  color: var(--text-soft);
  text-decoration: line-through;
}
.p1-banner-arrow {
  font-size: 14px;
  color: var(--text-soft);
}
.p1-banner-delta {
  font-size: 12px;
  font-weight: 600;
  color: var(--ok);
}

/* ─── Phase 1 focus bar ────────────────────────────────────────────────── */
.p1-focus-bar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--brand-pale);
  border: 1px solid var(--brand-600);
  border-radius: 6px;
  padding: 8px 14px;
  margin-bottom: 12px;
  font-size: 12px;
}
.p1-focus-lbl {
  color: var(--text-soft);
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.6px;
}
.p1-focus-name {
  font-weight: 700;
  color: var(--brand-600);
  font-size: 13px;
}
.p1-focus-clear {
  background: transparent;
  border: 1px solid var(--brand-600);
  color: var(--brand-600);
  padding: 4px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
}
.p1-focus-clear {
  margin-left: auto;
}
.p1-focus-clear:hover {
  background: var(--brand-600);
  color: var(--on-brand);
}

/* ─── Phase 2 layout ───────────────────────────────────────────────────── */
.p2-grid {
  display: grid;
  /* Three tracks for three children. The trailing 1fr was left over from a
     fourth column that no longer exists, and now that the grid fills the
     viewport it was a dead strip down the right-hand side. */
  grid-template-columns: 1fr 110px 1fr;
  gap: 12px;
  /* Sized by content, not by the viewport: five rows of figures stretched down
     an 800px column left most of each card empty. stretch is still right, so the
     two cards and the stripe between them share the tallest one's height, and
     the leftover space simply falls below the grid. */
  align-items: stretch;
  flex: 0 0 auto;
}
.p2-col {
  background: var(--surface);
  border: 1px solid var(--card-line);
  box-shadow: var(--shadow-card);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}
.p2-col-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}
.p2-col-num {
  width: 22px;
  height: 22px;
  background: var(--brand-600);
  color: var(--on-brand);
  font-weight: 700;
  font-size: 11px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.p2-col-title {
  font-weight: 600;
  font-size: 13px;
}
.p2-col-sub {
  color: var(--text-faint);
  font-size: 10px;
  margin-left: auto;
}

.p2-stats {
  display: flex;
  flex-direction: column;
}
.p2-stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 9px 14px;
  border-bottom: 1px solid var(--line);
}
.p2-stat-key {
  color: var(--text-soft);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.p2-stat-val {
  font-size: 14px;
  font-weight: 700;
}

.p2-stat-val.violation {
  color: var(--danger);
}

.p2-routes-wrap {
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: auto;
}
.p2-routes-label {
  color: var(--text-soft);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.p2-routes-select {
  width: 100%;
}

/* Saving stripe between cols 2 and 3 */
.p2-saving-stripe {
  background: var(--ok-bg);
  border: 1px solid var(--ok);
  border-radius: 8px;
  padding: 12px 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  min-height: 0;
  font-size: 11px;
  text-align: center;
  overflow: hidden;
}
.p2-stripe-arrow {
  color: var(--ok);
  font-size: 20px;
  font-weight: 700;
}
.p2-stripe-lbl {
  color: var(--ok);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  text-align: center;
  line-height: 1.3;
}
.p2-stripe-stats {
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
  margin-top: 12px;
  width: 100%;
}
.p2-stripe-stats > div {
  width: 100%;
}
.p2-stripe-stat-val {
  font-size: 13px;
  font-weight: 700;
  color: var(--ok);
  word-break: break-all;
}
.p2-stripe-stat-lbl {
  font-size: 9px;
  color: var(--text-soft);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

/* ─── Day mode toggle (Map | Table) ──────────────────────────────────────── */
.day-mode-toggle {
  display: inline-flex;
  flex-shrink: 0;
}
.dm-btn {
  background: var(--surface-2);
  color: var(--text-soft);
  border: 1px solid var(--line);
  padding: 5px 18px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.dm-btn:first-child {
  border-radius: 4px 0 0 4px;
}
.dm-btn:last-child {
  border-radius: 0 4px 4px 0;
  border-left: none;
}
.dm-btn:hover {
  background: var(--hover);
  color: var(--text);
}
.dm-btn.active {
  background: var(--brand-600);
  color: var(--on-brand);
  border-color: var(--brand-600);
}

/* ─── Demo reveal gate ───────────────────────────────────────────────────── */
/* One class on <body>, set from app.js. Everything the optimizer "produced"
   hangs off it — and until the run happens the backend has not sent those
   numbers at all, so this hides empty elements, not withheld ones. */
body:not(.computed) .p1-grid {
  grid-template-columns: 1fr;
}
body:not(.computed) .p1-panel-opt,
body:not(.computed) #p1-banner,
body:not(.computed) #headline,
body:not(.computed) .day-mode-toggle {
  display: none;
}
body.computed #simulate-btn {
  display: none;
}
/* No map behind the summed all-scenarios totals -- see applyMode in js/app.js. */
body.all-scenarios .day-mode-toggle {
  display: none;
}

/* Sits on the brand gradient in #topbar, so it copies co2-tool's navBtn
   (dashboard/dashboardStyles.js) exactly: translucent white, 10px radius, 13px
   600. Nothing here may use a theme token -- the gradient is brand blue in both
   themes, so --brand-pale and friends flip out from under it and a hover turns
   black in dark mode. Fixed rgba only. */
#simulate-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 10px;
  color: #fff;
  padding: 7px 14px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  transition:
    background 0.15s ease,
    border-color 0.15s ease;
}
#simulate-btn:hover {
  background: rgba(255, 255, 255, 0.26);
}
#simulate-btn:active {
  transform: translateY(1px);
}

/* Optical centring for text on the gradient chrome. Flex centring aligns the
   line box, and the line box is not symmetric around the glyphs: Inter's em box
   is ~14 up / ~3 down at 13px, which lands the ink 2px below centre (the button
   measured 12.5px above the caps and 8.5px below the baseline). Trimming to
   cap-height/alphabetic makes the box the ink, so centring centres what you see.
   The button's padding grows to compensate for the shorter box and hold the same
   33px height. Guarded together because they only make sense as a pair: without
   text-box support the taller line box plus the bigger padding would inflate the
   button. Firefox has no text-box yet and keeps the 2px offset. */
@supports (text-box-trim: trim-both) {
  #simulate-btn {
    padding: 11px 14px;
  }
  #simulate-btn .btn-label {
    text-box: trim-both cap alphabetic;
  }
}
/* Not .am-avatar: its initials are an anonymous flex item, which text-box does
   not reach -- same reason the button needed .btn-label. One caps-only glyph in
   a 28px circle lands 1px low, which is not worth a wrapper span. */
.am-name {
  text-box: trim-both cap alphabetic;
  /* The trimmed box ends at the baseline and the cap line, but .am-name also
     carries overflow:hidden for its ellipsis, and overflow clips at the padding
     edge -- so descenders (the g in "Demo-gebruiker") and capital diacritics got
     cut. Padding puts the clip edge back outside the ink; the matching negative
     margin takes it straight back out of the layout, so the flex centring still
     sees the trimmed box and the optical alignment is unchanged. */
  padding-block: 3px 4px;
  margin-block: -3px -4px;
}

/* ─── Fake-run overlay ───────────────────────────────────────────────────── */
.compute-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: var(--scrim);
  display: flex;
  align-items: center;
  justify-content: center;
}
.co-box {
  width: 440px;
  max-width: 90vw;
  background: var(--surface);
  border: 1px solid var(--card-line);
  box-shadow: var(--shadow-card);
  border-radius: 8px;
  padding: 24px;
  text-align: center;
}
.co-spinner {
  width: 28px;
  height: 28px;
  margin: 0 auto 14px;
  border: 3px solid var(--line);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: co-spin 0.9s linear infinite;
}
@keyframes co-spin {
  to {
    transform: rotate(360deg);
  }
}
.co-title {
  font-size: 14px;
  font-weight: 700;
}
.co-stage {
  margin-top: 6px;
  font-size: 12px;
  color: var(--text-soft);
  min-height: 16px;
}
.co-bar {
  margin: 14px 0 10px;
  height: 5px;
  border-radius: 3px;
  background: var(--surface-2);
  overflow: hidden;
}
.co-bar-fill {
  width: 0;
  height: 100%;
  background: var(--brand-600);
  transition: width 0.4s linear;
}
/* Scrolls itself to the bottom as stages land (see app.js). The scrollbar is
   hidden rather than the overflow: the box has to keep scrolling, it just should
   not sprout a track two thirds of the way through the run. */
.co-log {
  max-height: 96px;
  overflow-y: auto;
  scrollbar-width: none;
  text-align: left;
  font-size: 10px;
  line-height: 1.6;
  color: var(--text-faint);
}
.co-log::-webkit-scrollbar {
  display: none;
}

/* ─── Leaflet chrome ─────────────────────────────────────────────────────── */
/* Leaflet ships its own white popups, controls and attribution strip, which
   read as holes in the dark theme. Retint them from the palette; the tiles
   themselves are raster and are swapped in JS instead (see js/map.js). */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-card);
}
.leaflet-popup-content {
  font-size: 12px;
}
.leaflet-popup-content b {
  color: var(--text);
}
.leaflet-container a.leaflet-popup-close-button {
  color: var(--text-faint);
}
.leaflet-container a.leaflet-popup-close-button:hover {
  color: var(--text);
}

.leaflet-bar a,
.leaflet-bar a:hover {
  background: var(--surface);
  color: var(--text);
  border-bottom-color: var(--line);
}
.leaflet-bar a:hover {
  background: var(--hover);
}
/* Leaflet's own .leaflet-bar a.leaflet-disabled is two classes deep, so it beats
   the rule above and leaves a light-gray button sitting on the dark theme. */
.leaflet-bar a.leaflet-disabled,
.leaflet-bar a.leaflet-disabled:hover {
  background: var(--surface-2);
  color: var(--text-faint);
}

/* Kept legible without shouting -- attribution is a requirement, not content. */
.leaflet-control-attribution {
  background: var(--surface) !important;
  color: var(--text-faint);
  font-size: 10px;
}
.leaflet-control-attribution a {
  color: var(--text-soft);
}
