/* ---------- Typeface ----------
   Public Sans (USWDS / GSA, SIL OFL — see fonts/LICENSE-public-sans.txt): the federal
   government's own interface face, fitting for a NIST Handbook 135 / FEMP-style LCC tool.
   Vendored (latin subset, variable 400–800) rather than linked from Google Fonts so the PWA
   and the MV3 extension build keep working offline. Characters outside the subset (arrows,
   math symbols) fall back to the system stack in --font. */
@font-face {
  font-family: "Public Sans"; font-style: normal; font-weight: 400 800; font-display: swap;
  src: url("../fonts/public-sans-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans"; font-style: italic; font-weight: 400 800; font-display: swap;
  src: url("../fonts/public-sans-latin-italic.woff2") format("woff2");
}

/* ---------- Design tokens — "Ledger" ----------
   Visual identity borrowed from the analyst's columnar pad: pale green-grey paper, grey-green
   rules, blue-ink actions, red reserved for negatives/failures. The one signature device is
   the accountant's double rule — under the masthead (a ledger page heading) and under result
   totals (.metric-value) — so it always means "heading" or "total", never decoration.
   Two accent roles are kept from the original "Clinical Modern" system (DESIGN_SYSTEM.md §2):
   --accent for text/links/focus rings/active-tab, --accent-contrast for filled buttons under
   white text. Contrast ratios below were computed with the WCAG relative-luminance formula. */
:root {
  /* Tells the browser which palette to use for native form chrome it renders itself —
     the date-input calendar popup, its picker-indicator icon, scrollbars, etc. — none of
     which are otherwise reachable with CSS. Without this, a date picker opened in dark mode
     renders with the OS's default light chrome regardless of the app's own theme. */
  color-scheme: light;
  --bg: #e9eee6;                  /* ledger paper */
  --surface: #fbfcf9;             /* sheet */
  --surface-2: #f2f5ef;
  --fg: #17241e;                  /* ink — 15.6:1 on --surface */
  --muted: #526058;               /* pencil — 6.43:1 on --surface, 5.62:1 on --bg */
  --border: #c9d4c4;              /* ledger rule (decorative) */
  /* 3.51:1 against --surface — meets WCAG 1.4.11 Non-text Contrast (3:1) for
     input/select/button boundaries, which otherwise have no other visual edge when their
     background matches the surrounding card's background exactly. */
  --border-strong: #7c8a81;
  --accent: #1d4f91;              /* blue ink — 7.91:1 on --surface */
  --accent-hover: #163d70;
  --accent-contrast: #1d4f91;     /* button fills — 8.14:1 under white text */
  --accent-contrast-hover: #163d70;
  --accent-fg: #ffffff;
  --accent-soft: rgba(29, 79, 145, 0.10);
  --rule-double: #17241e;         /* the double rule (masthead, totals) */
  --pass: #15803d;
  --pass-bg: #e3f3e6;
  --fail: #c62828;                /* ledger red — 5.46:1 on --surface */
  --fail-bg: #fbe9e7;
  --warn: #a35a00;                /* 5.07:1 on --surface (the old #d97706 was ~3:1) */
  --warn-bg: #fdf0dc;
  /* Escalation-rate mode color coding (Fixed/Inflation/Variable) — matches
     gui/escalation_tab.py's spinbox stylesheet colors exactly (not just "loosely carried
     over" — these are the literal hex values from _update_row_style there), as a text color
     + left-edge accent rather than a filled background: a background wipes out the select's
     chevron (drawn as its own background-image, see "Dropdowns" below) and made the control
     unreadable as a dropdown. */
  --esc-fixed: #104c4d;
  --esc-inflation: #e65100;
  --esc-variable: #2e7d32;
  --type-onetime: #3949ab;
  --type-recurring: #ad1457;
  /* Paper doesn't float: resting controls get a hairline bottom edge, not a drop shadow.
     Only things that genuinely sit above the page (menus, dialogs, pickers) use --shadow-md. */
  --shadow: 0 1px 0 rgba(23, 36, 30, 0.06);
  --shadow-md: 0 6px 18px rgba(23, 36, 30, 0.14), 0 1px 3px rgba(23, 36, 30, 0.08);
  --radius: 5px;
  --radius-lg: 7px;
  --font: "Public Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Standard width for a single value/select field in a settings form (General/Escalation/
     GHG tabs) — one number to tune instead of a different ad-hoc width per row. */
  --field-w: 150px;
}

