/* components.css — the reusable performance surface.
 *
 * The reference layout: a row of KPI cards (one big number, a sparkline, one
 * supporting sub-metric) over a dense sortable table, with date-range pills, a
 * platform segmented control, a campaign selector, and an updated-at stamp with
 * a manual refresh.
 *
 * Everything here is built from tokens.css. No hex, no bare px on anything that
 * has a scale. If a value is not in the scale, the scale is wrong, not the rule.
 */

/* ================================================== control bar */
.perfbar {
  display: flex; align-items: center; gap: var(--sp-8);
  flex-wrap: wrap; margin-bottom: var(--sp-16);
}
.perfbar .spacer { margin-left: auto; }

/* Date-range pills and the platform control are the ONLY --r-full elements in
   the product. Buttons are not pills. */
.pillgroup, .seg {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  background: var(--surface-sunken); border-radius: var(--r-full);
  padding: var(--sp-2); box-shadow: var(--ring);
}
.pillgroup button, .seg button {
  height: var(--ctl-sm); padding: 0 var(--sp-12);
  border: none; background: none; cursor: pointer;
  border-radius: var(--r-full); color: var(--text-secondary);
  font: var(--t-label-13); font-family: var(--font-ui);
  transition: background-color var(--dur-base) var(--ease-out);
}
@media (any-hover: hover) and (any-pointer: fine) {
  .pillgroup button:hover, .seg button:hover { background: var(--surface-hover); }
}
.pillgroup button[aria-pressed="true"], .seg button[aria-pressed="true"] {
  background: var(--surface-raised); color: var(--text-primary);
  box-shadow: var(--ring); font-weight: var(--fw-medium);
}

.perfselect {
  height: var(--ctl-sm); padding: 0 var(--sp-8);
  border: none; box-shadow: var(--ring); border-radius: var(--r-6);
  background: var(--surface-raised); color: var(--text-primary);
  font: var(--t-label-13); font-family: var(--font-ui); cursor: pointer;
  max-width: 260px;
}

/* Freshness is the newest FACT date, never the request time. A view that says
   "updated now" while the feed is two days stale is how people learn to trust
   stale numbers. */
.freshness {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  color: var(--text-tertiary); font: var(--t-label-12); font-family: var(--font-ui);
}
.freshness.stale { color: var(--warning-text); }
.refreshbtn {
  height: var(--ctl-sm); width: var(--ctl-sm);
  display: inline-flex; align-items: center; justify-content: center;
  border: none; box-shadow: var(--ring); border-radius: var(--r-6);
  background: var(--surface-raised); color: var(--text-secondary); cursor: pointer;
  font-size: 13px; transition: background-color var(--dur-base) var(--ease-out);
}
@media (any-hover: hover) and (any-pointer: fine) {
  .refreshbtn:hover { background: var(--surface-hover); color: var(--text-primary); }
}
.refreshbtn[aria-busy="true"] { color: var(--text-quaternary); cursor: default; }

/* ================================================== KPI cards */
/* One hero number and exactly ONE supporting sub-metric. Two big numbers on one
   card is a reject-on-sight defect: the eye cannot tell which is the subject. */
/* Explicit counts rather than auto-fit. auto-fit with a 220px minimum breaks a
   four-card row into 3 + 1, stranding one card beside a gap the width of two,
   which reads as a missing card rather than a wrap. Stepping 4 -> 2 -> 1 keeps
   every row balanced and keeps card heights equal across each row, which is
   what makes the numbers scannable. */
.kpirow {
  display: grid; gap: var(--sp-12); margin-bottom: var(--sp-16);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1180px) { .kpirow { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .kpirow { grid-template-columns: minmax(0, 1fr); } }
.kpicard {
  background: var(--surface-raised); border-radius: var(--r-8);
  box-shadow: var(--elev-card);           /* flat at rest, never a shadow */
  padding: var(--sp-16);
  display: flex; flex-direction: column; gap: var(--sp-6);
  min-height: 132px;                      /* fixed height across the row */
}
.kpicard .k {
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11);
  color: var(--text-tertiary);
  /* sentence case, no text-transform: no uppercase eyebrows anywhere */
}
.kpicard .v {
  font: var(--t-display-32); font-family: var(--font-numeric);
  letter-spacing: var(--tr-display-32);
  color: var(--text-primary);
  font-variant-numeric: lining-nums tabular-nums slashed-zero;
  display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap;
  /* Motion is banned here: a number that animates is a number you cannot read */
}
.kpicard .sub {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary); margin-top: auto;
}
.kpicard .spark { width: 100%; height: 28px; display: block; opacity: .85; }

/* Delta chip. The arrow is decoupled from the colour: direction is a fact,
   good/bad is a judgement, and on ACOS, CPC or CAC they point opposite ways. */
.delta-chip {
  display: inline-flex; align-items: center; gap: 3px;
  height: 20px; padding: 0 var(--sp-6); border-radius: var(--r-4);
  font: var(--t-label-11); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: 0;
}
.delta-chip.up   { color: var(--positive-text); background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); }
.delta-chip.down { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }
.delta-chip.flat { color: var(--neutral-text);  background: var(--neutral-fill);  box-shadow: 0 0 0 1px var(--neutral-border); }

/* ================================================== dense table */
.densewrap {
  background: var(--surface-raised); border-radius: var(--r-8);
  box-shadow: var(--elev-card); overflow: hidden;
}
.densescroll { overflow-x: auto; }        /* the TABLE scrolls, never the page */

table.dense {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font: var(--t-body-13); font-family: var(--font-ui);
  letter-spacing: var(--tr-body-13);
}
table.dense th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-raised);
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11);
  color: var(--text-tertiary); text-align: left;
  font-weight: var(--fw-medium); text-transform: none;
  padding: var(--sp-8) var(--sp-10);
  border-bottom: 1px solid var(--gridline);
  white-space: nowrap; user-select: none;
}
table.dense th.sortable { cursor: pointer; }
@media (any-hover: hover) and (any-pointer: fine) {
  table.dense th.sortable:hover { color: var(--text-primary); }
}
table.dense th .sortmark { color: var(--text-quaternary); margin-left: 3px; }
table.dense th[aria-sort] .sortmark { color: var(--accent-text); }

table.dense td {
  padding: var(--row-pad-y) var(--sp-10);
  border-bottom: 1px solid var(--border-subtle);
  height: var(--row-h);
  color: var(--text-primary);
  vertical-align: middle;
}
/* Numbers are right-aligned and tabular, enforced by the cell, not the caller.
   nowrap matters: without it a state dot and its number wrap onto two lines in
   a narrow column, which breaks the tabular rhythm the whole table depends on
   and makes rows different heights. */
table.dense td.num, table.dense th.num { text-align: right; white-space: nowrap; }
table.dense td.num {
  font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums slashed-zero;
}
table.dense td.muted { color: var(--text-tertiary); }

/* Selection is a background, never a size or border change, so rows cannot
   shift under the pointer. */
@media (any-hover: hover) and (any-pointer: fine) {
  table.dense tbody tr:hover td { background: var(--surface-hover); }
}

/* First column sticky so the row keeps its identity while the metrics scroll */
table.dense .stick {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface-raised);
  box-shadow: 1px 0 0 var(--border-subtle);
}
table.dense thead .stick { z-index: 3; }

/* Totals: ratios are RECOMPUTED from summed numerator and denominator, never
   averaged down the column. Averaging weights a $12 campaign the same as a
   $2,000 one. */
table.dense tfoot td {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--surface-sunken);
  font-weight: var(--fw-semi);
  border-top: 1px solid var(--gridline);
  border-bottom: none;
}
table.dense tfoot .stick { background: var(--surface-sunken); }

/* Platform badge is NEUTRAL by design. Platform is identity, not state, and
   hue spent on identity is hue unavailable for state. */
.platbadge {
  display: inline-flex; align-items: center; height: 20px;
  padding: 0 var(--sp-6); border-radius: var(--r-4);
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border);
  white-space: nowrap;
}

/* State colour on a metric goes on a DOT beside the number, never on the digits:
   tinted digits fight the tabular rhythm and fail in greyscale. */
.statedot {
  display: inline-block; width: 6px; height: 6px; border-radius: var(--r-2);
  margin-right: var(--sp-6); vertical-align: middle;
}
.statedot.good { background: var(--positive-mark); }
.statedot.bad  { background: var(--negative-mark); }
.statedot.warn { background: var(--warning-mark); }

/* ================================================== empty / loading / error */
.state {
  padding: var(--sp-40) var(--sp-24); text-align: center;
  color: var(--text-tertiary); font: var(--t-body-13); font-family: var(--font-ui);
}
.state .big { font: var(--t-title-17); font-family: var(--font-ui); color: var(--text-secondary); margin-bottom: var(--sp-6); }
.state.error { color: var(--negative-text); }
.skel {
  background: var(--surface-sunken); border-radius: var(--r-4);
  animation: skelpulse 1200ms var(--ease-out) infinite;
}
/* The ONLY permitted skeleton animation: an opacity pulse. No shimmer sweeps. */
@keyframes skelpulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

/* A note that a column is missing because the feed does not exist yet, rather
   than a blank column with no explanation. */
.missingnote {
  padding: var(--sp-8) var(--sp-12);
  color: var(--text-tertiary); font: var(--t-label-12); font-family: var(--font-ui);
  border-top: 1px solid var(--border-subtle);
}

/* ================================================== responsive */
/* The app was unusable below tablet width: one media query in the whole
   stylesheet and a fixed 232px rail. */
@media (max-width: 900px) {
  #rail { width: var(--rail-mini); flex-basis: var(--rail-mini); padding: var(--sp-12) var(--sp-8); }
  #rail .lbl, #rail .brandtext, #rail .chev, #rail kbd,
  #rail .acctwho, #rail .acctgear { display: none; }
  #rail .navgroup { justify-content: center; padding: var(--sp-6) 0; font-size: 0; }
  #rail .navlinks { display: block !important; }
  #view { padding: var(--sp-16) var(--sp-12) var(--sp-48); }
  /* KPI columns are set by the explicit steps above, not here. */
  .kpicard .v { font: var(--t-display-24); font-family: var(--font-numeric); letter-spacing: var(--tr-display-24); }
}
@media (min-width: 1440px) {
  #view { padding: var(--sp-32) var(--sp-32) var(--sp-64); }
}

/* ================================================== idea box (Phase 7) */
/* Bottom corner of every screen. Deliberately quiet: it is always there, so it
   must never compete with the data. Neutral at rest, accent only on hover. */
#ideafab {
  position: fixed; right: var(--sp-20); bottom: var(--sp-20); z-index: 80;
  width: 40px; height: 40px; border-radius: var(--r-full);
  border: none; cursor: pointer;
  background: var(--surface-raised); color: var(--text-secondary);
  box-shadow: var(--ring), var(--shadow-md);
  font-size: 17px; line-height: 1;
  transition: background-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
@media (any-hover: hover) and (any-pointer: fine) {
  #ideafab:hover { background: var(--accent-fill); color: var(--accent-on-fill); }
}

#ideabox {
  position: fixed; inset: 0; z-index: 95; display: none;
  align-items: center; justify-content: center;
  background: var(--surface-backdrop);
  padding: var(--sp-20);
}
#ideabox.open { display: flex; }
#ideacard {
  width: 100%; max-width: 520px;
  background: var(--surface-raised); border-radius: var(--r-12);
  box-shadow: var(--elev-modal); padding: var(--sp-20);
}
#ideacard h2 {
  font: var(--t-title-17); font-family: var(--font-ui);
  letter-spacing: var(--tr-title-17); color: var(--text-primary);
  margin-bottom: var(--sp-4);
}
.ideameta {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); margin-bottom: var(--sp-12);
}
.ideameta b { color: var(--text-secondary); font-family: var(--font-mono); font-weight: var(--fw-regular); }
#ideatext {
  width: 100%; resize: vertical; padding: var(--sp-10);
  border: none; box-shadow: var(--ring); border-radius: var(--r-6);
  background: var(--surface-canvas); color: var(--text-primary);
  font: var(--t-body-13); font-family: var(--font-ui);
  letter-spacing: var(--tr-body-13);
}
.ideabtns {
  display: flex; align-items: center; gap: var(--sp-8); margin-top: var(--sp-12);
}
.ideahint {
  margin-right: auto; color: var(--text-quaternary);
  font: var(--t-label-11); font-family: var(--font-ui);
}
.ideabody { max-width: 460px; white-space: pre-wrap; }
.ideanote {
  margin-top: var(--sp-4); color: var(--text-tertiary);
  font: var(--t-label-12); font-family: var(--font-ui);
}

