/* build: 2026-09-22t — named worst/expected/best; PDF tiles on one row.

   styles.css and login.js are the only files served without a session, so this comment is the one
   thing that can confirm from outside which build is live. A deploy that changes only server code
   has nothing else to check against. Bump it whenever you ship. */
/* ============================================================================
   Cash Flow Planner — styled to the MCC Analytics design language.
   Tokens, card treatment, motion and chart chrome are lifted from
   mcc-analytics/ui/src/index.css so the two products read as one system.

   The seven categorical hues are slots 1-7 of the same validated palette,
   assigned to sections in a FIXED order and never cycled - so Technology is
   the same blue whether one section is on screen or all seven. Both modes
   were run through the palette validator:

     light (surface #fcfcfb): all checks pass; three hues sit under 3:1
                              contrast, so the relief rule applies - every
                              series carries a visible direct label and there
                              is a full table view underneath the charts.
     dark  (surface #1a1a19): all checks pass, including contrast.

   Green/red are status only and are deliberately NOT in the series set, so a
   funded/shortfall verdict can never be confused with a section colour. The
   chrome accent is its own step, separate from series-1, because a section
   heading is not the Technology channel.
   ========================================================================== */

:root {
  color-scheme: light;

  --surface-page: #f4f4f1;
  --surface-1: #fcfcfb;
  --surface-2: #f0efec;
  --surface-sunk: #e9e8e3;
  --border: #e2e1dc;
  --border-strong: #cfcec8;

  --text-primary: #0b0b0b;
  --text-secondary: #52514e;
  --text-muted: #83817b;

  --accent: #256abf;
  --accent-2: #4a3aa7;
  --accent-wash: rgba(37, 106, 191, 0.08);

  --series-1: #2a78d6; /* Technology         */
  --series-2: #eb6834; /* Marketing          */
  --series-3: #1baf7a; /* Sales              */
  --series-4: #eda100; /* People             */
  --series-5: #e87ba4; /* Legal & Compliance */
  --series-6: #008300; /* Operations         */
  --series-7: #4a3aa7; /* Travel             */

  --prior: #b6b5ae;

  --good: #067647;
  --bad: #c2332f;

  /* The three cases, named once. Red for the worst, blue for the expected, green for the best -
     and the same colour means the same case on every chart, on screen and in the PDF, so a reader
     never has to re-learn a palette between two figures. */
  --case-worst: #c2332f;
  --case-expected: #2a78d6;
  --case-best: #17925f;
  --warn: #b26a00;

  --grid: #e8e7e3;
  --radius-card: 18px;
  --font: Inter, ui-sans-serif, system-ui, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
}

/* The app is a light document — a budget people print and share. It does not
   follow the OS dark preference; dark is only ever an explicit choice via the
   theme toggle, which stamps data-theme on the root. */
:root[data-theme='dark'] {
  color-scheme: dark;

  --surface-page: #121211;
  --surface-1: #1a1a19;
  --surface-2: #232322;
  --surface-sunk: #151514;
  --border: #2f2f2d;
  --border-strong: #43433f;

  --text-primary: #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted: #8e8d84;

  --accent: #3987e5;
  --accent-2: #9085e9;
  --accent-wash: rgba(57, 135, 229, 0.14);

  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;

  --prior: #5a5a55;

  --good: #4bbd7c;
  --bad: #e66767;
  /* Lifted for the dark ground: the light values go muddy on it. */
  --case-worst: #ef6a63;
  --case-expected: #4f9bf0;
  --case-best: #2fb37c;
  --warn: #e0a33a;

  --grid: #2a2a28;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
button, input, select, textarea { font: inherit; color: inherit; }

/* Money and deltas must line up column-to-column. */
.tnum { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }

/* ---------------------------------------------------------------- surface */

.card {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  /* Two stacked shadows rather than one: a tight dark edge to seat the card on
     the page, and a wide soft one for depth. A single large blur reads as a
     drop shadow; this reads as a surface. */
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--text-primary) 5%, transparent),
    0 8px 24px -12px color-mix(in srgb, var(--text-primary) 14%, transparent);
}

/* A one-pixel highlight along the top edge. It is what makes a flat rectangle
   read as a lit surface rather than a coloured box. */
.card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  border-radius: var(--radius-card) var(--radius-card) 0 0;
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--text-primary) 10%, transparent) 20%,
    color-mix(in srgb, var(--text-primary) 10%, transparent) 80%,
    transparent
  );
  pointer-events: none;
}

.card.is-clickable { transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease; cursor: pointer; }
.card.is-clickable:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  box-shadow:
    0 2px 4px color-mix(in srgb, var(--text-primary) 6%, transparent),
    0 16px 32px -14px color-mix(in srgb, var(--text-primary) 20%, transparent);
}