/* Dark: the same ledger read by lamplight — green-black paper, pale blue ink. Keep this block,
   the OS-preference block below, and :root[data-theme="dark"] in sync manually. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #0f1713;
    --surface: #16211c;
    --surface-2: #1c2923;
    --fg: #e3eae4;
    --muted: #9aa9a0;               /* 6.75:1 on --surface */
    --border: #2a3831;
    --border-strong: #65756b;       /* 3.4:1 against --surface — see light-theme note above */
    --accent: #8db6f0;              /* 7.95:1 on --surface as text */
    --accent-hover: #b0cdf5;
    --accent-contrast: #2e62ac;     /* 6.07:1 under white text — the pale --accent can't hold white text */
    --accent-contrast-hover: #25528f;
    --accent-fg: #ffffff;
    --accent-soft: rgba(141, 182, 240, 0.14);
    --rule-double: #9aa9a0;
    --pass: #4ade80;
    --pass-bg: #12301f;
    --fail: #f87171;
    --fail-bg: #3a1a1a;
    --warn: #fdba74;
    --warn-bg: #3a2410;
    --esc-fixed: #5eead4;
    --esc-inflation: #fdba74;
    --esc-variable: #86efac;
    --type-onetime: #8c9eff;
    --type-recurring: #f48fb1;
    --shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
    --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
  }
}
/* An explicit toggle always wins over the OS preference. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f1713; --surface: #16211c; --surface-2: #1c2923; --fg: #e3eae4; --muted: #9aa9a0;
  --border: #2a3831; --border-strong: #65756b; --accent: #8db6f0; --accent-hover: #b0cdf5;
  --accent-contrast: #2e62ac; --accent-contrast-hover: #25528f;
  --accent-fg: #ffffff; --accent-soft: rgba(141, 182, 240, 0.14); --rule-double: #9aa9a0;
  --pass: #4ade80; --pass-bg: #12301f;
  --fail: #f87171; --fail-bg: #3a1a1a;
  --warn: #fdba74; --warn-bg: #3a2410;
  --esc-fixed: #5eead4; --esc-inflation: #fdba74; --esc-variable: #86efac;
  --type-onetime: #8c9eff; --type-recurring: #f48fb1;
  --shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
}

/* ---------- Accessibility utilities ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -40px; z-index: 100;
  background: var(--accent-contrast); color: var(--accent-fg);
  padding: 8px 14px; border-radius: 6px; font-weight: 600; font-size: 12.5px;
  transition: top 0.12s ease;
}
.skip-link:focus { top: 8px; }

/* ---------- Prompt dialog (replaces window.prompt()) ---------- */
.modal-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(23, 36, 30, 0.45);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
}
.modal-dialog {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); padding: 18px; width: 100%; max-width: 360px;
}
.modal-title { font-weight: 700; font-size: 13.5px; margin-bottom: 12px; color: var(--fg); }
.modal-label { display: block; font-size: 11.5px; color: var(--muted); margin-bottom: 6px; }
.modal-input { width: 100%; margin-bottom: 16px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* ---------- Export Detail picker (see showExportDetailDialog in export-excel.js) ---------- */
.export-detail-dialog { max-width: 480px; max-height: 85vh; display: flex; flex-direction: column; }
.export-detail-groups { overflow-y: auto; flex: 1; min-height: 0; margin: 10px 0; border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.export-detail-group { margin-bottom: 12px; }
.export-detail-group:last-child { margin-bottom: 0; }
.export-detail-group-title { font-size: 12px; font-weight: 700; color: var(--muted); margin-bottom: 6px; }
.export-detail-group .checklist-item { display: flex; padding: 3px 0; }

/* ---------- NIST 135 reference help dialog (see help.js) ---------- */
.help-dialog { max-width: 720px; max-height: 85vh; display: flex; flex-direction: column; }
.help-citation { color: var(--muted); font-style: italic; font-size: 11.5px; margin: 0 0 10px; }
.help-privacy { font-size: 12px; margin: 0 0 12px; padding: 8px 10px; border-left: 3px solid var(--accent); background: var(--surface-2); border-radius: 0 var(--radius) var(--radius) 0; }
.help-dialog .subtabs { margin-bottom: 10px; flex-shrink: 0; }
.help-body { overflow-y: auto; flex: 1; min-height: 0; padding-right: 6px; font-size: 12.5px; line-height: 1.55; }
.help-body h3 { color: var(--accent); font-size: 14px; margin: 4px 0 10px; }
.help-body h4 { color: var(--fg); font-size: 12.5px; margin: 14px 0 4px; }
.help-body h4:first-of-type { margin-top: 0; }
.help-body p { margin: 4px 0; }
.help-body ul, .help-body ol { margin: 4px 0 8px 20px; padding: 0; }
.help-body pre {
  background: var(--surface-2); border: 1px solid var(--border); padding: 8px 10px;
  border-radius: var(--radius); font-family: var(--mono); font-size: 12px; margin: 6px 0; overflow-x: auto;
}
.help-body table.help-glossary { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 12px; }
.help-body table.help-glossary td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
.help-body table.help-glossary td:first-child { font-weight: 700; width: 90px; }
.help-body table.help-glossary tr:nth-child(even) { background: var(--surface-2); }

/* ---------- Base ---------- */
* { box-sizing: border-box; }
/* overflow:hidden here (not just on #app below) is the part that actually stops the
   page-level scrollbar: the UA coerces html's effective overflow to auto whenever its
   computed value is literally "visible", regardless of whether anything visually escapes a
   clipped descendant — so #app's own overflow:hidden alone left html measuring a full,
   unclamped content height and showing its own native scrollbar right next to #content's,
   even though nothing was actually rendered outside the viewport. Setting overflow:hidden
   explicitly here is respected as-is (no coercion), which removes that second scrollbar
   outright. */
html, body { height: 100%; overflow: hidden; }
body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--fg);
  font-size: 13px;
  line-height: 1.45;
  font-variant-numeric: tabular-nums; /* every figure in a cost tool should line up in columns */
  -webkit-font-smoothing: antialiased;
}
/* overflow:hidden is load-bearing, not decorative: html's effective overflow is coerced to
   auto by the UA regardless of its own overflow value (the "root element scrolls the
   viewport" rule), so without this, any content that slips past #content's own
   overflow-y:auto (even by a sub-pixel amount) silently promotes to a second, page-level
   scrollbar spanning the full unclamped content height — exactly the "double vertical
   scrollbar, extends past the cards" symptom this fixes. #sidebar/#content are the only
   two regions meant to scroll; this guarantees nothing else ever can. */
#app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

a { color: var(--accent); }

input, select, textarea, button {
  font-family: inherit;
  font-size: 12px;
  background: var(--surface);
  color: var(--fg);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 5px 8px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease;
}
input:hover, select:hover, textarea:hover { border-color: var(--muted); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
}
input:disabled, textarea:disabled { opacity: 0.55; cursor: not-allowed; }
input[type="checkbox"], input[type="radio"] { width: auto; padding: 0; accent-color: var(--accent); cursor: pointer; }