/* ================================================== naming builder (Phase 5) */
.nform {
  display: grid; gap: var(--sp-12);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.nfield { display: flex; flex-direction: column; gap: var(--sp-4); }
.nlabel {
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11); color: var(--text-tertiary);
}
.nfield .perfselect { width: 100%; max-width: none; height: var(--ctl-md); }
.nhint {
  font: var(--t-micro-10); font-family: var(--font-ui);
  letter-spacing: var(--tr-micro-10); color: var(--text-quaternary);
}
.nouts { display: grid; gap: var(--sp-10); }
.nout {
  background: var(--surface-canvas); border-radius: var(--r-8);
  box-shadow: var(--ring); padding: var(--sp-12) var(--sp-16);
}
.nouthead { display: flex; align-items: center; gap: var(--sp-8); margin-bottom: var(--sp-4); }
.nouthead .k {
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11); color: var(--text-tertiary);
}
.nouthead button { margin-left: auto; }
/* The generated name is the subject of this screen, so it is the only mono
   string allowed to be this large. */
.noutval {
  font-family: var(--font-mono); font-size: 15px; line-height: 22px;
  color: var(--text-primary); word-break: break-all;
}
.noutwhy {
  margin-top: var(--sp-4); color: var(--text-tertiary);
  font: var(--t-label-12); font-family: var(--font-ui);
}
td.nreason {
  color: var(--text-tertiary); font: var(--t-label-12); font-family: var(--font-ui);
  padding-top: 0; border-bottom: 1px solid var(--border-subtle);
}

/* ============================== Supply screen (F35) */
/* .klabel / .kval / .ksub had NO rule anywhere, so the Supply value strip
   rendered as three lines of undifferentiated body text while every other KPI
   strip used .card .k/.v/.d. The Supply screen was the one screen the design
   system did not reach. Same anatomy as the KPI card, one step smaller because
   this strip carries a location count rather than a headline. */
.klabel {
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11); color: var(--text-tertiary);
}
.kval {
  font: var(--t-display-24); font-family: var(--font-numeric);
  letter-spacing: var(--tr-display-24); color: var(--text-primary);
  font-variant-numeric: lining-nums tabular-nums slashed-zero;
  margin-top: var(--sp-2);
}
.ksub {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary); margin-top: var(--sp-2);
}

/* Per-market coverage chips under a SKU-level reorder row. Colour is state, and
   the label carries the market, so the row still reads in greyscale. */
.geochips { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.geochip {
  display: inline-flex; align-items: center; height: 20px;
  padding: 0 var(--sp-6); border-radius: var(--r-4);
  font: var(--t-label-11); font-family: var(--font-ui);
  white-space: nowrap;
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border);
}
.geochip.oos, .geochip.reorder_now {
  color: var(--negative-text); background: var(--negative-fill);
  box-shadow: 0 0 0 1px var(--negative-border);
}
.geochip.low, .geochip.excess {
  color: var(--warning-text); background: var(--warning-fill);
  box-shadow: 0 0 0 1px var(--warning-border);
}
.geochip.healthy {
  color: var(--positive-text); background: var(--positive-fill);
  box-shadow: 0 0 0 1px var(--positive-border);
}

/* ==== 26/07 build wave. Each workstream styles ONLY inside its own marker
   block below (parallel-edit safety: unique anchors, no shared lines). ==== */
/* ---- wp1 scm ops ---- */
/* Payment chips carry the PO folder's own convention (yellow = deposit paid,
   green = fully paid), so the screen and the folder read the same. */
.paychip {
  display: inline-flex; align-items: center; height: 20px; padding: 0 var(--sp-8);
  border-radius: var(--r-4); font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border); white-space: nowrap;
}
.paychip.p-amber { color: var(--warning-text); background: var(--warning-fill); box-shadow: 0 0 0 1px var(--warning-border); }
.paychip.p-green { color: var(--positive-text); background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); }

.etachip {
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary); white-space: nowrap;
}
.etachip.soon    { color: var(--warning-text); }
.etachip.late    { color: var(--negative-text); font-weight: var(--fw-medium, 500); }
.etachip.arrived { color: var(--positive-text); }
.etachip.none    { color: var(--text-quaternary); }
td .overdue { color: var(--negative-text); }

/* Two bars in one track: shipped behind, received in front, so a PO that is
   fully shipped and half received reads as one shape. */
.scmprog { display: inline-flex; align-items: center; gap: var(--sp-6); }
.scmbar {
  position: relative; display: inline-block; width: 74px; height: 6px;
  border-radius: 99px; background: var(--surface-sunken); overflow: hidden;
}
.scmbar .f { position: absolute; inset: 0 auto 0 0; height: 100%; border-radius: 99px; }
.scmbar .f.s { background: var(--info-mark); opacity: 0.45; }
.scmbar .f.r { background: var(--positive-mark); }

.trend { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary); }
.trend.t-rising  { color: var(--negative-text); }
.trend.t-falling { color: var(--positive-text); }

/* Narrative cards (ux.md §1): the sentence first, its numbers under it. */
.scmissues { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--sp-10); }
.scmissue {
  background: var(--surface-canvas); border-radius: var(--r-8);
  box-shadow: var(--ring); padding: var(--sp-12) var(--sp-14);
  border-left: 3px solid var(--neutral-border);
}
.scmissue.s-warn  { border-left-color: var(--warning-mark); }
.scmissue.s-error { border-left-color: var(--negative-mark); }
.scmissuehead { display: flex; align-items: center; gap: var(--sp-6); margin-bottom: var(--sp-6); }
.scmissuehead .mono { font-family: var(--font-mono); font-size: 11px; color: var(--text-tertiary); }
.scmissuetext { color: var(--text-primary); font: var(--t-label-13, inherit); font-family: var(--font-ui); }
.scmev { display: flex; flex-wrap: wrap; gap: var(--sp-6); margin-top: var(--sp-8); }
.scmev span {
  font: var(--t-label-11); font-family: var(--font-numeric); color: var(--text-tertiary);
  background: var(--surface-sunken); border-radius: var(--r-4); padding: 1px var(--sp-6);
}
.scmissue button { margin-top: var(--sp-8); }

/* The PO drawer. A right-hand sheet rather than a route, because the board
   behind it is the context you are checking the PO against. */
#scmdrawer .scmscrim { position: fixed; inset: 0; background: var(--surface-backdrop); z-index: 60; }
#scmdrawer .scmpanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(920px, 94vw); z-index: 61;
  background: var(--surface-raised); box-shadow: -8px 0 32px rgba(0, 0, 0, 0.18);
  overflow-y: auto; padding: var(--sp-20) var(--sp-24) var(--sp-32);
}
#scmdrawer h2 { margin: 0; font-size: 18px; }
#scmdrawer h3 { margin: var(--sp-20) 0 var(--sp-8); font-size: 13px; color: var(--text-secondary); }
.scmhead { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-16); }
.scmpills { display: flex; align-items: center; gap: var(--sp-6); margin-top: var(--sp-6); flex-wrap: wrap; }
.scmkvs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--sp-8); margin-top: var(--sp-16);
}
.scmkv { background: var(--surface-sunken); border-radius: var(--r-4); padding: var(--sp-6) var(--sp-10); }
.scmkv span {
  display: block; font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11); color: var(--text-tertiary);
}
.scmkv b { font-family: var(--font-numeric); font-variant-numeric: tabular-nums; font-weight: var(--fw-medium, 500); }
.scmacts { display: flex; flex-wrap: wrap; gap: var(--sp-6); margin-top: var(--sp-16); }
/* The print link is an anchor because it opens a document, but it sits beside
   buttons and must not read as body-text link. */
#scmdrawer a.ghost {
  display: inline-block; text-decoration: none; font-weight: 600;
  color: var(--text-primary); background: var(--surface-raised);
  border: 1px solid var(--border-default); border-radius: var(--r-6);
  padding: 4px var(--sp-10); margin-right: var(--sp-6);
}
#scmdrawer a.ghost:hover { background: var(--surface-hover); }
.scmreceive { display: grid; gap: var(--sp-10); }
.scmreceive .listctl label { display: inline-flex; align-items: center; gap: var(--sp-6); }
.scmreceive input[type="number"] { width: 90px; text-align: right; }
tr.clickrow { cursor: pointer; }
tr.clickrow:hover { background: var(--surface-hover); }
@media (max-width: 900px) { #scmdrawer .scmpanel { width: 100vw; } }
/* ---- /wp1 ---- */
/* ---- wp2 creative v2 ---- */
/* The creative board is the one screen where the IMAGE is the data, so the card
   gives the poster the whole top edge at its native aspect and everything else
   is chrome around it. 4:5 is Meta's dominant feed ratio; letterboxing a 1:1
   into it beats cropping a 4:5 down to a square, which cuts the hook text out
   of half the account's ads. */
.cre2grid {
  display: grid; gap: var(--sp-16);
  grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
  align-items: start;
}
.cre2card {
  background: var(--card); border: 1px solid var(--border-subtle); border-radius: var(--r-8);
  overflow: hidden; display: flex; flex-direction: column;
}
.cre2media {
  position: relative; aspect-ratio: 4 / 5; background: var(--surface-sunken);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.cre2media img { width: 100%; height: 100%; object-fit: contain; display: block; }
.cre2media.empty { cursor: default; }
.cre2noimg {
  font: var(--t-label-12); color: var(--text-quaternary); text-align: center; padding: var(--sp-16);
}
.cre2noimg .sub { display: block; margin-top: var(--sp-4); color: var(--text-tertiary); }
/* Overlays sit on a scrim rather than the raw image: a play badge over a white
   product shot was invisible in exactly the ads that need it most. */
.cre2play, .cre2kind, .cre2var {
  position: absolute; font: var(--t-label-11); border-radius: var(--r-full);
  padding: 2px var(--sp-8); background: rgba(0, 0, 0, .62); color: #fff; letter-spacing: .01em;
}
.cre2play { left: var(--sp-8); bottom: var(--sp-8); }
.cre2play b { font-family: var(--font-numeric); font-weight: var(--fw-medium); }
.cre2kind { right: var(--sp-8); bottom: var(--sp-8); }
.cre2var  { right: var(--sp-8); top: var(--sp-8); }

.cre2body { padding: var(--sp-10) var(--sp-12) var(--sp-12); display: flex; flex-direction: column; gap: var(--sp-6); }
.cre2head { display: flex; align-items: center; gap: var(--sp-6); }
.cre2head .nm { font: var(--t-label-13); font-weight: var(--fw-semi); color: var(--text-primary); flex: 1; min-width: 0; }
.cre2meta { font: var(--t-micro-10); color: var(--text-tertiary); }
.cre2copy { font: var(--t-body-13); color: var(--text-secondary); line-height: var(--tr-body-13); }
.cre2hl { font: var(--t-label-12); color: var(--text-primary); }
.cre2hl .cta {
  font: var(--t-micro-10); color: var(--text-tertiary); border: 1px solid var(--border-subtle);
  border-radius: var(--r-2); padding: 0 4px; margin-left: var(--sp-4);
}
.cre2by { font: var(--t-micro-10); color: var(--text-quaternary); }

/* Metric chips. The number is tabular so a column of cards stays scannable, and
   the tint is a FILL, never coloured digits: colour on the digits reads as part
   of the value. Neutral is the default, because a 10% band around the account average
   is noise, and tinting noise teaches the wrong lesson. */
.cre2chips { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.crechip {
  display: inline-flex; align-items: baseline; gap: 4px; padding: 2px var(--sp-6);
  border-radius: var(--r-4); font: var(--t-micro-10); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border); color: var(--neutral-text);
}
.crechip .k { opacity: .7; text-transform: lowercase; }
.crechip .v { font-family: var(--font-numeric); font-variant-numeric: tabular-nums; font-weight: var(--fw-medium); }
.crechip.up   { background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); color: var(--positive-text); }
.crechip.down { background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); color: var(--negative-text); }
.crechip.none { opacity: .55; }

