/* ---------- Design tokens ---------- */
:root {
  --bg: #f6f7f9;
  --panel: #ffffff;
  --panel-2: #fbfbfc;
  --ink: #1a1c1f;
  --ink-2: #565b64;
  --muted: #8a909b;
  --line: #e7e9ee;
  --brand: #5b8def;
  --brand-ink: #ffffff;
  --pos: #1f9d67;
  --neg: #d6455a;
  --warn: #c67a1e;
  --accent-1: #5b8def;
  --accent-2: #7d6cf0;
  --accent-3: #12b3a6;
  --accent-4: #e8a13a;
  --accent-5: #d76b9a;
  --shadow: 0 1px 2px rgba(20,24,34,.06), 0 6px 18px rgba(20,24,34,.05);
  --radius: 14px;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1115;
    --panel: #171a20;
    --panel-2: #1c2028;
    --ink: #eceef2;
    --ink-2: #b6bcc7;
    --muted: #7f8794;
    --line: #262b34;
    --brand: #6f9bf2;
    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.28);
  }
}

/* Audience segments — a deliberately monochrome scale, matching the ad team's
   own reporting. Identity is carried by lightness rather than hue, which is the
   one categorical scheme that survives every form of colour-vision deficiency
   unchanged. Adjacent steps are >=15 apart in OKLab L in both themes (measured,
   not eyeballed); dark mode is stepped separately against the dark panel rather
   than flipped, because an inverted light ramp lands too close together at the
   bottom end. Bars carry a hairline edge so the faintest step still reads
   against the surface, and every bar is direct-labelled. */
:root {
  --aud-engaged: #1a1c1f;
  --aud-prospecting: #565b64;
  --aud-existing: #8a909b;
  --aud-unclassified: #c3c8d1;
}
@media (prefers-color-scheme: dark) {
  :root {
    --aud-engaged: #eceef2;
    --aud-prospecting: #b6bcc7;
    --aud-existing: #7f8794;
    --aud-unclassified: #515863;
  }
}

/* Metal ranges — hue-coded, unlike the audience scale above, because these five
   are the buckets the merchandising team already thinks in and each has a
   settled identity: gold reads warm, silver cool, men indigo, gifting green,
   demi-fine the house rose. A lightness ramp would have been the safer scheme,
   but it is already spoken for one block up and two grey ramps on one dashboard
   is how a reader stops trusting either.

   Both sets were computed rather than picked: each clears the OKLCH lightness
   band and chroma floor for its own surface, keeps every adjacent pair >=15
   OKLab dE apart under normal vision (17.4 light, 15.1 dark) and holds >=3:1
   WCAG contrast against the panel it sits on. Dark is stepped against the dark
   panel at L 0.64, not flipped — the light steps sit at L 0.75+ there and blow
   out.

   Under simulated colour-vision deficiency the light set clears 8 dE on every
   adjacent pair (worst 9.0, deutan, gifting/demi-fine). The dark set's worst
   adjacent pair is that same one at **7.3 dE**, inside the 6-8 floor band rather
   than above it. That is deliberate and it is the ceiling available: gifting's
   hue was chosen by maximising its worst CVD distance to the four colours
   already shipping, and moving demi-fine to buy more would repaint a range
   people are already reading. The floor band is only legal with secondary
   encoding, so it is mandatory here and present: every bar carries its own
   value label and its range name on the axis, the legend is always drawn, and
   every table row names its range. Colour is never the only cue. */
:root {
  --metal-gold: #c2870f;
  --metal-silver: #0f9aa4;
  --metal-men: #5b6ee0;
  --metal-gifting: #22a968;
  --metal-demifine: #c44f96;
}
@media (prefers-color-scheme: dark) {
  :root {
    --metal-gold: #b78016;
    --metal-silver: #079fa9;
    --metal-men: #6f83e5;
    --metal-gifting: #1ca665;
    --metal-demifine: #c26a9d;
  }
}

.chart-legend { display: flex; gap: 18px; justify-content: center;
  flex-wrap: wrap; margin: 2px 0 10px; }
.chart-legend .item { display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--ink-2); }
.chart-legend .sw { width: 11px; height: 11px; border-radius: 50%;
  flex: 0 0 auto; box-shadow: inset 0 0 0 1px rgba(128,128,128,.28); }

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--ink);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}
.muted { color: var(--muted); }

/* ---------- Layout ---------- */
.app { display: grid; grid-template-columns: 232px 1fr; min-height: 100vh; }

.sidebar {
  background: var(--panel);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  position: sticky;
  top: 0;
  height: 100vh;
  /* Nothing escapes the sidebar; the nav inside it owns the scrolling. */
  overflow: hidden;
}
.brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px 18px; }
.brand-mark { color: var(--brand); font-size: 20px; }
.brand-text { font-weight: 700; font-size: 17px; letter-spacing: .2px; }

/* The nav scrolls on its own, so the brand above it and the sync/sign-out box
   below stay put however many tabs there are.
   `min-height: 0` is the load-bearing half: a flex item defaults to
   `min-height: auto`, which refuses to shrink below its content, so `flex: 1`
   alone let the list grow past the sidebar's 100vh and pushed the footer off
   the bottom — reaching the last tab meant scrolling the whole dashboard. */
.nav {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1; min-height: 0; overflow-y: auto;
  /* Room for the scrollbar so it never sits on top of a label. */
  padding-right: 4px; margin-right: -4px;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.nav::-webkit-scrollbar { width: 6px; }
.nav::-webkit-scrollbar-track { background: transparent; }
.nav::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.nav::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.nav-item {
  display: flex; align-items: center; gap: 10px;
  border: 0; background: transparent; color: var(--ink-2);
  padding: 10px 10px; border-radius: 10px; cursor: pointer;
  font-size: 13.5px; text-align: left; width: 100%; transition: .12s;
}
.nav-item .ic { width: 18px; text-align: center; font-size: 13px; }
.nav-item:hover { background: var(--panel-2); color: var(--ink); }
.nav-item.active { background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand); font-weight: 600; }