/* ---------- Dropdowns ---------- */
/* Native <select> styled to match the rest of the input system: flat chrome removed,
   a custom chevron drawn in CSS so it recolors with the theme instead of using the
   OS-native arrow, which stays grey and looks out of place on a dark theme.

   .combo-input is the same chevron applied to a plain <input list="..."> (backed by a
   <datalist>) — used where the field must accept free text with suggestions rather than
   a closed set of choices (e.g. ECM Category, which can be a brand-new ad-hoc value; the
   Assembly Grid's paste-friendly cells). It's a real text input, so appearance/cursor are
   left alone — only the chevron affordance is shared, so it *reads* as the same kind of
   control as every other dropdown in the app instead of looking like a stray text box. */
select, .combo-input {
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 15px) center, calc(100% - 10px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select { appearance: none; -webkit-appearance: none; cursor: pointer; }
select:hover, .combo-input:hover { background-image: linear-gradient(45deg, transparent 50%, var(--fg) 50%), linear-gradient(135deg, var(--fg) 50%, transparent 50%); }
select:focus, .combo-input:focus { background-image: linear-gradient(45deg, transparent 50%, var(--accent) 50%), linear-gradient(135deg, var(--accent) 50%, transparent 50%); }
select:disabled { cursor: not-allowed; }
select::-ms-expand { display: none; }
/* Forced-colors mode (Windows High Contrast) strips background-image by default, which
   would leave `appearance: none` selects (and .combo-input) with no visible open-affordance. */
@media (forced-colors: active) {
  select, .combo-input { appearance: auto; background-image: none; padding-right: 8px; }
}

button {
  cursor: pointer;
  background: var(--surface);
  font-weight: 500;
  box-shadow: var(--shadow);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  line-height: 1;
}
button:hover { border-color: var(--accent); color: var(--accent); }
button:active { transform: translateY(1px); }
button:disabled { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }
button:disabled:hover { border-color: var(--border-strong); color: var(--fg); }
button svg { flex-shrink: 0; }
/* Keyboard focus is always visible on every button (row actions, tabs, menu items), drawn
   outside the control so it never collides with a filled or borderless style. */
button:focus-visible, summary:focus-visible, .sidebar-item:focus-visible, .nav-section-header:focus-visible, .nav-category-header:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
button.primary {
  background: var(--accent-contrast); color: var(--accent-fg); border-color: var(--accent-contrast);
  font-weight: 650; box-shadow: none;
}
button.primary:hover { background: var(--accent-contrast-hover); border-color: var(--accent-contrast-hover); color: var(--accent-fg); }

/* Icon-only square buttons used for row-level actions (duplicate/delete/move/rename). */
button.btn-icon {
  padding: 5px;
  width: 26px;
  height: 26px;
  justify-content: center;
  box-shadow: none;
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}
button.btn-icon:hover { background: var(--surface-2); border-color: var(--border); color: var(--accent); }
button.btn-icon.danger:hover { background: var(--fail-bg); border-color: transparent; color: var(--fail); }

/* ---------- Header ---------- */
#app-header {
  /* Needs BOTH an explicit `position` (z-index on a merely-static flex item wasn't reliably
     honored) AND a z-index higher than anything that can appear inside the scrollable
     content area. Neither #app-body nor #content nor #app itself establishes a stacking
     context, so a nested sticky bar's z-index (e.g. .subtabs at 3) is compared directly
     against this element's at the document root, regardless of DOM nesting depth — with the
     old z-index:2 this lost to .subtabs and the File/Export dropdown menus (which overflow
     below the header into that space) got painted over by it. 20 clears every in-content
     z-index used elsewhere (.subtabs/.chart-controls/.run-action-bar top out at 3) while
     staying well under .modal-overlay's 1000 and .skip-link:focus's 100. */
  position: relative;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  z-index: 20;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 5px;
  background: var(--accent-contrast);
  color: #fff; font-weight: 800; font-size: 11px;
  flex-shrink: 0;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
#app-header h1 { font-size: 21px; margin: 0; font-weight: 800; letter-spacing: -0.015em; line-height: 1.05; }
.brand-tagline { color: var(--muted); font-size: 11.5px; font-weight: 500; }
.proj-name { color: var(--muted); font-size: 11.5px; display: flex; align-items: center; gap: 5px; margin-top: 1px; }
.dirty-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: inline-block; }

.header-actions { display: flex; gap: 10px; align-items: center; margin-left: auto; flex-wrap: wrap; }
/* Header dropdowns (File, Export — see toggleDropdownMenu in app.js) */
.dropdown { position: relative; display: inline-block; }
.dropdown-menu {
  display: none; flex-direction: column; gap: 2px;
  position: absolute; top: 100%; left: 0; margin-top: 4px; min-width: 170px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: 4px; z-index: 50;
}
.dropdown-menu.open { display: flex; }
.dropdown-menu button {
  justify-content: flex-start; width: 100%; text-align: left;
  border: none; box-shadow: none; background: transparent; border-radius: 4px;
}
.dropdown-menu button:hover { background: var(--surface-2); }

/* Prominent, clickable warning chip (not a subtle text hint) — matches the desktop app's
   own stale-results label, which is deliberately loud (bold, colored background) since a
   parameter change (e.g. zeroing funding %) leaves the numbers on screen unchanged until
   this is clicked, and a missed recalc easily reads as "the app didn't apply my change". */
.stale-badge {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--warn-bg); color: var(--warn); font-size: 11.5px; font-weight: 700;
  padding: 6px 12px; border-radius: 4px; border: 1px solid currentColor; box-shadow: none;
}
.stale-badge:hover { background: var(--warn-bg); color: var(--warn); filter: brightness(0.95); }
.stale-badge svg { flex-shrink: 0; }

/* Single button that cycles auto -> light -> dark -> auto (see cycleTheme in app.js),
   showing the icon for the theme it's currently on. */
.theme-btn {
  border: 1px solid var(--border); box-shadow: none; background: var(--surface-2); border-radius: 4px;
  padding: 4px 8px; font-size: 13px; line-height: 1;
  min-width: 24px; min-height: 24px; justify-content: center; /* WCAG 2.2 SC 2.5.8 minimum */
}
.theme-btn:hover { color: inherit; background: var(--surface); }