.cre2gate { display: flex; flex-direction: column; gap: 3px; font: var(--t-micro-10); color: var(--text-tertiary); }
.cre2gate .bar { height: 4px; border-radius: var(--r-full); background: var(--surface-sunken); overflow: hidden; }
.cre2gate .bar > div { height: 100%; border-radius: var(--r-full); }

.cre2tags { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; }
.cre2tag {
  font: var(--t-micro-10); color: var(--text-secondary); background: var(--surface-raised);
  border: 1px solid var(--border-subtle); border-radius: var(--r-4); height: var(--ctl-sm);
  padding: 0 var(--sp-4); max-width: 120px;
}
.cre2tag:disabled { opacity: .45; }
.cre2star { display: inline-flex; margin-left: auto; }
.cre2star button {
  background: none; border: 0; padding: 0 1px; cursor: pointer; font-size: 13px; line-height: 1;
  color: var(--text-quaternary);
}
.cre2star button.on { color: var(--warning-mark); }

/* Filter bar and saved views (the PIM views pattern, generalised per ux.md §1) */
.cre2bar .perfbar { flex-wrap: wrap; gap: var(--sp-8); }
.cre2f { display: flex; flex-direction: column; gap: 2px; }
.cre2f > span { font: var(--t-micro-10); color: var(--text-tertiary); }
.cre2views { display: flex; flex-wrap: wrap; gap: var(--sp-6); margin-top: var(--sp-8); }
.cre2view { display: inline-flex; align-items: stretch; border: 1px solid var(--border-subtle); border-radius: var(--r-full); overflow: hidden; }
.cre2view button {
  background: var(--surface-raised); border: 0; cursor: pointer; font: var(--t-label-11);
  color: var(--text-secondary); padding: 2px var(--sp-10);
}
.cre2view button:hover { background: var(--surface-hover); }
.cre2view .x { color: var(--text-quaternary); padding: 2px var(--sp-6); border-left: 1px solid var(--border-subtle); }

.cre2sort { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-4); margin: var(--sp-16) 0 var(--sp-10); }
.cre2sort .lbl { font: var(--t-micro-10); color: var(--text-tertiary); margin-right: var(--sp-4); }
.cre2sort button {
  background: none; border: 1px solid transparent; border-radius: var(--r-4); cursor: pointer;
  font: var(--t-label-11); color: var(--text-secondary); padding: 2px var(--sp-8);
}
.cre2sort button[aria-pressed="true"] { background: var(--surface-selected); border-color: var(--border-subtle); color: var(--text-primary); }
.cre2sort .meta { margin-left: auto; font: var(--t-micro-10); color: var(--text-tertiary); }

/* Verdict lane: narrative card first, evidence grid under it (ux.md §1). The
   left border carries the verdict so the lane can be read down its edge. */
.cre2lane { display: grid; gap: var(--sp-10); grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.cre2verdict {
  border: 1px solid var(--border-subtle); border-left-width: 3px; border-radius: var(--r-6);
  padding: var(--sp-10) var(--sp-12); background: var(--surface-raised);
  display: flex; flex-direction: column; gap: var(--sp-6);
}
.cre2verdict.v-keep { border-left-color: var(--positive-mark); }
.cre2verdict.v-kill { border-left-color: var(--negative-mark); }
.cre2verdict.v-hold { border-left-color: var(--warning-mark); }
.cre2verdict .head { display: flex; align-items: center; gap: var(--sp-6); }
.cre2verdict .nm { font: var(--t-label-12); font-weight: var(--fw-semi); color: var(--text-primary); }
.cre2verdict .say { font: var(--t-body-13); color: var(--text-secondary); line-height: var(--tr-body-13); }
.cre2verdict .ev { font: var(--t-micro-10); color: var(--text-tertiary); font-family: var(--font-numeric); }
.cre2verdict .act { display: flex; gap: var(--sp-6); }

.cre2fat .row {
  display: flex; flex-wrap: wrap; gap: var(--sp-10); align-items: baseline;
  padding: var(--sp-6) 0; border-bottom: 1px solid var(--border-subtle); font: var(--t-label-12);
}
.cre2fat .row:last-child { border-bottom: 0; }
.cre2fat .nm { flex: 1; min-width: 160px; color: var(--text-primary); }
.cre2fat .mono { font-family: var(--font-numeric); color: var(--negative-text); }

@media (max-width: 720px) {
  .cre2grid { grid-template-columns: 1fr; }
  .cre2lane { grid-template-columns: 1fr; }
}
/* ---- /wp2 ---- */
/* ---- wp3 seo pipeline ---- */
/* Status lanes. A draft's life reads left to right, so the lanes are columns
   until the viewport is too narrow to hold four, then they stack in the same
   order rather than reflowing into an unreadable grid. */
.seolanes {
  display: grid; gap: var(--sp-16);
  grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
  align-items: start; margin-bottom: var(--sp-20);
}
.seolane {
  background: var(--surface-raised); border: 1px solid var(--border-default);
  border-radius: var(--r-12); padding: var(--sp-12) var(--sp-12) var(--sp-8);
  box-shadow: var(--shadow-xs);
}
.seolanehead {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-8);
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-primary);
}
.seocount {
  font: var(--t-label-11); font-family: var(--font-numeric);
  color: var(--text-tertiary); font-variant-numeric: tabular-nums;
}
.seolanehint {
  margin: var(--sp-4) 0 var(--sp-10);
  font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-tertiary);
}
.seoempty {
  font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-tertiary);
  padding: var(--sp-8) 0 var(--sp-12);
}

/* One draft. Kept quiet: the only thing competing for attention on this screen
   should be the words, which live behind "Read the draft". */
.seocard {
  border: 1px solid var(--border-subtle); border-radius: var(--r-8);
  background: var(--surface-canvas); padding: var(--sp-10) var(--sp-12);
  margin-bottom: var(--sp-8);
}
.seotop { display: flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap; margin-bottom: var(--sp-6); }
.seokind {
  display: inline-flex; align-items: center; height: 18px; padding: 0 var(--sp-8);
  border-radius: var(--r-full); font: var(--t-micro-10); font-family: var(--font-ui);
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--neutral-text); background: var(--neutral-fill);
}
.seotitle {
  font: var(--t-label-13); font-family: var(--font-ui); color: var(--text-primary);
  line-height: 1.35;
}
.seometa {
  margin-top: var(--sp-4); font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-tertiary); display: flex; gap: var(--sp-4); flex-wrap: wrap; align-items: baseline;
}
.seoq { color: var(--text-secondary); }
.seodot { opacity: .5; }
/* Three lines of the body, so a card says what the post is about without
   pretending to be the post. */
.seoexcerpt {
  margin-top: var(--sp-6); font: var(--t-body-13); font-family: var(--font-ui);
  color: var(--text-secondary);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.seonote {
  margin-top: var(--sp-8); padding: var(--sp-6) var(--sp-8); border-radius: var(--r-4);
  background: var(--surface-sunken); font: var(--t-body-13); font-family: var(--font-ui);
  color: var(--text-secondary);
}
.seofoot {
  margin-top: var(--sp-8); font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-tertiary); line-height: 1.5;
}
.seofoot.seoerr { color: var(--negative-text); }
.seofoot code, .seobanner code {
  font-family: var(--font-mono); font-size: 11px;
  background: var(--surface-sunken); border-radius: var(--r-2); padding: 1px 4px;
}
.seoacts { margin-top: var(--sp-10); display: flex; gap: var(--sp-6); flex-wrap: wrap; align-items: center; }
/* "Read the draft" is an anchor so it can be opened in a tab, but it stands
   beside two buttons, so it borrows their chrome. The base form control rule
   in app.css only reaches button/input/select/textarea, which is why an
   anchor with .ghost .mini rendered as bare blue text. */
.seoacts a.seoread {
  display: inline-flex; align-items: center; text-decoration: none;
  font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 8px;
  color: var(--ink); background: var(--card); border: 1px solid var(--line);
}
.seoacts a.seoread:hover { background: var(--paper); }

/* The reject note. Required, so it is a field and not a confirm dialog: the
   note is the deliverable of a rejection, not an afterthought. */
.seoreject { margin-top: var(--sp-8); display: flex; gap: var(--sp-6); flex-wrap: wrap; align-items: center; }
.seoreject[hidden] { display: none; }
.seonoteinput {
  flex: 1 1 100%; min-width: 0; padding: var(--sp-6) var(--sp-8);
  border: 1px solid var(--border-strong); border-radius: var(--r-4);
  background: var(--surface-raised); color: var(--text-primary);
  font: var(--t-body-13); font-family: var(--font-ui);
}
.seonoteinput:focus-visible { outline: 2px solid var(--accent-fill); outline-offset: 1px; }

.seobanner {
  border: 1px solid var(--warning-border); background: var(--warning-fill); color: var(--warning-text);
  border-radius: var(--r-8); padding: var(--sp-10) var(--sp-12); margin-bottom: var(--sp-16);
  font: var(--t-body-13); font-family: var(--font-ui); line-height: 1.55;
}
.seofailed { border-color: var(--negative-border); }
.seokinds { display: flex; gap: var(--sp-4); flex-wrap: wrap; font-size: 11px; }
.seorejwrap { margin-bottom: var(--sp-20); }
.seorejlist {
  margin-top: var(--sp-10); display: grid; gap: var(--sp-8);
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
}
/* ---- /wp3 ---- */
/* ---- wp4 ufi ---- */
/* The UFI cell (Compliance -> Hazard). One field like any other; the generate
   button and the two notes hang under it instead of becoming a verdict panel. */
.pimcol .ufigen { margin-top: 4px; font-size: 10.5px; }
.pimcol .ufihint { margin: 4px 0 0; font-size: 11px; line-height: 1.4; }
.ufinote { margin: 2px 0 12px; font-size: 12px; }
.ufinote .ghost { margin-left: 6px; }
/* ---- /wp4 ---- */
/* ---- wp5 home ---- */
/* Home (phase 6). Calm and dense: the morning screen is read in fifteen
   seconds, so nothing here competes with the numbers. Every colour is a state
   token, every size a scale step; the strip reuses .kpirow / .kpicard rather
   than growing a second card. */

/* Freshness sits under the page head and reads from the newest FACT, never
   from the time of the request. */
.homefresh {
  display: flex; align-items: center; gap: var(--sp-6);
  margin-bottom: var(--sp-16);
  color: var(--text-tertiary); font: var(--t-label-12); font-family: var(--font-ui);
}
.homefresh.stale { color: var(--warning-text); }

/* One dot vocabulary for the whole screen: neutral unless something is wrong.
   Colour is state; the label beside it always carries the same meaning in
   words, so the screen still reads in greyscale. */
.homedot {
  flex: none; width: 6px; height: 6px; border-radius: var(--r-full);
  background: var(--neutral-border);
}
.homedot.good { background: var(--positive-mark); }
.homedot.warn { background: var(--warning-mark); }
.homedot.bad  { background: var(--negative-mark); }

/* Pulse strip. The label doubles as the link to the screen that owns the
   number, which is what stops the strip being decoration. */
.homepulse .kpicard .k { display: flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap; }
.homek { color: inherit; text-decoration: none; }
.homearrow { color: var(--text-quaternary); }
@media (any-hover: hover) and (any-pointer: fine) {
  .homek:hover { color: var(--accent-text); }
  .homek:hover .homearrow { color: var(--accent-text); }
}
/* "as of the morning sync": a number that is stale BY CONSTRUCTION says so on
   its own face, not in a footnote. */