.sidebar-foot { border-top: 1px solid var(--line); padding-top: 12px; margin-top: 8px; }
.sync-box { margin-bottom: 12px; }
.sync-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.sync-label { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.sync-detail { font-size: 11px; color: var(--muted); margin-top: 3px; line-height: 1.45; }
.sync-detail.busy { color: var(--brand); }
.sync-detail.err { color: var(--neg); }
.filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 14px; }
.filters input, .filters select {
  background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 9px; padding: 7px 10px; font-size: 13px; font-family: inherit;
}
.filters input { min-width: 200px; }
.filters input:focus, .filters select:focus { outline: none; border-color: var(--brand); }
.pager { display: flex; gap: 8px; align-items: center; justify-content: flex-end; margin-top: 12px; font-size: 13px; color: var(--muted); }
.attr-cell { font-size: 12px; color: var(--ink-2); }
.attr-cell .dim { color: var(--muted); }
.store-name { font-weight: 600; font-size: 13px; }
.store-domain { color: var(--muted); font-size: 11.5px; }

.main { padding: 22px 26px 40px; min-width: 0; }
.topbar { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 18px; }
.topbar h1 { margin: 0 0 2px; font-size: 22px; }
.topbar-controls { display: flex; align-items: center; gap: 12px; }
.range { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 12.5px; }
.range select {
  background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 9px; padding: 7px 10px; font-size: 13px;
}
.badge { font-size: 11px; font-weight: 600; padding: 4px 9px; border-radius: 999px; text-transform: uppercase; letter-spacing: .4px; }
.badge-mock { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.badge-live { background: color-mix(in srgb, var(--pos) 16%, transparent); color: var(--pos); }

.warning-bar {
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  color: var(--warn); padding: 10px 14px; border-radius: 10px; margin-bottom: 16px; font-size: 13px;
  line-height: 1.55;
}
.ok-bar {
  background: color-mix(in srgb, var(--pos) 11%, transparent);
  border: 1px solid color-mix(in srgb, var(--pos) 32%, transparent);
  color: var(--pos); padding: 10px 14px; border-radius: 10px; margin-bottom: 16px; font-size: 13px;
  line-height: 1.55;
}
.inline-fix {
  background: none; border: 0; padding: 0; margin-left: 4px;
  color: inherit; font: inherit; font-weight: 700; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.inline-fix:hover { opacity: .78; }

/* ---------- Cards / grids ---------- */
.grid { display: grid; gap: 16px; }
/* Grid items default to min-width:auto, so a wide table props its card open and
   the overflow escapes to the page instead of scrolling inside .table-wrap.
   Cells are white-space:nowrap, so a table only has to gain a column to hit it. */
.grid > * { min-width: 0; }
/* auto-fit rather than a fixed 6 columns: INR amounts run long (₹296,882,144)
   and would otherwise overflow their card. */
.kpi-grid { grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); }
.cols-2 { grid-template-columns: 2fr 1fr; }
/* For pairs where both halves are tables of similar weight, rather than the
   chart-plus-table pairing 2fr/1fr is sized for. */
.cols-even { grid-template-columns: 1fr 1fr; }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 1100px) { .kpi-grid { grid-template-columns: repeat(3, 1fr); } .cols-2, .cols-even, .cols-3 { grid-template-columns: 1fr; } }
@media (max-width: 720px) {
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .app { grid-template-columns: 1fr; }
  /* Stacked layout: the sidebar is just the top of the page, so the nav goes
     back to its natural height rather than becoming a second scroll box. */
  .sidebar { position: static; height: auto; overflow: visible; }
  .nav { overflow-y: visible; min-height: auto; }
}

.card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; box-shadow: var(--shadow);
}
.card h3 { margin: 0 0 12px; font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
.card-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 12px; }
.card-head h3 { margin: 0; }

.kpi .kpi-label { color: var(--muted); font-size: 12px; margin-bottom: 8px; }
.kpi .kpi-value { font-size: clamp(18px, 1.55vw, 24px); font-weight: 700; letter-spacing: -.3px; }
.kpi .kpi-delta { font-size: 12px; margin-top: 6px; display: inline-flex; align-items: center; gap: 4px; }
.delta-pos { color: var(--pos); }
.delta-neg { color: var(--neg); }

.section-gap { margin-top: 16px; }

/* A card head carrying more than one control. .card-head is a two-way split, so
   the caption, any switch and the download button travel as a single node. */
.head-tools { display: inline-flex; align-items: center; gap: 10px; }
.dl-btn {
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-2);
  border-radius: 9px; padding: 5px 10px; cursor: pointer;
  font: 600 12px/1 inherit; font-family: inherit; white-space: nowrap; transition: .12s;
}
.dl-btn:hover { color: var(--brand); border-color: var(--brand); }
.dl-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ---------- Tables ---------- */
.table-wrap { overflow-x: auto; }
/* Inventory lists every SKU in scope rather than a top-N slice, so the tables
   scroll inside the card. Without a height the sticky header has no scrolling
   ancestor of its own and rides off the top of the page with the rows. */
.table-tall { max-height: 460px; overflow-y: auto; }
td code { font-size: 11.5px; color: var(--ink-2); background: var(--panel-2);
          border: 1px solid var(--line); border-radius: 5px; padding: 2px 6px; }
/* Collab product titles run to ~90 characters ("… X LoveChild By Masaba Bare
   Browns Luxe Matte Lipstick"). Cells are nowrap, so one of those pushes days
   left, on hand and net sales past the right edge — the columns the table
   exists for. Clip the name instead; the full text stays in the tooltip. */