.selftest {
  font-size: 10.5px;
  padding: 3px 8px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--muted);
  flex-basis: 100%;
  font-family: var(--mono);
}
.selftest.pass { color: var(--pass); background: var(--pass-bg); }
.selftest.fail { color: var(--fail); background: var(--fail-bg); font-weight: 600; }

/* ---------- Top tabs ---------- */
/* The masthead (header + top tabs) closes with the ledger's double rule — the one place,
   besides result totals, where that device appears. The active tab's ink bar sits just above it. */
#top-tabs {
  display: flex;
  border-bottom: 3px double var(--rule-double);
  padding: 0 20px;
  background: var(--surface);
  gap: 4px;
}
.tab-btn {
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  padding: 11px 14px 9px;
  border-bottom-width: 3px;
  font-size: 13px;
  color: var(--muted);
  font-weight: 550;
}
.tab-btn:hover { color: var(--fg); border-bottom-color: var(--border-strong); }
.tab-btn.active { border-bottom-color: var(--accent); font-weight: 700; color: var(--fg); }

/* ---------- Layout ---------- */
#app-body { display: flex; flex: 1; min-height: 0; }
#sidebar {
  width: 260px;
  border-right: 1px solid var(--border);
  overflow-y: auto;
  flex-shrink: 0;
  background: var(--surface);
}
/* Sidebar header now just holds the nav-tree filter box (see .nav-filter below) — the
   ECMs/Bundles/Pathways lists it used to switch between are merged into one always-visible
   tree (.nav-tree), mirroring gui/ecm_list_panel.py's single QTreeWidget. */
.sidebar-header {
  padding: 10px 10px 8px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.nav-filter { width: 100%; font-size: 12px; padding: 6px 10px; }
.sidebar-list { list-style: none; margin: 0; padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.nav-section-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 8px 4px; margin-top: 4px; cursor: pointer; border-radius: 6px;
  font-size: 12.5px; font-weight: 700; color: var(--fg);
}
.nav-section-header:hover { color: var(--fg); background: var(--surface-2); }
.nav-section-header:first-child { margin-top: 0; }
.nav-section-label { display: flex; align-items: center; gap: 5px; }
.nav-section-label svg { flex-shrink: 0; }
.nav-category-header {
  display: flex; align-items: center; gap: 5px;
  padding: 5px 10px 3px 14px; margin-top: 2px; cursor: pointer; border-radius: 6px;
  font-size: 11.5px; font-weight: 600; color: var(--muted);
}
.nav-category-header:hover { color: var(--fg); background: var(--surface-2); }
.nav-category-header svg { flex-shrink: 0; }
.sidebar-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 7px 10px; cursor: pointer; border-radius: 0 4px 4px 0;
  border-left: 3px solid transparent;
}
.sidebar-item.nav-nested { padding-left: 22px; }
.sidebar-item:hover { background: var(--surface-2); }
.sidebar-item.selected { background: var(--accent-soft); border-left-color: var(--accent); font-weight: 600; }
.sidebar-item.baseline { color: var(--accent); }
.item-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; }
.item-actions { display: flex; gap: 2px; flex-shrink: 0; }
/* 24x24 is the WCAG 2.2 SC 2.5.8 minimum target size; keep sidebar row actions at least there. */
.item-actions button.btn-icon { width: 24px; height: 24px; }
.item-actions button:hover { background: var(--surface); }

/* No page carries its own top padding — every .content view (entity pages, Global
   Parameters, Optimization, Monte Carlo) has a .subtabs bar, and .subtabs owns that space
   instead (see below) so the sticky bar's opaque background fully covers the scrollport's
   top edge and scrolled content can never peek out above/behind it. (.full only affects
   width/sidebar visibility elsewhere, not padding, so it needs no separate padding rule.) */
.content { flex: 1; min-width: 0; overflow-y: auto; padding: 0 20px 18px; }

/* Sticky so the subtab bar (Edit/Results/Compare/…) stays put while its body scrolls
   underneath — before this it scrolled away with the content, so switching subtabs on a
   long page meant scrolling back to the top first. */
.subtabs {
  display: flex; gap: 2px; margin-bottom: 14px; padding-top: 18px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 3; background: var(--bg);
}
/* "What am I working on" strip above the subtab bar (see renderBodyInner in app.js) — the
   entity-type glyph + type + item id/title stay visible regardless of scroll position or
   which subtab (Edit/Results/Compare/Charts) is active. */
.context-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 10px 2px 0; font-size: 12px; color: var(--muted);
  position: sticky; top: 0; z-index: 3; background: var(--bg);
}
.context-bar svg { flex-shrink: 0; }
.context-type { font-weight: 700; font-size: 12px; }
.context-sep { color: var(--border-strong); }
.context-title { color: var(--fg); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.subtab-btn {
  border: none; border-bottom: 2px solid transparent; border-radius: 4px 4px 0 0;
  background: transparent; box-shadow: none; padding: 7px 14px; color: var(--muted); font-weight: 550;
}
.subtab-btn:hover { color: var(--fg); background: var(--surface-2); }
.subtab-btn.active { border-bottom-color: var(--accent); font-weight: 700; color: var(--fg); }

/* ---------- Content primitives ---------- */
/* Collapsible section cards, built on native <details>/<summary> (see app.js `card()`).
   Using <details> means expand/collapse needs no JS re-render — the browser handles it —
   we only persist the open/closed flag in STATE.collapsed so it survives the next render. */
details.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 14px;
  background: var(--surface);
  overflow: hidden;
}
details.card > summary {
  list-style: none;
  cursor: pointer;
  padding: 11px 14px;
  font-weight: 700;
  color: var(--fg);
  font-size: 13.5px;
  letter-spacing: -0.005em;
  display: flex;
  align-items: center;
  gap: 8px;
  user-select: none;
}
details.card > summary::-webkit-details-marker { display: none; }
details.card > summary::before {
  content: "▸";
  display: inline-block;
  font-size: 13px;
  color: var(--muted);
  transition: transform 0.15s ease;
  flex-shrink: 0;
}
details.card[open] > summary::before { transform: rotate(90deg); }
details.card > summary:hover { color: var(--accent); }
details.card > summary:hover::before { color: var(--accent); }
details.card > .card-body { padding: 2px 14px 14px; }
details.card[open] > summary { border-bottom: 1px solid var(--border); }