.homeflag {
  display: inline-flex; align-items: center; height: 16px;
  padding: 0 var(--sp-4); border-radius: var(--r-4);
  font: var(--t-micro-10); font-family: var(--font-ui);
  letter-spacing: var(--tr-micro-10);
  color: var(--warning-text); background: var(--warning-fill);
  box-shadow: 0 0 0 1px var(--warning-border);
}
/* An absent number is a state with a reason, never a zero. */
.homenull { color: var(--text-tertiary); font-style: normal; }
.homefine { color: var(--text-quaternary); font: var(--t-micro-10); font-family: var(--font-ui); }
.homemuted { color: var(--text-tertiary); font-weight: var(--fw-regular); }

/* Attention: four panels, two up on a wide screen, one up on a laptop
   half-screen. */
.homegrid {
  display: grid; gap: var(--sp-16);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: var(--sp-16);
}
@media (max-width: 1100px) { .homegrid { grid-template-columns: minmax(0, 1fr); } }
.homegrid .panel { margin-bottom: 0; }

.homelist { display: flex; flex-direction: column; }
.homeitem {
  display: flex; align-items: flex-start; gap: var(--sp-8);
  padding: var(--sp-8) var(--sp-6);
  border-top: 1px solid var(--border-subtle);
  color: var(--text-primary); text-decoration: none;
  border-radius: var(--r-4);
}
.homelist .homeitem:first-child { border-top: none; }
@media (any-hover: hover) and (any-pointer: fine) {
  .homeitem:hover { background: var(--surface-hover); }
}
.homeitem .homedot { margin-top: 6px; }
.homeitembody { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.homeitem .t { font: var(--t-label-13); font-family: var(--font-ui); color: var(--text-primary); }
.homeitem .d { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }
.homen {
  flex: none; align-self: center;
  font: var(--t-label-13); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--text-secondary);
}
/* All-clear is a sentence, not a blank panel: "nothing here" and "nothing
   loaded" look identical when both render as empty space. */
.homeok {
  padding: var(--sp-8) var(--sp-6);
  color: var(--text-tertiary); font: var(--t-body-13); font-family: var(--font-ui);
}

/* Week context: three cells, same anatomy as the Supply value strip. */
.homeweek {
  display: grid; gap: var(--sp-16);
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 760px) { .homeweek { grid-template-columns: minmax(0, 1fr); } }
.homewcell .kval { display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap; }
.homewnote { display: block; margin-top: var(--sp-12); line-height: 15px; }

/* Lineage, folded. Which fact table a number came from matters exactly when
   somebody doubts it, and never before. */
.homelineage {
  margin-bottom: var(--sp-16);
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary);
}
.homelineage summary { cursor: pointer; color: var(--text-tertiary); }
.homelineage ul { margin: var(--sp-8) 0 0 var(--sp-16); display: grid; gap: var(--sp-4); }
.homelineage b { color: var(--text-secondary); font-weight: var(--fw-medium); }

/* Quick links. Chips, not buttons: these are navigation, not actions. */
.homelinks { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
.homelinks a {
  display: inline-flex; align-items: center; height: var(--ctl-sm);
  padding: 0 var(--sp-12); border-radius: var(--r-6);
  background: var(--surface-raised); box-shadow: var(--ring);
  color: var(--text-secondary); text-decoration: none;
  font: var(--t-label-13); font-family: var(--font-ui);
  transition: background-color var(--dur-base) var(--ease-out);
}
@media (any-hover: hover) and (any-pointer: fine) {
  .homelinks a:hover { background: var(--surface-hover); color: var(--text-primary); }
}
/* ---- /wp5 ---- */
/* ---- wp6 alerts ---- */
/* The Alerts screen. Severity is carried by the row's left edge as well as its
   badge: the badge names it, the edge lets you count errors without reading. */
.alrcards .kval.bad { color: var(--negative-text); }

.alrbanner {
  padding: var(--sp-10) var(--sp-12); margin-bottom: var(--sp-16);
  border-radius: var(--r-8); box-shadow: 0 0 0 1px var(--info-border);
  background: var(--info-fill); color: var(--info-text);
  font: var(--t-body-13); font-family: var(--font-ui);
}
.alrbanner.warn { background: var(--warning-fill); color: var(--warning-text); box-shadow: 0 0 0 1px var(--warning-border); }
.alrbanner.ok { background: var(--positive-fill); color: var(--positive-text); box-shadow: 0 0 0 1px var(--positive-border); }
.alrbanner .mono { font-family: var(--font-mono); font-size: 12px; }

.alrrow td { vertical-align: top; }
.alrrow.s-error td:first-child { box-shadow: inset 3px 0 0 var(--negative-mark); }
.alrrow.s-warn td:first-child { box-shadow: inset 3px 0 0 var(--warning-mark); }
/* Acked rows stay in the list (an alert you closed is still evidence) but stop
   competing for attention. */
.alrrow.done { opacity: .55; }
.alrmain { max-width: 620px; }
.alrmain .name { display: block; margin-top: var(--sp-2); }
.alrmeta {
  margin-top: var(--sp-2); color: var(--text-tertiary);
  font: var(--t-label-12); font-family: var(--font-ui);
}
.alrmeta.err { color: var(--negative-text); }
.alrref { color: var(--accent-text); text-decoration: none; }
.alrref:hover { text-decoration: underline; }
/* ---- /wp6 ---- */
/* ---- wp7 ads engine ---- */
/* The executor screen. Everything here exists to make "nothing was sent" the
   loudest fact on the page: the guard chips are the same size as the action,
   and the shadow button is deliberately NOT the primary style. */
.execplats { display: flex; flex-wrap: wrap; gap: var(--sp-8); margin-top: var(--sp-12); }
.execplat {
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-6) var(--sp-10); border-radius: var(--r-md);
  background: var(--surface-sunken); box-shadow: var(--ring);
}
.execplat b { font: var(--t-label-13); font-family: var(--font-ui); text-transform: uppercase; letter-spacing: 0.04em; }
.execplat.off { opacity: 0.55; }

.execcard {
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--sp-12) var(--sp-14); margin-bottom: var(--sp-10);
  background: var(--surface-raised);
}
.execcard.blocked { border-color: var(--negative-border); background: var(--negative-fill); }
.execact { display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap; }
.execop {
  font: var(--t-label-12); font-family: var(--font-mono);
  text-transform: uppercase; letter-spacing: 0.05em;
  padding: var(--sp-2) var(--sp-6); border-radius: var(--r-sm);
  background: var(--surface-sunken); color: var(--text-secondary);
}
.execsum { font: var(--t-body-13); font-family: var(--font-ui); font-weight: var(--fw-medium); }
.execmeta { margin-top: var(--sp-4); font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }

.execguards { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-8); }
.execguard {
  display: inline-flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-8); border-radius: var(--r-full);
  font: var(--t-label-12); font-family: var(--font-ui); cursor: help;
}
.execguard.ok { color: var(--positive-text); background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); }
.execguard.no { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }
.execguard .dot { font-weight: var(--fw-bold); }

.execdetail { margin-top: var(--sp-8); }
.execfoot { display: flex; align-items: center; gap: var(--sp-10); margin-top: var(--sp-10); flex-wrap: wrap; }
.execunq { margin: var(--sp-8) 0 0 var(--sp-16); font: var(--t-body-13); font-family: var(--font-ui); }
.execunq li { margin-bottom: var(--sp-4); }

.execrule { padding: var(--sp-10) 0; border-top: 1px solid var(--line2); }
.execrule:first-of-type { border-top: none; }
.execrulehead { display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.execskips { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-6); }
.chipflat {
  padding: var(--sp-2) var(--sp-8); border-radius: var(--r-sm);
  background: var(--surface-sunken); color: var(--text-tertiary);
  font: var(--t-label-12); font-family: var(--font-ui);
}
.execrulenote { margin-top: var(--sp-6); }
/* ---- /wp7 ---- */

/* ==== 26/07 wave 2. Same rule: style ONLY inside your own marker block. ==== */
/* ---- w2-1 ui fixes ---- */
/* The shared search/filter bar (uikit.js). It lives in a panel head beside the
   title, so it has to read as chrome, never as a field you are expected to
   fill in before the table means anything. */
.uifbar { display: inline-flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap; }
.uifbar .uifq, .uifbar .uifsel {
  font: var(--t-body-13); font-family: var(--font-ui);
  height: var(--ctl-sm); padding: 0 var(--sp-8);
  border: 1px solid var(--border-default); border-radius: var(--r-6);
  background: var(--surface-raised); color: var(--text-primary);
}
.uifbar .uifq { min-width: 190px; }
.uifbar .uifq:focus, .uifbar .uifsel:focus { outline: 2px solid var(--accent-fill); outline-offset: -1px; }
.uifcount {
  font: var(--t-label-11); font-family: var(--font-ui);
  letter-spacing: var(--tr-label-11); color: var(--text-tertiary);
}
.uifhide { display: none !important; }
/* A hit inside a COLLAPSED family must still show, so the filter's reveal has
   to beat .famchild.hide. Rows are table rows everywhere this is used. */
tr.uifshow { display: table-row; }
.uitip { border-bottom: 1px dotted var(--border-strong, var(--border-default)); cursor: help; }

/* N1: pacing cards were one per row on a 1200px screen, so the right half of
   the page was empty. Two up from 1100px. */
.pacegrid { display: grid; gap: var(--sp-12); grid-template-columns: 1fr; margin-bottom: var(--sp-20); }
@media (min-width: 1100px) { .pacegrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* N8: the idea box is a 40px button fixed 20px off the bottom corner. Without
   clearance it lands on the last table row's action button. */
#view { padding-bottom: calc(40px + var(--sp-20) + var(--sp-16)); }

/* N9: the drawer and the lightbox used to appear between two frames. The
   motion scale already exists in tokens.css; reduced-motion is handled there
   globally, so these need no second guard. */
@keyframes uifade { from { opacity: 0; } to { opacity: 1; } }
@keyframes uislide { from { transform: translateX(28px); opacity: 0.4; } to { transform: none; opacity: 1; } }
@keyframes uipop { from { transform: scale(0.97); opacity: 0; } to { transform: none; opacity: 1; } }
#scmdrawer .scmscrim { animation: uifade var(--dur-overlay) var(--ease-out); }
#scmdrawer .scmpanel { animation: uislide var(--dur-overlay) var(--ease-out-soft); }
#lightbox.open { animation: uifade var(--dur-overlay) var(--ease-out); }
#lightbox.open img { animation: uipop var(--dur-slow) var(--ease-out-soft); }

/* N10: the palette is the fastest way out of an empty screen, and nothing but
   the rail button taught it. */
.statekbd { margin-top: var(--sp-8); font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }
.statekbd kbd {
  font-family: var(--font-numeric); font-size: 11px; padding: 1px var(--sp-4);
  border: 1px solid var(--border-default); border-radius: var(--r-4); background: var(--surface-sunken);
}

/* C4: the Inbox card leads with a verdict, then chips, then the evidence.
   The dense justification and the payload JSON live inside the disclosure. */
.sugg .verdict { font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-secondary); margin: var(--sp-6) 0; max-width: 78ch; }
.suggchips { display: flex; flex-wrap: wrap; gap: var(--sp-6); margin: var(--sp-8) 0 var(--sp-4); }
.suggchip {
  display: inline-flex; gap: var(--sp-6); align-items: baseline;
  background: var(--surface-sunken); border-radius: var(--r-full);
  padding: 2px var(--sp-10); font: var(--t-label-12); font-family: var(--font-ui);
}
.suggchip b { font-family: var(--font-numeric); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.suggchip span { color: var(--text-tertiary); }
.suggmove {
  display: inline-flex; align-items: center; gap: var(--sp-6); margin: var(--sp-4) 0;
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary);
}
.suggmove code {
  font-family: var(--font-numeric); background: var(--surface-sunken);
  border-radius: var(--r-4); padding: 1px var(--sp-6);
}
.suggmore { margin-top: var(--sp-6); }
.suggmore > summary { cursor: pointer; font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }
.suggmore .why { white-space: pre-wrap; font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-secondary); margin: var(--sp-8) 0; max-width: 82ch; }