td.cell-wide { max-width: 232px; overflow: hidden; text-overflow: ellipsis; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { color: var(--muted); font-weight: 600; font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px; position: sticky; top: 0; background: var(--panel); }
tbody tr:hover { background: var(--panel-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

.pill { font-size: 11px; font-weight: 600; padding: 3px 8px; border-radius: 999px; display: inline-block; }
.pill-green { background: color-mix(in srgb, var(--pos) 16%, transparent); color: var(--pos); }
.pill-red { background: color-mix(in srgb, var(--neg) 16%, transparent); color: var(--neg); }
.pill-amber { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.pill-gray { background: color-mix(in srgb, var(--muted) 18%, transparent); color: var(--ink-2); }
.pill-blue { background: color-mix(in srgb, var(--brand) 16%, transparent); color: var(--brand); }

/* ---------- Charts ---------- */
.chart { width: 100%; display: block; }
.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--ink-2); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

.bar-row { display: grid; grid-template-columns: 130px 1fr auto; align-items: center; gap: 10px; padding: 7px 0; }
.bar-row .label { color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { background: var(--panel-2); border-radius: 6px; height: 10px; overflow: hidden; border: 1px solid var(--line); }
.bar-fill { height: 100%; border-radius: 6px; }
.bar-row .val { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 600; }

.mini-list { display: flex; flex-direction: column; gap: 2px; }
.stat-inline { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--line); }
.stat-inline:last-child { border-bottom: 0; }

.loading { color: var(--muted); padding: 40px; text-align: center; }
.empty { color: var(--muted); padding: 24px; text-align: center; font-size: 13px; }

/* ---------- Filter controls ---------- */
.filter-wrap { position: relative; }
.dropdown {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow); padding: 8px; min-width: 230px; max-height: 340px; overflow-y: auto;
}
/* The Daily tab's own channel menu borrows this look rather than restating it,
   so the two pickers cannot drift apart visually. */
.dropdown .chk, .chan-menu .chk {
  display: flex; align-items: center; gap: 9px; padding: 8px 9px; border-radius: 8px;
  font-size: 13px; cursor: pointer; color: var(--ink-2);
}
.dropdown .chk:hover, .chan-menu .chk:hover { background: var(--panel-2); }
.dropdown .chk input, .chan-menu .chk input { accent-color: var(--brand); width: 15px; height: 15px; }
.dropdown .chk-actions, .chan-menu .chk-actions { display: flex; justify-content: space-between; padding: 6px 6px 8px; border-top: 1px solid var(--line); margin-top: 6px; }
.dropdown .link-btn, .chan-menu .link-btn { background: none; border: 0; color: var(--brand); font-size: 12px; cursor: pointer; font-weight: 600; }

.chan-pick { position: relative; }
.chan-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow); padding: 8px; min-width: 250px; max-height: 340px; overflow-y: auto;
}
.chan-menu[hidden] { display: none; }
.custom-range { display: flex; align-items: center; gap: 6px; }
.custom-range[hidden] { display: none; }  /* class display:flex would beat [hidden] */
.custom-range input[type=date] {
  background: var(--panel); color: var(--ink); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 8px; font-size: 12.5px; font-family: inherit; color-scheme: light dark;
}
.custom-range .arrow { color: var(--muted); }

/* ---------- Funnel ---------- */
.funnel { display: flex; flex-direction: column; gap: 8px; }
.funnel-row { display: grid; grid-template-columns: 130px 1fr; align-items: center; gap: 12px; }
.funnel-row .flabel { color: var(--ink-2); font-size: 13px; }
.funnel-bar { position: relative; background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; height: 34px; overflow: hidden; }
.funnel-fill { height: 100%; display: flex; align-items: center; padding-left: 10px; color: #fff; font-weight: 600; font-size: 12.5px; border-radius: 8px 0 0 8px; white-space: nowrap; }
.funnel-pct { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---------- Segmented controls (Funnels tab) ---------- */
.seg-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* .seg-row carries no margin of its own, so stacked rows would sit flush. */
.seg-row + .seg-row { margin-top: 10px; }
.seg-spacer { flex: 1; }
.seg {
  display: inline-flex; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 10px; padding: 3px; gap: 3px;
}
.seg-btn {
  border: 0; background: transparent; color: var(--ink-2); cursor: pointer;
  font-size: 12.5px; font-weight: 600; padding: 6px 12px; border-radius: 8px;
  font-family: inherit; white-space: nowrap; transition: .12s;
}
.seg-btn:hover { color: var(--ink); }
.seg-btn.on { background: var(--panel); color: var(--brand); box-shadow: var(--shadow); }
.seg-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* A .seg used as a card's own control. .card-head aligns on the baseline, which
   for an inline-flex box is its last line's — that dropped the switch a few
   pixels below the heading it sits beside. */
.card-head .seg { align-self: center; }
tr.no-sess td { opacity: .7; }
.sub-note { font-size: 11px; color: var(--muted); margin-top: 2px; }
/* period-over-period change, sitting under the figure it belongs to */
.d { font-size: 11px; font-weight: 600; margin-top: 3px; font-variant-numeric: tabular-nums; }
.d-up { color: var(--pos); }
.d-down { color: var(--neg); }
.d-new { color: var(--brand); font-weight: 700; }
/* Change without a verdict — ad spend moving is not itself good or bad. */
.d-flat { color: var(--muted); }

/* ---- hourly tab ---- */
/* Eleven columns of figures, each with a delta under it, so the default row
   padding would push a day off the screen. */
table.tight th, table.tight td { padding: 7px 10px; }
/* An ordinary row is two lines tall — figure plus its change underneath. The
   totals and the partial hour have no change to show, so without this they come
   out 15px shorter and read as squeezed against the rows below.
   Selector has to out-specify `table.tight td` above, which is why it repeats
   the table qualifier rather than standing alone. */
table.tight .row-total td,
table.tight .row-partial td { padding-top: 14px; padding-bottom: 14px; }
.row-total td { background: var(--panel-2); font-weight: 700; border-bottom: 2px solid var(--line); }
/* The hour still running. Dimmed because it is not comparable to the rows
   below it — it has had less time to earn what it is being read against. */
.row-partial td { opacity: .72; font-style: italic; }
.tag-partial {
  display: inline-block; margin-left: 8px; padding: 2px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 700; font-style: normal; letter-spacing: .3px;
  background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn);
}
/* A qualifier under a column head, for a column that means something narrower
   than its name suggests. */
.hdr-note {
  display: block; font-size: 9.5px; font-weight: 600; letter-spacing: .2px;
  text-transform: none; color: var(--warn); opacity: .85; margin-top: 1px;
}
.hour-pick {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--muted); font-size: 12.5px; font-weight: 600;
}
.hour-pick input {
  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: 8px; padding: 6px 9px; font-family: inherit; font-size: 12.5px;
}
.pace-row { display: flex; flex-wrap: wrap; gap: 10px 28px; }
.pace-item { min-width: 92px; }
.pace-label { color: var(--muted); font-size: 11.5px; margin-bottom: 4px; }
.pace-val { font-size: clamp(16px, 1.3vw, 21px); font-weight: 700; letter-spacing: -.3px; }
/* The hourly tab fixes its own window and channel set, so these controls would
   be levers wired to nothing. Hidden by class rather than by the `hidden`
   attribute, which the period select owns — leaving it be means returning to
   another tab restores whatever state that select was in. */