/* A lone .field-row stacks label+input full-width, which is correct for a single
   setting but wastes most of a wide card when several unrelated settings are stacked
   one-per-line underneath each other. .field-grid flows its .field-row children into
   as many columns as fit, so a card full of short settings reads as a compact grid
   instead of a hallway of near-empty rows. */
.field-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 4px 24px; }
.field-grid .field-row { margin-bottom: 0; }
/* Capped, not just flex:1 — otherwise the same field renders at a different width in
   every card depending on how many grid columns happen to fit, which is what made inputs
   look inconsistently sized across the General/Escalation/GHG tabs. .grow (e.g. the
   project Name field) opts back out where a fixed width would be actively cramped. */
.field-grid .field-row input, .field-grid .field-row select { min-width: 0; flex: 1; max-width: var(--field-w); }
.field-grid .field-row.grow input, .field-grid .field-row.grow select { max-width: none; }
.field-row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.field-row label { min-width: 110px; color: var(--muted); flex-shrink: 0; }
/* Rows with two controls (a mode <select> plus its value <input>, e.g. Secondary
   Escalation Rates) need more room than one .field-grid column can spare — .field-list
   stacks them full-width instead, at a fixed, consistent control width per row. */
.field-list { display: flex; flex-direction: column; gap: 8px; }
.field-list .field-row select { width: 108px; flex: 0 0 auto; }
.field-list .field-row input { width: var(--field-w); flex: 0 0 auto; }
/* .grow (Project Name) opts out of the fixed control width above and stretches to fill the
   row instead — a free-text name benefits from the room, unlike a date or a rate value. */
.field-list .field-row.grow input { width: auto; flex: 1 1 auto; }
.field-row-inline { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 8px; }
.field-row-inline label { display: flex; flex-direction: column; font-size: 11.5px; color: var(--muted); gap: 3px; font-weight: 600; }
.field-row-inline input, .field-row-inline select { font-weight: 400; text-transform: none; width: var(--field-w); }
.field-row-inline label.grow { flex: 1 1 260px; }
.field-row-inline label.grow input, .field-row-inline label.grow select { width: 100%; }
/* Generic "this optional feature is on" toggle row (Enable Carbon Tax, Enable Funding, ...)
   — a soft accent background strip when active, not a word, so it stays obvious at a glance
   without needing separate copy per feature. Deliberately a color strip rather than a filled
   <select> background (that would wipe out a dropdown's chevron, see .esc-mode-* above) —
   these are plain checkbox labels, so a background reads fine here. */
.feature-toggle { display: flex; align-items: center; gap: 6px; margin-top: 10px; padding: 4px 8px; border-radius: 4px; font-weight: 500; text-transform: none; letter-spacing: normal; font-size: 12px; }
.feature-toggle.active { background: var(--accent-soft); }
/* Carbon tax additionally highlights the "Carbon Tax ($)" column of the variable-rates
   table when active — row cells keep the lighter left-edge accent (.esc-mode-* above) since
   tinting every row's background across a tall table would be too heavy. */
th.carbon-tax-active { background: var(--accent-soft); color: var(--accent); }
/* Scoped under .data-table (matching where these inputs actually live) so this beats
   .data-table td input's later `border: none` reset at equal specificity otherwise. */
.data-table td.carbon-tax-active input, .data-table input.carbon-tax-active { border-left: 3px solid var(--accent); }

textarea { width: 100%; resize: vertical; margin-top: 4px; }

/* Unit abbreviations with meaningful mixed case (ekWh, ...) read as wrong/unfamiliar units
   when forced upper (EKWH) by an ancestor header/label style (details.card summary,
   .item-field label, .data-table th, ...). text-transform is computed per element, so a
   local `none` here always wins over an inherited `uppercase`, regardless of where it's
   nested. Wrap just the unit text, not the whole label. */
.unit-case { text-transform: none; }
.hint { color: var(--muted); font-size: 11px; margin: 4px 0; }
.empty-hint { color: var(--muted); font-style: italic; padding: 30px; text-align: center; }

/* ---------- Item row-cards (cost items, embodied carbon, ECM schedule, phases) ----------
   Deliberately not a <table>: a table's column widths balloon to fit each input's intrinsic
   size, which is exactly what forced inputs to spill past their card. A wrapping flex row
   with min-width:0 fields simply wraps to a second line on narrow cards instead, and reads
   more like a web form than a spreadsheet ported from the desktop app.
   The border/background/rounding live on the .item-list container, not each .item-row, and
   only the first row shows its .item-field labels (every input still carries its own
   aria-label, so nothing is lost for screen readers) — otherwise an N-row list paid for N
   copies of "Name / Cost / Start / Type / Interval" stacked above N individually-boxed rows,
   which is what actually made these lists feel so much larger than the data in them. */
.cost-list { max-width: 100%; }
.item-list {
  display: flex; flex-direction: column; margin-bottom: 10px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2);
  overflow: hidden; /* clips row-hover backgrounds to the list's own rounded corners */
}
.item-row {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end;
  padding: 8px 10px; border-bottom: 1px solid var(--border);
}
.item-row:last-child { border-bottom: none; }
.item-row:hover { background: var(--surface); }
.item-row:not(:first-child) .item-field label { display: none; }
/* Bare .item-field (Cost, Type, GHG, Unit Rate, dates, …) shares the row's leftover width
   evenly rather than staying pinned to its basis — dates in particular need the room (a
   date input's calendar icon reads as cramped below ~130px). .grow (Name/Description/free
   text) still gets first claim on extra space, but only modestly more than everything
   else now, so a long title doesn't squeeze every other field down to its minimum.
   .narrow (Interval, row-index columns) stays fixed and compact — those never need more
   than a couple of digits. */