/* A headline set in the accent gradient. Both stops are contrast-checked. */
.accent-gradient {
  background: linear-gradient(100deg, var(--accent) 0%, var(--accent-2) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.card-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 18px 20px 0;
}
.card-title { font-size: 14px; font-weight: 600; }
.card-sub { font-size: 12px; color: var(--text-muted); }
.card-body { padding: 14px 20px 20px; }
/* The card's own export sits at the far right of its heading row, clear of the title and subtitle. */
#outlook-card .card-head > .btn { margin-left: auto; align-self: center; }
.ol-note {
  grid-column: 1 / -1; margin: 10px 0 0; font-size: 12px; color: var(--text-muted);
  border-left: 2px solid var(--border); padding-left: 10px;
}

/* ----------------------------------------------------------------- motion */

@media (prefers-reduced-motion: no-preference) {
  .grow-in { animation: grow-in 620ms cubic-bezier(0.16, 1, 0.3, 1) both; transform-origin: bottom; }
  .fade-in { animation: fade-in 420ms cubic-bezier(0.16, 1, 0.3, 1) both; }
}
@keyframes grow-in { from { transform: scaleY(0); opacity: 0.4; } to { transform: scaleY(1); opacity: 1; } }
@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ------------------------------------------------------------- app frame */

.app { max-width: 1480px; margin: 0 auto; padding: 0 20px 72px; }

/* The board is a reading surface and holds a comfortable measure. The editors
   are spreadsheets — every column has to show its value, so they take the whole
   window and trade the side margins for column width. */
body[data-tab='vars'] .app,
body[data-tab='revenue'] .app { max-width: min(2100px, 100%); padding-left: 16px; padding-right: 16px; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 16px;
  /* Wrap. Without it the bar is a single unbreakable row: the brand will not shrink (both lines
     are nowrap), so below roughly 900px the tab strip was drawn straight over the plan name, and
     on a phone the Export button hung off the right edge of the document. */
  flex-wrap: wrap; row-gap: 10px;
  padding: 14px 0 13px; margin-bottom: 18px;
  background: color-mix(in srgb, var(--surface-page) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
/* min-width:0 has to reach the text itself, not just the flex row, or the nowrap lines below set
   the brand's width and nothing can give. */
.brand > div { min-width: 0; }
.brand-name, .brand-sub { overflow: hidden; text-overflow: ellipsis; }
.brand-mark {
  width: 32px; height: 32px; border-radius: 10px; flex: none;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 13px;
  letter-spacing: -0.02em;
}
.brand-name { font-size: 15px; font-weight: 650; white-space: nowrap; letter-spacing: -0.01em; }
.brand-sub { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }
/* Was a flex spacer, which only works on a bar that never wraps: it sits on the FIRST row and
   absorbs the slack there, so once the bar wrapped, everything on the second row packed to the
   left and the primary action slid from the right edge to the far left and back again as you
   resized. `margin-left: auto` travels with the controls instead, so they stay right-aligned on
   whichever row they end up on. */
.topbar-spacer { display: none; }
/* The controls travel as one group and keep to the right of whatever row they wrap onto. */
.topbar-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-left: auto; justify-content: flex-end;
}
/* Once the bar is too tight to hold the name and the tabs side by side, give the brand its own
   row rather than letting a truncated name fight the tab strip for the same pixels. */
@media (max-width: 900px) { .brand { flex: 1 1 100%; } }

.tabs {
  display: inline-flex; flex-wrap: wrap; padding: 2px; border-radius: 10px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.tab {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 7px 15px; border-radius: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--text-secondary); white-space: nowrap; transition: color 140ms ease, background 140ms ease;
}
.tab:hover { color: var(--text-primary); }
.tab[aria-selected='true'] {
  background: var(--surface-1); color: var(--text-primary);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--text-primary) 8%, transparent);
}

/* ---------------------------------------------------------------- buttons */

.btn {
  appearance: none; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-1); color: var(--text-primary);
  padding: 8px 14px; border-radius: 10px; font-size: 12.5px; font-weight: 600;
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  transition: background 140ms ease, border-color 140ms ease, transform 60ms ease;
}
.btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: transparent; color: #fff; }
.btn-primary:hover { background: color-mix(in srgb, var(--accent) 82%, #000); border-color: transparent; }
.btn-ghost { background: var(--surface-2); border-color: var(--border); color: var(--text-secondary); }
.btn-ghost:hover { color: var(--text-primary); }
.btn-sm { padding: 5px 10px; font-size: 11.5px; border-radius: 8px; }
.btn-danger:hover { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, var(--border)); }

/* =========================================================== SOLVER BAND */

.solver { padding: 22px 24px; margin-bottom: 16px; }
.solver-grid {
  display: grid; gap: 26px;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 900px) {
  .solver-grid { grid-template-columns: minmax(250px, 320px) minmax(0, 1fr); }
}

.field-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-muted);
}

.money-input {
  display: flex; align-items: center; gap: 4px; margin-top: 8px;
  border: 1px solid var(--border-strong); border-radius: 12px;
  background: var(--surface-2); padding: 9px 13px;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.money-input:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.money-input span { font-size: 22px; color: var(--text-muted); font-weight: 500; }
.money-input input {
  border: 0; background: transparent; outline: none; width: 100%;
  font-size: 27px; font-weight: 650; letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.buffer-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.buffer-row input[type='range'] { flex: 1; accent-color: var(--accent); min-width: 0; }
.buffer-value { font-size: 13px; font-weight: 650; min-width: 38px; text-align: right; }

.hero-figure {
  font-size: clamp(32px, 4.6vw, 46px); font-weight: 680;
  letter-spacing: -0.035em; line-height: 1.02; margin: 4px 0 6px;
  font-variant-numeric: tabular-nums;
}
.hero-caption { font-size: 13px; color: var(--text-secondary); max-width: 68ch; line-height: 1.55; }
.hero-caption b { color: var(--text-primary); font-weight: 650; }

.verdict {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700;
  border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-secondary);
}
.verdict i { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--text-muted); }
.verdict.is-good i { background: var(--good); }
.verdict.is-warn i { background: var(--warn); }
.verdict.is-bad i { background: var(--bad); }

.req-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
  gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  margin-top: 18px;
}
.req { background: var(--surface-1); padding: 12px 14px; display: flex; flex-direction: column; gap: 2px; }
.req.is-headline { background: var(--accent-wash); }
.req-label { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
             text-transform: uppercase; color: var(--text-muted); }
.req-value { font-size: 20px; font-weight: 660; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; }
.req-note { font-size: 11px; color: var(--text-muted); line-height: 1.35; }