.topbar-controls.no-filters .filter-wrap,
.topbar-controls.no-filters .range,
.topbar-controls.no-filters .custom-range { display: none; }
.cmp-note { font-size: 12.5px; color: var(--muted); margin: 12px 0 0; }
.cov-note {
  font-size: 12.5px; color: var(--ink-2); line-height: 1.55;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 9px 12px; margin: 0 0 12px;
}
/* The shorthand above sets margin-top to 0, and it is declared after
   .section-gap, so `class="cov-note section-gap"` silently lost its top margin
   and the note sat flush against the controls above it. */
.cov-note.section-gap { margin-top: 16px; }

.src-tag {
  display: inline-block; margin-left: 8px; padding: 2px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .03em; vertical-align: middle;
  background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand);
}
.dim-hint { font-size: 12.5px; color: var(--muted); margin: 10px 0 0; }
.cols-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .cols-4 { grid-template-columns: repeat(2, 1fr); } }

/* ---------- Highlights (the "what to look at" strip) ---------- */
.hl-wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.hl {
  display: grid; grid-template-columns: auto 1fr; gap: 12px; align-items: start;
  background: var(--panel); border: 1px solid var(--line); border-left-width: 3px;
  border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
}
.hl-mark {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  font-size: 11px; font-weight: 800; flex: none;
}
.hl-top { font-size: 13.5px; color: var(--ink); line-height: 1.45; }
.hl-detail { font-size: 12.5px; color: var(--muted); margin-top: 4px; line-height: 1.5; }
.hl-good { border-left-color: var(--pos); }
.hl-good .hl-mark { background: color-mix(in srgb, var(--pos) 16%, transparent); color: var(--pos); }
.hl-warn { border-left-color: var(--warn); }
.hl-warn .hl-mark { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.hl-bad { border-left-color: var(--neg); }
.hl-bad .hl-mark { background: color-mix(in srgb, var(--neg) 16%, transparent); color: var(--neg); }

/* traffic bar sits under the row label rather than in its own column */
td .mini-bar { display: block; margin-top: 5px; max-width: 180px; }

.mini-bar {
  display: inline-block; height: 7px; border-radius: 3px;
  background: var(--brand); vertical-align: middle; min-width: 2px;
}
.note { font-size: 12px; color: var(--muted); margin-top: 10px; line-height: 1.5; }
.note code { font-size: 11px; }

/* ---------- Buttons ---------- */
.btn {
  border: 1px solid var(--line); background: var(--panel); color: var(--ink);
  padding: 8px 14px; border-radius: 9px; font-size: 13px; font-weight: 600; cursor: pointer;
  transition: .12s;
}
.btn:hover { background: var(--panel-2); }
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.btn-primary:hover { filter: brightness(1.05); background: var(--brand); }
.btn-primary:disabled { opacity: .6; cursor: default; }
.btn-danger { background: transparent; border-color: color-mix(in srgb, var(--neg) 50%, transparent); color: var(--neg); }
.btn-danger:hover { background: color-mix(in srgb, var(--neg) 12%, transparent); }
.icon-btn { border: 0; background: transparent; color: var(--muted); font-size: 16px; cursor: pointer; padding: 4px 8px; border-radius: 8px; }
.icon-btn:hover { background: var(--panel-2); color: var(--ink); }

/* ---------- Modal ---------- */
.overlay {
  position: fixed; inset: 0; background: rgba(10,12,16,.55);
  display: flex; align-items: center; justify-content: center; padding: 20px; z-index: 50;
  backdrop-filter: blur(2px);
}
.overlay[hidden] { display: none; }  /* class display:flex would otherwise beat [hidden] */
.modal {
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: var(--shadow); width: 100%; max-width: 460px; padding: 22px 24px;
}
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.modal-head h2 { margin: 0; font-size: 18px; }
.field { display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }
.field > span { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.field input {
  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: 9px; padding: 10px 12px; font-size: 13.5px; font-family: inherit;
}
.field input:focus { outline: none; border-color: var(--brand); }
.hint { color: var(--muted); font-size: 11.5px; line-height: 1.5; }
.hint code, .privacy-note code, code { background: var(--panel-2); border: 1px solid var(--line); border-radius: 5px; padding: 1px 5px; font-size: 11.5px; }
.form-error {
  margin-top: 14px; background: color-mix(in srgb, var(--neg) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--neg) 40%, transparent); color: var(--neg);
  padding: 10px 12px; border-radius: 9px; font-size: 12.5px; line-height: 1.5;
}
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
.privacy-note { color: var(--muted); font-size: 11px; line-height: 1.5; margin: 14px 0 0; }
#connected-state { margin: 8px 0 4px; }
#connected-state .btn { margin-top: 12px; }

/* Original id kept beside a mapped campaign name, so rows stay traceable
   back to Google Ads. */
.id-tag {
  display: inline-block; margin-left: 7px; padding: 1px 6px; border-radius: 5px;
  font-size: 10.5px; font-weight: 600; vertical-align: middle;
  background: var(--panel-2); border: 1px solid var(--line); color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* Shown only when logins are enabled. */
.signout { font-size: 11px; color: var(--muted); margin-bottom: 8px; line-height: 1.5; }
.signout a { color: var(--brand); text-decoration: none; font-weight: 600; }
.signout a:hover { text-decoration: underline; }

/* ---- CM2 tab ---- */
/* Period stays visible here — the ladder is a period P&L — but the channel
   picker does not: the four online channels are fixed, because the ladder
   charges every unit a shipping leg, a gateway fee and a share of ad spend
   that a retail counter sale never incurs. */
.topbar-controls.no-channels .filter-wrap { display: none; }
/* Eighteen columns of figures. Same treatment as the hourly table: without it
   a SKU row runs off the side before the margin columns are reached. */
table.ladder th, table.ladder td { padding: 7px 9px; font-size: 12.5px; }
table.ladder td.name { white-space: normal; min-width: 190px; max-width: 280px; }
table.ladder .sku { display: block; font-size: 10.5px; color: var(--muted); margin-top: 2px; }
/* The three margin lines. A left rule marks where each stage of the ladder
   begins, so GM1/GM2/CM1/CM2 read as four blocks rather than one wall of
   numbers. */
table.ladder th.stage, table.ladder td.stage { border-left: 1px solid var(--line); }
table.ladder td.neg { color: var(--neg); }
/* The composition bar: one unit of ASP, split into where it goes. */
.asp-bar { display: flex; height: 26px; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); }
.asp-seg { display: flex; align-items: center; justify-content: center; font-size: 10.5px; font-weight: 700; color: #fff; min-width: 0; overflow: hidden; white-space: nowrap; }
.asp-legend { display: flex; flex-wrap: wrap; gap: 10px 16px; margin-top: 12px; font-size: 12px; color: var(--ink-2); }
.asp-legend span { display: inline-flex; align-items: center; gap: 6px; }
.asp-legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.asp-legend b { font-variant-numeric: tabular-nums; color: var(--ink); }
/* The formula chain, so a figure here can be reconciled against the sheet
   line by line rather than taken on trust. */
.basis { display: flex; flex-direction: column; gap: 0; font-size: 12.5px; }
.basis-row { display: grid; grid-template-columns: 1fr auto; gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--line); align-items: baseline; }
.basis-row:last-child { border-bottom: 0; }
.basis-row .f { color: var(--muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; }
.basis-row .v { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
.basis-row.step .v { color: var(--brand); }

/* A .cov-note that is a warning rather than context — the spend sheet not
   covering every day on screen makes CM2 read high, which has to be visible
   before the number is, not in a footnote under it. */
.warn-note {
  border-color: color-mix(in srgb, var(--warn) 45%, var(--line));
  background: color-mix(in srgb, var(--warn) 9%, var(--panel-2));
  color: var(--ink-2);
}
.warn-note strong { color: var(--warn); }

/* ---------- CM2 assumption inputs ----------
   The four rates the CM2 ladder rests on that this store cannot measure. An
   overridden box is tinted and its label goes brand-coloured, because the one
   thing a reader must never do is mistake an assumption someone typed for a
   figure Shopify reported. */
.rate-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; margin-bottom: 12px;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 12px;
}
/* The boxes wrap inside here rather than loose in the row. Loose, they were
   flex items in their own right, so filling the width left Reset — which
   carries margin-left:auto — stranded on a line of its own with a 30px empty
   band beside it. As one item they wrap among themselves and Reset stays put.
   The 340px basis is what decides the layout: above it the lead, the boxes and
   Reset share a line; below it the boxes take a row to themselves and Reset
   drops under them. A flex basis rather than a media query because what
   constrains this row is the *column* it sits in, and that depends on the
   sidebar — a viewport breakpoint was wrong by ~300px and squeezed five inputs
   into a 241px column at 726px wide. */
.rate-fields {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  flex: 1 1 340px; min-width: 0;
}
/* A rate row following the compare-mode buttons would otherwise sit flush
   against them — .seg-row carries no margin of its own by design. */
.seg-row + .rate-row { margin-top: 12px; }
.rate-lead {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; cursor: help; margin-right: 2px;
}
.rate-in {
  display: inline-flex; align-items: center; gap: 6px; cursor: help;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 9px; padding: 4px 8px 4px 10px;
}
.rate-lbl { font-size: 12px; color: var(--ink-2); font-weight: 600; white-space: nowrap; }
.rate-in input {
  width: 58px; border: 0; background: transparent; color: var(--ink);
  font: 600 13px/1 inherit; font-family: inherit; text-align: right;
  padding: 2px 0; -moz-appearance: textfield; appearance: textfield;
}
/* The spinner Chrome draws on focus lands on top of the digits in a box this
   narrow — "16.54" reads as "16.5⇅". Firefox was already told to drop it. */
.rate-in input::-webkit-outer-spin-button,
.rate-in input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.rate-in input:focus { outline: 0; }
.rate-in:focus-within { border-color: var(--brand); }
.rate-pc { font-size: 12px; color: var(--muted); }
/* Overridden — no longer the finance sheet's own figure. */
.rate-in.on { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 7%, var(--panel)); }
.rate-in.on .rate-lbl { color: var(--brand); }
.rate-reset {
  flex: 0 0 auto; align-self: center; margin-left: auto;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--ink-2); border-radius: 9px; padding: 6px 12px; cursor: pointer;
  font: 600 12.5px/1 inherit; font-family: inherit; transition: .12s;
}
.rate-reset:hover:not(:disabled) { color: var(--ink); border-color: var(--muted); }
.rate-reset:disabled { opacity: .4; cursor: default; }
/* Stacked layout: Reset reads better under the boxes on the left than pinned to
   the far right of a row it is the only thing on. */