.item-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 130px; min-width: 0; }
.item-field.grow { flex: 1.4 1 150px; }
.item-field.narrow { flex: 0 1 84px; }
.item-field label { font-size: 11.5px; color: var(--muted); font-weight: 600; }
.item-field input, .item-field select { width: 100%; min-width: 0; }
.item-field .readonly-cell { padding: 5px 0; }
.item-row-actions { display: flex; gap: 4px; align-items: center; margin-left: auto; }
.item-list-empty { color: var(--muted); font-style: italic; font-size: 12px; padding: 10px; }

/* ---------- Tables ---------- */
/* Spreadsheet look: full grid lines (vertical + horizontal, not just row separators),
   an outer bounding box that hugs the table's actual size, tabular-width digits, and
   cell "inputs" that read as the cell itself rather than a rounded control floating
   inside it. width:auto still sizes columns to content — a "Year" column of 4-digit
   numbers or a "$0" cell doesn't get stretched to an equal share of the card. */
.table-scroll {
  overflow-x: auto; max-width: 100%; width: fit-content;
  border: 1px solid var(--border); border-radius: var(--radius);
}
/* Opt out of the content-hugging width above for tables that should instead fill and
   resize with their card — paired with .data-table.plain-inputs below. */
.table-scroll.fill { width: 100%; }
.data-table { border-collapse: collapse; width: auto; min-width: 320px; font-variant-numeric: tabular-nums; }
.data-table th, .data-table td {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 5px 9px;
  text-align: center;
}
.data-table th:last-child, .data-table td:last-child { border-right: none; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table th {
  background: var(--surface-2); font-size: 11.5px;
  color: var(--muted); font-weight: 650;
  position: sticky; top: 0;
}
.data-table tbody tr:hover { background: var(--surface-2); }
/* An <input>/<select> inside a cell IS the cell — the grid line is its only border, and
   focus/hover highlight the cell instead of drawing a separate input outline on top of it.
   Only background-color/border/box-shadow are touched here, not the `background` or
   `padding` shorthands — those would also blank out the chevron `background-image` and
   its reserved `padding-right` that `select, .combo-input` draws everywhere else in the
   app, which is what made Assembly Grid's dropdown cells look unstyled (no chevron, no
   box) next to every other dropdown. */
.data-table td input, .data-table td select {
  border: none; border-radius: 0; background-color: transparent; box-shadow: none; padding: 2px 4px;
  text-align: center; /* text-align on <td> doesn't reach into an <input>'s own text */
}
.data-table td select, .data-table td input.combo-input { padding-right: 28px; }
.data-table td input:hover, .data-table td select:hover { background-color: var(--surface-2); }
.data-table td input:focus, .data-table td select:focus {
  background-color: var(--accent-soft); box-shadow: inset 0 0 0 1.5px var(--accent); outline: none;
}
/* Capped, not just min-width'd — otherwise an <input width:100%> inside an auto-layout
   table still claims the browser's ~20-character default intrinsic input width per cell. */
.data-table input, .data-table select { width: 100%; min-width: 60px; max-width: 130px; }
.data-table.small td, .data-table.small th { padding: 3px 6px; font-size: 11px; }
.data-table.small td input, .data-table.small td select { padding: 1px 3px; }
.data-table.small td select, .data-table.small td input.combo-input { padding-right: 24px; }
/* Opts a table out of the borderless spreadsheet-cell input look above — keeps the table's
   grid/header/hover structure, but the cell input goes back to the app's normal bordered
   field so a short, fixed-row table (e.g. ECM Annual Energy) reads as one more input on
   the page instead of switching styles into "big spreadsheet" mode. */
.data-table.plain-inputs td input, .data-table.plain-inputs td select {
  border: 1px solid var(--border-strong); border-radius: 6px; background-color: var(--surface); box-shadow: none; padding: 5px 8px;
}
.data-table.plain-inputs td input:hover, .data-table.plain-inputs td select:hover { border-color: var(--muted); background-color: var(--surface); }
.data-table.plain-inputs td input:focus, .data-table.plain-inputs td select:focus {
  border-color: var(--accent); background-color: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); outline: none;
}
/* table-layout:fixed with no per-column widths splits the table evenly across all
   columns (equal widths) and lets it fill/resize with .table-scroll.fill's 100% width,
   instead of every column just hugging its own content at whatever width that happens to
   need. The 130px cap above is for genuinely dense multi-column spreadsheets — here the
   input should fill its (now equal-width) column instead of stopping short of it. */
.data-table.plain-inputs { table-layout: fixed; width: 100%; }
.data-table.plain-inputs input, .data-table.plain-inputs select { max-width: none; }
.readonly-cell { color: var(--muted); }

.row-good { background: var(--pass-bg); }
.row-bad { background: var(--fail-bg); }
/* Backs up the good/bad row tint with a text glyph so the distinction isn't color-only
   (WCAG 1.4.1 Use of Color). */
.status-mark { font-weight: 800; }
.row-good .status-mark { color: var(--pass); }
.row-bad .status-mark { color: var(--fail); }
/* Compare tab's baseline row — neutral gray (not green/red, it isn't "cost-effective" or
   not, it's the reference point), bold label, mirrors gui/comparison_tab.py's ROW_BASELINE. */
.row-baseline { background: var(--surface-2); font-weight: 700; }
.row-baseline td:first-child { font-weight: 700; }