/* C5 / H2 / H7: one line standing in for a cohort, with the cohort behind it. */
.rollup { border: 1px solid var(--border-default); border-radius: var(--r-6); padding: var(--sp-8) var(--sp-12); margin-bottom: var(--sp-10); }
.rollup > summary { cursor: pointer; font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-secondary); }
.rollup > summary b { color: var(--text-primary); }
.rollup[open] { background: var(--surface-sunken); }
.rollup .rollbody { margin-top: var(--sp-10); }

/* N5: "Forest Revive / 2 Bars & Bag" was breaking into four stacked lines in a
   narrow first column. One line, elided, full text on hover. */
.econame { display: block; max-width: 26ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* H4: provisional days are still real rows, just not settled ones. Muted, not
   hidden, and the banner above the table says why before the minus sign does. */
tr.provisional td { color: var(--text-tertiary); }
tr.provisional td b { font-weight: var(--fw-medium, 500); }

/* H1: the platform id stays visible under the name it resolves to, because the
   id is what you paste into Meta. */
.subid { display: block; font: var(--t-micro-10); font-family: var(--font-numeric); color: var(--text-quaternary); }

/* N2: the KPI sparkline was an unlabelled line beside a 30d pill. It now says
   the period and its extremes, and the hit strip gives a per-day value on
   hover without charts.js growing a tooltip engine for a 120px graphic. */
.sparkwrap { position: relative; display: block; }
.sparkhit { position: absolute; inset: 0; display: flex; }
.sparkhit > span { flex: 1 1 0; cursor: crosshair; }
.sparklbl {
  display: block; margin-top: var(--sp-2);
  font: var(--t-micro-10); font-family: var(--font-ui); color: var(--text-quaternary);
}

/* H6: a delta the data cannot support is not rendered as a number. */
.crenocmp {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); cursor: help;
  border-bottom: 1px dotted var(--border-default);
}

/* N4: Timeline SKU links were browser-default blue against the token palette. */
.pimdiff, .tllink { font-family: var(--font-ui); }
a.tllink { color: var(--accent-text, var(--text-primary)); text-decoration: none; border-bottom: 1px solid var(--border-default); }
a.tllink:hover { border-bottom-color: var(--accent-fill); }
.pagenote {
  display: flex; gap: var(--sp-8); align-items: baseline; flex-wrap: wrap;
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); margin-top: var(--sp-8);
}
/* ---- /w2-1 ---- */
/* ---- w2-2 profit ---- */
/* The Profit screen. Sellerboard's layout, Polaris's visual language: the tile
   is a kpicard that happens to be a button, the P&L is the dense table, and
   the only colour spent is on state (delta chips, absences, allocated cells).
   Nothing here introduces a hex value or a new radius. */

/* Explicit column counts, never auto-fit: the preset rows are 4 or 5 tiles and
   auto-fit strands the last one beside a two-tile gap (the kpirow lesson). */
.protiles {
  display: grid; gap: var(--sp-12); margin-bottom: var(--sp-16);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.protiles[data-n="5"] { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.protiles[data-n="1"] { grid-template-columns: minmax(0, 1fr); max-width: 420px; }
@media (max-width: 1320px) { .protiles[data-n="5"] { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1180px) {
  .protiles, .protiles[data-n="5"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .protiles, .protiles[data-n="5"] { grid-template-columns: minmax(0, 1fr); }
}

/* A tile is selectable, so it is a button. Selection is a ring and a surface,
   never a size change: tiles must not shift under the pointer. */
.protile {
  display: flex; flex-direction: column; gap: var(--sp-6);
  text-align: left; cursor: pointer; width: 100%;
  background: var(--surface-raised); border: none;
  border-radius: var(--r-8); box-shadow: var(--elev-card);
  padding: var(--sp-16);
  color: var(--text-primary); font-family: var(--font-ui);
  transition: background-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
@media (any-hover: hover) and (any-pointer: fine) {
  .protile:hover { background: var(--surface-hover); }
}
.protile.on {
  background: var(--surface-selected);
  box-shadow: var(--ring-strong);
}
.protile.bad { cursor: default; }
.protilehead { display: flex; align-items: baseline; gap: var(--sp-6); flex-wrap: wrap; }
.protitle {
  font: var(--t-label-13); font-family: var(--font-ui);
  color: var(--text-primary);
}
.prodates {
  font: var(--t-micro-10); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--text-tertiary); margin-left: auto;
}
/* One hero number per tile. The five supporting lines below it are deliberately
   small and uniform: two competing display numbers on one card is the defect. */
.proheadline {
  font: var(--t-display-24); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums slashed-zero;
  color: var(--text-primary);
  display: flex; align-items: baseline; gap: var(--sp-6); flex-wrap: wrap;
}
.protile .prosub {
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-tertiary); margin-top: calc(-1 * var(--sp-4));
}
.prolines {
  display: flex; flex-direction: column; gap: var(--sp-2);
  margin-top: var(--sp-6); padding-top: var(--sp-8);
  border-top: 1px solid var(--border-subtle);
}
.prorow { display: flex; align-items: baseline; gap: var(--sp-8); }
.prorow .k {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary);
}
.prorow .v {
  margin-left: auto; font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums;
  font-size: 12px; color: var(--text-secondary);
  display: inline-flex; align-items: baseline; gap: var(--sp-4);
}
.prorow .v b { color: var(--text-primary); font-weight: var(--fw-semi); }
.prorow .v b.neg { color: var(--negative-text); }

/* Freshness and history caveats ride ON the tile, not in a footnote nobody
   scrolls to: a period that ends today is not comparable with one that closed. */
.proflags { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-6); }
.proflag {
  font: var(--t-micro-10); font-family: var(--font-ui);
  color: var(--text-tertiary); background: var(--surface-sunken);
  border-radius: var(--r-4); padding: 1px var(--sp-6);
}
.proflag.warn { color: var(--warning-text); background: var(--warning-fill); }
.promore {
  font: var(--t-micro-10); font-family: var(--font-ui);
  color: var(--text-quaternary); margin-top: var(--sp-6);
}
.protile.on .promore { color: var(--accent-text); }

/* ---- the expanded P&L */
.propanel { margin-bottom: var(--sp-16); }
.prohead {
  display: flex; align-items: center; gap: var(--sp-12);
  padding: var(--sp-12) var(--sp-16) 0;
}
.prohead h2 {
  font: var(--t-title-17); font-family: var(--font-ui);
  color: var(--text-primary); margin: 0;
}
.prohead h2 .meta {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); margin-left: var(--sp-6);
}
.prohead .freshness { margin-left: auto; }
.pronote {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary);
  padding: var(--sp-8) var(--sp-16) 0;
}
.pronote.warn, .pronote .warn { color: var(--warning-text); }

table.propnl { margin-top: var(--sp-12); }
table.propnl th[scope="row"] {
  position: static; text-align: left; white-space: normal;
  font: var(--t-body-13); font-family: var(--font-ui);
  color: var(--text-secondary); font-weight: var(--fw-regular);
  border-bottom: 1px solid var(--border-subtle);
  padding: var(--row-pad-y) var(--sp-10);
}
/* Subtotals read as subtotals. A rule above the line and a heavier number is
   the whole hierarchy: no fills, no boxes, no second colour. */
/* :not(.prowhy) throughout: the provenance column must never take a row's
   emphasis. Without it the subtotal rows shout their own footnotes. */
table.propnl tr.pro-strong th[scope="row"],
table.propnl tr.pro-strong td:not(.prowhy) {
  font-weight: var(--fw-semi); color: var(--text-primary);
}
table.propnl tr.pro-rule th[scope="row"],
table.propnl tr.pro-rule td { border-top: 1px solid var(--gridline); }
table.propnl tr.pro-dim th[scope="row"],
table.propnl tr.pro-dim td:not(.prowhy) { color: var(--text-tertiary); }
table.propnl tr.pro-none th[scope="row"] { color: var(--text-tertiary); }
/* Specificity matters here: `table.dense td` sets the primary text colour, so
   a bare .prowhy loses and the provenance column shouts louder than the
   numbers it explains. */
table.dense td.prowhy {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); max-width: 46ch; white-space: normal;
}

/* An absence is a sentence, never a number. This is the rule the whole screen
   turns on: 0.00% for sessions we do not have would read as a dead listing. */
.pro-absent {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-quaternary); font-style: normal;
}
/* A caveat attached to one line. Neutral by default, amber only when the
   number on that line is actually misleading without it. */
.promark {
  display: inline-flex; align-items: center; height: 17px;
  padding: 0 var(--sp-6); margin-left: var(--sp-4);
  border-radius: var(--r-4); white-space: nowrap;
  font: var(--t-micro-10); font-family: var(--font-ui);
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border);
}
.promark.warn {
  color: var(--warning-text); background: var(--warning-fill);
  box-shadow: 0 0 0 1px var(--warning-border);
}

/* ---- the product table */
/* An allocated cell is marked in the CELL, because the column header scrolls
   away and the reader needs to know at the point of reading that this number
   was apportioned by revenue share rather than measured. */
table.dense td.pro-alloc { color: var(--text-secondary); font-style: italic; }
.promono {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--text-secondary);
}
.proname { margin-left: var(--sp-6); color: var(--text-primary); }
.progeo {
  margin-left: var(--sp-6); font: var(--t-micro-10); font-family: var(--font-mono);
  color: var(--text-quaternary); letter-spacing: .04em;
}
.prorange { display: inline-flex; align-items: center; gap: var(--sp-6); }
.prorange input[type="date"] {
  height: var(--ctl-sm); padding: 0 var(--sp-8);
  border: none; box-shadow: var(--ring); border-radius: var(--r-6);
  background: var(--surface-raised); color: var(--text-primary);
  font: var(--t-label-13); font-family: var(--font-ui);
}
/* ---- /w2-2 ---- */
/* ---- w2-3 skumap ---- */
/* The presence matrix. Four fixed slots per row, ALWAYS all four: the absence
   of a market is the finding half the time ("selling in DE, listed nowhere
   else"), and a chip that disappears when it is off makes rows unscannable
   because the columns stop lining up. Off is drawn, just quietly. */
.skmchip {
  display: inline-block; min-width: 30px; text-align: center;
  margin-right: var(--sp-2); padding: 1px var(--sp-6);
  border-radius: var(--r-6); box-shadow: var(--ring);
  font: var(--t-label-11); font-family: var(--font-ui); letter-spacing: var(--tr-label-11);
  color: var(--text-tertiary); background: var(--surface-sunken);
}
.skmchip.on { color: var(--text-primary); background: var(--surface-raised); box-shadow: var(--ring-strong); }

/* SKU keys run to 40 characters (amzn.gr.BOBR-BO-0001-4u1bf2bVXOOENyj5-VG) and
   are read character by character when you are deciding whether two of them
   are the same product. Monospace, and allowed to wrap rather than truncate. */
.skmkey b { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); word-break: break-all; }
.skmkey .name { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }

/* The action cell: suggestions, then the full picker, then park. Wraps on a
   narrow window instead of pushing the table into a horizontal scroll. */
.skmact { display: flex; align-items: center; gap: var(--sp-6); flex-wrap: wrap; }
.skmact .primaryish { box-shadow: var(--ring-strong); font-weight: 600; }
.skmact select.mini {
  height: var(--ctl-sm); padding: 0 var(--sp-6); max-width: 170px;
  font: var(--t-label-11); font-family: var(--font-ui);
}
.perfcheck {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  font: var(--t-label-13); font-family: var(--font-ui); color: var(--text-secondary);
  user-select: none; cursor: pointer;
}
/* ---- /w2-3 ---- */
/* ---- w2-4 supply v2 ---- */
/* Every colour below is a token, so both themes come out of the same rules.
   Nothing here restyles a shared class: the Supply screen's v2 additions all
   carry their own names. */

/* The two pipeline cards beside the location values: money that has left and
   has not landed. Marked with a left edge so the strip reads as "here" then
   "coming", rather than as six equal locations. */
.card.scmpipe { border-left: 3px solid var(--info-mark); }