@media (max-width: 720px) {
  .rate-reset { margin-left: 0; }
}

/* ---------- Pool Health ----------
   Stock health is a status scale, not a set of categories, so it gets its own
   tokens rather than reusing --pos/--warn/--neg. Those three are the store's
   semantic trio and they are the right colours for a labelled pill, but they
   are near-identical under deuteranopia (green vs red measures ΔE 2.6 in OKLab,
   where 8 is the floor). The mix bar puts the three states edge to edge with no
   text between them, so the ramp below also *darkens* with severity: the
   ordering survives with no colour vision at all. Light and dark are stepped
   separately against their own surface rather than flipped — every pair clears
   ΔE 8 under both protanopia and deuteranopia, and every step clears 3:1
   contrast against the panel it sits on. */
:root {
  --health-ok: #2f9e72;
  --health-low: #b8791f;
  --health-out: #94303f;
}
@media (prefers-color-scheme: dark) {
  :root {
    --health-ok: #3daa7d;
    --health-low: #c68020;
    --health-out: #bd4a5c;
  }
}

.pill-ok   { background: color-mix(in srgb, var(--health-ok) 15%, transparent);  color: var(--health-ok); }
.pill-low  { background: color-mix(in srgb, var(--health-low) 16%, transparent); color: var(--health-low); }
.pill-out  { background: color-mix(in srgb, var(--health-out) 16%, transparent); color: var(--health-out); }

/* The bar is the only place colour carries meaning without a label beside it,
   so the counts live in their own columns and the legend is always present. */
.mix { display: flex; gap: 2px; height: 11px; min-width: 130px; }
.mix > i { display: block; border-radius: 2px; }
.mix > i:first-child { border-radius: 4px 2px 2px 4px; }
.mix > i:last-child { border-radius: 2px 4px 4px 2px; }
.mix > i:only-child { border-radius: 4px; }
.mix-ok { background: var(--health-ok); }
.mix-low { background: var(--health-low); }
.mix-out { background: var(--health-out); }
.mix-none { background: color-mix(in srgb, var(--muted) 55%, transparent); }
td.mix-cell { width: 26%; min-width: 150px; }