/* ---------- Copy/paste cell selection (see copypaste.js) ---------- */
.data-table td.cell-selected,
.item-field.cell-selected {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  outline: 1.5px solid var(--accent);
  outline-offset: -1.5px;
  position: relative;
}
.item-field.cell-selected { border-radius: 4px; }
/* One-time dismissible tip (see app.js copyPasteHintBanner) — replaces what used to be
   a repeated <p> under every copyable card; on an ECM/Bundle edit page that meant the
   same sentence appearing 5-7 times on one screen. */
.tip-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--accent-soft); border: 1px solid var(--accent);
  border-radius: var(--radius); padding: 8px 8px 8px 12px; margin-bottom: 14px;
  font-size: 12px; color: var(--fg);
}
.tip-banner button.btn-icon { flex-shrink: 0; color: var(--muted); }
.tip-banner button.btn-icon:hover { color: var(--fg); background: transparent; border-color: transparent; }

/* ---------- Fuel escalation/GHG columns ---------- */
/* Fixed width (not just min-width) plus flex:0 0 <width> and an explicit 100% on the
   select/input children — a bare <select> sizes to its widest visible option text
   ("Inflation", "Variable") regardless of any min-width on its container, which is what
   pushed 8 columns past one row's available width in the first place. Locking every column
   to the same narrow width, with children forced to fill it, is what actually gets all 8
   fuels (Energy Escalation Rates, GHG Factors — both use these classes) onto one row. */
.fuel-esc-row { display: flex; gap: 10px; flex-wrap: wrap; }
.fuel-esc-col {
  display: flex; flex-direction: column; gap: 5px; width: 160px; flex: 0 0 160px;
  background: var(--surface-2); padding: 8px; border-radius: var(--radius); border: 1px solid var(--border);
}
.fuel-esc-col select, .fuel-esc-col input { width: 100%; min-width: 0; }
.fuel-esc-label { font-size: 12px; font-weight: 700; color: var(--fg); }
/* The ekWh-equivalent readout under a GHG factor entered in a non-ekWh unit (see
   renderGhgSubtab) — small and muted since it's a reference value, not something to edit. */
.fuel-esc-converted { font-size: 10px; color: var(--muted); }
/* A fuel's current input unit shown inline next to its name (ECM Energy table, etc.) when
   it isn't the ekWh default — small and muted, a label not a value. */
.hint-inline { font-size: 10px; color: var(--muted); font-weight: 400; text-transform: none; }
/* Escalation-mode color coding — Fixed/Inflation/Variable get their own tint so the source
   of a rate (typed in, following the general inflation rate, or driven by the year-by-year
   table) reads at a glance without checking the mode dropdown. Deliberately just a text
   color + a thin left-edge accent, not a filled background: a `background` fill on <select>
   wipes out its chevron (a background-image, see "Dropdowns" above), which made the mode
   picker unreadable as a dropdown. Applied to both the mode select and the rate value input;
   see .esc-mode-* usage in ui-global.js. */
.esc-mode-fixed { color: var(--esc-fixed); border-left: 3px solid var(--esc-fixed); }
.esc-mode-inflation { color: var(--esc-inflation); border-left: 3px solid var(--esc-inflation); }
.esc-mode-variable { color: var(--esc-variable); border-left: 3px solid var(--esc-variable); }

/* Same text-color + left-edge-accent treatment as .esc-mode-* above, applied to the
   One-time/Recurring Type dropdown on cost item rows (Capital, Replacements, Maintenance,
   Salvage, Embodied Carbon) so a row's timing pattern reads at a glance. */
.type-onetime { color: var(--type-onetime); border-left: 3px solid var(--type-onetime); }
.type-recurring { color: var(--type-recurring); border-left: 3px solid var(--type-recurring); }

/* ---------- Date picker (vendor/datepicker.min.js, vanillajs-datepicker) ----------
   Every date field in the app is `<input type="text" class="iso-date">` (see
   web/js/datepicker-init.js) rather than native `<input type="date">`, so its popup can be
   configured to open in year-select mode first — not possible with a browser-native picker,
   which owns its own UI with no styling/scripting hook. The vendored base CSS is close to
   unstyled by design (meant to be reskinned per project); every rule below just re-points its
   colors at this app's existing tokens, so it automatically follows the light/dark theme the
   same way every other control does — no separate dark-mode block needed here. */
.iso-date { cursor: pointer; }
.datepicker-picker { background-color: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); font-family: inherit; }
.datepicker-title, .datepicker-footer { background-color: var(--surface-2); box-shadow: none; border-bottom: 1px solid var(--border); }
.datepicker-footer { border-top: 1px solid var(--border); border-bottom: none; }
.datepicker-controls .button { background-color: var(--surface); border-color: var(--border); color: var(--fg); }
.datepicker-controls .button:hover { background-color: var(--surface-2); border-color: var(--accent); color: var(--accent); }
.datepicker-controls .button:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.datepicker-header .datepicker-controls .button:hover, .datepicker-header .datepicker-controls .button:active { background-color: var(--surface-2); }
.datepicker .dow { color: var(--muted); }
.datepicker .week { color: var(--muted); }
.datepicker-cell:not(.disabled):hover { background-color: var(--surface-2); }
.datepicker-cell.focused:not(.selected) { background-color: var(--accent-soft); }
.datepicker-cell.selected, .datepicker-cell.selected:hover { background-color: var(--accent-contrast); color: var(--accent-fg); }
.datepicker-cell.disabled { color: var(--border-strong); }
.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) { color: var(--muted); }
.datepicker-cell.today:not(.selected) { background-color: var(--accent-soft); color: var(--accent); font-weight: 700; }
.datepicker-cell.today.focused:not(.selected) { background-color: var(--accent-soft); }
.datepicker-input.in-edit { border-color: var(--accent); }
.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active { box-shadow: 0 0 0 3px var(--accent-soft); }