/* Days of stock, the house rule as one chip: green over 30, amber 8 to 30,
   red under 8. Also used for "Now" in the order-in column. */
.coverchip {
  display: inline-flex; align-items: center; height: 20px; padding: 0 var(--sp-8);
  border-radius: var(--r-4); white-space: nowrap;
  font: var(--t-label-11); font-family: var(--font-numeric);
  font-variant-numeric: tabular-nums;
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border);
}
.coverchip.c-green { color: var(--positive-text); background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); }
.coverchip.c-amber { color: var(--warning-text);  background: var(--warning-fill);  box-shadow: 0 0 0 1px var(--warning-border); }
.coverchip.c-red   { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }

/* Board flags. Outline rather than fill, so a row carrying three of them does
   not out-shout the state pill next to it. */
.scmflags { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.scmflag {
  display: inline-flex; align-items: center; height: 18px; padding: 0 var(--sp-6);
  border-radius: var(--r-4); white-space: nowrap;
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-secondary); box-shadow: 0 0 0 1px var(--border-default);
}
.scmflag.f-value  { color: var(--info-text);     box-shadow: 0 0 0 1px var(--info-border); }
.scmflag.f-growth { color: var(--positive-text); box-shadow: 0 0 0 1px var(--positive-border); }
.scmflag.f-min    { color: var(--negative-text); box-shadow: 0 0 0 1px var(--negative-border); }
.scmflag.f-oos    { color: var(--warning-text);  box-shadow: 0 0 0 1px var(--warning-border); }

/* An overdue balance in the drawer's key-value grid, tinted rather than only
   worded (teardown H3). */
.scmkv.overdue b { color: var(--negative-text); }
.paychip.p-red { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }

/* A sub-heading inside a section body, for the transit-stats table under the
   shipments lane. */
h3.scmsub { margin: var(--sp-20) 0 var(--sp-8); font-size: 13px; color: var(--text-secondary); }

/* The forecast card and the PO maker need more width than the PO drawer. */
#scmdrawer .scmpanel.wide { width: min(1120px, 96vw); }

/* The three stat boxes at the top of the forecast card (ux.md §2). */
.scmstats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-10); margin-top: var(--sp-16);
}
.scmstat {
  background: var(--surface-sunken); border-radius: var(--r-8);
  padding: var(--sp-10) var(--sp-14); border-left: 3px solid var(--neutral-border);
}
.scmstat .kval { font: var(--t-display-24); font-family: var(--font-numeric); margin-top: var(--sp-2); }
.scmstat .ksub { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.scmstat.c-green { border-left-color: var(--positive-mark); }
.scmstat.c-amber { border-left-color: var(--warning-mark); }
.scmstat.c-red   { border-left-color: var(--negative-mark); }

.scmbuffer, .scmnotes { display: grid; gap: var(--sp-10); margin-top: var(--sp-8); }
.scmbuffer .listctl label { display: inline-flex; align-items: center; gap: var(--sp-6); }
.scmbuffer input[type="number"] { width: 110px; text-align: right; }

/* "skip ahead" sits with the lifecycle buttons but must not look like one. */
.scmskip {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary);
}

/* ---- the PO maker ---- */
.wizsteps {
  display: flex; gap: var(--sp-8); list-style: none; margin: var(--sp-16) 0; padding: 0;
  flex-wrap: wrap;
}
.wizsteps li {
  display: inline-flex; align-items: center; gap: var(--sp-6);
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary);
  padding: var(--sp-4) var(--sp-10); border-radius: 99px; box-shadow: 0 0 0 1px var(--border-subtle);
}
.wizsteps li .n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 99px;
  background: var(--neutral-fill); color: var(--neutral-text);
  font-family: var(--font-numeric); font-size: 11px;
}
.wizsteps li.on { color: var(--text-primary); box-shadow: 0 0 0 1px var(--border-strong, var(--border-default)); }
.wizsteps li.on .n { background: var(--info-fill); color: var(--info-text); }
.wizsteps li.done .n { background: var(--positive-fill); color: var(--positive-text); }

.wizform { display: grid; gap: var(--sp-10); }
.wizgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--sp-10); }
.wizgrid label, .wizwide { display: grid; gap: var(--sp-4); font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.wizwide { margin-top: var(--sp-10); }
.wizwide textarea { width: 100%; font: inherit; font-family: var(--font-ui); }
.wizlines input[type="number"] { width: 110px; text-align: right; }
.wizlines tr.off { opacity: 0.45; }
.wiznav { display: flex; gap: var(--sp-8); margin-top: var(--sp-20); }
.wizdone {
  margin-top: var(--sp-16); padding: var(--sp-12) var(--sp-14);
  background: var(--positive-fill); border-radius: var(--r-8);
  box-shadow: 0 0 0 1px var(--positive-border);
}
.wizdone p { margin: 0 0 var(--sp-8); }

/* Step 4 renders the house PO inline, so what you approve is what prints. The
   print route itself stays the authority; this is the same layout in the
   screen's own tokens rather than a second definition of the document. */
.poprint {
  background: var(--surface-raised); border-radius: var(--r-8); box-shadow: var(--ring);
  padding: var(--sp-20) var(--sp-24); margin-top: var(--sp-12);
  font: var(--t-label-12); font-family: var(--font-ui);
}
.poprint .pptitle { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: var(--sp-16); }
.poprint .pptitle h1 { font-size: 20px; font-weight: 400; margin: 0; letter-spacing: -0.2px; }
.poprint .pptitle .co { font-weight: 600; }
.poprint .pphead { display: grid; grid-template-columns: 1fr 220px; gap: var(--sp-20); margin-bottom: var(--sp-16); }
.poprint .pphead > div > div { margin-bottom: var(--sp-4); }
.poprint .pplbl { font-weight: 600; color: var(--text-secondary); }
.poprint table.ppitems { width: 100%; border-collapse: collapse; margin-bottom: var(--sp-12); }
.poprint table.ppitems th {
  text-align: left; background: var(--surface-sunken); padding: var(--sp-4) var(--sp-6);
  border-top: 1px solid var(--border-default); border-bottom: 1px solid var(--border-default);
  font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-secondary);
}
.poprint table.ppitems td { padding: var(--sp-4) var(--sp-6); border-bottom: 1px solid var(--border-subtle); }
.poprint table.ppitems .num { text-align: right; font-family: var(--font-numeric); font-variant-numeric: tabular-nums; }
.poprint .ppsums { display: flex; justify-content: flex-end; gap: var(--sp-20); flex-wrap: wrap; }
.poprint .ppsum { min-width: 150px; }
.poprint .ppsum .h { font-weight: 600; text-align: right; color: var(--text-secondary); }
.poprint .ppsum .t {
  display: flex; justify-content: space-between; font-weight: 600;
  border-top: 1px solid var(--border-default); padding-top: var(--sp-2); margin-top: var(--sp-2);
  font-family: var(--font-numeric); font-variant-numeric: tabular-nums;
}
.poprint .ppnotes { margin-top: var(--sp-16); color: var(--text-secondary); }
.poprint .ppnotes p { margin: var(--sp-4) 0; }

@media (max-width: 900px) {
  .poprint .pphead { grid-template-columns: 1fr; }
}
/* ---- /w2-4 ---- */
/* ---- w2-5 health ---- */
/* Home: the first-day comparison note, said once above the strip (N3). Tuned
   to read as an explanation, not an error — the numbers below it are fine. */
.homealign {
  margin: var(--sp-8) 0 var(--sp-4);
  padding: var(--sp-8) var(--sp-12);
  border-radius: var(--r-6);
  border: 1px solid var(--line2);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  font: var(--t-body-13);
}
.homealign b { color: var(--text-primary); font-weight: 600; }

/* Home: the health block. Full width, above the two-up attention grid, because
   "what is broken" outranks "what needs a decision". */
.homehealth { margin-top: var(--sp-12); }
.homehealth .meta { font-variant-numeric: tabular-nums; }
.homehbad { color: var(--negative-text); font-weight: 600; }
.homehwarn { color: var(--warning-text); font-weight: 600; }
.homehfoot { margin-top: var(--sp-8); }

/* Alerts: the grouped info lane (H9). One row per kind until it is opened. */
.alrinfopanel { margin-top: var(--sp-12); }
.alrgroup { border-top: 1px solid var(--line2); }
.alrgroup:first-of-type { border-top: none; }
.alrgroup > summary {
  display: flex; align-items: center; gap: var(--sp-8);
  padding: var(--sp-8) 0; cursor: pointer; list-style: none;
  font: var(--t-body-13);
}
.alrgroup > summary::-webkit-details-marker { display: none; }
.alrgroup > summary::before {
  content: '▸'; color: var(--text-tertiary); font-size: 11px;
  transition: transform 120ms ease; display: inline-block;
}
.alrgroup[open] > summary::before { transform: rotate(90deg); }
.alrgroup > summary:hover { color: var(--text-primary); }
.alrgroup > summary b { font-variant-numeric: tabular-nums; }
.alrgroup .tablewrap { margin: 0 0 var(--sp-12) var(--sp-16); }
/* ---- /w2-5 ---- */

/* ==== 26/07 wave 3 (the ERP pack). Same rule: your own marker block only. ==== */
/* ---- w3-1 cashflow ---- */
/* The Cash screen. Four fills carry the whole chart, so they are declared once
   and flipped once for dark. Committed money is solid; projected money is
   hatched, because "nothing has been ordered" has to be visible at a glance
   and a colour alone would not say it. */
:root {
  --cf-balance:   #2b71de;
  --cf-deposit:   #7aa8ec;
  --cf-draft:     rgba(var(--ink-rgb), 0.22);
  --cf-projected: rgba(var(--ink-rgb), 0.30);
  --cf-cum:       var(--accent-500);
}
:root[data-theme="dark"] {
  --cf-balance:   #5b96f0;
  --cf-deposit:   #35608f;
  --cf-draft:     rgba(var(--paper-rgb), 0.26);
  --cf-projected: rgba(var(--paper-rgb), 0.34);
  --cf-cum:       #E8926E;
}

/* chips: how sure the date is, and whether it is late. Two separate facts, so
   two separate chips, never one blended colour. */
.cfchip {
  display: inline-flex; align-items: center; height: 18px; padding: 0 var(--sp-6);
  border-radius: var(--r-4); font: var(--t-label-11); font-family: var(--font-ui);
  white-space: nowrap; vertical-align: middle;
  color: var(--neutral-text); background: var(--neutral-fill);
  box-shadow: 0 0 0 1px var(--neutral-border);
}
.cfchip.cf-firm      { color: var(--positive-text); background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border); }
.cfchip.cf-estimated { color: var(--warning-text);  background: var(--warning-fill);  box-shadow: 0 0 0 1px var(--warning-border); }
.cfchip.cf-needsdate { color: var(--info-text);     background: var(--info-fill);     box-shadow: 0 0 0 1px var(--info-fill); }
.cfchip.cf-overdue   { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }
.cfchip.cf-oos       { color: var(--negative-text); background: var(--negative-fill); box-shadow: 0 0 0 1px var(--negative-border); }
.cfchip.cf-reorder_now { color: var(--warning-text); background: var(--warning-fill); box-shadow: 0 0 0 1px var(--warning-border); }

/* the timeline. Columns are HTML because the x axis is buckets, not dates;
   the cumulative is one SVG laid over the same column centres. */
.cfchart { margin-top: var(--sp-8); }
.cfplot { position: relative; }
/* Pinned to the BAR box, not the whole plot: the value labels sit above the
   bars, and letting the line use their height would put the cumulative on a
   silently different scale from the columns underneath it. 18px = the value
   row (14px line) plus the 4px gap; 150px = .cfbar. Measured, not guessed. */