.health-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 12px;
  font-size: 12px; color: var(--ink-2); }
.health-legend span { display: inline-flex; align-items: center; gap: 6px; }
.health-legend i { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto;
  box-shadow: inset 0 0 0 1px rgba(128,128,128,.25); }

.num-ok { color: var(--health-ok); }
.num-low { color: var(--health-low); }
.num-out { color: var(--health-out); }

/* The catalogue crawl takes ~33s, so the tab has a real waiting state. */
.index-card { text-align: center; padding: 40px 24px; }
.index-card h3 { margin: 0 0 8px; }
.index-card p { color: var(--ink-2); font-size: 13px; max-width: 46ch;
  margin: 0 auto 8px; line-height: 1.6; }
.index-card .progress { font-size: 12px; color: var(--muted); margin-top: 14px; }

/* ---- web vs app ---- */
/* A KPI tile holding two storefronts instead of one figure. Every number on
   that tab is only meaningful beside its counterpart, so the pairing belongs
   inside the card rather than being left to the reader to do across ten of
   them. Wider than a normal tile because it carries two labelled rows. */
.kpi-split { min-width: 0; }
.kpi-split .split-row { display: flex; align-items: baseline;
  justify-content: space-between; gap: 10px; padding: 5px 0; }
.kpi-split .split-row + .split-row { border-top: 1px solid var(--line); }
.kpi-split .split-key { display: inline-flex; align-items: center; gap: 6px;
  color: var(--muted); font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.kpi-split .split-key i { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.kpi-split .split-val { font-size: clamp(14px, 1.05vw, 17px); font-weight: 700;
  letter-spacing: -.2px; text-align: right; display: flex; flex-direction: column;
  align-items: flex-end; }
/* The change keeps its own line under the figure, as it does everywhere else on
   the dashboard. The slot is reserved whether or not there is a change to put in
   it: a long rupee figure used to wrap its delta and push the divider below it
   down, so the two storefronts sat at different heights from one card to the
   next along the row. */
.kpi-split .split-d { display: block; min-height: 15px; }
.kpi-split .split-val .d { margin: 1px 0 0; }
@media (min-width: 1101px) { .kpi-split { min-width: 210px; } }

/* The spanning header that says which five columns belong to which storefront.
   Colour-matched to the chart lines, because the table and the chart above it
   are the same two series and nothing else on the row says which is which. */
.grp-head th { border-bottom: 0; padding-bottom: 2px; z-index: 3; }
.grp-head .grp {
  text-align: center; color: var(--grp); letter-spacing: .04em;
  border-bottom: 2px solid color-mix(in srgb, var(--grp) 45%, transparent);
}
/* Two header rows, and `th` is sticky at top:0 for all of them — left alone the
   second would ride up over the first as the page scrolls. The offset is the
   group row's own height: 7px padding + a 17px line + 2px + its 2px rule.
   Shared with the A/B table, which has the same two-row head for the same
   reason: one rule, so the two cannot drift a pixel apart. */
.wva-table thead tr:last-child th,
.ab-table thead tr:last-child th,
.pr-table thead tr:last-child th { top: 28px; z-index: 2; }
/* A gutter column, so the eye does not have to count five cells to find where
   one storefront ends and the other begins. */
.wva-table .wva-gap,
.ab-table .ab-gap-col,
.pr-table .pr-gap { width: 14px; min-width: 14px; padding: 0; border-bottom: 0; }

/* ---------- Pool Report ----------
   Eight groups wide by up to seven metrics each, so the day column has to stay
   put while the rest scrolls; without it the reader loses which row they are on
   about three pools in. */
.pr-table th:first-child,
.pr-table td:first-child {
  position: sticky; left: 0; z-index: 1;
  background: var(--panel); border-right: 1px solid var(--line);
}
.pr-table thead th:first-child { z-index: 4; }
.pr-table .row-total td:first-child { background: var(--panel-2); }

/* Prose in the coverage card. Left to itself a sentence runs the full width of a
   1900px monitor, which is about 200 characters a line and unreadable; the
   paragraphs also need their own rhythm, since .card only spaces its heading. */
.pr-prose { max-width: 96ch; margin: 0 0 10px; line-height: 1.55; }
.pr-prose:last-of-type { margin-bottom: 0; }

/* The coverage table is six columns of which one is a name and five are figures.
   Without this the browser shares the width equally and the numbers drift a
   third of a screen away from the platform they belong to. */
.pr-cov th:first-child, .pr-cov td:first-child { width: 40%; }

/* The pool the picker is on, marked in the summary so the selection is visible
   in the comparison table as well as in the matrix it filters. */
.pr-table tbody tr.pr-sel td, tr.pr-sel td { background: var(--panel-2); }
tr.pr-sel td:first-child { box-shadow: inset 2px 0 0 var(--accent, #5b8def); }

/* ---------- Headless A/B Test ----------
   Thirteen columns of ratios, where the reader's whole job is telling two
   things apart: the two arms, and whether the difference between them is real.
   Everything below serves one of those. */

/* Gaps are signed from the headless arm's point of view — green is headless
   ahead — and sit in a tabular figure so a column of them scans as a column. */
.ab-gap { font-weight: 700; font-variant-numeric: tabular-nums; }
.ab-gap.up { color: var(--pos); }
.ab-gap.down { color: var(--neg); }
.kpi .ab-gap { font-size: inherit; }

/* A p-value is the one number here that gets read as a verdict, so a
   significant one is marked rather than left to be spotted: 0.041 and 0.410 are
   two characters apart on screen and opposite conclusions. */
.ab-p { font-variant-numeric: tabular-nums; color: var(--muted); }
.ab-p.sig {
  color: var(--brand); font-weight: 700;
  background: color-mix(in srgb, var(--brand) 12%, transparent);
  border-radius: 5px; padding: 1px 6px;
}

/* The headline sentence. The left rule is the only colour on the tab that
   states a conclusion, so it is only ever painted when the test actually
   reached one — `flat` stays neutral rather than picking a side quietly. */
.ab-verdict { border-left: 3px solid var(--muted); }
.ab-verdict.win { border-left-color: var(--pos); }
.ab-verdict.loss { border-left-color: var(--neg); }
.ab-verdict.flat { border-left-color: var(--warn); }
.ab-verdict strong:first-child { color: var(--ink); }


/* ---------- Loss of Revenue ----------
   The group header repeats Web vs App's two-row pattern, but the three groups
   here are stages of one calculation rather than competing series, so they share
   one neutral rule instead of being colour-coded against a chart. */
.lor-table .grp {
  text-align: center; color: var(--muted); letter-spacing: .04em;
  font-weight: 600;
  border-bottom: 2px solid color-mix(in srgb, var(--muted) 32%, transparent);
}
.lor-table thead tr:last-child th { top: 28px; }

/* A directionless delta: same weight and size as the coloured ones, so these
   tiles do not read as less important than the tiles beside them — only as
   tiles making no claim about which way is better. */
.delta-flat { color: var(--muted); }

/* The how-long-out split. Reuses the Pool Health tokens rather than
   --pos/--warn/--neg for the reason documented there: this is an ordered
   severity scale read with the segments edge to edge and no text between them,
   and that ramp darkens as well as shifts hue, so the ordering survives with no
   colour vision at all. The scale runs the other way here — the *shortest*
   stockout is the most actionable, so it gets the strongest colour. */
.lor-bar {
  display: flex; height: 10px; border-radius: 999px; overflow: hidden;
  background: var(--panel-2); margin-top: 4px;
}
.lor-bar span { display: block; min-width: 0; }
.lor-dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 999px;
  margin-right: 7px; vertical-align: baseline;
}
.lor-note { font-size: 12px; line-height: 1.45; max-width: 46ch; white-space: normal; }

/* Revenue tiers. A five-step ordered ramp read off a bar with no labels on it,
   so it darkens as it descends as well as shifting hue — same requirement, and
   same OKLab spacing, as the Pool Health scale. Stepped separately for light
   and dark against their own surface rather than flipped. */
:root {
  --tier-fresh: #2878b8;
  --tier-hero: #1f7a5a;
  --tier-top:  #3f8f6d;
  --tier-mid:  #b8791f;
  --tier-slow: #a4562c;
  --tier-dead: #7d7f88;
  --tier-none: #4d5058;
}
@media (prefers-color-scheme: dark) {
  :root {
    --tier-fresh: #55a9e6;
    --tier-hero: #34c48c;
    --tier-top:  #59b98f;
    --tier-mid:  #d99a3c;
    --tier-slow: #cf7448;
    --tier-dead: #767a86;
    --tier-none: #4a4e58;
  }
}

/* ---------- SKU Performance ----------
   The four range buttons are the tab's spine, not a filter, so they are a row of
   cards rather than a .seg: each carries its own SKU count and revenue, which is
   most of what a reader wants before choosing one. */
.st-ranges {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
}
.st-range {
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 14px; text-align: left; cursor: pointer;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); color: var(--ink-2);
  font: inherit; transition: border-color .12s, color .12s;
}
.st-range:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--line)); color: var(--ink); }
.st-range.on {
  border-color: var(--brand); color: var(--ink);
  box-shadow: inset 0 -2px 0 var(--brand), var(--shadow);
}
.st-range:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.st-range-name { font-weight: 600; font-size: 14px; }
.st-range-sub { font-size: 11.5px; color: var(--muted); }