.coverage-track {
  height: 8px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--border); overflow: hidden; margin: 16px 0 7px;
}
.coverage-fill { height: 100%; background: var(--accent); transition: width 500ms cubic-bezier(0.16, 1, 0.3, 1); }
.coverage-fill.is-over { background: var(--good); }
.coverage-fill.is-short { background: var(--warn); }
.coverage-legend { display: flex; justify-content: space-between; gap: 12px;
                   font-size: 11.5px; color: var(--text-muted); }

/* ------------------------------------------------------------- stat tiles */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: 14px; margin-bottom: 16px; }
.tile { padding: 15px 17px; }
.tile-label { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
              text-transform: uppercase; color: var(--text-muted); }
.tile-value { font-size: 26px; font-weight: 660; letter-spacing: -0.03em; margin-top: 4px;
              font-variant-numeric: tabular-nums; }
.tile-note { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }

/* --------------------------------------------------------------- filters */

.filterbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 16px; margin-bottom: 16px;
}
.filter-group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.segmented { display: inline-flex; flex-wrap: wrap; padding: 2px; border-radius: 10px;
             background: var(--surface-2); border: 1px solid var(--border); }
.segmented button {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  padding: 6px 12px; border-radius: 6px; font-size: 12px; font-weight: 600;
  color: var(--text-secondary); white-space: nowrap; transition: color 140ms ease, background 140ms ease;
}
.segmented button:hover { color: var(--text-primary); }
.segmented button[aria-pressed='true'] {
  background: var(--surface-1); color: var(--text-primary);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--text-primary) 8%, transparent);
}

.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  appearance: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 999px; font-size: 12px; font-weight: 600;
  border: 1px solid var(--border); background: var(--surface-1);
  color: var(--text-secondary); transition: opacity 140ms ease, background 140ms ease;
}
.chip:hover { background: var(--surface-2); }
.chip i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.chip[aria-pressed='true'] { color: var(--text-primary); }
.chip[aria-pressed='false'] { opacity: 0.42; }
.chip[aria-pressed='false'] i { background: var(--text-muted) !important; }

.sep { width: 1px; align-self: stretch; min-height: 24px; background: var(--border); }

/* ---------------------------------------------------------- board layout */

/* minmax(0, 1fr), not the implicit `auto`.

   A grid item's default min-width is `auto` - 'as wide as my content insists' - so any single
   child that will not shrink sizes the whole track, and every sibling card inherits that width.
   The board was growing past the page and the entire lower half scrolled sideways while the
   cards above it sat neatly in place: fine at the top, bulging below.

   Deliberately NOT written as 'the culprit is X'. An earlier version of this comment blamed the
   scenario strip; measuring it - serve the old stylesheet, remove one child at a time, re-measure
   the track - cleared both that and the statement table, which cannot be the cause anyway since a
   scroll container's automatic minimum size is zero. Which child misbehaves depends on the data in
   the plan, so naming one would be wrong again the next time the data changes. This line makes the
   board immune to all of them, which is the point.

   .board-row already does this one line down; the outer grid was the one that never got it. */
.board { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
.board-row { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1040px) { .board-row.split { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); } }
/* Two cards of equal weight, side by side above 1040px and stacked below it. */
@media (min-width: 1040px) { .board-row.pair-even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.board-row.pair-even > .card { display: flex; flex-direction: column; }
.board-row.pair-even > .card > .card-body { flex: 1 1 auto; display: flex; flex-direction: column; }

/* ------------------------------------------------------ scenario compare */

.compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 14px; }
.scen { text-align: left; appearance: none; padding: 14px 15px 9px; font: inherit; color: inherit; }
.scen[aria-pressed='true'] {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  box-shadow:
    0 0 0 3px var(--accent-wash),
    0 1px 2px color-mix(in srgb, var(--text-primary) 5%, transparent),
    0 8px 24px -12px color-mix(in srgb, var(--text-primary) 14%, transparent);
}
.scen-name { font-size: 12.5px; font-weight: 650; display: flex; align-items: center; gap: 7px; }
.scen-name i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); flex: none; }
.scen[aria-pressed='true'] .scen-name i { background: var(--accent); }
.scen-total { font-size: 22px; font-weight: 660; letter-spacing: -0.03em; margin-top: 4px;
              font-variant-numeric: tabular-nums; }
.scen-blurb { font-size: 11.5px; color: var(--text-muted); min-height: 2.6em; margin-top: 2px; line-height: 1.35; }

/* ------------------------------------------------------------- viz chrome */

.viz { position: relative; width: 100%; }
.viz-grid { stroke: var(--grid); stroke-width: 1; shape-rendering: crispEdges; }
.viz-baseline { stroke: var(--border-strong); stroke-width: 1; shape-rendering: crispEdges; }
.viz-axis-label { fill: var(--text-muted); font-size: 11px; font-weight: 500;
                  font-family: var(--font); font-variant-numeric: tabular-nums; }
/* The plan month leads; the calendar month qualifies it. */
.viz-axis-num { fill: var(--text-secondary); font-weight: 650; font-size: 11px; }
.viz-axis-sub { font-size: 10px; }
.viz-row-label { fill: var(--text-secondary); font-size: 12.5px; font-weight: 500; font-family: var(--font); }
.viz-row-value { fill: var(--text-primary); font-size: 12.5px; font-weight: 650;
                 font-family: var(--font); font-variant-numeric: tabular-nums; }