/* ---------- Metric cards ---------- */
.metric-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
.metric-card {
  border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 12px 14px 14px;
  background: var(--surface);
}
.metric-label { font-size: 12px; color: var(--muted); font-weight: 600; }
/* A result total, so it gets the accountant's double underline — the same device that closes
   the masthead. Inline-block so the rule hugs the figure, not the card width. */
.metric-value {
  display: inline-block; font-size: 22px; font-weight: 750; margin-top: 6px; letter-spacing: -0.015em;
  padding-bottom: 3px; border-bottom: 3px double var(--rule-double);
}

.inline-metrics { display: flex; gap: 18px; flex-wrap: wrap; font-size: 12.5px; }
.inline-metrics span { padding: 4px 0; }

/* ---------- Compare ---------- */
.compare-controls { display: flex; gap: 18px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.compare-controls label { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; }
/* Sticks directly under the (also sticky) subtabs bar — --subtabs-h is measured in JS
   (see charts.js pinChartControls) since the bar's exact height varies with font metrics. */
.chart-controls {
  position: sticky; top: var(--subtabs-h, 46px); z-index: 2;
  background: var(--bg); padding: 6px 0; margin-bottom: 6px;
}
.checklist { display: flex; flex-wrap: wrap; gap: 10px 16px; }
.checklist-item { display: flex; align-items: center; gap: 5px; font-size: 12px; }

/* ---------- Compare tab item picker (search-to-add + chips, replaces a flat checkbox
   list that stopped scaling once a project had more than a handful of ECMs/Bundles/
   Pathways to choose from — see renderComparePanel in ui-shared.js). ---------- */
.compare-picker { position: relative; }
/* details.card sets overflow:hidden (to clip content cleanly to its rounded corners), which
   also clips any absolutely-positioned descendant that extends past the card's own
   flow-determined height — position:absolute escapes normal layout flow, not an ancestor's
   overflow clipping. The dropdown does exactly that (it's taller than the search box it
   hangs off), so without this override it's fully clipped away and invisible despite valid
   geometry and z-index:50 — confirmed via document.elementsFromPoint, which didn't include
   it in the hit-test stack at all. Only relaxed while a dropdown is actually open (:has()),
   plus a stacking context bump so it still paints above sibling cards' own content. */
details.card:has(.compare-picker-dropdown) { position: relative; z-index: 10; overflow: visible; }
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; min-height: 26px; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--accent-soft); color: var(--accent); border-radius: 4px;
  padding: 3px 6px 3px 10px; font-size: 12px; font-weight: 600;
}
.chip-remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; padding: 0; border: none; box-shadow: none;
  background: transparent; color: inherit; border-radius: 50%;
}
.chip-remove:hover { background: rgba(0, 0, 0, 0.12); }
.compare-picker-search { width: 100%; }
.compare-picker-dropdown {
  position: absolute; top: 100%; left: 0; right: 0; margin-top: 4px;
  max-height: 240px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-md); padding: 4px; z-index: 50;
}
.compare-picker-item {
  display: block; width: 100%; text-align: left;
  border: none; box-shadow: none; background: transparent; border-radius: 4px;
  padding: 6px 8px; font-size: 12.5px; font-weight: 400;
}
.compare-picker-item:hover { background: var(--surface-2); }
.compare-picker-empty { padding: 8px; font-size: 12px; color: var(--muted); font-style: italic; }

/* ---------- Run action bar (Monte Carlo / Sequence Explorer / Optimization) ---------- */
/* The Run button leads its page's settings cards rather than trailing them (previously
   buried below several stacked cards) and stays visible while scrolling through them,
   reusing the same sticky-under-subtabs positioning as .chart-controls above. */
.run-action-bar {
  position: sticky; top: var(--subtabs-h, 46px); z-index: 2;
  background: var(--bg); padding: 8px 0; margin-bottom: 10px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}

/* ---------- Charts ---------- */
/* Fills whatever width/height is actually available instead of a fixed box: width is the
   full content column (no max-width cap — Plotly's `responsive: true` config already redraws
   the plot to fit whenever its container resizes, so there's nothing extra to wire up JS-side
   for either dimension). Height is viewport-relative via --chart-controls-bottom (set by
   pinChartControls() in charts.js, measured fresh on every render since the space above the
   chart varies — GHG Comparison/Cash Flow Comparison add an extra control row plain charts
   don't have) rather than a fixed px value that left a lot of dead space on a tall monitor.
   min-height is the old fixed value, kept as a floor for short viewports. */
.chart-container {
  position: relative;
  height: calc(100vh - var(--chart-controls-bottom, 260px) - 20px);
  min-height: 420px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 14px;
}
.timeline-label { font-size: 12px; color: var(--muted); font-family: var(--mono); }
.summary-text { font-family: var(--mono); font-size: 11.5px; white-space: pre-wrap; margin: 0; }

/* ---------- Narrow screens ----------
   The desktop layout pins a 260px sidebar beside the content, which left ~130px for forms on
   a phone. Below 760px the item tree stacks above the content (capped so the page stays
   reachable) and the top tabs scroll sideways instead of wrapping their labels. */
#top-tabs { overflow-x: auto; }
.tab-btn { white-space: nowrap; flex-shrink: 0; }
@media (max-width: 760px) {
  #app-header { padding: 10px 16px; }
  #top-tabs { padding: 0 8px; }
  #app-body { flex-direction: column; }
  #sidebar { width: 100%; max-height: 34vh; border-right: none; border-bottom: 1px solid var(--border); }
  .content { padding: 0 16px 18px; }
  .field-row { flex-wrap: wrap; }
  .field-row label { min-width: 0; flex-basis: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ---------- Framed (frame-guard.js) ----------
   Shown only when another site puts this page in a frame: everything but the
   "open it in a new tab" note is hidden before it paints. */
html.framed body > *:not(.framed-note) { display: none !important; }
.framed-note { margin: 40px auto; max-width: 60ch; padding: 0 16px; text-align: center; color: var(--fg); font-size: 14px; }
.framed-note a { color: var(--accent); }