.cfcum {
  position: absolute; left: 0; right: 0; top: 18px; height: 150px;
  pointer-events: none; z-index: 2; overflow: visible;
}
.cfcum polyline {
  fill: none; stroke: var(--cf-cum); stroke-width: 1.5px;
  stroke-linejoin: round; stroke-linecap: round; stroke-dasharray: 3 3; opacity: .9;
}
.cfcols { display: flex; align-items: flex-end; gap: var(--sp-6); }
.cfcol { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-4); }
.cfval {
  font: var(--t-label-11); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums; color: var(--text-secondary);
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cfbar {
  height: 150px; display: flex; flex-direction: column; justify-content: flex-end;
  border-bottom: 1px solid var(--gridline);
}
.cfseg { display: block; width: 100%; min-height: 2px; }
.cfseg:first-child { border-radius: var(--r-2) var(--r-2) 0 0; }
.cfseg.bal { background: var(--cf-balance); }
.cfseg.dep { background: var(--cf-deposit); }
.cfseg.drf { background: var(--cf-draft); }
.cfseg.prj {
  background: repeating-linear-gradient(45deg,
    var(--cf-projected) 0 4px, transparent 4px 8px);
  box-shadow: inset 0 0 0 1px var(--cf-projected);
}
.cfempty { display: block; height: 2px; background: var(--border-subtle); }
.cflabel {
  font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-secondary);
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cfsub { font: var(--t-micro-10); font-family: var(--font-ui); color: var(--text-tertiary); text-align: center; }
.cfcol.late .cflabel, .cfcol.late .cfsub { color: var(--negative-text); }
.cfcol.now  .cflabel { color: var(--text-primary); font-weight: var(--fw-semi); }
/* This week is marked on the BASELINE, not by filling the column. A tinted
   column background reads as a fourth data series sitting behind the bars. */
.cfcol.now  .cfbar   { border-bottom: 2px solid var(--accent-fill); }
.cflegend {
  display: flex; flex-wrap: wrap; gap: var(--sp-12); margin-top: var(--sp-12);
  font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary);
}
.cflegend span { display: inline-flex; align-items: center; gap: var(--sp-6); }
.cflegend .sw { width: 10px; height: 10px; border-radius: var(--r-2); display: inline-block; }
.cflegend .sw.bal { background: var(--cf-balance); }
.cflegend .sw.dep { background: var(--cf-deposit); }
.cflegend .sw.drf { background: var(--cf-draft); }
.cflegend .sw.prj { background: repeating-linear-gradient(45deg, var(--cf-projected) 0 3px, transparent 3px 6px); box-shadow: inset 0 0 0 1px var(--cf-projected); }
.cflegend .sw.cum { background: none; border-top: 2px dashed var(--cf-cum); height: 0; border-radius: 0; }
.cffoot { margin-top: var(--sp-8); font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }

/* the four-way split of the whole book */
.cfsplit { display: grid; gap: var(--sp-12); grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .cfsplit { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .cfsplit { grid-template-columns: minmax(0, 1fr); } }
.cfsplit > div { display: flex; flex-direction: column; gap: var(--sp-2); }
.cfsplit .k { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.cfsplit .v {
  font: var(--t-title-20); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums slashed-zero; color: var(--text-primary);
}

/* currency exposure strip */
.cfexp { display: grid; gap: var(--sp-12); grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.cfexpcard {
  background: var(--surface-sunken); border-radius: var(--r-8); padding: var(--sp-12);
  display: flex; flex-direction: column; gap: var(--sp-4);
}
.cfexpcard .k { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.cfexpcard .v {
  font: var(--t-title-20); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums slashed-zero; color: var(--text-primary);
}
.cfexpcard .sub { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary); }

/* small type used across the screen */
.cffine { font: var(--t-micro-10); font-family: var(--font-ui); color: var(--text-tertiary); }
.cfwarn { color: var(--warning-text); }
.cfcur {
  font: var(--t-micro-10); font-family: var(--font-ui); color: var(--info-text);
  background: var(--info-fill); border-radius: var(--r-2); padding: 1px 4px; cursor: help;
}
.cfwhy { max-width: 340px; white-space: normal; font: var(--t-label-12); font-family: var(--font-ui); }

/* the two lanes that are deliberately NOT on the timeline */
.cfundated .cffix { margin: var(--sp-8) 0 0; padding-left: var(--sp-16); }
.cfundated .cffix li { font: var(--t-body-13); font-family: var(--font-ui); margin-bottom: var(--sp-4); }
.cfstale > summary { color: var(--text-secondary); }
/* Sheet-imported rows carry whatever the sheet had in its PO column, and one
   live row is a 130-character instruction to the spreadsheet's reader. Clamp
   rather than clean: the data is not this screen's to rewrite. */
.cfstale table.dense td.stick { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cfproj .note { margin-bottom: var(--sp-10); }

/* the assumptions footer: the whole honesty budget, one line per rule */
.cfassume { margin-top: var(--sp-16); }
.cfrules { display: grid; grid-template-columns: minmax(140px, 220px) 1fr; gap: var(--sp-6) var(--sp-16); margin-top: var(--sp-12); }
@media (max-width: 720px) { .cfrules { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2) 0; } }
.cfrules dt { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-primary); }
.cfrules dd { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary); margin: 0 0 var(--sp-6); }

/* a KPI card may carry state, but only on the label: tinted digits fight the
   tabular rhythm and fail in greyscale (the dense-table rule, same reason). */
.kpicard.cf-bad  .k { color: var(--negative-text); }
.kpicard.cf-warn .k { color: var(--warning-text); }

/* .rollup[open] paints itself sunken; inside a .panel that reads as a hole */
.panel.rollup[open] { background: var(--card); }
/* ---- /w3-1 ---- */
/* ---- w3-2 cost trueup ---- */
/* The Costs section on the Profit screen. It reuses the profit table styling
   throughout; what it adds is the shape of a propose-first list, which is a
   NUMBERED before -> after column pair with its evidence beside it. */
.cstwrap { margin-top: var(--sp-16); }
/* The section header is the toggle. Closed is the default state, so it has to
   look like something you open, not like a heading that failed to load. */
.cstoggle {
  display: flex; align-items: center; gap: var(--sp-6); width: 100%;
  padding: var(--sp-10) var(--sp-16); border: none; background: none;
  text-align: left; cursor: pointer;
  font: var(--t-label-13); font-family: var(--font-ui);
  color: var(--text-primary);
}
.cstoggle:hover { color: var(--accent-text); }
.cstoggle .chev { color: var(--text-tertiary); }
.cstoggle .famagg { margin-left: var(--sp-6); }
.csth {
  margin: var(--sp-20) var(--sp-16) var(--sp-8);
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary); font-weight: var(--fw-semi);
}
.cstthr { display: inline-flex; align-items: center; gap: var(--sp-6);
  font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-secondary); }
.cstthr input {
  width: 56px; height: var(--ctl-sm); padding: 0 var(--sp-6);
  border: none; box-shadow: var(--ring); border-radius: var(--r-6);
  background: var(--surface-raised); color: var(--text-primary);
  font: var(--t-label-13); font-family: var(--font-mono);
}
/* The line number IS the propose-first contract: "row 14" has to mean the
   same thing in the list, in the batch and in a message to Kevin. */
.cstnum {
  display: inline-block; min-width: 2.2ch; margin-right: var(--sp-6);
  font: var(--t-micro-10); font-family: var(--font-mono);
  color: var(--text-quaternary); text-align: right;
}
table.cstlist td.cstarrow { width: 1.5rem; padding: 0; color: var(--text-quaternary); }
table.dense td.cstwhy {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); max-width: 58ch; white-space: normal;
}
table.dense td.cstwhy b { color: var(--text-secondary); font-weight: var(--fw-semi); }
/* The one button that rewrites a number the whole P&L rests on. It is allowed
   to look different from every other button on this screen. */
.cstapply { box-shadow: 0 0 0 1px var(--warning-border); color: var(--warning-text); }
/* ---- /w3-2 ---- */
/* ---- w3-3 planning ---- */
/* Two more board flags (0061). Both inherit .scmflag's shape and only pick a
   tint, so they follow the theme tokens in light and dark like the other four.
   f-gap is negative because an uncovered window is money already going; f-season
   is informational because a factor is a decision, not a fault. */
.scmflag.f-gap    { color: var(--negative-text); box-shadow: 0 0 0 1px var(--negative-border); }
.scmflag.f-season { color: var(--info-text);     box-shadow: 0 0 0 1px var(--info-border); }

/* The seasonal grid. Twelve months have to fit beside a family name without a
   horizontal scrollbar on a laptop, so the inputs are 46px and wrap rather than
   forcing the table wider than the panel. */
.scmseason td { vertical-align: top; }
.scmseason .seasrow { display: flex; flex-direction: column; gap: var(--sp-6); }
.seasmonths { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.seasm { display: flex; flex-direction: column; gap: 1px; }
.seasm span {
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-tertiary); text-align: center;
}
.seasm input {
  width: 46px; padding: var(--sp-2) var(--sp-4); text-align: right;
  font-family: var(--font-numeric); font-variant-numeric: tabular-nums;
  border-radius: var(--r-4);
}
/* Q4 is the case erp.md named, so the three months it means are marked. The
   tint is a background wash, not a border, so a filled input still reads as an
   input in both themes. */
.seasm.q4 input { background: var(--info-fill); }
.seasm.q4 span  { color: var(--info-text); }
.seasnote { min-width: 260px; flex: 1 1 260px; }

/* The lost-sales stat row reuses .scmstats from the forecast card; only the
   chart under it needs to be pushed off the cards. */
#app .scmstats + .chart { margin-top: var(--sp-8); }
/* ---- /w3-3 ---- */
/* ---- w3-4 finance analytics ---- */
/* Finance screen: three stacked sections, each with its own head. Everything
   here resolves through the tokens, so both themes come for free. */
.finsec { margin-top: var(--sp-24); }
.finsec:first-of-type { margin-top: var(--sp-12); }