.viz-track { fill: var(--surface-2); }
.viz-seg { transition: opacity 120ms ease; }
.viz-seg.is-dim { opacity: 0.3; }
.viz-cross { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.viz-ref { stroke: var(--text-secondary); stroke-width: 1.5; stroke-dasharray: 5 4; }
/* The funding label sits at the right edge, where the plotted lines end up too. A halo in the
   surface colour keeps it readable where a line runs underneath it. */
.viz-ref-label {
  fill: var(--text-secondary); font-size: 11px; font-weight: 650; font-family: var(--font);
  paint-order: stroke; stroke: var(--surface-1); stroke-width: 3px; stroke-linejoin: round;
}

.viz-tip {
  position: absolute; top: 0; left: 0; pointer-events: none; z-index: 20;
  min-width: 196px; max-width: 290px;
  background: var(--surface-1); border: 1px solid var(--border-strong);
  border-radius: 12px; padding: 10px 12px; font-size: 12.5px;
  box-shadow:
    0 1px 2px color-mix(in srgb, var(--text-primary) 8%, transparent),
    0 12px 28px -12px color-mix(in srgb, var(--text-primary) 26%, transparent);
}
.viz-tip-head {
  display: flex; justify-content: space-between; gap: 14px;
  font-weight: 650; padding-bottom: 7px; margin-bottom: 5px;
  border-bottom: 1px solid var(--border);
}
.viz-tip-head span { font-variant-numeric: tabular-nums; }
.viz-tip-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; color: var(--text-secondary); }
.viz-tip-row i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.viz-tip-row span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viz-tip-row b { color: var(--text-primary); font-weight: 650; font-variant-numeric: tabular-nums; }
.viz-tip-row b.is-bad { color: var(--bad); }

.legend { display: flex; flex-wrap: wrap; gap: 7px 18px; margin-top: 14px;
          padding-top: 13px; border-top: 1px solid var(--border); }