.st-search {
  min-width: 220px; padding: 6px 10px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); font: inherit; font-size: 13px;
}
.st-search:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.st-select { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.st-select select {
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); font: inherit; font-size: 13px;
  max-width: 190px;
}

/* The tag pill. Coloured from the tier's own variable so it reads the same as
   the Loss of Revenue tab's bands, which are the same bands. */
.st-tag {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
}

/* Collections and Product tags: a lead name, a count of the rest, and the whole
   list on hover. Products here sit in 23 collections and carry 14 tags, so the
   cell is a summary by necessity — the name is allowed to clip, the count that
   says how much is hidden is not, and neither is allowed to push the other
   fifteen columns off the table. */
.st-list { display: inline-flex; align-items: baseline; gap: 4px; max-width: 200px; }
.st-list-lead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-list-more { flex: none; }

/* Sortable headers. The button is the whole cell so the hit area matches what
   looks clickable, and the caret only appears on the sorted column. */
.st-table th { padding: 0; }
.st-table th.grp, .st-table .grp-head th { padding: 6px 10px; }
.st-sort {
  width: 100%; padding: 8px 10px; text-align: inherit;
  background: none; border: 0; color: inherit; font: inherit; font-weight: 600;
  cursor: pointer; white-space: nowrap;
}
.st-table th.num .st-sort { text-align: right; }
.st-sort:hover { color: var(--brand); }
.st-table th.st-sorted .st-sort { color: var(--brand); }
.st-table th.st-sorted .st-sort::after { content: " ▾"; font-size: 9px; }
.st-table th.st-sorted.asc .st-sort::after { content: " ▴"; }

/* Columns that do not apply to the selected range — the stock four on Gold. Kept
   in place and greyed rather than removed: a column that vanishes reads as a
   layout bug, and the note above the table says why these are blank. */
.st-dim { opacity: .38; }

/* Creative Mix and Women vs Men. The residual row is the spend whose ad name
   did not parse — italic and dimmed so it reads as "not a creative type"
   rather than as one more bucket ranked alongside the others. */
.resid-row td { color: var(--muted); font-style: italic; }
.resid-row td strong { font-weight: 500; }
/* The men's column on the paired rate tables. Tinted rather than bordered so a
   ten-column table still scans as five pairs and not ten separate columns. */
td.alt, th.alt { background: color-mix(in srgb, var(--brand) 5%, transparent); }
th.sub { font-size: 10px; font-weight: 600; letter-spacing: .04em; opacity: .8; }
.share-cell { font-variant-numeric: tabular-nums; color: var(--ink-2); }
.sub-note.pos { color: var(--pos); }
.sub-note.neg { color: var(--neg); }