.fintiles {
  display: grid; gap: var(--sp-8); margin: var(--sp-8) 0 var(--sp-12);
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}
.fintile {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-12); border: 1px solid var(--line2); border-radius: var(--r-8);
  background: var(--surface-raised);
}
.fintlabel { font: var(--t-label-11); color: var(--text-tertiary); text-transform: uppercase; letter-spacing: .04em; }
.fintval { font: var(--t-title-20); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.fintsub { font: var(--t-body-13); color: var(--text-tertiary); }

/* the flag list: the proposal a human approves before anything is raised */
.finflags { margin: var(--sp-12) 0; padding: var(--sp-12); border: 1px solid var(--line2);
            border-radius: var(--r-8); background: var(--surface-sunken); }
.finflags h3 { font: var(--t-body-13); margin: 0 0 var(--sp-8); }
.finflags ul { list-style: none; margin: 0; padding: 0; }
.finflags li { padding: var(--sp-8) 0; border-top: 1px solid var(--line2); font: var(--t-body-13); }
.finflags li:first-child { border-top: none; }
.finflags li.finraised { color: var(--text-tertiary); }
.finevidence { display: block; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.finactions { margin-top: var(--sp-8); display: flex; align-items: center; gap: var(--sp-8); flex-wrap: wrap; }

.finmono { font-family: var(--font-mono); font-size: 12px; }
.fingeo { color: var(--text-tertiary); font-size: 11px; }
.finname { display: block; color: var(--text-tertiary); font-size: 11px; }
.finminlabel { font: var(--t-body-13); color: var(--text-tertiary); display: inline-flex; align-items: center; gap: 4px; }
.finminlabel input { width: 68px; }

/* verdict pills. Colour follows the token pairs, so dark mode inverts cleanly. */
.finflag, .fincheck, .finok, .finmuted, .finchip {
  display: inline-block; padding: 1px 6px; border-radius: var(--r-4);
  font: var(--t-label-11); white-space: nowrap;
}
.finflag  { color: var(--negative-text); background: var(--negative-fill); }
.fincheck { color: var(--warning-text);  background: var(--warning-fill); }
.finok    { color: var(--positive-text); background: var(--positive-fill); }
.finmuted { color: var(--text-tertiary); background: var(--surface-sunken); }
.finchip  { margin-right: var(--sp-4); }

.finbad  { color: var(--negative-text); }
.fingood { color: var(--positive-text); }
table.dense tr.finrowflag td { background: var(--negative-fill); }
table.dense tr.finmonth td {
  background: var(--surface-sunken); font: var(--t-label-11);
  text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary);
}
.finpointer { font-style: italic; }
/* ---- /w3-4 ---- */

/* ==== 26/07 wave 4. Same rule: your own marker block only. ==== */
/* ---- w4-1 fees reports ---- */
/* Report buttons. They ride the existing .ghost.mini shape so a screen gains
   two downloads without gaining a new visual language. */
.uirep { display: inline-flex; gap: 6px; align-items: center; }
.uirep button { white-space: nowrap; }

/* Fee coverage. The lane is the whole point of the table, so it gets the only
   colour: everything else stays greyscale, per the tokens rule. */
.fclane {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font: var(--t-label-11); border: 1px solid transparent; white-space: nowrap;
}
.fclane-pnl       { background: var(--positive-fill); color: var(--positive-text); border-color: var(--positive-border); }
.fclane-unmapped  { background: var(--warning-fill);  color: var(--warning-text);  border-color: var(--warning-border); }
.fclane-excluded  { background: var(--neutral-fill);  color: var(--neutral-text);  border-color: var(--neutral-border); }
.fclane-count     { background: transparent;          color: var(--text-secondary); border-color: var(--border-subtle); }
.fclane-unhandled { background: var(--negative-fill); color: var(--negative-text); border-color: var(--negative-border); }
button.finchip { cursor: pointer; font: var(--t-label-11); }
button.finchip.on { outline: 2px solid var(--focus-ring-color); outline-offset: 1px; }
.fcabsent { margin-top: 14px; }
.fcabsent .cfrules dt { margin-top: 8px; }

/* Reconcile. The verdict is the headline, so it is the one thing sized up. */
.recwrap .recbar { align-items: center; flex-wrap: wrap; gap: 10px; }
.recfile { display: inline-flex; align-items: center; gap: 8px; }
.recfile input[type="file"] {
  font: var(--t-body-13); max-width: 260px; color: var(--text-secondary);
}
.recfile input[type="file"]::file-selector-button {
  font: var(--t-label-12); padding: 5px 11px; margin-right: 9px; cursor: pointer;
  border: 1px solid var(--border-default); border-radius: 7px;
  background: var(--surface-raised); color: var(--text-primary);
}
.recfile input[type="file"]::file-selector-button:hover { background: var(--surface-hover); }
.recverdict {
  font: var(--t-label-13); padding: 3px 12px; border-radius: 999px; text-transform: uppercase;
  letter-spacing: .05em;
}
.recresult { margin-top: 8px; }
.recchips { margin: 6px 0 10px; gap: 6px; }
.recgate { border-left: 2px solid var(--border-default); padding-left: 10px; }
.recshapes { margin-top: 14px; }
table.dense th[rowspan] { vertical-align: bottom; }
/* ---- /w4-1 ---- */
/* ---- w4-2 buy view ---- */
/* The Buy screen. Deliberately thin: it reuses .kpicard, .coverchip, .scmflag,
   table.dense and .panel wholesale, and adds only the shapes a buying card
   needs that nothing else in the app has. Every colour is a token, so both
   themes follow without a branch. */

/* The unit after a count, so "5 SKUs" reads as one number and not as two. */
.kpicard .v .buyunit {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-tertiary); letter-spacing: 0;
}

/* The one link out of the control bar. button.ghost is a BUTTON selector, so an
   anchor wearing that class gets browser-default blue: this is the link. */
.buylink {
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary); text-decoration: none;
}
.buylink:hover { color: var(--accent-text); text-decoration: underline; }

/* The rhythm and flag counts, directly under the cards. Outline chips, same
   family as the board's, because a count is not a state worth filling in. */
.buychips {
  display: flex; flex-wrap: wrap; gap: var(--sp-6);
  margin: calc(-1 * var(--sp-8)) 0 var(--sp-16);
}
.buychips .scmflag { height: 22px; padding: 0 var(--sp-8); }

/* One supplier, one card. */
.buycard { padding-bottom: var(--sp-12); }
.buyhead {
  display: flex; align-items: baseline; gap: var(--sp-8); flex-wrap: wrap;
  padding: 0 var(--sp-16);
}
.buyhead h2 { margin: 0; }
.buyhead a { color: inherit; text-decoration: none; }
.buyhead a:hover { color: var(--accent-text); text-decoration: underline; }

/* A sentence the card has to say out loud: a missing term, a lead time that
   the last orders disagree with, a value that is short a line. */
.buynote {
  margin: var(--sp-8) var(--sp-16) 0;
  font: var(--t-label-12); font-family: var(--font-ui);
  color: var(--text-secondary);
}
.buysupnote { color: var(--text-tertiary); }
.buyblocked { color: var(--warning-text); }
.buydraftwarn { color: var(--text-secondary); }
/* Armed by the first click on a card that already has a draft standing: the
   warning was there before, and now it is the thing being overridden. */
.buydraftwarn.buyarmed {
  color: var(--warning-text);
  background: var(--warning-fill);
  box-shadow: 0 0 0 1px var(--warning-border);
  border-radius: var(--r-4);
  padding: var(--sp-6) var(--sp-8);
  margin-left: var(--sp-16); margin-right: var(--sp-16);
}

/* The line table. The tick box column is narrow and the quantity box is the
   only editable thing on the row, so it is the only control that looks like
   one. */
.buylines .buyck { width: 28px; text-align: center; }
.buylines td.buysug { color: var(--text-secondary); cursor: help; }
.buylines .name { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.buylines a { color: inherit; text-decoration: none; }
.buylines a:hover { color: var(--accent-text); text-decoration: underline; }
.buylines input.buyqty {
  width: 84px; height: var(--ctl-sm); padding: 0 var(--sp-6);
  text-align: right;
  font: var(--t-label-13); font-family: var(--font-numeric);
  font-variant-numeric: lining-nums tabular-nums;
}
/* An unticked line stays legible. Struck through would read as deleted, and it
   is not deleted: it is simply not on this order. */
.buylines tr.off td { opacity: .45; }
.buylines tr.off input.buyqty { opacity: 1; }
.buymiss { color: var(--warning-text); font: var(--t-label-12); font-family: var(--font-ui); }

/* The three numbers a buyer checks before clicking: how many lines, what the
   deposit is, when it lands. */
.buyfoot {
  display: flex; flex-wrap: wrap; gap: var(--sp-6) var(--sp-20);
  padding: var(--sp-12) var(--sp-16) 0;
  font: var(--t-label-13); font-family: var(--font-ui);
  color: var(--text-secondary);
}
.buyfoot b { color: var(--text-primary); font-family: var(--font-numeric); font-variant-numeric: tabular-nums; }

.buyact {
  display: flex; align-items: center; gap: var(--sp-12); flex-wrap: wrap;
  padding: var(--sp-12) var(--sp-16) var(--sp-4);
}
/* The promise next to the button, in the house voice. It is not fine print:
   it is the reason the button is safe to press. */
.buysafe { font: var(--t-label-12); font-family: var(--font-ui); color: var(--text-tertiary); }

/* Created in this session. Positive, because something good happened, and it
   carries the two next steps rather than a bare confirmation. */
.buymade {
  display: flex; align-items: center; gap: var(--sp-12); flex-wrap: wrap;
  margin: var(--sp-12) var(--sp-16) 0; padding: var(--sp-10) var(--sp-12);
  border-radius: var(--r-4);
  background: var(--positive-fill); box-shadow: 0 0 0 1px var(--positive-border);
  font: var(--t-label-13); font-family: var(--font-ui); color: var(--positive-text);
}
.buymade a { color: var(--positive-text); text-decoration: underline; }
/* ---- /w4-2 ---- */
/* ---- w4-3 bom forecast ---- */
/* Semantic tokens only, so both themes follow without a second rule. */

/* The banner. Deliberately the loudest thing on the page: everything under it
   is a model output and nobody should read it as a commitment. */
.fcbanner { font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-secondary); }
.fcbanner b { color: var(--text-primary); }

/* Totals strip, same shape as the forecast card's stat boxes. */
.fcstats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-10); margin-bottom: var(--sp-18);
}
.fcstat {
  background: var(--surface-sunken); border-radius: var(--r-8);
  padding: var(--sp-10) var(--sp-14); border-left: 3px solid var(--neutral-border);
}
.fcstat .kval { font: var(--t-display-24); font-family: var(--font-numeric); margin-top: var(--sp-2); }
.fcstat .ksub { font: var(--t-label-11); font-family: var(--font-ui); color: var(--text-tertiary); }
.fcstat.c-amber { border-left-color: var(--warning-mark); }

/* Week grid: 13 columns wants to overflow on a half-screen laptop, so the
   first column sticks and the rest scroll inside .tablewrap (ux.md §1). */
.fcweeks { min-width: 720px; }
.fcweeks th:first-child, .fcweeks td:first-child {
  position: sticky; left: 0; background: var(--surface-raised); z-index: 1;
}
.fcweeks td.fcseason { box-shadow: inset 0 -2px 0 var(--warning-mark); }

/* Family table. A family row is a control, a SKU row is detail: the twist and
   the indent carry that, not colour, so the states survive both themes. */
.fctable { min-width: 860px; }
.fctable tr.fcfam { cursor: pointer; }
.fctable tr.fcfam:hover { background: var(--surface-hover); }
.fctable tr.fcfam.open { background: var(--surface-sunken); }
.fctwist { display: inline-block; width: 12px; color: var(--text-tertiary); }
.fctable tr.fcsku td { font: var(--t-body-13); font-family: var(--font-ui); }
.fcindent { padding-left: var(--sp-24) !important; }

.fcflag {
  display: inline-flex; align-items: center; height: 18px; padding: 0 var(--sp-6);
  border-radius: var(--r-4); white-space: nowrap; margin-right: var(--sp-4);
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-secondary); box-shadow: 0 0 0 1px var(--border-default);
}
.fcflag.f-season { color: var(--warning-text);  box-shadow: 0 0 0 1px var(--warning-border); }
.fcflag.f-nopr   { color: var(--negative-text); box-shadow: 0 0 0 1px var(--negative-border); }
.fcflag.f-bom    { color: var(--info-text);     box-shadow: 0 0 0 1px var(--info-border); }

/* A price chip says WHICH source answered. The ASP fallback is tinted because
   it is a measurement standing in for a maintained number, not the same thing. */
.fcprice {
  display: inline-block; padding: 0 var(--sp-4); border-radius: var(--r-4);
  font: var(--t-label-11); font-family: var(--font-numeric); white-space: nowrap;
  color: var(--text-secondary); box-shadow: 0 0 0 1px var(--border-default);
}
.fcprice.p-asp_90d { color: var(--warning-text); box-shadow: 0 0 0 1px var(--warning-border); }

/* ---- the BOM block on the Supply forecast card ---- */
.bomrule { font: var(--t-body-13); font-family: var(--font-ui); color: var(--text-tertiary); }
.bomchip {
  display: inline-flex; align-items: center; height: 18px; padding: 0 var(--sp-6);
  border-radius: var(--r-4); white-space: nowrap;
  font: var(--t-label-11); font-family: var(--font-ui);
  color: var(--text-secondary); box-shadow: 0 0 0 1px var(--border-default);
}
.bomchip.b-explode   { color: var(--info-text);     box-shadow: 0 0 0 1px var(--info-border); }
.bomchip.b-assembly  { color: var(--text-secondary); }
.bomchip.b-uncertain { color: var(--warning-text);  box-shadow: 0 0 0 1px var(--warning-border); }
.bomchip.b-pending   { color: var(--text-tertiary); border-style: dashed; }
/* The constraint component, ux.md §2's "constraint component in red". */
.bomtable tr.bomconstraint td { color: var(--negative-text); }
.bomtable tr.bomconstraint td:first-child { box-shadow: inset 3px 0 0 var(--negative-mark); }
/* ---- /w4-3 ---- */
/* ---- w4-4 adoption auth ---- */
/* ---- /w4-4 ---- */
/* ---- w4-5 backlog remainder ---- */
/* ---- /w4-5 ---- */