.legend-item { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-secondary); }
.legend-item i { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.legend-item b { color: var(--text-primary); font-weight: 650; font-variant-numeric: tabular-nums; }

/* Crossing markers: the month one line meets another, called out on the chart itself. */
.viz-mark-drop { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.55; }
.viz-mark-ring { fill: var(--surface-1); stroke-width: 2.5; }
.viz-mark-box { fill: var(--surface-1); stroke: var(--border); stroke-width: 1; opacity: 0.97; }
.viz-mark-title { font-size: 11px; font-weight: 650; fill: var(--text-primary); }
.viz-mark-sub { font-size: 10.5px; fill: var(--text-muted); font-variant-numeric: tabular-nums; }
/* The two marks explained, one block each, under the pane each one describes.

   This was a single paragraph: first capped at a readable measure, which left half the row empty,
   then flowed into two CSS columns, which filled the row but made you read the left column and
   jump to the right mid-sentence. Neither is what the content is. It is two independent
   definitions, so it is now two independent blocks - each one starts and finishes where it sits,
   and lands under the chart it belongs to. */
.ol-explain {
  display: grid; gap: 10px 16px; grid-template-columns: minmax(0, 1fr);
  margin: 20px 0 4px;
  font-size: 12px; line-height: 1.55; color: var(--text-muted);
}
.ol-explain p { margin: 0; }
.ol-explain b { color: var(--text-secondary); font-weight: 650; }
@media (min-width: 900px) {
  .ol-explain { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The three cases, side by side. Three across only above 1240px, where each still gets ~380px;
   two across in the middle band, and one on a phone, because three charts squeezed into a third
   of a small screen each compare nothing. */
.ol-split { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .ol-split { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1240px) { .ol-split { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ol-pane { margin: 0; min-width: 0; border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px 16px; }
.ol-pane-head { font-size: 13px; font-weight: 650; margin-bottom: 8px; }
/* auto-fit, not a fixed count: a pane is ~408px wide when three are across and ~690px when two
   are, and the number of readings changes with whether there is any capital. Let it pack. */
.ol-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 8px 12px; margin-bottom: 10px;
}
.ol-stat { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ol-stat span { font-size: 10.5px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); }
.ol-stat b { font-size: 14px; font-weight: 650; font-variant-numeric: tabular-nums; }
.ol-stat.is-neg b { color: var(--bad, #c0392b); }
/* Each pane names its own two lines. The shared legend under both charts made the reader look away
   from the chart to find out what a colour meant, and sat tight against the pane borders. */
/* Clear of the x-axis labels above it, not tucked against the bottom of the pane. */
.ol-legend { margin-top: 16px; padding-top: 13px; gap: 6px 14px; }
.ol-legend .legend-item { font-size: 11.5px; }


/* ---------------------------------------------------------------- tables */

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 14px; }
table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
th, td { padding: 9px 13px; text-align: left; white-space: nowrap; border-bottom: 1px solid var(--border); }
thead th {
  position: sticky; top: 0; z-index: 1; background: var(--surface-2);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-muted);
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tr.is-total td { font-weight: 650; background: var(--surface-2); }
.cell-dot { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 8px; vertical-align: -1px; }
.cell-zero { color: var(--text-muted); }
.cell-scaled { color: var(--accent); font-weight: 650; }

/* The statement is wider than any screen once a plan runs past a year, so it scrolls sideways
   inside its card. Two columns are pinned rather than scrolled away: the section name on the
   left, so a row stays identifiable, and the Total on the right, because a total you have to go
   looking for is the one number on the table nobody should have to hunt for. The months scroll
   between them.

   ONLY above 720px, and that floor is the whole point of this media query. Both pinned columns
   are content-sized and fixed - a section name plus a full accounting figure is about 290px
   between them - so on a phone they eat the entire scrollport and leave a 12px sliver for the
   months. Below about 390px they physically overlap and the Total paints on top of the section
   name, cutting it mid-word. Pinning a column is only a kindness while there is something left
   to scroll between the pins; narrower than that, plain scrolling shows strictly more. */
@media (min-width: 720px) {
  #table-monthly th:first-child, #table-monthly td:first-child {
    position: sticky; left: 0; z-index: 2; background: var(--surface-1);
  }
  #table-monthly th:last-child, #table-monthly td:last-child {
    position: sticky; right: 0; z-index: 2; background: var(--surface-1);
  }
  /* The pinned cells sit ON TOP of the scrolling ones, so they need their own opaque ground or
     the months slide through underneath them. Every row that carries a tint has to repeat it
     here for that reason - including the sub-rows, which the first version of this missed and
     left as a visible seam down both pinned columns. */
  #table-monthly tr.is-total td:first-child, #table-monthly tr.is-total td:last-child { background: var(--surface-2); }
  #table-monthly tr.is-sub td:first-child, #table-monthly tr.is-sub td:last-child {
    background: color-mix(in srgb, var(--surface-2) 45%, var(--surface-1));
  }
  #table-monthly tbody tr:hover td:first-child, #table-monthly tbody tr:hover td:last-child { background: var(--surface-2); }
  #table-monthly thead th:first-child, #table-monthly thead th:last-child { z-index: 3; background: var(--surface-2); }
  /* A hairline on the inner edge of each pinned column, so it reads as a held column rather than
     as a number that happens to be sitting there. An empty-state row is a single cell that is
     both first AND last child; it gets neither pin nor hairline. */
  #table-monthly th:first-child:not(:last-child), #table-monthly td:first-child:not(:last-child) { box-shadow: 1px 0 0 var(--border); }
  #table-monthly th:last-child:not(:first-child), #table-monthly td:last-child:not(:first-child) { box-shadow: -1px 0 0 var(--border); }
  #table-monthly td:first-child:last-child { position: static; box-shadow: none; }
}

/* A 2px overlay scrollbar is not an affordance. Give the scroller a real one that is visible
   without hovering, so it is obvious the months continue past the edge. */
.table-wrap { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.table-wrap::-webkit-scrollbar { height: 10px; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 99px;
  border: 3px solid var(--surface-1); background-clip: padding-box;
}
.table-wrap::-webkit-scrollbar-thumb:hover { background: var(--text-muted); background-clip: padding-box; }

/* ======================================================= VARIABLES SCREEN */

.vars-layout { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1100px) { .vars-layout { grid-template-columns: minmax(0, 1fr) 288px; } }
@media (min-width: 1700px) { .vars-layout { grid-template-columns: minmax(0, 1fr) 320px; } }

.section-block { margin-bottom: 16px; }
.section-head {
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  padding: 12px 16px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 14px 14px 0 0; border-bottom: 0;
}
.section-swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; }
.section-name { font-size: 13.5px; font-weight: 650; }
.section-total { font-size: 12.5px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }

.items {
  border: 1px solid var(--border); border-radius: 0 0 14px 14px;
  /* overflow-x was `hidden`, which does not clip decoration - it silently DELETES content. At
     1280 the row's own minimums add up to more than the column is wide, so the rightmost field
     of every line was simply gone, with no scrollbar and nothing to suggest anything was
     missing. `auto` keeps the same clipping but makes the remainder reachable. */
  overflow-x: auto; overflow-y: hidden; background: var(--surface-1);
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.items::-webkit-scrollbar { height: 10px; }
.items::-webkit-scrollbar-track { background: transparent; }
.items::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 99px;
  border: 3px solid var(--surface-1); background-clip: padding-box;
}
.item-row {
  display: grid; align-items: center; gap: 8px;
  grid-template-columns: 30px minmax(150px, 1.9fr) 88px minmax(146px, 1.1fr) 120px 62px 66px 30px;
  padding: 6px 14px; border-bottom: 1px solid var(--border);
}
.item-row:last-child { border-bottom: 0; }
.item-row.is-off { opacity: 0.42; }
.item-row.head {
  background: var(--surface-sunk); font-size: 10px; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted);
  padding-top: 7px; padding-bottom: 7px;
}
@media (max-width: 940px) {
  .item-row { grid-template-columns: 30px minmax(0, 1fr) minmax(0, 1fr); }
  .item-row.head { display: none; }
  .item-row > * { min-width: 0; }
}

.inp {
  width: 100%; min-width: 0; text-overflow: ellipsis;
  border: 1px solid transparent; background: transparent;
  border-radius: 8px; padding: 5px 8px; font-size: 13px; outline: none;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.inp:hover { border-color: var(--border); }
.inp:focus { border-color: var(--accent); background: var(--surface-1); box-shadow: 0 0 0 3px var(--accent-wash); }
.inp.num { text-align: right; font-variant-numeric: tabular-nums; }
select.inp { cursor: pointer; border-color: var(--border); background: var(--surface-1); }

.range-pair { display: flex; align-items: center; gap: 4px; }
.range-pair .inp { text-align: right; }
.range-dash { color: var(--text-muted); font-size: 12px; flex: none; }

.switch { display: inline-flex; align-items: center; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 30px; height: 17px; border-radius: 999px; position: relative;
  background: var(--surface-sunk); border: 1px solid var(--border-strong);
  transition: background 160ms ease, border-color 160ms ease;
}
.switch-track::after {
  content: ''; position: absolute; top: 1px; left: 1px;
  width: 13px; height: 13px; border-radius: 50%; background: var(--text-muted);
  transition: transform 160ms ease, background 160ms ease;
}
.switch input:checked + .switch-track { background: var(--accent); border-color: transparent; }
.switch input:checked + .switch-track::after { transform: translateX(13px); background: #fff; }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 3px var(--accent-wash); }

.icon-btn {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  color: var(--text-muted); border-radius: 8px; padding: 4px 7px; line-height: 1; font-size: 15px;
  transition: background 140ms ease, color 140ms ease;
}
.icon-btn:hover { background: var(--surface-2); color: var(--bad); }

.side-note { font-size: 12.5px; color: var(--text-secondary); line-height: 1.55; }
.side-note + .side-note { margin-top: 12px; }
.side-note strong { color: var(--text-primary); font-weight: 650; }
.side-note code {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 5px; font-size: 11.5px;
}

.savebar {
  position: sticky; bottom: 16px; z-index: 30; margin-top: 16px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 17px;
}
.savebar .grow { flex: 1 1 auto; }
.dirty-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--warn);
             display: inline-block; margin-right: 8px; vertical-align: 1px; }

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(8px);
  z-index: 100; padding: 11px 19px; border-radius: 999px;
  background: var(--text-primary); color: var(--surface-page);
  font-size: 13px; font-weight: 600;
  box-shadow: 0 12px 28px -12px color-mix(in srgb, var(--text-primary) 40%, transparent);
  opacity: 0; pointer-events: none; transition: opacity 200ms ease, transform 200ms ease;
}
.toast.is-on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------------------------------------------------------- why ---
   The panel that shows how a figure was arrived at. Deliberately plain: it is
   arithmetic being checked, not a thing being sold. */
.tile.is-clickable { text-align: left; width: 100%; font: inherit; color: inherit; }
/* The estimate under a runway floor. Deliberately quieter than the figure above it: same line of
   sight, different weight, so the guess is never read as the measurement. */
.tile-est {
  display: block; margin-top: 3px; font-size: 12px; font-weight: 600;
  letter-spacing: 0; color: var(--text-muted);
}

.tile-why {
  display: block; margin-top: 9px; font-size: 11px; color: var(--text-muted);
  border-top: 1px dashed var(--border); padding-top: 8px;
}
.tile.is-clickable:hover .tile-why { color: var(--accent); }

.why-link {
  display: block; margin-top: 10px; background: none; border: 0; padding: 0;
  font: inherit; font-size: 11px; color: var(--text-muted); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px; text-decoration-style: dotted;
}
.why-link:hover { color: var(--accent); }

.why-ov {
  position: fixed; inset: 0; z-index: 120; display: grid; place-items: center;
  padding: 20px; background: color-mix(in srgb, var(--text-primary) 34%, transparent);
}
.why-box {
  width: 100%; max-width: 460px; max-height: 84vh; overflow-y: auto;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 20px 22px 22px;
  box-shadow: 0 24px 60px -24px color-mix(in srgb, var(--text-primary) 45%, transparent);
}
.why-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.why-title { font-size: 13px; font-weight: 600; color: var(--text-muted); }
.why-result { font-size: 27px; font-weight: 700; letter-spacing: -0.02em; margin-top: 2px; }
.why-x {
  background: none; border: 0; cursor: pointer; font-size: 16px; line-height: 1;
  color: var(--text-muted); padding: 4px;
}
.why-x:hover { color: var(--text-primary); }
.why-lead { margin: 12px 0 0; font-size: 13px; line-height: 1.55; }
.why-rows { margin: 16px 0 0; display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.why-rows > div { display: flex; justify-content: space-between; gap: 14px; background: var(--surface-1); padding: 9px 12px; font-size: 12.5px; }
.why-rows dt { color: var(--text-muted); }
.why-rows dd { margin: 0; font-weight: 600; text-align: right; }
.why-foot { margin: 14px 0 0; font-size: 11.5px; color: var(--text-muted); line-height: 1.5; }

.empty { padding: 30px; text-align: center; color: var(--text-muted); font-size: 13px; }

[hidden] { display: none !important; }

/* SVG needs an explicit box for transform-origin to resolve against the mark. */
svg .grow-in { transform-box: fill-box; }

/* ------------------------------------------------------------ user chip */

.user-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 4px 11px 4px 4px; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 12.5px; font-weight: 600; color: var(--text-secondary);
  max-width: 190px;
}
.user-avatar {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: 10.5px; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
}
.user-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

@media (max-width: 860px) {
  .user-chip, #btn-signout { display: none; }
}

/* The PDF report renders off-screen at a fixed width so its charts measure a
   real box. print.css pulls it back into flow and hides the live app. */
#report-root {
  position: absolute;
  left: -100000px;
  top: 0;
  width: 1040px;
  pointer-events: none;
}