/* Paired share bars — spend against revenue, one pair per bucket. */
.wbar-head { display: grid; grid-template-columns: 130px 1fr 76px; align-items: center;
  gap: 10px; padding: 0 0 8px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.wbar-key { display: flex; align-items: center; gap: 7px; text-transform: none; letter-spacing: 0;
  font-size: 11.5px; }
.wbar-key i { display: inline-block; width: 11px; height: 8px; border-radius: 2px; }
.wbar-gap-head { text-align: right; }
.wbar-row { display: grid; grid-template-columns: 130px 1fr 76px; align-items: center;
  gap: 10px; padding: 7px 0; }
.wbar-row.resid .wbar-label { color: var(--muted); font-style: italic; }
.wbar-label { color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wbar-tracks { display: flex; flex-direction: column; gap: 3px; }
.wbar-track { position: relative; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 5px; height: 11px; }
.wbar-fill { height: 100%; border-radius: 4px; }
.wbar-key i.spend, .wbar-fill.spend { background: #5b8def; }
.wbar-key i.rev, .wbar-fill.rev { background: #e8a13a; }
.wbar-v { position: absolute; right: 6px; top: -1px; font-size: 9.5px; line-height: 13px;
  color: var(--ink-2); font-variant-numeric: tabular-nums; }
.wbar-gap { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600;
  font-size: 12px; color: var(--muted); }
.wbar-gap.pos { color: var(--pos); }
.wbar-gap.neg { color: var(--neg); }
.chart-key { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap;
  font-size: 12px; color: var(--muted); padding-top: 4px; }
.chart-key i { display: inline-block; width: 12px; height: 3px; border-radius: 2px;
  vertical-align: middle; margin-right: 6px; }
.weight-block { padding: 4px 0 14px; border-bottom: 1px solid var(--line); margin-bottom: 14px; }
.cell-share { font-size: 10.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  margin-top: 1px; }

/* ---------- Ad Mix ----------
   One table, three levels deep, carrying two ad accounts. The levels are told
   apart by indent and weight rather than by background colour: a coloured
   stripe per level would fight the vertical swatches and the platform chips,
   which are the two things here that have to read at a glance. */
.am-table td { vertical-align: middle; }
.am-table tbody tr:hover { background: var(--hover, rgba(128, 128, 128, .06)); }
.am-dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  margin-right: 8px; vertical-align: -1px;
  box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .28);
}
.am-indent { display: inline-block; width: 22px; }
.am-indent2 { display: inline-block; width: 48px; }
.am-l3 td { font-size: 12px; }
.am-geo { font-size: 11px; letter-spacing: .04em; }

/* One text colour for the whole body of the table. Levels are told apart by
   indent, weight and size; a second and third shade of grey on top of that
   made the sub-rows look disabled rather than nested. The only colour left
   is the kind that means something: the goal and ROAS pills, the vertical
   swatch and the platform chip borders. */
.am-table td,
.am-table td .muted,
.am-table .am-geo, .am-table .am-unit, .am-table .am-handle,
.am-table .am-plat, .am-table .share-cell, .am-table .am-rolled,
.am-table .am-blank, .am-table .am-partial, .am-table .am-more td {
  color: var(--ink);
}

/* A rule between every column. Nine right-aligned columns with nothing
   between them ran together into one block, and the count, live and spend
   columns were easy to read across into their neighbours. */
.am-table th + th,
.am-table td + td { border-left: 1px solid var(--line); }

/* ---------- Ad Mix planning columns ----------
   Ads required, Ads goal, Spends required and Spend goal are the reader's own
   figures and the arithmetic on them. They carry a tint across the whole band
   so they read as one block, distinct from the columns the ad accounts answer:
   a target is an opinion, and every figure beside it is a measurement. */
.am-table th.am-plan,
.am-table td.am-target-cell {
  background: color-mix(in srgb, var(--brand) 5%, transparent);
}
.am-table th.am-plan { border-bottom-color: color-mix(in srgb, var(--brand) 35%, transparent); }
.am-target {
  width: 82px; padding: 4px 6px; text-align: right;
  font: inherit; font-size: 12px; font-variant-numeric: tabular-nums;
  color: var(--ink); background: var(--panel);
  border: 1px solid var(--line); border-radius: 6px;
}
.am-target::placeholder { color: var(--muted); }
.am-target:focus { outline: 0; border-color: var(--brand); }
/* A box with something in it is the exception, so it is the one that is marked.
   An empty box is the resting state of most of this table. */
.am-target.on {
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, var(--panel));
  font-weight: 600;
}
/* A rolled-up target is a sum of the boxes below it, never a box itself —
   italic so it cannot be mistaken for something that can be typed into. */
.am-rolled { font-style: italic; color: var(--ink-2); }
.am-noanswer { font-style: italic; font-size: 11px; }
/* A goal that covers only the planned rows under it, not the whole level. */
.am-partial { color: var(--muted); margin-left: 4px; cursor: help; font-weight: 700; }
.am-range .card-head h3 { display: flex; align-items: center; }
.am-reset { margin-left: auto; }
/* A range name in the summary is a link to that range's table, and each table
   links back. Styled as the text it replaces, with the underline on hover only,
   so the summary still reads as a table rather than a list of links. */
.am-jump { color: inherit; text-decoration: none; cursor: pointer; }
.am-jump:hover strong, .am-top:hover { text-decoration: underline; }
.am-top { margin-left: auto; font-size: 12px; color: var(--muted); white-space: nowrap; }
.am-range, #am-summary { scroll-margin-top: 16px; }

.am-unit { font-size: 10px; color: var(--muted); margin-left: 3px; }
.am-handle { font-size: 11px; color: var(--muted); margin-left: 8px; }
/* Which account a format row is reading. Every figure on that row is against
   that platform's own total, so the chip is not decoration — it names the
   denominator. */
.am-plat {
  display: inline-block; margin-left: 8px; padding: 1px 6px;
  border-radius: 999px; font-size: 9.5px; letter-spacing: .05em;
  text-transform: uppercase; border: 1px solid var(--line); color: var(--muted);
}
.am-plat-meta { border-color: color-mix(in srgb, #5b8def 45%, var(--line)); }
.am-plat-google { border-color: color-mix(in srgb, #e8a13a 45%, var(--line)); }
/* A cell that cannot carry a figure, rather than one that carries zero. */
.am-blank { color: var(--muted); font-style: italic; }
/* The residual is not a format in the same sense as the others — it is what the
   naming convention did not say — so it reads as an aside rather than a peer. */
.am-residual td:first-child { font-style: italic; }
.am-more td { font-size: 11.5px; color: var(--muted); }