/* ------------------------------------------------------ sub-sections ---- */

.group-block + .group-block { border-top: 1px solid var(--border); }

.group-head {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 14px 7px 10px;
  background: color-mix(in srgb, var(--surface-2) 55%, var(--surface-1));
}
.group-rule { width: 3px; height: 15px; border-radius: 2px; flex: none; opacity: 0.85; }
.group-name {
  font-size: 12px; font-weight: 650; letter-spacing: 0.01em;
  flex: 0 1 200px; min-width: 0;
}
input.group-name { padding: 3px 6px; }
.group-name.is-none { color: var(--text-muted); font-weight: 600; font-style: italic; }
.group-total { font-size: 12px; font-weight: 650; color: var(--text-secondary); }
.group-count { font-size: 11px; color: var(--text-muted); }

/* The row grid gains a sub-section column. */
.item-row {
  grid-template-columns: 30px minmax(200px, 2.3fr) minmax(132px, 0.9fr) 84px
                         minmax(132px, 1.05fr) 146px 84px 86px 28px;
}

/* Start / duration cells carry an optional "pin to cash-flow positive" rule. */
.rule-cell { display: flex; align-items: center; gap: 2px; min-width: 0; }
.rule-cell .inp { flex: 1 1 auto; min-width: 0; }
.rule-chip {
  flex: 1 1 auto; text-align: center; font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.02em; padding: 4px 4px; border-radius: 6px;
  color: var(--good); background: color-mix(in srgb, var(--good) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--good) 35%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rule-toggle {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  color: var(--text-muted); opacity: 0.35; padding: 2px 3px; font-size: 9px;
  border-radius: 4px; line-height: 1; flex: none;
  transition: opacity 140ms ease, color 140ms ease;
}
.rule-toggle:hover { opacity: 1; color: var(--accent); background: var(--surface-2); }
.rule-toggle.is-on { opacity: 1; color: var(--good); }

.warn-note {
  display: block; margin-top: 10px; padding: 9px 12px; border-radius: 10px;
  font-size: 12px; line-height: 1.45;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
}
@media (max-width: 1040px) {
  .item-row { grid-template-columns: 30px minmax(0, 1fr) minmax(0, 1fr); }
  .item-row.head { display: none; }
  .item-row > * { min-width: 0; }
}
select.inp-group { font-size: 12px; }

/* Expandable section rows in the monthly statement. */
tr.is-expandable { cursor: pointer; }
.caret {
  display: inline-block; margin-right: 6px; color: var(--text-muted);
  transition: transform 140ms ease; font-weight: 700;
}
.caret.is-open { transform: rotate(90deg); }
tr.is-sub td { background: color-mix(in srgb, var(--surface-2) 45%, transparent);
               color: var(--text-secondary); font-size: 12px; }
tr.is-sub td:first-child { padding-left: 26px; }
.sub-indent {
  display: inline-block; width: 12px; height: 9px; margin-right: 8px;
  border-left: 2px solid; border-bottom: 2px solid; border-radius: 0 0 0 3px;
  opacity: 0.55; vertical-align: 2px;
}

/* ------------------------------------------------------- 24-mo outlook -- */
/* Four now: the plan's cost, then a case either side of the middle one. Two-up in the middle band
   rather than four cramped columns. */
.ol-summary { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
@media (min-width: 1240px) { .ol-summary { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 760px) { .ol-summary { grid-template-columns: 1fr; } }
.ol-case {
  display: flex; flex-direction: column; gap: 3px;
  border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px;
  background: var(--surface-1);
}
.ol-case b { font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.ol-case span { font-size: 13.5px; }
.ol-case .ol-when { font-size: 11.5px; color: var(--text-muted); }
/* Expenses read as the thing being measured against, so they sit quieter than the two cases. */
.ol-cost { background: color-mix(in srgb, var(--text-primary) 4%, var(--surface-1)); }

/* The three price models. A small table, not a form: they are read across as much as edited. */
.pricing { margin-bottom: 16px; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
.pricing-head, .pricing-row {
  display: grid; grid-template-columns: minmax(120px, 1fr) 150px 170px; gap: 10px;
  align-items: center; padding: 9px 14px;
}
.pricing-head {
  background: color-mix(in srgb, var(--text-primary) 4%, var(--surface-1));
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted);
}
.pricing-row { border-top: 1px solid var(--border); }
.pricing-row label { font-size: 13px; font-weight: 600; }
@media (max-width: 760px) {
  .pricing-head { display: none; }
  .pricing-row { grid-template-columns: 1fr 1fr; }
  .pricing-row label { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------- revenue ---- */

/* on · name · price model · trucks low · trucks high · start · notes · delete */
/* on · name · price model · trucks low · trucks expected · trucks high · start · notes · delete */
.client-row {
  grid-template-columns: 30px minmax(150px, 1.7fr) minmax(108px, 0.7fr) 76px 76px 76px 148px
                         minmax(110px, 1.1fr) 28px;
}
/* The truck counts carry their own label. Wide, the column header says it already, so the label
   is hidden and the input fills the cell exactly as the other fields do. */
.crow-num { display: block; min-width: 0; }
.crow-num .inp { width: 100%; }
.crow-num > i { display: none; }
@media (max-width: 1040px) {
  .crow-num > i {
    display: block; font-style: normal; margin-bottom: 3px;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--text-muted);
  }
}

.plan-start { display: inline-flex; align-items: center; gap: 8px; }
.plan-start .inp { border-color: var(--border); background: var(--surface-1); width: 150px; }
/* Narrow: the row has EIGHT children, so collapsing to three columns simply reflowed them into
   implicit rows and whichever field landed in the 30px checkbox column was rendered 30px wide with
   its content clipped - rows reading 'C.', 'L.', 'B.' instead of a client name. Place the children
   explicitly instead: the toggle, the name and the delete keep the top line, and everything else
   stacks indented underneath, with the two truck counts sharing a line because they are read as a
   pair. */
@media (max-width: 1040px) {
  .client-row {
    grid-template-columns: 30px repeat(3, minmax(0, 1fr)) 28px;
    row-gap: 8px; align-items: center;
  }
  .client-row > :nth-child(1) { grid-column: 1; grid-row: 1; }
  .client-row > :nth-child(2) { grid-column: 2 / 5; grid-row: 1; }
  .client-row > :nth-child(9) { grid-column: 5; grid-row: 1; }
  .client-row > :nth-child(3) { grid-column: 2 / -1; }   /* price model    */
  .client-row > :nth-child(4) { grid-column: 2 / 3; }    /* trucks low     */
  .client-row > :nth-child(5) { grid-column: 3 / 4; }    /* trucks expected*/
  .client-row > :nth-child(6) { grid-column: 4 / 5; }    /* trucks high    */
  .client-row > :nth-child(7) { grid-column: 2 / -1; }   /* start date     */
  .client-row > :nth-child(8) { grid-column: 2 / -1; }   /* notes          */
  /* The header's eight labels cannot line up with a stacked row, and a header that points at the
     wrong control is worse than none. The controls keep their aria-label and title. */
  .client-row.head { display: none; }
}

/* With vs without revenue, side by side. */
.compare-pair {
  display: grid; gap: 1px; background: var(--border);
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  margin-bottom: 18px;
}
.pair { background: var(--surface-1); padding: 14px 16px; }
.pair.is-good { background: color-mix(in srgb, var(--good) 7%, var(--surface-1)); }
.pair.is-delta { background: var(--surface-2); }
.pair-label {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--text-muted);
}
.pair-value { font-size: 24px; font-weight: 660; letter-spacing: -0.03em; margin: 4px 0 2px; }
.pair-note { font-size: 11.5px; color: var(--text-secondary); line-height: 1.4; }
.pair-meta {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 9px;
  padding-top: 8px; border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--text-muted);
}
.pair-meta b { color: var(--text-primary); font-weight: 650; }


/* The backup link is an anchor so the browser handles the download itself. */
a.btn { text-decoration: none; }
@media (max-width: 1000px) { #btn-backup { display: none; } }

/* ------------------------------------------------ stepped-rate schedules */

.schedule-summary {
  appearance: none; cursor: pointer; width: 100%; text-align: left;
  border: 1px solid var(--border); background: var(--surface-1);
  border-radius: 8px; padding: 5px 8px; font-size: 12.5px; font-weight: 600;
  color: var(--text-primary); font-variant-numeric: tabular-nums;
  display: flex; align-items: center; gap: 5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.schedule-summary:hover { border-color: var(--border-strong); background: var(--surface-2); }
.schedule-summary .caret { color: var(--text-muted); transition: transform 140ms ease; }
.schedule-summary.is-open .caret { transform: rotate(90deg); }

.schedule-editor {
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface-2) 55%, var(--surface-1));
  padding: 8px 14px 10px 44px;
}
.step-head { font-size: 11.5px; color: var(--text-muted); margin-bottom: 7px; }
.step-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
.step-arrow { font-size: 11.5px; color: var(--text-muted); min-width: 32px; }
.step-at { font-size: 11.5px; color: var(--text-muted); }
.step-from { width: 62px; }
/* The chip is flex:1 in a table row, where it should match the month input. */
.step-row .rule-chip { flex: 0 0 62px; width: 62px; }
.step-amount { width: 112px; }
.step-cadence { font-size: 11.5px; color: var(--text-secondary); }
.step-row .inp { border-color: var(--border); background: var(--surface-1); }
.step-actions { display: flex; gap: 6px; margin-top: 8px; }
.rule-chip.is-muted {
  color: var(--text-muted); background: var(--surface-2);
  border-color: var(--border);
}

/* --------------------------------------------------------------- audit log */

.audit-when { color: var(--text-muted); font-size: 12px; white-space: nowrap; }
.audit-kind {
  display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-muted);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 5px; padding: 1px 6px; margin-right: 8px; vertical-align: 1px;
}
.audit-from { color: var(--text-muted); text-decoration: line-through; }
.audit-to { color: var(--text-primary); font-weight: 600; }
#table-audit td em { color: var(--text-secondary); font-style: normal; font-weight: 600; }

/* ------------------------------------------------- horizon in the solver */

/* The horizon drives every figure in this band, so it sits with the inputs
   rather than under the outputs it dictates. */
.horizon-row { margin-top: 16px; }
.horizon-row .field-label { display: block; margin-bottom: 7px; }
.horizon-row .field-label em {
  font-style: normal; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text-muted); margin-left: 6px;
}

/* A choice that changes every number on the page should look like a choice. */
.segmented.is-prominent {
  display: grid; grid-template-columns: repeat(4, 1fr); width: 100%;
  padding: 3px; border-radius: 11px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
}
.segmented.is-prominent button {
  padding: 8px 4px; font-size: 13px; font-weight: 600; border-radius: 8px;
  color: var(--text-secondary);
}
.segmented.is-prominent button:hover {
  background: color-mix(in srgb, var(--surface-1) 70%, transparent);
  color: var(--text-primary);
}
.segmented.is-prominent button[aria-pressed='true'] {
  background: var(--accent); color: #fff;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* The "where the money goes" card shares a row with the taller monthly chart,
   so its body stretches and the bars spread to fill it rather than leaving a
   block of white space underneath. */
@media (min-width: 1040px) {
  .board-row.split > .card { display: flex; flex-direction: column; }
  .board-row.split > .card > .card-body { flex: 1 1 auto; display: flex; flex-direction: column; }
  .board-row.split > .card > .card-body > #viz-sections { flex: 1 1 auto; min-height: 0; }
}

/* The distance from the spend curve to the funding line, drawn and labelled. */

/* Capital allocation reads section → sub-section → line, largest first. */
tr.alloc-section td {
  font-weight: 650; background: var(--surface-2);
  border-top: 1px solid var(--border-strong);
}
tr.alloc-group td {
  font-size: 12px; font-weight: 600; color: var(--text-secondary);
  background: color-mix(in srgb, var(--surface-2) 45%, transparent);
}
tr.alloc-group td:first-child { padding-left: 30px; }
tr.alloc-item.is-nested td:first-child { padding-left: 48px; }

/* ------------------------------------------------------------ theme menu */

.theme-menu { position: relative; }
.theme-dot {
  width: 10px; height: 10px; border-radius: 50%; display: inline-block;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
}
.theme-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: 248px; padding: 14px;
  background: var(--surface-1); border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: 0 2px 6px color-mix(in srgb, var(--text-primary) 8%, transparent),
              0 16px 36px -14px color-mix(in srgb, var(--text-primary) 30%, transparent);
}
.theme-section + .theme-section { margin-top: 14px; }
.theme-section .field-label { display: block; margin-bottom: 7px; }
.theme-section .segmented { width: 100%; display: grid; grid-template-columns: 1fr 1fr; }

.swatches { display: flex; gap: 7px; flex-wrap: wrap; }
.swatch {
  appearance: none; cursor: pointer; width: 26px; height: 26px; border-radius: 8px;
  background: var(--swatch); border: 1px solid color-mix(in srgb, var(--text-primary) 18%, transparent);
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.swatch:hover { transform: translateY(-1px); }
.swatch[aria-pressed='true'] {
  box-shadow: 0 0 0 2px var(--surface-1), 0 0 0 4px var(--swatch);
}
.theme-note {
  margin: 13px 0 0; padding-top: 11px; border-top: 1px solid var(--border);
  font-size: 11.5px; line-height: 1.45; color: var(--text-muted);
}
