/* Screen-level layout for the v2 quote spine.
 *
 * **Layout only.** Every colour, radius, shadow and type token resolves through
 * `ui/tokens-v2.css`; every component class comes from `ui/harness/components.css`. Nothing
 * here re-declares a design value — if a hex appears in this file it is a mistake.
 *
 * Geometry follows the design bundle: a 1240px content max, a two-column grid of
 * `minmax(420px, 1.25fr) minmax(320px, 1fr)`, and a sticky right rail at 78px.
 */

.iq-page {
  max-width: var(--max-content-wide);
  margin: 0 auto;
  padding: var(--pad-screen);
  background: var(--fc-paper);
  color: var(--fc-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
}

body { margin: 0; background: var(--fc-paper); }

/* ── header ──────────────────────────────────────────────────────────────── */

.iq-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.iq-title { font: var(--h1); letter-spacing: var(--tracking-h1); margin: 0; }
.iq-lead  { font-size: var(--text-lead); color: var(--fc-steel); margin: 6px 0 0; max-width: 62ch; }

/* ── the two columns ─────────────────────────────────────────────────────── */

.iq-grid {
  display: grid;
  grid-template-columns: minmax(420px, 1.25fr) minmax(320px, 1fr);
  gap: 20px;
  align-items: start;
}
.iq-col, .iq-rail { display: flex; flex-direction: column; gap: var(--gap-card); }
.iq-rail { position: sticky; top: var(--sticky-top); }

/* One column under 980px. The entry fields stay full width rather than shrinking — a numeric
   field narrow enough to clip a figure is worse than a taller page. */
@media (max-width: 980px) {
  .iq-grid { grid-template-columns: minmax(0, 1fr); }
  .iq-rail { position: static; }
}

.iq-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.iq-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .iq-2, .iq-3 { grid-template-columns: 1fr; } }

/* ── card headings ───────────────────────────────────────────────────────── */

/* ⚠ **The card head, lifted from `Card.jsx` on 20 Aug — it was 12px with no rule.**
   The design gives a card's head its own band: `14.5px`, `-0.01em`, and a `#F1EFEA` rule
   separating it from the body. Ours was two-thirds the size and floated straight into the
   fields under it, which is most of why a screen full of correct content reads as flat.
   **Every v2 screen draws these**, so it is one rule rather than a decision taken five times.
   The values are the export's, not eyeballed. */
.iq-kicker {
  font-family: var(--font-display);
  font-size: 14.5px;
  letter-spacing: var(--tracking-h2);
  font-weight: 700;
  color: var(--fc-ink);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--fc-line-soft);
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.iq-kicker-sub { font-size: var(--text-meta); color: var(--fc-steel); line-height: 1.45;
                 margin: 10px 0 14px; }

/* ⚠ **The rule was in the wrong place, and it was my own doing on 20 Aug.** `Card.jsx`'s head
   is a *band*: one padded strip carrying the title AND its description, with the rule at the
   band's bottom edge — full-bleed, so it meets both sides of the card. Putting the rule on the
   title alone drove it *between* the title and the sentence explaining it, which reads as two
   unrelated things rather than a heading and its subtitle.

   `:has()` rather than a wrapper `<div>`, because the alternative is editing every card on
   fourteen screens to add a container that carries no meaning of its own.

   ⚠ **`:first-child` is load-bearing.** Several cards carry a *second* kicker partway down —
   screen 03's "Hand-entered lines" is one — and a full-bleed negative margin on those would
   punch the band through the middle of the card. Only the head of a card is a head. */
.fc-card > .iq-kicker:first-child,
.fc-card > .iq-kicker:first-child + .iq-kicker-sub {
  margin-inline: calc(var(--pad-card-x) * -1);
  padding-inline: var(--pad-card-x);
}
.fc-card > .iq-kicker:first-child {
  margin-top: calc(var(--pad-card-y) * -1);
  padding-top: 13px;
}
/* A title with a sentence under it: the rule belongs to the sentence, which is the band's
   last line. A title on its own keeps it. */
.fc-card > .iq-kicker:first-child:has(+ .iq-kicker-sub) {
  border-bottom: none;
  padding-bottom: 1px;
}
.fc-card > .iq-kicker:first-child + .iq-kicker-sub {
  margin-block: 0 14px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--fc-line-soft);
}
.iq-kicker + .iq-2, .iq-kicker + .iq-3, .iq-kicker + .fc-kv { margin-top: 12px; }
.iq-src {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 400;
  color: var(--fc-faint);
}
.iq-badge {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 400;
  color: var(--field-override-dot);
}
/* The A6 suffix — the changing half of the badge line. Muted rather than mono, because it is a
   sentence about state and the `· 6` beside it is a count. */
.iq-badge__state { font-size: 11px; font-weight: 400; color: var(--fc-faint); }

/* ── §07a · the supplier-register search on Material call-up ──────────────────────────────
   ⚠⚠ **`[hidden]` is declared FIRST and it is load-bearing.** `.mc-search` sets `display`, and
   a class that sets `display` beats the UA stylesheet's `[hidden] { display: none }` — the
   `.savebar[hidden]` defect, which has now arrived three times on this project. */
/* ── PIN A5 · one `Type n · quantities` card per block, in the rail ──────────────────────── */
/* ⚠⚠ **One card per fence-type block, stacked in block order — pin A5, repaired 2026-08-26.**
   These used to be sections INSIDE the `Calculated quantities` card, separated by a rule. That
   card's body is `.fc-kv` — `display: grid` with two columns — so each `.iq-typecard` became a
   GRID ITEM and two types drew **side by side**, with a third wrapping to a second row. The
   separator rules below were doing nothing a reader could see, because nothing was ever stacked.

   **They are real cards now**, siblings in `.iq-rail`, so the stacking and the gap are the
   rail's own and hold at any number of types. */
.iq-typecards { display: flex; flex-direction: column; gap: var(--gap-card); }
/* Nothing to draw on the single-type path — and `display: none` rather than an empty box, so
   the rail's `gap` does not leave a hole where no card is. */
.iq-typecards:empty { display: none; }

.mc-search[hidden] { display: none; }
.mc-search { display: block; margin-top: 14px; }
.mc-search__status { margin-top: 8px; font-size: var(--text-meta); color: var(--fc-steel); }
.mc-search__status--error { color: var(--fc-red); }
.mc-search__results { margin-top: 8px; }
.mc-search__results:empty { display: none; }
.mc-hit { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 4px 12px;
          width: 100%; text-align: left; padding: 8px 10px; cursor: pointer;
          border: 1px solid var(--fc-line); border-radius: var(--radius-card);
          background: none; margin-bottom: 6px; }
.mc-hit:hover { background: var(--fc-well); }
.mc-hit__name { font-size: 12.5px; font-weight: 600; color: var(--fc-ink); }
.mc-hit__meta { font-size: 11px; color: var(--fc-faint); font-family: var(--font-mono); }
.mc-hit__price { font-size: 12.5px; font-weight: 600; text-align: right; }
/* ⚠ **The adjustment marker takes the no-GST statement's treatment** — quiet, italic,
   secondary. §07a: *"not a warning colour: an ordinary fact about a price."* */
.mc-hit__list { grid-column: 2; font-size: 11px; font-style: italic; color: var(--fc-faint);
                text-align: right; }
/* ⚠ **`--fc-red`, the same token `.mc-pending` and `.mc-addrow__pending` already use.** One
   fact, one treatment: a POA row and a pending schedule line are the same state seen twice, and
   a second colour for it would be this project's one-fact-two-wordings shape in a stylesheet.
   ⚠ `--fc-rust` does not exist — the token sweep caught it being invented here. */
.mc-hit__poa { font-size: 12.5px; font-weight: 600; color: var(--fc-red); text-align: right; }
.mc-nomatch { display: flex; align-items: center; justify-content: space-between; gap: 12px;
              padding: 10px; border: 1px dashed var(--fc-line-strong);
              border-radius: var(--radius-card); font-size: 12.5px; color: var(--fc-steel); }

/* ⚠ **The calculated-quantities block reverses `.fc-kv`'s column sizing, and that is the actual
   defect.** The shared rule is `grid-template-columns: auto 1fr` — the KEY sized to content, the
   value taking the remainder. That is right where keys are short labels, which is every other
   `.fc-kv` on this interface. Here the keys are resolved post descriptions running to
   "Corner / Strainer / Double Gate / Single Gate Post — 50 NB XL", so `auto` claimed almost the
   whole row and the value broke to "32 ×" / "40" / "NB" / "XL" over four lines.

   Swapped rather than widened: the VALUE is the short, fixed-shape half — a count, a multiply
   sign and a pipe size — and the description is the half that should wrap. `minmax(0, 1fr)` on
   the key rather than `1fr`, because a bare `1fr` has an `auto` minimum and refuses to shrink
   below its longest unbreakable word.

   Scoped to this block, never to `.fc-kv` itself: that rule lives in the shared
   `harness/components.css` and `site_markup.js` renders against it too. A layout defect on one
   screen is not a reason to reflow another. */
#iq_quantities.fc-kv { grid-template-columns: minmax(0, 1fr) auto; }

/* The engine's own note about a resolved post — "merged: …", "gate width escalated". It sits on
   its own row across both columns, because even a healthy value column is the width of a pipe
   size and these run to a full sentence. Right-aligned so it reads as belonging to the figure
   above it rather than to the label. */
.iq-rownote {
  grid-column: 1 / -1;
  justify-self: end;
  margin-top: -5px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--field-override-dot);
  text-align: right;
}

/* ── the fence-type search results ───────────────────────────────────────── */

.iq-results { display: flex; flex-direction: column; gap: 2px; margin: 12px 0; }
.iq-result {
  display: flex;
  gap: 10px;
  align-items: baseline;
  border: none;
  text-align: left;
  background: transparent;
  border-radius: 8px;
  padding: 7px 10px;
  cursor: pointer;
  font-family: var(--font-body);
}
.iq-result:hover { background: var(--fc-well); }
.iq-result--picked { background: var(--fc-info-tint); }
.iq-result__n { font-family: var(--font-mono); font-size: 11px; color: var(--fc-faint); }
.iq-result--picked .iq-result__n { color: var(--fc-info); }
.iq-result__name { font-size: 13px; font-weight: 500; color: var(--fc-steel); line-height: 1.35; }
.iq-result--picked .iq-result__name { font-weight: 700; color: var(--fc-marine); }
.iq-result__code {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fc-faint);
}
.iq-noresult { font-size: var(--text-meta); color: var(--fc-mute); padding: 7px 10px; }

/* A read-only ƒ field holding text rather than a figure — the input is flattened into the
   well so it reads as calculated, not typeable. */
.iq-flat {
  border: none;
  background: transparent;
  text-align: right;
  width: 100%;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--fc-ink);
}

/* ── the total ───────────────────────────────────────────────────────────── */

.iq-total { margin-top: 10px; }
.iq-total .fc-money {
  font-size: 22px;
  background: transparent;
  padding: 0;
  color: #fff;
}
/* `.fc-pending` already carries the amber notice ink; on the dark panel it needs the lighter
   end of the scale to stay legible, which is the one place this screen overrides a component. */
.iq-total .fc-money.fc-pending { color: var(--fc-amber-line); font-size: 16px; }
.iq-status { font-size: var(--text-fine); color: var(--fc-mist); margin-top: 8px; min-height: 1.2em; }

/* Field labels are `<span>` inside a `<label>` here rather than a nested label, so the whole
   block stays clickable and the dot sits on the baseline with the text. */
.fc-field__label { display: flex; align-items: baseline; gap: 6px; }

/* ── screen 03 — material call-up ────────────────────────────────────────── */

/* The job header grid — build spec §06 S1.01. **Four across, two rows, and the height is
   fixed**: values truncate with an ellipsis rather than wrapping, because a wrapping cell
   would break the rule alignment across all four columns. That is the spec's own reason and
   it is why `nowrap` here is structural rather than a preference.

   The rules are drawn per cell — `border-left` except column 1, `border-top` on row 2 — so
   the grid keeps its lines without a table. `overflow: hidden` on the shell is what stops a
   truncated value escaping the rounded corner. */
.mc-jobhead {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  background: var(--surface-card);
  border: 1px solid var(--fc-line); border-radius: var(--radius-card);
  overflow: hidden; margin-bottom: 18px;
}
.mc-jh { padding: 10px 14px; min-width: 0; border-left: 1px solid var(--fc-line-soft); }
.mc-jh:nth-child(4n + 1) { border-left: none; }
.mc-jh:nth-child(n + 5)  { border-top: 1px solid var(--fc-line-soft); }
.mc-jh__k {
  display: block; font-family: var(--font-body); font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--fc-mute);
}
.mc-jh__v {
  display: block; font-size: 12.5px; color: var(--fc-ink); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A cell with no engine field behind it. Quiet, and it says why on hover — it is not a value
   that failed to load, and the two must not look alike. */
.mc-jh__v--unwired { color: var(--fc-faint); cursor: help; }

@media (max-width: 860px) { .mc-jobhead { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mc-jh { border-left: 1px solid var(--fc-line-soft); border-top: 1px solid var(--fc-line-soft); }
  .mc-jh:nth-child(odd) { border-left: none; }
  .mc-jh:nth-child(-n + 2) { border-top: none; }
}

.mc-modes { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mc-scroll { overflow-x: auto; }        /* wide tables scroll in their own box, never the page */
/* ⚠ **The `.savebar[hidden]` rule, written before it is needed.** Neither `.mc-scroll`
   (`overflow-x` only) nor `.sum-sub` declares a `display` today, so the UA stylesheet's
   `[hidden] { display: none }` works unaided — **which is exactly the state `.savebar`,
   `.cq-wording`, `.cq-offer__row` and `.cm-lump` were all in until somebody added one.** Four
   recorded instances in this file, every one found by driving the page rather than by a source
   test. What these two hide is the hand-entered money editor (24 Aug), so the failure mode is
   the empty table Bevan ruled out reappearing for everybody. */
.mc-scroll[hidden], .sum-sub[hidden] { display: none !important; }
/* A refused ops correction, said beside the control. Semantic red — this is a failure to save,
   not a warning about a figure. Hidden until there is something to say.
   ⚠ The colour is the TOKEN. `--fc-bad` does not exist and a raw `#B3401F` is what
   `test_design_tokens.py` refuses — both caught this line within a minute of it being written. */
.mc-ops__said { margin: 6px 0 0; font-size: 12px; color: var(--fc-red); font-weight: 600; }

/* The manual-row delete — ruled 21 Aug, matching the purchase-order revision's control.
   `Document Lifecycle - Records.md` specifies that one as `all: unset` with the semantic red,
   and this is the same gesture on the same kind of row, so it is the same treatment rather
   than a second one invented here.
   ⚠ The colour is the TOKEN, not the design's `#B3401F` literal. They are the same value today
   and `tests/test_design_tokens.py` refuses the literal precisely so they cannot drift apart. */
/* The template preview — read-only, between the picker and the estimator's own cell.
   ⚠ **Ruled 21 Aug, `Client_Quote!B13`: "pick a template to preview, or type directly below."**
   It is a `<pre>` and not a second textarea on purpose: the whole point of the separation is
   that this copy cannot be edited. An editable one is the collapse the ruling forbids, wearing
   a different tag. */
.cq-tpl { margin: 8px 0; padding: 10px 12px; border: 1px dashed var(--fc-line);
          border-radius: var(--radius-card); background: var(--fc-well); }
.cq-tpl__k { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
             color: var(--fc-mute); margin-bottom: 6px; }
.cq-tpl__body { margin: 0 0 8px; font-size: 12px; line-height: 1.5; white-space: pre-wrap;
                font-family: var(--font-body); color: var(--fc-ink); }
.cq-tpl__body.cq-words--mono { font-family: var(--font-mono); }

.mc-drop-h { width: 44px; }
.mc-drop-c { text-align: center; }
.mc-drop {
  all: unset;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fc-red);
  padding: 2px 6px;
}
/* Material call-up §06 — the manual job's empty state. Dashed, because the block is not
   missing: it is waiting. ⚠ The spec's `#D8D4C8` is `--fc-line-strong` to within a hair, and a
   second copy of a colour disagrees silently and for ever — `test_design_tokens` refuses the
   literal here for exactly that reason. */
.mc-empty { border: 1px dashed var(--fc-line-strong); border-radius: 12px;
            padding: 26px 24px; text-align: center; }
.mc-empty__title { font-weight: 700; font-size: 15px; color: var(--fc-marine); }
.mc-empty__body { margin: 8px auto 0; max-width: 580px; font-size: 12.5px; line-height: 1.55;
                  color: var(--fc-mute); }
/* `gate-rescue` — the made-size hint under a gate row's width. Spans the row so a long
   sentence does not squeeze the five inputs above it. Amber while it is a question; green once
   the estimator has answered it with a typed cost. */
.gs-flag { grid-column: 1 / -1; font-size: 11px; line-height: 1.45; color: var(--fc-amber);
           padding: 2px 0 0 2px; }
.gs-flag--ok { color: var(--fc-green); }
/* `gate-rescue` — a gate the catalogue does not make, on the fabrication order. */
.mc-fab__made { padding: 9px 14px; border-top: 1px solid var(--fc-line);
                background: var(--fc-amber-tint); font-size: 12px; line-height: 1.5; }
.mc-fab__why { display: block; font-family: var(--font-mono); font-size: 10.5px;
               color: var(--fc-amber); }
.mc-fab__hand { display: block; font-size: 11px; color: var(--fc-green); }
.mc-badge { margin-left: 8px; font-size: 11px; padding: 2px 8px; }
/* The category chip — build spec §05. Mono and small because it is a classification, not a
   word in the description it precedes. `--fc-calc` is the spec's own category-chip fill. */
.mc-section {
  display: inline-block; margin-right: 8px; padding: 1px 6px; border-radius: 5px;
  background: var(--fc-calc); font-family: var(--font-mono); font-size: 9px;
  letter-spacing: 0.04em; color: var(--fc-mute); vertical-align: 1px;
}
.mc-note { font-size: var(--text-fine); color: var(--fc-mute); margin-top: 3px; }
/* The ops correction column. Blank means the calculated figure stands — the tint promises
   "you can change this" and the blank must not read as a zero. */
.mc-onsite { background: var(--field-input-bg); box-shadow: inset 0 0 0 1px var(--field-input-border); }

/* ── the schedule's two money columns — build spec §06 block 04 ────────────────
   ⚠ **Different on purpose, and the spec says so in as many words:** *"a rate is exact to the
   cent because it is a price you were quoted; a line total is rounded to the dollar because it
   is a quantity times that price. Do not unify them."* The weight carries the same split — the
   rate is reference, the total is the figure somebody reads down the column. */
.mc-rate  { font-family: var(--font-mono); color: var(--fc-steel); font-variant-numeric: tabular-nums; }
.mc-total { font-family: var(--font-mono); font-weight: 600; color: var(--fc-ink);
            font-variant-numeric: tabular-nums; }
/* An unpriced line, R2 — never `$0.00`, which reads as material supplied free. The line's own
   `.mc-note` says why; this says that. */
.mc-pending { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-red);
              white-space: nowrap; }

/* ── §06 block 02 · the mode bar ─────────────────────────────────────────────
   Below the job header, on its own row — the design keeps the screen header for the
   classification and the print actions. */
.mc-modebar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
              margin-bottom: 12px; }

/* ── §06 block 05 · the add-row bar ─────────────────────────────────────────── */
.mc-addrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
             margin: 12px 0 22px; }
.mc-addrow__why { flex: 1; min-width: 240px; font-size: var(--text-meta); color: var(--fc-mute);
                  line-height: 1.5; }
/* The one word the design sets in the attention colour, because it is the state the whole
   block exists to make visible. */
.mc-addrow__pending { color: var(--fc-red); font-weight: 700; }
.mc-addrow__said { font-family: var(--font-mono); font-weight: 600; font-size: var(--text-fine);
                   color: var(--fc-green); background: var(--fc-green-tint);
                   border: 1px solid #BFDCCB; border-radius: var(--radius-badge);
                   padding: 5px 10px; }
/* Computed, never decorative — it turns amber the moment a line is pending. */
.mc-addrow__status { font-weight: 700; font-size: var(--text-meta); color: var(--fc-green);
                     white-space: nowrap; }
.mc-addrow__status.is-pending { color: var(--fc-amber); }

/* ── §06 block 06 · ops quantity adjustment ─────────────────────────────────── */
.mc-ops { margin-bottom: 22px; padding: 0; }
.mc-ops__head { display: flex; align-items: flex-start; justify-content: space-between;
                gap: 16px; flex-wrap: wrap; padding: 13px 18px; }
.mc-ops__title { font-weight: 700; font-size: 14.5px; color: var(--fc-ink); }
.mc-ops__sub { font-size: var(--text-meta); color: var(--fc-mute); margin-top: 3px;
               max-width: 78ch; line-height: 1.5; }
.mc-ops__body { border-top: 1px solid var(--fc-line); }
/* ✅ `.mc-ops__gap` is GONE with the notice it styled — the per-supplier % is ruled out
   (Bevan, 23 Aug), not pending, and a gap notice for a control that will never arrive is a
   promise rather than an honest absence. The reasoning is kept on screen-03's own comment. */
.mc-ops__prov { font-family: var(--font-mono); font-size: 10px; color: var(--fc-faint);
                margin-top: 2px; }
.mc-ops__auto { color: var(--fc-green); }
/* ⚠ The only editable cell on this table — override styling, because blank means the engine's
   figure stands and a typed 0 is a real "none needed". */
.mc-ops__in { width: 100%; box-sizing: border-box; padding: 5px 8px;
              border: 1px solid var(--field-override-border);
              background: var(--field-override-bg); border-radius: var(--radius-badge);
              font-family: var(--font-mono); font-size: var(--text-fine); text-align: right;
              color: var(--fc-ink); }
.mc-ops__adj { font-family: var(--font-mono); font-size: var(--text-fine);
               color: var(--fc-amber); }
.mc-ops__foot { display: flex; align-items: center; justify-content: space-between; gap: 16px;
                flex-wrap: wrap; padding: 10px 18px; background: var(--fc-well);
                border-top: 2px solid var(--fc-marine); }
.mc-ops__note { font-size: var(--text-meta); color: var(--fc-amber); }
.mc-ops__fig { font-family: var(--font-mono); font-weight: 600; font-size: var(--text-detail);
               font-variant-numeric: tabular-nums; }

/* ── §06 block 10 · the costing summary ─────────────────────────────────────────
   ⚠ **The red bar is the CLASSIFICATION, not a warning tone borrowed for emphasis.** This block
   is the one that must never be shared, and the bar is what says so on a printed copy. */
.mc-sum { margin-bottom: 22px; }
.mc-sum__bar { display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
               flex-wrap: wrap; padding: 11px 18px; background: var(--fc-red);
               color: var(--fc-white); border-radius: var(--radius-card) var(--radius-card) 0 0; }
/* The engine's band, dropped into the bar. `.sum-band` carries Classic's own box treatment,
   which is cancelled here — the bar IS the band's presentation on this screen. */
.mc-sum__band { flex: 1; min-width: 0; }
.mc-sum__band .sum-band { background: none; border: none; padding: 0; margin: 0;
                          color: inherit; font-weight: 700; font-size: var(--text-detail); }
.mc-sum__note { font-family: var(--font-mono); font-size: 10.5px; color: var(--fc-red-line); }
.mc-sum__body { background: var(--surface-card); border: 1px solid var(--fc-line);
                border-top: none; border-radius: 0 0 var(--radius-card) var(--radius-card);
                padding: 18px 20px; }

.mc-po { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--gap-grid); }
.mc-po-card {
  border: 1px solid var(--fc-line); border-radius: var(--radius-card);
  padding: var(--pad-card-sm); background: var(--surface-card);
}
/* Ruling 1a — a record rather than an action: quiet, dashed, no control. */
.mc-po-card--none { border-style: dashed; background: var(--fc-well); }
.mc-po-name { font-family: var(--font-body); font-size: 13px; font-weight: 700; }
.mc-po-n { font-family: var(--font-mono); font-size: 11.5px; color: var(--fc-mute); margin: 2px 0 10px; }
.mc-po-why { font-size: var(--text-fine); color: var(--fc-mute); line-height: 1.45; }
/* The raised-order line. Ruled 13 Sep (slot-2 review, ruling 1): after a raise it IS the edge
   into 14 for the same job — an anchor in the line's own words, not a new control. */
.mc-po-said { font-family: var(--font-mono); font-size: var(--text-fine); margin-top: 8px; }
.mc-po-said__link { color: var(--fc-ink); text-decoration: underline; text-underline-offset: 2px; }

/* ⚠ **These selectors were `.mc-c--<kind>` and matched nothing after 20 Aug.** The screen's
   own twenty-line renderer emitted `mc-c mc-c--<kind>`; the shared renderer in
   `ui/costing_sheet.js` emits `k-<kind>`, which is why the sheet read as flat white with a few
   unexplained amber boxes — `override` was one of the handful of kinds the old renderer
   happened to label the same way a rule here expected.

   **This is a rename, not a restyle.** The palette is the one that was already here; what
   changed is that the kinds now come from the renderer the engine's cell kinds were written
   for, so a tint cannot promise "you can change this" about a cell that is not editable. */
/* ── The column model — design's spec, 23 Aug, and it is the fix for a real defect ──────
   ⚠⚠ **The sheet was 1269px inside a 1098px card**, so columns F and G — `Total Ex GST`,
   `SUB TOTAL`, the sell figures and the labour value — sat OUTSIDE the card. Bevan found it on
   the rendered page: *"totals run outside the side"*, *"Labour value sits outside the line"*.
   **No test caught it** because `.mc-scroll` is `overflow-x: auto`, so it scrolled rather than
   clipping — invisible on a wide monitor, plain on paper and on a narrow screen.

   The cause was `table-layout: auto` plus `white-space: nowrap`: the description column took
   whatever it wanted (492px of the 1269) and the money columns were pushed off the end to pay
   for it. **So description is the ONLY column that flexes, and every numeric column is pinned** —
   the spec's `.sr { grid-template-columns: 40px 120px minmax(220px,1fr) 96px 110px 110px 120px }`.

   ⚠ **The floor goes on the SCROLLER'S CHILD, never on the row.** `min-width` on a row would make
   the sheet overflow its card again instead of scrolling inside it — the same defect wearing
   different trousers. Design's §02 states the rule: *"The sheet scrolls inside its card; columns
   never shrink."*

   ⚠ **The widths below are OURS, not the spec's numbers, and that is deliberate.** The spec's
   sheet runs `B C D E F G`; ours runs `A B C D E F G` — one column more, and its description is
   `C` where ours is `B`. The letters do not align, so the spec's per-column pixels cannot be
   copied across. **The MODEL is what was cleared and the model is what is applied**; the pinned
   widths are each column's own settled width. Sent back to design as a mismatch to reconcile. */
/* ⚠⚠ **`table-layout: fixed` with `width: 100%`, and BOTH halves were re-tested on 2026-08-28.**
   Bevan asked for one-line labels here to match Classic, then authorised copying Classic's
   column model to get them. **Both attempts were refused by the rendered page** and both
   captures are kept beside this file's contract:

   | attempt | what it did |
   |---|---|
   | `nowrap` under FIXED layout | widths settle before the text is measured, so a long label overflows its cell and lands on the next one — row 64 read `TRIPPLE OR SEMI TRANSPORT TO 0ITE` |
   | AUTO layout, no `width: 100%` (Classic's model) | description took the width it wanted and **`F` RATE and `G` TOTAL were pushed off the card** — *"totals run outside the side"*, the 2026-08 defect exactly |
   | collapsing the rail to fund it | **recovers 100px of a 340px gap, not 236** — still 240px short |

   ⚠⚠ **It is arithmetic, and the arithmetic was measured rather than assumed.** With `nowrap`
   the table wants **1442px**. `.mc-scroll` gets **1102px** at a 1440 viewport. Hiding the
   sidebar entirely raises that to **1202px** — the rail's 236px does NOT all arrive, because the
   card and the main padding absorb the rest. **Three attempts, three refusals, 240px still
   missing at the width this whole project is captured at.**

   **So the widths stay pinned and the cells stay wrapping.** One line on this sheet needs
   something else to give — a smaller type size, fewer columns, a viewport near 1780, or
   accepting that the totals sit behind a horizontal scroll. **Each of those is a decision, not
   a fix**, and none of them is Code's to take. Carded. */
.mc-sheet { font-family: var(--font-mono); font-size: 11.5px;
            table-layout: fixed; min-width: 880px; width: 100%; }
/* ⚠⚠ **Cells WRAP, and `nowrap` has now been refused here twice — see the block above for both
   attempts and why each failed.** Nothing is ever truncated: ellipsis was tried and clipped seven
   labels by 15-43px, and **a truncated label on a costing sheet is worse than a wide one** —
   the figure beside it stops being checkable, which is the one thing this sheet exists for.
   ⛔ No `text-overflow`, no `overflow: hidden`. The second would hide a collision, not prevent
   one. Money never wraps anyway — `1,043.84` carries no space to break at. */
.mc-sheet td, .mc-sheet th { padding: 4px 8px; white-space: normal; }
/* **Seven tracks: the row gutter, then `B` to `G`.** It was eight — a 40px track for a
   column `A` that exists on no row of the master — and eight tracks in a card sized for
   seven is what put `G` outside it. The letters now line up with the master, so these are
   the spec's own per-column widths rather than numbers near them.
   40 + 120 + 96 + 110 + 110 + 120 = 596 fixed, description takes the rest. */
.mc-sheet .colhead:nth-child(1) { width: 40px; }   /* row gutter   */
.mc-sheet .colhead:nth-child(2) { width: 120px; }  /* B  tier / LM */
/* ⚠ **Only description flexes.** It was starved by `nowrap` money columns as wide as their
   longest label, which is what pushed F and G out of the card. A money cell needs room for
   `1,043.84`, not for whatever the widest label was. */
.mc-sheet .colhead:nth-child(3) { width: auto; min-width: 220px; }  /* C  description */
.mc-sheet .colhead:nth-child(4) { width: 96px; }   /* D  QTY            */
.mc-sheet .colhead:nth-child(5) { width: 110px; }  /* E  DAYS / HOURS   */
.mc-sheet .colhead:nth-child(6) { width: 110px; }  /* F  RATE           */
.mc-sheet .colhead:nth-child(7) { width: 120px; }  /* G  TOTAL          */
/* The column letters and the row gutter. **`B C D E F G` across the top is the point of the
   sheet**, not decoration: it is what makes "check row 106" a sentence somebody can act on
   over the phone. */
.mc-sheet .colhead { color: var(--fc-faint); font-family: var(--font-mono); font-size: 10px;
                     font-weight: 600; text-align: center; border-bottom: 1px solid var(--fc-line); }
/* `CostSheet.jsx` sets the gutter on its own ground with a rule down its edge, so the row
   numbers read as the sheet's margin rather than as a first column of data. */
.mc-sheet .rownum { color: var(--fc-faint); text-align: right; width: 34px; font-weight: 400;
                    background: var(--fc-paper); border-right: 1px solid var(--fc-line); }
/* ── The twelve cell kinds, and the five that were not here ─────────────────────
   ⚠ **The kind map was a truncation and truncation reads as present.** `.k-work` was
   right-aligned; the enum value is `WORKING = "working"`, so `.k-work` matched nothing and
   `.k-working` had no rule — and `.k-formula` had none either. **Those two kinds carry every
   derived figure in the sheet**, so every calculated number sat left-aligned in a column of
   right-aligned inputs, which is the one alignment convention a spreadsheet has. `.k-label`,
   `.k-bold`, `.k-warning`, `.k-percent-unit` and `.spill` were absent outright.

   `.k-warning` is the one that mattered beyond looks: row 47 is
   *"⚠ INTERNAL COSTING — MANAGEMENT USE ONLY"*, and with no rule it rendered as ordinary
   sheet text on a screen an estimator may well have open in front of a client.

   **Now enforced.** `tests/test_design_tokens.py::TestEveryCellKindIsStyled` reads the `Kind`
   enum out of `engine/costing_sheet.py` and refuses a kind with no rule — and its mirror
   refuses a `.k-*` rule naming no kind, which is what `.k-work` was. Classic has carried all
   of these in `ui/app.css` since day one; only v2 lost them in the `.mc-c--<kind>` rename. */
.mc-sheet .k-number, .mc-sheet .k-percent, .mc-sheet .k-override,
.mc-sheet .k-formula, .mc-sheet .k-working, .mc-sheet .k-sell,
.mc-sheet .spill { text-align: right; }

/* Untinted, because they are not yours to type in. Weight and colour are the whole signal:
   `working` and `sell` are derived figures that decide something, `formula` is derived and
   merely reports. `CostSheet.jsx`'s `calc` style is the source — mono, ink, right, tabular. */
.mc-sheet .k-label    { color: var(--fc-ink); }
.mc-sheet .k-bold     { color: var(--fc-marine); font-weight: 600; }
.mc-sheet .k-formula  { color: var(--fc-ink); }
.mc-sheet .k-working  { color: var(--fc-marine); font-weight: 600; }
.mc-sheet .k-sell     { color: var(--fc-marine); font-weight: 700; font-size: var(--text-meta); }

/* The four tinted kinds carry the same promise here as on screen 01: a tint means "you can
   change this". `TINTED` in the renderer is the list; `untinted` is what it puts on an
   editable cell that deliberately carries none — the tier radios, which are a choice on a row
   of rates rather than a typed figure, and the one kind with no rule of its own by design.

   ⚠ **The blue is `--sheet-input-*`, not `--field-input-*`.** A sheet cell has no label, so
   the tint is the label — see the token's own note. Pointing these at the form-field white
   is what made the sheet read as flat with a few unexplained amber boxes. */
/* ⚠⚠ **8px, to match Classic after Drop 96 — by a different mechanism, because the same one is
   not available.** Classic puts the border and the radius on the `input`; here the tint and its
   ring are on the CELL, drawn as an inset `box-shadow`, and `.mc-sheet input` is
   `background: none; border: 0`. So the radius has to go on the cell.

   ⚠ **`border-collapse: collapse` (from `.fc-table`) means a cell's BORDER cannot be rounded —
   but nothing here uses a border.** The tint is a `background` and the ring is a `box-shadow`,
   and both are clipped by `border-radius` regardless of the collapsed border model. **Verified
   rendered rather than assumed**: computed `8px` on all 49 tinted cells, and the rounding is
   visible in `reference/spine-shots/2026-08-28-v2-costing-radius/`.

   ⚠ **A `background-clip: padding-box` was tried here first and was DEAD ON ARRIVAL** — the
   `background:` shorthand on the four rules below resets `background-clip` to `border-box`, so
   it computed as `border-box` and never did anything. It read like the load-bearing part of the
   fix. **The radius alone is what works**, which the measurement is how anybody knows.

   ⛔ **Do not switch the table to `border-collapse: separate` to "improve" this** — it would put
   a gap and a second edge on every hairline in a sheet whose row and column letters are
   load-bearing. */
.mc-sheet .k-number, .mc-sheet .k-text,
.mc-sheet .k-percent, .mc-sheet .k-override { border-radius: 8px; }
.mc-sheet .k-number   { background: var(--sheet-input-bg);    box-shadow: inset 0 0 0 1px var(--sheet-input-border); }
.mc-sheet .k-text     { background: var(--sheet-input-bg);    box-shadow: inset 0 0 0 1px var(--sheet-input-border); }
.mc-sheet .k-percent  { background: var(--sheet-input-bg);    box-shadow: inset 0 0 0 1px var(--sheet-input-border); }
/* ⚠⚠ **AN OVERRIDE CELL IS AN INPUT UNTIL SOMETHING IS TYPED IN IT — corrected 10 Sep.**
   This rule was the amber, unconditionally, so every override cell on the sheet wore the
   "somebody changed this" treatment from the moment it was drawn. Measured at 1440x1000 on an
   untouched fixture: `G73` and all six gate override cells rendered
   `background: rgb(255, 253, 246)` with `value: ""` — **seven cells announcing an override
   nobody had typed.**

   Build spec §03 defines the state as *"a typed value that **differs from the computed one**"*,
   and it lists Input and Override as two of four separate treatments. So a blank override cell
   takes the **Input** treatment — identical to `.k-number`, which keeps the promise this sheet's
   tint makes (*you can change this*) — and the amber arrives with the typed figure.

   ⚠ **Equal-to-computed is not an override either**, which is `CR-2026-09-10` item 5's own
   third case: `R4` says G73 *"by default holds the same rate as F73"*, so typing the figure
   that is already there has changed nothing. `overridden()` in `ui/costing_sheet.js` compares
   them as numbers and owns which cells get the state class. */
.mc-sheet .k-override { background: var(--sheet-input-bg); box-shadow: inset 0 0 0 1px var(--sheet-input-border); }
.mc-sheet .k-override.is-override,
.mc-sheet .k-percent.is-override { background: var(--field-override-bg);
                                   box-shadow: inset 0 0 0 1px var(--field-override-border); }
/* ⚠⚠ **The `%` used to wrap onto a second line, on every percent cell on the sheet.**
   `.mc-sheet input` is `width: 100%`, so the field filled the whole cell and pushed the unit
   below it. Measured: row 72's `E72` and the three gate-margin cells `F88`/`F89`/`F90` each
   rendered **44px tall — two lines** — with the `%` under the figure instead of beside it.

   **Flex, so the unit is laid out and the field takes what is left.** `min-width: 0` is the
   load-bearing half: `.mc-sheet input` carries `min-width: 56px`, which a flex item honours as
   a floor and which is what would keep the row wrapping at these track widths. */
.mc-sheet .k-percent { display: flex; align-items: center; justify-content: flex-end; }
.mc-sheet .k-percent input { flex: 1 1 auto; width: auto; min-width: 0; }
.mc-sheet .k-percent-unit { font-family: var(--font-mono); font-size: 11px;
                            color: var(--fc-steel); margin-left: 3px; flex: 0 0 auto; }
.mc-sheet .untinted   { background: none; box-shadow: none; }
/* ⚠ Row 47 — the confidentiality band. It is the only cell on the screen that says who may
   see the screen, and it had no rule at all. */
.mc-sheet .k-warning  { background: var(--fc-red-tint); color: var(--fc-red);
                        font-weight: 700; font-size: 11px; letter-spacing: 0.04em; }
.mc-sheet .k-band     { background: var(--fc-calc); font-weight: 700; }
/* ⚠ **`+ Add manual row` — a control the sheet OFFERS, not a figure it holds.** Marine, per
   the spec, and deliberately NOT tinted like an input: the tint on this sheet means *type
   here*, and there is nothing to type into a button. What it adds is the row below it, which
   is tinted because that one does take a figure. */
.mc-sheet .k-action   { background: none; }
.mc-sheet .k-action .sheet-add { font: 700 11.5px/1 var(--font-body); color: #fff;
                             background: var(--fc-marine); border: 0; border-radius: 8px;
                             padding: 6px 13px; cursor: pointer; }
.mc-sheet .k-action .sheet-action-note { font-family: var(--font-mono); font-size: 10.5px;
                                     color: var(--fc-mute); margin-left: 10px; }
/* Section headers are a band, not a row — twelve sections in an unbroken 64-row run is the
   thing the heights exist to break up. `HEIGHT_CLASS` in the renderer owns which is which. */
.mc-sheet .h-band td { background: var(--fc-calc); font-weight: 700;
                       border-top: 1px solid var(--fc-line-strong); }
.mc-sheet .h-spacer td { height: 8px; padding: 0; }
.mc-sheet .h-blank td { height: 4px; padding: 0; }
.mc-sheet .is-alert { box-shadow: inset 0 0 0 1px var(--fc-red-line); }
/* `labour-54` — typed, and not counted. Greyed rather than cleared: the ruling's own words are
   that dropped days must be visible AS dropped, never silent. The `title` carries what it needs
   (`Cell.note`), which `tooltip()` already puts there. */
.mc-sheet .is-dropped { background: var(--fc-well); }
.mc-sheet .is-dropped input { color: var(--fc-faint); text-decoration: line-through; }
.mc-sheet input { font-family: var(--font-mono); font-size: 11.5px; width: 100%;
                  min-width: 56px; background: none; border: 0; text-align: right; }
/* ⚠ **A text input on this sheet is LEFT-aligned, and every one of them was right.** Build spec
   §03 lists it as a treatment of its own — *"Input, text. Same, left-aligned. Descriptions and
   labour line names."* — and the rule above is what overrode it, because it aligns by element
   rather than by kind. Measured: `C50`-`C54` each computed `text-align: right` while holding
   `Post Setting`, `Rail & Cable`, `Mesh & Lace`. The numeric cells are untouched: right is
   correct for every one of them, and it is the one alignment convention a spreadsheet has. */
.mc-sheet .k-text input { text-align: left; }
.mc-sheet input[type="radio"] { width: auto; min-width: 0; }
.mc-in { padding: 5px 8px; font-size: 12.5px; background: var(--field-input-bg);
         border: 1px solid var(--field-input-border); }

/* ── screen 02 — site markup ─────────────────────────────────────────────── */

.sm-grid { display: grid; grid-template-columns: minmax(420px, 1.6fr) minmax(300px, 1fr);
           gap: 20px; align-items: start; }
@media (max-width: 980px) { .sm-grid { grid-template-columns: 1fr; } }
.sm-mapcard { padding: 0; overflow: hidden; }
.sm-map { height: 560px; width: 100%; }
/* The attribution is rendered, not hidden — the imagery is DATED EVIDENCE, and a fence traced
   on a 2019 capture may not be what is on the ground. */
.sm-attrib { font-family: var(--font-mono); font-size: 10.5px; color: var(--fc-mute);
             padding: 6px 12px; border-top: 1px solid var(--fc-line-soft); }
/* ⚠ **`display: flex` on `.fc-btn` beats the `hidden` attribute**, so a hidden button is a
   visible button — the `.savebar[hidden]` defect, in the component layer this time. The first
   element to need it was Input quote's "back to the plan" link, and
   `tests/test_charge_mode.py::test_no_element_toggled_by_hidden_has_its_display_overridden`
   refused the commit rather than letting it ship always-visible. */
.fc-btn[hidden] { display: none; }

/* The layer chips. Four independent switches, so they carry `aria-pressed` rather than the
   `aria-selected` the tool strip and plan switcher use — those are tabs, where exactly one is
   chosen, and these are not alternatives to each other. */
.sm-layers { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.sm-layer-chip { font-family: var(--font-mono); font-size: 10.5px; cursor: pointer;
                 padding: 4px 9px; border-radius: 2px; background: var(--fc-paper);
                 color: var(--fc-mute); border: 1px solid var(--fc-line); }
.sm-layer-chip[aria-pressed="true"] { background: var(--fc-marine); color: var(--fc-white);
                                      border-color: var(--fc-marine); }

/* The post preview's own line. Mono, because it carries a spacing and a count and both are
   figures somebody checks. */
.sm-preview { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute);
              margin-top: 10px; }
.sm-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.sm-runs { display: flex; flex-direction: column; gap: 4px; }
.sm-run { display: flex; align-items: baseline; gap: 10px; font-size: var(--text-detail); }
.sm-run__n { font-family: var(--font-mono); font-size: 11px; color: var(--fc-faint); width: 14px; }
.sm-run__m { font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums; }
.sm-run__v { margin-left: auto; font-size: var(--text-fine); color: var(--fc-mute); }

/* ── screen 04 — client quote ─────────────────────────────────────────────────
   **The document is a fixed-gutter paper object inside a narrow screen column.** Every
   horizontal edge inside it lands on the same 42px gutter — that alignment is what makes it read
   as printed rather than as a web page, so nothing may sit at an arbitrary inset. The single
   exception is the Includes band's 94px left inset, which aligns to the Description column. */

/* ⚠ **880px, and it is the ARRANGEMENT that bought it back.** This screen was 1240px because
   the wording tools sat permanently in a left-hand column. They are behind a button now
   (Bevan, 22 Aug), so there is one column and the design's reading measure follows — E7 turned
   out not to be a trade between width and arrangement at all. */
.cq-page { max-width: 880px; margin-inline: auto; }
.cq-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* ── S3 · the acceptance strip, above the paper ───────────────────────────── */
.cq-offer { margin-bottom: var(--gap-card); }
.cq-offer__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* ⚠ **The `.savebar[hidden]` rule, and this one shipped for real.** `cq_offer_live` carries
   `hidden` and this class, so `display:flex` beat the UA stylesheet and BOTH halves of the
   acceptance bar drew at once — "Create acceptance link" above an "Offered · awaiting client"
   chip for a link that did not exist. The sweep that exists to catch this was blind to it: its
   class pattern had no `_`, so every BEM `__` class in the product was outside it. */
.cq-offer__row[hidden] { display: none !important; }
.cq-offer__k { margin: 0; }
.cq-offer__why, .cq-offer__note { font-size: var(--text-fine); color: var(--fc-mute);
                                  line-height: 1.5; flex: 1 1 260px; }
.cq-link { font-family: var(--font-mono); font-size: var(--text-meta); color: var(--fc-info);
           background: var(--fc-info-tint); border: 1px solid #C3DCEA;
           border-radius: var(--radius-badge); padding: 4px 10px; word-break: break-all; }

/* ── S2 · the wording panel ───────────────────────────────────────────────── */
.cq-wording { margin-bottom: var(--gap-card); }
/* ⚠ **The `.savebar[hidden]` lesson.** `.cq-wording__head` declares `display`, and the sweep
   that enforces this rule matches the class family — so the panel gets its own guard rather than
   relying on the UA stylesheet winning. A `display` anywhere in a hidden element's class family
   is one specificity accident away from a panel that is always open. */
.cq-wording[hidden] { display: none !important; }
.cq-wording__head { display: flex; align-items: baseline; justify-content: space-between;
                    gap: 14px; flex-wrap: wrap; margin-bottom: 4px; }
.cq-wording__k { font: 800 11px var(--font-body); text-transform: uppercase;
                 letter-spacing: var(--track-overline); color: var(--fc-marine); }
.cq-wording__strap { font-size: var(--text-meta); color: var(--fc-mute); flex: 1 1 320px; }
.cq-editor, #cq_editor { display: flex; flex-direction: column; gap: 0; }
.cq-block { border-top: 1px solid var(--fc-line-soft); padding: 10px 0;
            display: flex; flex-direction: column; gap: 7px; }
.cq-block--off { opacity: 0.55; }
.cq-block__row { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; }
.cq-block__lab { font-weight: 700; font-size: var(--text-meta); width: 110px; }
/* §1b — one wording box per gate SCHEDULE ROW. The sub-row is indented under the section's own
   header so the block still reads as one section with one hide toggle: what repeats inside it
   is the estimator's choice and their edit, not the section. */
.cq-block__row--sub { margin-left: 26px; }
.cq-block__sub { font-weight: 600; font-size: var(--text-meta); color: var(--fc-steel); }
.cq-row + .cq-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--fc-line-soft); }
.cq-toggle { font-family: var(--font-mono); font-size: 12px; width: 34px; padding: 3px 0;
             text-align: center; border-radius: 7px; }
/* ⚠ The fence section is **locked and renders `[−]` as static text, not a disabled button** —
   the spec is explicit, and a disabled control invites a click that can never work. */
.cq-locked { font-family: var(--font-mono); font-size: 12px; width: 34px; text-align: center;
             color: var(--fc-faint); }
.cq-words { min-height: 58px; font-size: 12px; line-height: 1.5; resize: vertical;
            font-family: var(--font-body); }
.cq-words--mono { font-family: var(--font-mono); }
.cq-chips { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
.cq-chip { font-family: var(--font-mono); font-size: 10.5px; padding: 2px 7px;
           background: var(--fc-info-tint); border: 1px solid #C3DCEA;
           border-radius: 6px; color: var(--fc-info); cursor: pointer; }
.cq-preview { font-size: var(--text-fine); color: var(--fc-mute); }
.cq-notemplates { font-size: var(--text-fine); color: var(--fc-mute); }
.cq-hidden-note { font-size: var(--text-fine); color: var(--fc-amber); }

/* ── the paper ─────────────────────────────────────────────────────────────── */
.cq-paper { background: var(--surface-card); border: 1px solid var(--fc-line);
            border-radius: var(--radius-doc); box-shadow: var(--shadow-float);
            overflow: hidden;
            -webkit-print-color-adjust: exact; print-color-adjust: exact; }

/* 01 · demo only */
.cq-sample { background: var(--fc-amber-tint); border-bottom: 1px solid var(--fc-amber-line);
             color: var(--fc-amber); font-family: var(--font-mono); font-size: 10.5px;
             font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
             text-align: center; padding: 6px 10px; }
.cq-sample[hidden] { display: none !important; }

/* 02 · letterhead — REMOVED 5 Oct 2026. `.cq-letterhead`, `.cq-lh__slot/__logo/__web/__right` and
   `.cq-brand` drew the old client-quote and Monthly Report logo letterhead; the ONE Company Banner
   (ui/v2/company-banner.css, quote-doc.css) superseded both, and no script or page emits these
   classes any more (counted across ui/ before removal). */

/* 03 + 04 · client, site, quote meta, subject */
.cq-meta { display: grid; grid-template-columns: 1fr auto; gap: 14px 34px;
           padding: 18px 42px; border-bottom: 1px solid var(--fc-line); }
.cq-meta__left { display: flex; flex-direction: column; gap: 12px; }
.cq-party__k { font-weight: 700; font-size: var(--text-meta); margin-bottom: 5px; }
.cq-party__v { font-size: var(--text-meta); color: var(--fc-steel); }
.cq-meta__right { text-align: right; }
.cq-qm__k { font: 800 18px var(--font-body); letter-spacing: -0.01em; }
.cq-qm__n { font-family: var(--font-mono); font-weight: 600; font-size: 13px; margin-top: 4px; }
.cq-qm__s { font-family: var(--font-mono); font-size: 10.5px; color: var(--fc-steel);
            margin-top: 8px; }
.cq-subject { grid-column: 1 / -1; display: flex; justify-content: space-between; gap: 20px;
              align-items: baseline; flex-wrap: wrap; border-top: 1px solid var(--fc-line-soft);
              padding-top: 10px; font-size: var(--text-meta); color: var(--fc-steel); }
.cq-subject[hidden] { display: none !important; }
.cq-valid { font-family: var(--font-mono); font-size: 10.5px; color: var(--fc-mute);
            white-space: nowrap; }

/* 05 + 07 · the one table */
.cq-lines { width: 100%; border-collapse: collapse; }
.cq-lines th { font: 700 11px var(--font-body); text-transform: uppercase;
               letter-spacing: var(--track-caps); color: var(--fc-mute); text-align: left;
               padding: 9px 14px; border-bottom: 1px solid var(--fc-line); }
.cq-lines td { padding: 12px 14px; vertical-align: top; }
.cq-lines th:first-child, .cq-lines td:first-child { padding-left: 42px; }
.cq-lines th:last-child,  .cq-lines td:last-child  { padding-right: 42px; }
.cq-c-qty { width: 80px; }
.cq-c-rate, .cq-c-amt { text-align: right; font-family: var(--font-mono); font-weight: 600;
                        font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cq-c-rate { width: 110px; }
.cq-c-amt  { width: 120px; }
/* ⚠ **`.cq-unit` is GONE with the one-table ruling** — RULED 21 Aug. The unit rode on the line
   only because one column carried two quantities; two tables put it in the column heading, which
   is what a column heading is for. A rule left behind for a class nothing emits is a decision the
   next reader takes for live. */
/* A repeated head — the second table starting under the first — gets air above it, so it reads
   as a new table rather than another row of the one before. `Client_Quote` does the same thing
   at rows 20 and 35. */
.cq-lines tr.cq-head:not(:first-child) th { padding-top: 26px; }
.cq-line__t { font-weight: 600; font-size: 13px; line-height: 1.4; color: var(--fc-ink); }
.cq-line__d { font-size: 12px; line-height: 1.45; color: var(--fc-steel); }
.cq-lines td.cq-page-head { font-family: var(--font-mono); font-size: var(--text-fine);
                            letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600;
                            color: var(--fc-marine); padding: 16px 42px 4px;
                            border-bottom: 1px solid var(--fc-line); }

/* 06 · the Includes band — ⚠ the 94px inset aligns it to the Description column, not the gutter */
.cq-inc-row td { padding: 0 !important; }
.cq-inc { background: var(--fc-paper); border-top: 1px solid var(--fc-line-soft);
          padding: 10px 42px 16px 94px; }
.cq-inc__k { font: 700 11px var(--font-body); text-transform: uppercase;
             letter-spacing: var(--track-caps); color: var(--fc-mute); margin-bottom: 5px; }
.cq-inc__b { font-size: var(--text-meta); line-height: 1.55; color: var(--fc-steel);
             white-space: pre-line; }

/* 08 · totals — the 2px rule marks the one figure the client acts on */
.cq-totals { padding: 18px 42px; background: var(--fc-well);
             border-top: 1px solid var(--fc-line); }
.cq-total { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0;
            font-size: var(--text-detail); }
.cq-total__v { font-family: var(--font-mono); font-weight: 600; font-size: 13px;
               font-variant-numeric: tabular-nums; }
.cq-total--grand { padding-top: 12px; margin-top: 6px; border-top: 2px solid var(--fc-marine);
                   font: 800 17px var(--font-body); letter-spacing: -0.01em; }
.cq-total--grand .cq-total__v { font-size: 17px; }

/* 09 · terms — sentence case, and blank renders nothing at all */
.cq-terms { padding: 20px 42px; font-size: 12px; color: var(--fc-steel); }
.cq-terms h3 { font: 700 11px var(--font-body); text-transform: uppercase;
               letter-spacing: var(--track-caps); color: var(--fc-mute); margin: 0 0 8px; }
.cq-terms p { margin: 0 0 5px; }
.cq-terms p:last-child { margin: 0; }

/* 10 · the QR block — absent until a link exists */
.cq-accept { display: flex; gap: 16px; align-items: center; flex-wrap: wrap;
             margin: 0 42px 14px; border: 1.5px solid var(--fc-marine);
             border-radius: 10px; padding: 13px 16px; }
.cq-accept[hidden] { display: none !important; }
.cq-accept__qr { width: 62px; height: 62px; border: 1.5px solid var(--fc-ink);
                 border-radius: 6px; padding: 5px; flex-shrink: 0; }
.cq-accept__body { flex: 1 1 250px; }
.cq-accept__body h3 { font: 800 13px var(--font-body); color: var(--fc-marine); margin: 0; }
.cq-accept__body p { font-size: 12px; line-height: 1.5; color: var(--fc-steel); margin: 3px 0 0; }
.cq-accept__link { font-family: var(--font-mono); color: var(--fc-info); word-break: break-all; }
.cq-accept__aside { font-size: 11px; color: var(--fc-mute); }

/* 11 · the ink band — always present, link or no link */
.cq-ink { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
          padding: 4px 42px 10px; font-size: var(--text-meta); }
.cq-ink__cap { font: 700 11px var(--font-body); text-transform: uppercase;
               letter-spacing: var(--track-caps); color: var(--fc-mute); }
.cq-ink__rule { display: inline-block; border-bottom: 1px solid var(--fc-ink); }
.cq-ink__rule--sig  { width: 210px; }
.cq-ink__rule--date { width: 110px; }

/* 12 */
.cq-closing { padding: 10px 42px 26px; font-size: var(--text-meta); color: var(--fc-steel);
              font-style: italic; }

/* Print: the shell and the editor are chrome, and chrome does not belong on a client's
   document. Nothing here declares a height or width — the document runs to the pages its
   content needs. */
/* The call-up's forward step. Right-aligned and separated, so it reads as *leaving the screen*
   rather than as another control on the costing summary above it. */
.mc-next { display: flex; justify-content: flex-end; margin-top: 18px; }

@media print {
  /* ⚠ **`.mc-next` and `.ps-actions` are navigation, not document.** The forward links
     added 22 Aug sit inside `main`, and this stylesheet hides chrome by an allowlist rather than
     by isolating the document — so anything added above or below the paper prints until it is
     named here. That shape is recorded in the design directory's README as the thing to fix.
     `.ps-actions` joined the list on 13 Sep (slot 2): screen 05's forward step moved from the
     band's `.ps-verdict__gate` — which this comment named and the list never carried — into the
     footer card, and the card is named here on the day it landed. */
  .rail, .topbar, .iq-head, .mc-next, .ps-actions, [data-print-hide] { display: none !important; }
  .shell { display: block; }
  .cq-grid { display: block; }
  .cq-paper { border: none; box-shadow: none; padding: 0; }
  .fc-table tr { break-inside: avoid; }
  /* ⚠ **No `thead` to repeat any more, and repeating one would now be wrong.** With two tables
     a header carried onto the next printed page could be the wrong one of the two — the fence
     head sitting above gates. The heads are body rows drawn where the table changes instead,
     exactly as the workbook repeats them at rows 11, 20 and 35. */
  .cq-lines tr.cq-head { break-after: avoid; }
  /* r82b — the acceptance box and the signature line are top-level blocks of the document and
     must not be split. A QR cut across a page break is a QR nobody can scan. */
  .cq-accept, .cq-ink, .cq-terms, .cq-totals { break-inside: avoid; }

  /* ⚠ **Defect 1 of the 20 Aug port: the tints were load-bearing on paper and had no promise
     they would reach it.** Browsers drop backgrounds when printing unless a page asks them not
     to. `.cq-paper` asked — and `print-color-adjust` inherits, so screen 04's navy table head
     and TOTAL survived by being inside it. **The material call-up is not inside it.** Its
     schedule is a document in its own right — *"this document renders when a quote will not"* —
     and on the printed copy the on-site correction column and every cell tint would have gone
     white on white, taking with them the one signal that says which figures somebody changed.
     Classic's `app.css` has carried this since the beginning; only v2 lost it. */
  .mc-sheet, .mc-sum__bar, .mc-ops__in, .mc-ops__foot, .fc-chip, .fc-notice,
  .ps-verdict, .ps-row {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}

/* ── Screen 05 · Pre-send check ─────────────────────────────────────────────────
   Values are the design's own (`screens/PreSendCheck.jsx`), expressed in tokens where a
   token holds that exact value. `#BFDCCB` is the one literal — the green line has no token
   name yet, and `components.css` already spells it out for `.fc-chip--ready`. */

/* ⚠ **880px, not the 1240px the other screens get, and the design is consistent about it.**
   Input quote, Site markup and Material call-up are 1240px; Pre-send check and Client quote
   are 880px. The split is not arbitrary — the three wide ones are workbenches you scan across,
   and these two are read a line at a time. `.ps-page` inherited `.iq-page`'s wide measure, so
   every check's detail ran to nearly 900px of text: a sentence that wide is one the eye loses
   its place in, which is a poor property for the screen whose whole job is "read this before
   you send it". */
.ps-page { max-width: 880px; margin-inline: auto; }   /* the design centres it — `margin: 0 auto` */

.ps-verdict { display: flex; align-items: center; justify-content: space-between;
              gap: 16px; flex-wrap: wrap; padding: 16px 20px; margin-bottom: 20px;
              border-radius: var(--radius-card);
              background: var(--fc-red-tint); border: 1px solid var(--fc-red-line); }
.ps-verdict.is-ready { background: var(--fc-green-tint); border-color: #BFDCCB; }

.ps-banner { font: 800 17px/1.2 'Hanken Grotesk', sans-serif; letter-spacing: -0.01em;
             color: var(--fc-red); }
.ps-verdict.is-ready .ps-banner { color: var(--fc-green); }
.ps-tally { font-size: var(--text-detail); color: var(--fc-steel); margin-top: 2px; }

.ps-notwired { margin-bottom: 20px; }

.ps-card { padding: 0; overflow: hidden; }

/* 22px / 1fr / 1.4fr — the name column stays narrow so the detail, which is the substance of
   the row, gets the width. `baseline` so a two-line detail does not drag its ✓ down with it. */
.ps-row { display: grid; grid-template-columns: 22px 1fr 1.4fr; gap: 14px;
          padding: 12px 18px; align-items: baseline;
          border-top: 1px solid var(--fc-line-soft); }
.ps-row:first-child { border-top: none; }
.ps-row.is-bad { background: var(--field-attention-bg); }

.ps-glyph { font-family: var(--font-mono); font-size: var(--text-detail);
            color: var(--fc-green); }
.ps-row.is-bad .ps-glyph { color: var(--fc-red); }

.ps-name { font-size: var(--text-detail); font-weight: 600; color: var(--fc-ink); }
.ps-cell { font-family: var(--font-mono); font-size: var(--text-fine); font-style: normal;
           color: var(--fc-mute); }

.ps-detail { font-size: var(--text-meta); color: var(--fc-steel); line-height: 1.45; }
.ps-row.is-bad .ps-detail { color: var(--fc-red); }
.ps-where { margin-top: 5px; font-size: var(--text-fine); color: var(--fc-info); }
/* The one edge onto 13 Rules & overrides (YES TO ALL item 8, 13 Sep) — the said line's link
   treatment on 03, on tokens; drawn only while the screen holds a working quote. */
.ps-log-link { display: inline-block; margin-top: 5px; font-size: var(--text-fine); color: var(--fc-ink);
               text-decoration: underline; text-underline-offset: 2px; }

/* ── slot 2 of the FULL MOCK TRANSFER, 13 Sep — the mock's job line, footer action bar and
   no-quote scaffold (Q-A), in the platform's tokens. Read from the vendored
   `screens/PreSendCheck.jsx`, which re-hashes to its r30 manifest pin. Nothing here is a new
   visual language: the footer is `fc-card`, its control is `fc-btn`; the values are the mock's
   own where a token holds that exact value (`#8B8779` is `--fc-mute`, `#E9E7E1` is `--fc-line`,
   `12px` radius is `--radius-card`, `16px 18px` is `--pad-card-sm`). */
/* The header's right slot — `job.ref · job.client`, mono 12px muted; `.iq-head` is already the
   mock's `flex / flex-end / space-between / gap 16 / wrap`, so it wraps under the subtitle at
   880px exactly as the capture shows. `hidden` needs the rule because nothing else sets `display`
   on a span, and the pattern this file keeps is to say so beside every `hidden` toggle. */
.ps-job { font-family: var(--font-mono); font-size: 12px; color: var(--fc-mute); }
.ps-job[hidden] { display: none !important; }
/* The band with no quote: neither red nor green — there is no verdict to publish. `--fc-well`
   ground, `--fc-line` border, the `—` headline in the muted ink. */
.ps-verdict.is-empty { background: var(--fc-well); border-color: var(--fc-line); }
.ps-verdict.is-empty .ps-banner { color: var(--fc-mute); }
/* A scaffold row: the glyph column kept (its span is empty; `visibility` holds the baseline),
   the detail — `not yet quoted` — in the faint ink. No attention tint: nothing has failed. */
.ps-row.is-blank .ps-glyph { visibility: hidden; }
.ps-row.is-blank .ps-detail { color: var(--fc-faint); }
/* The footer card — the mock's `tileWrap` with `padding: 16px 18px`, which `fc-card--sm` already
   is. 20px below the check card, the mock's card gap (the conflicts block and the time tile that
   sat between them in the mock are not drawn, so the gap is the footer's own margin). */
.ps-actions { margin-top: 20px; }
.ps-actions__row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ps-actions__why { margin: 9px 0 0; font-size: var(--text-fine); color: var(--fc-mute);
                   line-height: 1.5; }

/* ── Screen 01 · gate schedule + ⚙ panel groups (r62 / r24) ─────────────────────
   Columns are the design's own: `110px 1fr 1fr 1fr 34px` for type / width / qty / remove,
   widened by one to carry r62d's per-row install and margin overrides. */

.gs-head, .gs-row { display: grid;
                    grid-template-columns: 104px 1fr 62px 1fr 1fr 28px;
                    gap: 8px; align-items: center; }
.gs-head { margin: 10px 0 6px; font-size: var(--text-fine); color: var(--fc-mute);
           font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
.gs-row { margin-bottom: 7px; }
/* Preserve readable gate controls within a local scroller on narrow screens. */
@media (max-width: 640px) {
  .os-views { flex-wrap: wrap; max-width: 100%; box-sizing: border-box; }
  .iq-gate-scroll { overflow-x: auto; }
  .iq-gate-scroll .gs-head, .iq-gate-scroll .gs-row {
    min-width: 520px;
    grid-template-columns: 104px minmax(80px, 1fr) 62px minmax(90px, 1fr) minmax(90px, 1fr) 28px;
  }
}
.gs-del { border: 1px solid var(--fc-line); background: var(--fc-white);
          border-radius: var(--radius-badge); color: var(--fc-red); cursor: pointer;
          font-size: 15px; line-height: 1; padding: 6px 0; }
.gs-del:hover { background: var(--fc-red-tint); border-color: var(--fc-red-line); }
.gs-actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; }

/* A group heading inside the ⚙ panel. The panel is long by nature — it is the workbook's
   rows 29–52 — so it reads as named groups rather than one run of fields. */
.iq-sub { margin: 20px 0 10px; font: 700 12px/1.2 'Hanken Grotesk', sans-serif;
          text-transform: uppercase; letter-spacing: 0.06em; color: var(--fc-marine);
          border-top: 1px solid var(--fc-line-soft); padding-top: 14px; }
.iq-sub:first-of-type { border-top: none; padding-top: 0; margin-top: 14px; }
.iq-sub .iq-src { text-transform: none; letter-spacing: 0; font-weight: 500; }

/* ⚠ **The ⚙ panel's group dividers, `PH` in `InputQuote.jsx`** — `━━ POST & PIPE ━━`, mono,
   10px, `0.14em`, and the warm muted grey rather than marine. The rules are **characters, not
   borders**, which is why this reads as a divider carrying a word rather than a heading with a
   line above it; `iq-sub`'s `border-top` was the closest ours got and it is a different object.

   They are `::before`/`::after` so the markup stays the heading's own words — a `━━` typed into
   the HTML is decoration a screen reader announces. `content: ""` with `attr()` was the
   alternative and it cannot be styled separately, which the mono/uppercase treatment needs.

   ⚠ **`.iq-sub` is deliberately still here.** §4's "Gate schedule" is a heading inside a card,
   not a divider between groups of overrides, and the design draws no `PH` there. */
.iq-ph { display: flex; align-items: baseline; gap: 8px;
         margin: 18px 0 8px;
         font-family: var(--font-mono); font-size: 10px; font-weight: 700;
         letter-spacing: 0.14em; text-transform: uppercase; color: var(--fc-mute); }
.iq-ph::before, .iq-ph::after { content: "━━"; letter-spacing: 0; }
.iq-ph::after { order: 3; }
.iq-ph .iq-src { order: 2; text-transform: none; letter-spacing: 0; font-weight: 500; }

/* ── Screen 03 · the fabrication / bracing pair — build spec §06 S1.07 + S1.08 ──
   **1.1fr / 1fr, not 1fr / 1fr.** The spec's own reason: the fabrication rows carry four
   columns — size, derivation, count, metres — and the bracing rows carry two. */
.mc-pair { display: grid; grid-template-columns: 1.1fr 1fr; gap: 20px; margin-bottom: 24px; }
@media (max-width: 900px) { .mc-pair { grid-template-columns: 1fr; } }

/* The card head band already full-bleeds (`.fc-card > .iq-kicker:first-child`), so the banner
   sits flush under it by taking the same negative inline margin. */
.mc-fab__banner {
  margin-inline: calc(var(--pad-card-x) * -1);
  padding: 9px var(--pad-card-x);
  border-bottom: 1px solid var(--fc-line-soft);
  font-family: var(--font-body); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-caps);
  color: var(--fc-red);
}
.mc-fab__row {
  display: grid; grid-template-columns: 1fr 72px 84px; gap: 10px;
  align-items: baseline; padding: 11px 0;
  border-top: 1px solid var(--fc-line-soft);
}
.mc-fab__row:first-child { border-top: none; }
.mc-fab__size { font-family: var(--font-mono); font-weight: 600; font-size: 12.5px;
                color: var(--fc-ink); }
/* The working, in the engine's own words. It is what makes the count checkable. */
.mc-fab__from { display: block; font-family: var(--font-body); font-size: 11.5px;
                font-weight: 400; color: var(--fc-mute); margin-top: 2px; }
/* ⚠ **The count and the metres are different quantities and must never be swapped** — the
   spec says so in those words. The count is 6.5 m lengths to order; the metres is what they
   yield. Different weight and different size is the whole reason they read apart. */
.mc-fab__n { font-family: var(--font-mono); font-weight: 600; font-size: 15px;
             color: var(--fc-marine); text-align: right; }
.mc-fab__m { font-family: var(--font-mono); font-size: 11.5px; color: var(--fc-mute);
             text-align: right; }
.mc-fab__foot { font-style: italic; font-size: 11.5px; color: var(--fc-mute);
                margin: 12px 0 0; }

.mc-br__row { padding: 10px 0; border-top: 1px solid var(--fc-line-soft); }
.mc-br__row:first-child { border-top: none; padding-top: 0; }
.mc-br__k { display: block; font-family: var(--font-body); font-size: 10.5px; font-weight: 700;
            text-transform: uppercase; letter-spacing: var(--track-caps); color: var(--fc-mute); }
.mc-br__v { display: block; font-size: 13px; font-weight: 600; line-height: 1.4;
            color: var(--fc-ink); margin-top: 3px; }

/* ── Screen 03 · row 73's charge-mode strip (r83) ───────────────────────────────
   The design's own strip: a white card above the schedule carrying the switch, the tan
   lump cell, the greyed ƒ equivalent and the scope sentence. The lump input takes the
   field-override tokens directly rather than the `.fc-field--override` class, because it is
   an inline cell in a strip rather than a labelled field in a grid. ⚠ **The tokens, not the
   hexes** — it wrote the raw values until 20 Aug, and the reason above argues against the
   class, never against the token. */

.cm-strip { display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
            background: var(--fc-white); border: 1px solid var(--fc-line);
            border-radius: var(--radius-card); padding: 12px 18px; margin-bottom: 20px; }
.cm-label { font: 800 11px/1.2 'Hanken Grotesk', sans-serif; text-transform: uppercase;
            letter-spacing: 0.06em; color: var(--fc-marine); }
.cm-lump { display: inline-flex; align-items: center; gap: 6px; }
.cm-lump__label { font-family: var(--font-mono); font-size: 12px; color: var(--fc-ink); }
.cm-lump__input { width: 110px; padding: 6px 10px; border: 1.5px solid var(--field-override-border);
                  border-radius: var(--radius-badge); background: var(--field-override-bg);
                  font-family: var(--font-mono); font-size: 13px; font-weight: 600;
                  text-align: right; }
.cm-equiv { font-family: var(--font-mono); font-size: 12px; color: var(--fc-mute); }
.cm-note  { flex-basis: 100%; font-size: var(--text-fine); color: var(--fc-mute);
            line-height: 1.5; }

/* ⚠ **`display` on an element that also carries `hidden` beats the UA stylesheet.** These three
   are toggled by the `hidden` attribute, and `.cm-lump { display: inline-flex }` won — so the
   lump cell stayed on screen in per-metre mode, offering a figure that mode does not charge.
   Caught by driving the page, not by a source test: **this is the `.savebar[hidden]` defect
   again**, which this repository already records for the same reason ("`display: flex` on an
   element carrying `hidden` beats the UA stylesheet … with every source-level test passing,
   because only the rendered page shows it"). Any new class here that sets `display` and is
   toggled by `hidden` needs a line in this block. */
.cm-lump[hidden], .cm-equiv[hidden], .cm-note[hidden] { display: none !important; }

/* r82 · "Mark as sent · I emailed it myself" — the design's dashed, quieter treatment, because
   it records a fact rather than performing an action. */
/* The armed revision, named beside the controls that act on it. Mono, because it is an
   identifier a person reads back — the same treatment `.jf-revs__n` gives it in the folder. */
.cq-rev { font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 600;
          color: var(--fc-ink); }
.cq-rev__edited { margin-left: 8px; }
.cq-rev__edited[hidden] { display: none !important; }

.ps-marksent { font: 700 12.5px/1 'Hanken Grotesk', sans-serif; padding: 9px 16px;
               border-radius: var(--radius-badge); cursor: pointer;
               border: 1.5px dashed #C9C5BA; background: var(--fc-white);
               color: var(--fc-info); }
.ps-marksent:hover { border-color: var(--fc-info); }
.ps-marksent[hidden] { display: none !important; }
.ps-sent-note { margin-bottom: 20px; }
.ps-sent-note[hidden] { display: none !important; }

/* **The quote subject** — what the job is, in the estimator's own words. Drawn from `job_scope`,
   which the engine composes and this screen omitted until the 18 Aug re-audit. Sits under the
   meta block and above the lines, as `ClientQuote.jsx` places it. */
.cq-scope {
  margin: 0 0 18px; padding: 10px 14px;
  border-left: 3px solid var(--fc-line);
  font-size: 14px; line-height: 1.5; white-space: pre-wrap;
}

/* ── Screen 06 · Job tracker ────────────────────────────────────────────────────
   Values from the design's own `JobTracker.jsx`, expressed in tokens where one holds that
   value.

   ⚠ **This sits AFTER the `@media print` block, which is fine here and is worth saying why.**
   That block is screen 04's — it hides the chrome so a client quote prints as a document — and
   nothing below touches a selector in it. The trap this stylesheet sprang once was a rule that
   needed to be overridden BY the print block and was written after it. **A tracker does not
   print**, and if it ever does, its print rules go inside that block rather than a general rule
   going before it. */

.jt-search { min-width: 280px; margin: 0; }
/* ── slot 1 of the FULL MOCK TRANSFER, 13 Sep — the rendered mock's structure, in the platform's
   tokens. Read from the manifest-pinned bundle (`JobTracker.jsx` segment); the vendored `.jsx`
   is the r4 copy and draws an older layout. Nothing here is a new visual language: cards are
   `fc-card`, chips are `fc-seg`, buttons are `fc-btn`. */
.jt-head__right { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* The intake card — closed until the header action opens it; the mock's two-column grid. */
.jt-intake { margin-bottom: 16px; }
.jt-intake__desc { margin: 4px 0 12px; font-size: var(--text-meta); color: var(--fc-mute); }
.jt-intake__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.jt-intake__long { display: grid; gap: 12px; margin-top: 12px; margin-bottom: 14px; }
.jt-intake__note { margin: 8px 0 0; font-family: var(--font-mono); font-size: var(--text-fine);
                   color: var(--fc-mute); }
.jt-intake__close { all: unset; cursor: pointer; margin-top: 10px; font-size: 11.5px;
                    font-weight: 700; color: var(--fc-marine); border-bottom: 1px solid currentColor; }
/* The Follow-ups card — the mock's `1fr auto auto auto` rows, dividers between. */
.jt-followups { margin-bottom: 16px; }
.jt-followups__rows { margin: 0 calc(-1 * var(--pad-card-x)); }
.jt-followups__row { display: grid; grid-template-columns: 1fr auto auto auto; gap: 14px;
                     align-items: center; padding: 10px var(--pad-card-x);
                     border-top: 1px solid var(--fc-line-soft); }
.jt-followups__who { font-size: 13px; font-weight: 600; }
.jt-followups__meta { font-family: var(--font-mono); font-size: var(--text-fine);
                      color: var(--fc-mute); font-weight: 400; margin-left: 6px; }
.jt-followups__due { font-family: var(--font-mono); font-size: var(--text-fine);
                     color: var(--fc-red); white-space: nowrap; }
.jt-followups__none { margin: 0; padding: 12px var(--pad-card-x); font-size: 12.5px;
                      color: var(--fc-mute); border-top: 1px solid var(--fc-line-soft); }
/* The mock's chip count, mono so the numerals line up — `.jf-chip__n`'s treatment. */
.jt-chip__n { font-family: var(--font-mono); font-size: 11px; font-weight: 600; opacity: .7;
              margin-left: 5px; }
/* The control row beside the chips: threshold · sort · search, wrapping under at narrow widths. */
.jt-controls { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.jt-sort { min-width: 160px; margin: 0; }
.jt-workflow { white-space: nowrap; }
/* Five tiles: the mock's `minmax(170px, 1fr)`, so all five sit on one row at 1440. */
.jt-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
            gap: 12px; margin-bottom: 16px; }
.jt-tile__note { font-size: var(--text-fine); color: var(--fc-mute); margin-top: 4px; }
.jt-chips { margin-bottom: 14px; flex-wrap: wrap; }
.jt-table td { vertical-align: top; }
.jt-sub { font-size: var(--text-meta); color: var(--fc-mute); margin-top: 3px; }
/* A follow-up that has passed. **Colour is not the only signal** — the notice above the table
   carries the count in words, because a red date on a phone in sunlight is not a notification. */
.jt-overdue { color: var(--fc-red); font-weight: 700; }
/* ⚠⚠ **The tracker table's min-content width is 1172px and it cannot shrink below it**, because
   every `th` is `white-space: nowrap` (`.fc-table th`, deliberately — they are overlines, not
   wrapped sentences) and `.jt-actions` is `nowrap` too. At a 1280px viewport `main.iq-page`
   offers 980px, so the table ran 160px past the document edge and **the page scrolled sideways**;
   at 1440 it offers 1140px, the table still rendered 1172 and the overflow was absorbed by the
   page's own 32px right padding instead of being visible. Measured, both widths.

   ⛔ **The heads are NOT unwrapped and the padding is NOT reduced.** Either would shrink the
   table by changing what Design drew. The box scrolls instead, which is this project's existing
   answer to the identical question — see `.mc-scroll` a few hundred lines above, whose comment
   reads *"wide tables scroll in their own box, never the page"*, ruled for screen 03's material
   schedule.

   ⚠ **That makes two screen-local names for one pattern, and the right move is to promote it to
   a shared rule.** Deliberately not done here: `.mc-scroll` belongs to screen 03, this
   authorisation is screen 01 and screen 06, and quietly restyling a third screen is how the
   `box-sizing` bug survived — three people patching their own element. Recorded in the contract
   as debt rather than taken silently.

   ⚠ Safe to make a scroll container: nothing inside this table needs to escape its box. The
   status control is a native `<select>` (the UA renders its list outside the layout) and the
   reference picker is a `window.prompt` — there is no absolutely-positioned popover in here. */
.jt-scroll {
  overflow-x: auto;
  /* ⚠⚠ **This line exists because the scroll box REGRESSED 1440, where there was no defect.**
     The box is a child of `main.iq-page`, so it is 32px narrower than the page — and the table's
     min-content is 1172px against 1140px of content box at a 1440px viewport. The 32px it lost
     was the exact 32px the table used to bleed into, so `Delete` rendered as **`Dele`**: the
     repair for 1280 took a column edge off a width that had been fine.

     **Reclaiming the page's own right padding gives the box back exactly that 32px.** At 1440 it
     becomes 1172 — the table's min-content to the pixel — so nothing scrolls and nothing clips,
     which is byte-for-byte what shipped before. At 1280 it becomes 1012 against the same 1172,
     so the box still scrolls and **the page still does not**, which is the defect this was
     authorised for.

     ⚠ **Right only, never `margin-inline`.** Reclaiming both sides would move the table's LEFT
     edge out of line with every card above it — a visible misalignment traded for a fix.
     ⚠ **It cannot make the page scroll at any width**: the reclaimed 32px is `main`'s own
     padding, so the box's margin box stops at `main`'s border box, which `max-width` and
     `margin: 0 auto` keep inside the viewport. Measured at both widths, not reasoned. */
  margin-right: calc(-1 * var(--pad-screen-x));
}
/* ⚠ Stacked since slot 1 (13 Sep): the mock's ninth column, `Workflow no.`, put the table's
   min-content at 1333px against the 1172px box at 1440 — `Delete` clipped to `Dele`, the exact
   defect the 30 Aug contract measured. The three actions are unchanged and every one stays on
   the row; they sit one under another, so the column is one button wide instead of three. */
.jt-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
              max-width: 120px; }
/* The buttons wrap their own label inside that width — `Log call · re-date +7d` on two lines —
   rather than the column carrying the widest one whole; measured: 1221px → under the 1172px box. */
.jt-actions .fc-btn { white-space: normal; text-align: left; justify-content: flex-start; }
.jt-status { min-width: 140px; }
.jt-log { margin-top: 20px; }
.jt-log__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
                gap: 10px 14px; margin-bottom: 12px; }
.jt-elsewhere { margin-top: 18px; font-size: var(--text-meta); color: var(--fc-mute); }
.jt-month { min-width: 160px; margin: 0; }
.jt-today { font-family: var(--font-mono); font-size: var(--text-fine);
            color: var(--fc-mute); align-self: center; }
.jt-filters { display: flex; align-items: flex-end; justify-content: space-between;
              gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.jt-filters .jt-chips { margin-bottom: 0; }
.jt-threshold { min-width: 180px; margin: 0; }
.jt-unquoted { font-family: var(--font-mono); font-size: var(--text-fine);
               color: var(--fc-mute); }

/* ── the refusal a stale screen gets, on the row it is about ────────────────────
   Design, 31 Aug second review: the engine's sentence renders INSIDE the affected row — *"a
   full-width line spanning the row's columns, directly beneath the row's field rows, so it is on
   screen whenever the row is."*

   ⛔⛔ **The defect it repairs was measured, then photographed.** The sentence rendered into the
   page-level `#jt_status` line above the tiles: 591px above the row at 1440, and **925px above
   it at 1280, outside the viewport**. An estimator who chose `Won` on a stale row saw the
   control revert to `Lost` on its own and no explanation anywhere on screen.

   ⛔ **No colour, weight, glyph or wording is invented here, and that is Design's point 2** —
   the line carries `.iq-status` unchanged, which is the treatment `#jt_status` already has.
   These two rules are STRUCTURAL and nothing else: a table cannot hold a full-width line in a
   row that already carries eight cells, so the sentence is a second `<tr>`, and the divider
   between the two comes off so the pair reads as one row rather than as the next job's.

   ⚠ The gap under the row's fields is `.iq-status`'s own `margin-top`, which is why the cell's
   top padding goes — two sources of the same space would double it. */
.jt-refused > td { border-bottom: none; }
.jt-refusal > td { padding-top: 0; }

/* ── Screen 07 · Accounts ───────────────────────────────────────────────────────
   The claim ledger. Reuses the tracker's tiles and chips deliberately — they are the same
   object (a figure with a note, a filter over a list) and a second set of classes would be a
   second thing to keep in step. */
.mn-cap { margin-bottom: 16px; }
.mn-cap p { margin: 6px 0 0; font-size: var(--text-meta); }
/* The statute itself, in mono. **It reads as a citation rather than as prose** because that is
   what it is — a reference somebody may need to look up, not a sentence we wrote. */
.mn-citation { font-family: var(--font-mono); font-size: var(--text-fine);
               color: var(--fc-mute); line-height: 1.5; }
.mn-table td { vertical-align: top; }
.mn-actions { white-space: nowrap; }
.mn-amount { width: 110px; display: inline-block; margin-right: 6px;
             background: var(--field-input-bg); border-color: var(--field-input-border); }
.mn-boundary { margin-top: 20px; }
.mn-boundary p { margin: 8px 0 0; font-size: var(--text-meta); line-height: 1.6; }
.mn-honest { color: var(--fc-mute); }

/* ── Screen 08 · Time saved ─────────────────────────────────────────────────────
   Slot 2 of the FULL MOCK TRANSFER, 13 Sep 2026 — the mock's structure (`TimeSaved.jsx` at its
   manifest pin) in the platform's tokens: the tiles are the tracker's `jt-tile`, the formula band
   is `fc-notice--info`, the baseline panels sit on `--fc-well` (the mock's #F7F6F2), the send card
   is `fc-card` with the document on `--fc-paper` / `--fc-line` (the mock's #FAFAF8 / #E9E7E1). The
   document's 4px radius is the mock's and has no token — kept as a literal, said so here.
   Contract: `docs/ui-contracts/2026-09-13-time-saved-to-the-mock.md`. */
.ts-formula { margin-bottom: 16px; }
.ts-formula__fx { font-family: var(--font-mono); font-size: 12px; margin: 0 0 8px; }
.ts-sum { font-family: var(--font-mono); font-size: var(--text-meta); margin: 4px 0 0; }
.ts-note { font-size: var(--text-meta); color: var(--fc-mute); margin: 8px 0 0; line-height: 1.6; }
/* The mock's three panels — `repeat(3, 1fr)` with the intrinsic floor this stylesheet prefers. */
.ts-baseline { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
               gap: 12px; margin-bottom: 8px; }
.ts-assume { background: var(--fc-well); border-radius: var(--radius-card); padding: 13px 15px; }
.ts-assume .fc-field { margin: 0; }
.ts-baseline__note { margin: 0 0 20px; }
.ts-h3 { font: var(--h3); letter-spacing: -0.01em; margin: 0 0 4px; }
.ts-sub { font-size: var(--text-meta); color: var(--fc-mute); margin: 0 0 10px; }
.ts-table__scroll { overflow-x: auto; }
.ts-client { font-weight: 600; }
.ts-fence { font-size: var(--text-meta); color: var(--fc-steel); }
/* The row input — the one control for a quote's minutes. */
.ts-minutes { width: 104px; padding: 6px 9px; }  /* the mock's box is 80px and always holds a value; the build's empty box carries its own placeholder, which 80px clips */
.ts-minutes::placeholder { color: var(--fc-mute); font-size: var(--text-fine); }
/* The send card: the mock's white card, its head with a foot rule, the document block, the
   action row holding the platform's pending chip in place of a button nothing is behind. */
.ts-send { padding: 0; overflow: hidden; margin-top: 22px; }
.ts-send__head { padding: 14px 18px; display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
                 border-bottom: 1px solid var(--fc-line-soft); }
.ts-send__title { font-size: 13px; font-weight: 800; color: var(--fc-marine); }
.ts-send__sub { font-size: var(--text-fine); color: var(--fc-mute); flex: 1 1 300px; line-height: 1.5; }
.ts-doc { max-width: 420px; margin: 14px 18px; background: var(--fc-paper);
          border: 1px solid var(--fc-line); border-radius: 4px; }
.ts-doc__head { background: var(--fc-marine); color: var(--fc-white); padding: 10px 16px;
                font-size: var(--text-meta); font-weight: 800; }
.ts-doc__row { display: flex; justify-content: space-between; gap: 12px; padding: 8px 16px;
               border-top: 1px solid var(--fc-line-soft); font-size: var(--text-meta); }
.ts-doc__row:first-of-type { border-top: 0; }
.ts-doc__k { color: var(--fc-steel); }
.ts-doc__v { font-family: var(--font-mono); font-weight: 600; min-height: 1em; }
.ts-blocked { margin: 0; padding: 8px 16px; font-family: var(--font-mono); font-size: var(--text-fine);
              color: var(--fc-mute); border-top: 1px solid var(--fc-line-soft); }
.ts-doc__never { margin: 0; padding: 9px 16px; border-top: 2px solid var(--fc-marine);
                 font-size: var(--text-fine); color: var(--fc-green); line-height: 1.5; }
.ts-send__act { padding: 0 18px 16px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ts-send__why { font-size: var(--text-fine); color: var(--fc-mute); }

/* ── Screen 09 · Monthly report ─────────────────────────────────────────────────
   A document, not a dashboard: it prints and it leaves the building, so it carries the
   letterhead and it drops the chrome on paper.

   ── Slot 2 of the FULL MOCK TRANSFER — Owner, 13 Sep 2026 ──────────────────────────────────
   Values are the mock's own (`screens/MonthlyReport.jsx` at its r30 pin), expressed in tokens
   where a token holds that exact value — every colour the mock writes has one (`#173B53` is
   `--fc-marine`, `#A5B9C7` `--fc-mist`, `#8B8779` `--fc-mute`, `#5E6B75` `--fc-steel`, `#0F6A43`
   `--fc-green`, `#B3401F` `--fc-red`, `#8A5A16` `--fc-amber`, `#E9E7E1` `--fc-line`, `#F1EFEA`
   `--fc-line-soft`; its 4px radius is `--radius-doc`, its shadow `--shadow-float`). Contract:
   `docs/ui-contracts/2026-09-13-monthly-report-to-the-mock.md`. */

/* The mock's 880px measure — a document read a line at a time, exactly `.cq-page` / `.ps-page`. */
.mr-page { max-width: 880px; margin-inline: auto; }

/* The paper, as `.cq-paper`: flush sections inside it, colour-adjust so the band prints marine. */
.mr-paper { background: var(--surface-card); border: 1px solid var(--fc-line);
            border-radius: var(--radius-doc); box-shadow: var(--shadow-float);
            overflow: hidden; padding: 0;
            -webkit-print-color-adjust: exact; print-color-adjust: exact; }

/* The identity area is the ONE Company Banner (Owner, 5 Oct 2026 — supersedes the marine logo
   band). The banner itself is drawn by the shared helper (ui/v2/company-banner.css); here the
   report head — the title and the generated-by line — sits beneath it on the white paper. The
   report BODY below is unchanged. */
.mr-band { background: var(--surface-card); }
.mr-band__head { padding: 18px 42px 0; }
.mr-band__title { font: 800 24px/1.1 var(--font-body); letter-spacing: var(--tracking-h1);
                  color: var(--fc-ink); margin: 0; }
.mr-band__meta { font-family: var(--font-mono); font-size: 11px; color: var(--fc-steel);
                 margin-top: 6px; }

/* Every block is full-bleed inside the paper with the mock's padding and a hairline above. */
.mr-glance, .mr-section { padding: 20px 42px; margin: 0; }
.mr-section { border-top: 1px solid var(--fc-line); }
.mr-people { margin-top: 0; }

/* The mock's section kicker is an overline in the section's own colour — 12px, not the 11px
   `--overline`; the TEXT is the engine's `label`, the COLOUR is keyed by section name. */
.mr-kicker { font: 700 12px/1.3 var(--font-body); letter-spacing: var(--track-overline);
             text-transform: uppercase; color: var(--fc-mute); margin-bottom: 8px; }
.mr-kicker--won { color: var(--fc-green); }
.mr-kicker--lost { color: var(--fc-red); }
.mr-kicker--marine { color: var(--fc-marine); }
.mr-kicker--high { color: var(--fc-amber); }

/* At a glance — the mock's KVList as block rows on `--fc-line-soft` hairlines. */
.mr-glance .fc-kv { grid-template-columns: 1fr auto; gap: 0 12px; }
.mr-glance .fc-kv__k, .mr-glance .fc-kv__v { padding: 7px 0; border-top: 1px solid var(--fc-line-soft); }
.mr-glance .fc-kv > :nth-child(-n+2) { border-top: 0; }

/* The section tables — `fc-table` heads, the mock's row typography, the mock's column widths
   (px for its fixed columns, percentages for its `1fr` / `1.2fr` pair — table auto layout). */
.mr-table td { vertical-align: top; color: var(--fc-steel); font-size: var(--text-meta);
               padding: 7px 10px 7px 0; }
/* Heads wrap, as the mock's `WORKFLOW / NO.` does inside its 80px column and its summary
   sentence does across the row — `fc-table`'s `nowrap` is for a register, not a document. */
.mr-table th { padding: 0 10px 6px 0; white-space: normal; vertical-align: bottom; }
.mr-table td:first-child { font-weight: 600; color: var(--fc-ink); font-size: 13px; }
.mr-table td:last-child, .mr-table th:last-child { padding-right: 0; }
.mr-table--won th:nth-child(1) { width: 80px; }
.mr-table--won th:nth-child(2) { width: 35%; }
.mr-table--won th:nth-child(3) { width: 42%; }
.mr-table--won th:nth-child(4) { width: 100px; }
.mr-table--lost th:nth-child(1) { width: 40%; }
.mr-table--lost th:nth-child(2) { width: 48%; }
.mr-table--lost th:nth-child(3) { width: 100px; }
.mr-table--summary th:nth-child(2) { width: 100px; }
.mr-table--summary { margin-bottom: 10px; }
.mr-table--outstanding th:nth-child(1), .mr-table--high_value th:nth-child(1) { width: 33%; }
.mr-table--outstanding th:nth-child(2), .mr-table--high_value th:nth-child(2) { width: 39%; }
.mr-table--outstanding th:nth-child(3), .mr-table--high_value th:nth-child(3) { width: 120px; }
.mr-table--outstanding th:nth-child(4), .mr-table--high_value th:nth-child(4) { width: 100px; }
.mr-table--people th:nth-child(2) { width: 90px; }
.mr-table--people th:nth-child(3) { width: 130px; }
.mr-table--people th:nth-child(4) { width: 80px; }
.mr-table--people th:nth-child(5) { width: 90px; }
/* The mock's sixth column (`cols="1fr 90px 130px 80px 90px 110px"`) — drawn since 13 Sep, item 5. */
.mr-table--people th:nth-child(6) { width: 110px; }
/* An unquoted member — Q-A: the words, never `$0`. */
.mr-unquoted { font-family: var(--font-body); font-size: var(--text-fine); color: var(--fc-mute); }

/* The mock's total line, on its 2px MARINE rule — the team row takes the same rule. */
.mr-total { display: flex; justify-content: space-between; gap: 12px; padding-top: 10px;
            margin-top: 6px; border-top: 2px solid var(--fc-marine); font-size: 14px;
            font-weight: 700; color: var(--fc-ink); }
.mr-total__v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.mr-total__unpriced { font-weight: 400; font-size: var(--text-fine); color: var(--fc-mute); }
.mr-teamrow td { font-weight: 700; color: var(--fc-ink); font-size: 14px;
                 border-top: 2px solid var(--fc-marine); border-bottom: none; padding-top: 10px; }
/* Unattributed work. **Named, never blank** — a blank cell reads as a missing name rather than
   as work nobody recorded an author for. */
.mr-unattributed { font-style: italic; color: var(--fc-mute); }
.mr-note { font-size: var(--text-meta); color: var(--fc-mute); margin-top: 14px; line-height: 1.6; }
/* The filing note sits between Performance by user and the sections, as the mock places it. */
.mr-filing { margin: 0; padding: 12px 42px; border-top: 1px solid var(--fc-line-soft);
             font-size: 11.5px; line-height: 1.55; }
.mr-filing b { color: var(--fc-steel); }

@media print {
  .mr-paper { border: none; padding: 0; box-shadow: none; }
  .mr-section, .mr-people, .mr-glance { break-inside: avoid; }
}

/* ── Screen 10 · Price onboarding ───────────────────────────────────────────────
   The register is dense by nature — 654 lines across five categories — so the table is plain
   and the emphasis is reserved for the two states that matter: a line nobody has priced, and a
   figure somebody has overridden.

   ── slot 2 of the FULL MOCK TRANSFER, 13 Sep 2026 — `PriceOnboarding.jsx` at its manifest pin.
   The mock's geometry is copied where it is a decision (the 7fr/5fr top, the card grid's
   minmax(230px), the 40px/1fr/40px stepper, the badge's amber trio, the ƒ in `--fc-info`);
   every colour that a token names is the token; the ones no token names are the screen-scoped
   custom properties below, the `--po-drop-line` precedent (the drop colours moved up to the
   page so the adjustment card's `→` can read the same `#C9C5BA` once). `#EDEBE4` — the mock's
   zero-adjustment badge — appears nowhere in the token layer; named once here, flagged upstream. */
.po-page {
  --po-drop-line: #C9C5BA;
  --po-drop-bg: #FBFAF7;
  --po-drop-bg-over: #F2F8FB;
  --po-tint-zero: #EDEBE4;
}
.po-table td { vertical-align: top; }
.po-table input.po-base {
  width: 96px; text-align: right; font-family: var(--font-mono);
  background: var(--field-input-bg); border-color: var(--field-input-border);
}
/* The mock's `add price` box: the attention field state on a line nobody has priced. */
.po-table input.po-base--add {
  background: var(--field-attention-bg); border-color: var(--field-attention-border);
}
.po-code { font-family: var(--font-mono); font-size: var(--text-meta); }
/* A line with no base cost. **Not red** — an unpriced line is ordinary during onboarding, and
   colouring it as an error would make a full register look broken on day one. The mock's
   `can't price` in the live column IS red: it is the one cell that says the engine cannot
   answer. */
.po-missing td { background: var(--fc-amber-tint); }
.po-nomoney { font-size: 12px; color: var(--fc-red); }
.po-unpriced { color: var(--fc-amber); font-weight: 700; }
.po-suppliers { margin-bottom: 20px; }
.po-suppliers__reset { margin-left: auto; }

/* The mock's two-card top: the upload (7) beside the ask (5), collapsing to one column where
   the pair cannot share a row — one breakpoint, after the intrinsic rules have done what they
   can (a 7:5 pair has no auto-fit spelling). */
.po-top {
  display: grid; grid-template-columns: 7fr 5fr; gap: 16px; align-items: start;
  margin-bottom: 20px;
}
@media (max-width: 900px) { .po-top { grid-template-columns: 1fr; } }
.po-ask__rows { margin: -6px calc(var(--pad-card-x) * -1) calc(var(--pad-card-y) * -1); }
.po-ask__row {
  display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center;
  padding: 8px var(--pad-card-x); border-top: 1px solid var(--fc-line-soft);
}
.po-ask__row:first-child { border-top: none; }
.po-ask__name { font-family: var(--font-body); font-size: 13px; font-weight: 700; }
.po-ask__req { white-space: nowrap; }

/* The supplier cards — the mock's `repeat(auto-fit, minmax(230px, 1fr))` on `--fc-well`, whose
   own comment reads "supplier tiles". */
.po-sup-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; }
.po-sup { background: var(--fc-well); border-radius: var(--radius-card); padding: 16px; }
.po-sup__head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.po-sup__name { font-family: var(--font-body); font-size: 14.5px; font-weight: 700; }
.po-sup__badges { display: inline-flex; align-items: baseline; gap: 6px; white-space: nowrap; }
.po-sup__adj {
  font-family: var(--font-mono); font-size: var(--text-fine); padding: 2px 8px;
  border-radius: var(--radius-badge); background: var(--po-tint-zero); color: var(--fc-steel);
  border: 1px solid transparent;
}
.po-sup__adj--set { background: var(--fc-amber-tint); color: var(--fc-amber); border-color: var(--fc-amber-line); }
.po-sup__lines { font-size: var(--text-fine); color: var(--fc-mute); margin-top: 2px; }
/* r85g's catalogue cards (13 Sep, item 2) — the register card's own treatment, on tokens; the
   grid sits under its own sub-kicker inside the same section. */
.po-catalogue { margin-top: 14px; }
.po-catalogue .iq-kicker-sub { margin-bottom: 10px; }
.po-sup__step { display: grid; grid-template-columns: 40px 1fr 40px; gap: 8px; margin-top: 12px; }
.po-sup__btn {
  background: #fff; border: 1px solid var(--fc-line-strong); border-radius: var(--radius-control);
  font-family: var(--font-mono); font-size: 15px; padding: 9px 0; cursor: pointer;
  color: var(--fc-marine);
}
/* The card's box is the control — card-scoped, because the old `.po-table input.po-adj` rule was
   the table's (96px, right-aligned) and died with it. */
.po-sup input.po-adj {
  width: 100%; text-align: center; font-family: var(--font-mono); font-size: 16px;
  font-weight: 600; border-radius: var(--radius-control);
  background: var(--field-input-bg); border-color: var(--field-input-border);
}
.po-sup__totals {
  display: flex; justify-content: space-between; align-items: baseline; gap: 6px;
  margin-top: 13px; font-size: var(--text-meta);
}
.po-sup__base { font-family: var(--font-mono); color: var(--fc-mute); font-variant-numeric: tabular-nums; }
.po-sup__arrow { color: var(--po-drop-line); }
.po-sup__live { font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums; }
.po-sup .po-clear { margin-top: 8px; }

/* The mock's search band: the box grows, the ƒ sentence sits beside it. */
.po-searchband { display: flex; gap: 9px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
.po-search { flex: 1 1 320px; min-width: 0; margin: 0; }
.po-search .fc-field__control { font-family: var(--font-mono); font-size: var(--text-meta); }
.po-searchband__fx { font-size: var(--text-meta); color: var(--fc-mute); }
.po-fx { font-family: var(--font-mono); color: var(--fc-info); }

/* The unpriced banner — a flex notice, so `hidden` needs its own rule (the `fc-chip[hidden]`
   lesson, recorded twice in this file). */
.po-unpriced-banner { margin-bottom: 14px; align-items: center; }
.po-unpriced-banner[hidden] { display: none; }

/* The group title over the register — the mock's band, its meta beside it. */
.po-group { display: flex; align-items: baseline; gap: 10px; padding: 10px 4px 8px; }
.po-group__title { font-family: var(--font-body); font-size: 13px; font-weight: 700; }
.po-group__meta { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); }
.po-count { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute);
            margin-left: 5px; }
.po-pager { display: flex; align-items: center; gap: 14px; margin-top: 14px; }
.po-range { font-family: var(--font-mono); font-size: var(--text-meta); color: var(--fc-mute); }
/* The scope line under the supplier heading. **These rows count a different population from
   the tiles** — the whole book, gates included — which is the sentence that stops `Unpriced 6`
   here reading as a contradiction of `Unpriced 0` above. */
.po-suppliers__scope { font-size: var(--text-fine); color: var(--fc-mute); margin: -2px 0 10px; }

/* ── Screen 10 · the price-list upload ─────────────────────────────────────────
   `Form Controls.md` § File upload. Geometry is the design's: `2px dashed`, radius 12px,
   padding `26px 18px`, centred, `transition: background .15s, border-color .15s`.

   ⚠ **Two of the design's six colours ARE tokens and four are not, and the difference is
   checked rather than assumed.** `#1E6E96` is `--fc-info` and `#173B53` is `--fc-marine`, to
   the byte, so those are named. The dropzone's other three — resting `#C9C5BA` on `#FBFAF7`,
   dragging onto `#F2F8FB` — **appear nowhere in `reference/design-system/tokens/`**: they are
   one-off literals in the spec's prose, not part of the design's own token layer, and the
   nearest tokens we carry are near-misses rather than matches (`--fc-line-strong` is `#D9D6CE`,
   `--fc-well` is `#F7F6F2`, `--fc-info-tint` is `#E9F3F8`).

   ⚠ **A near-miss is the failure this stylesheet has already paid for** — the segmented
   control was 600 where the design set 500 and `#F7F6F2` where it set `#F1EFEA`, and *"a
   resting option read as half-selected"*. So the design's values are kept, **as three
   screen-scoped custom properties rather than as literals sprinkled through the rules**, which
   is the one-copy property the token layer exists for (declared on `.po-page` since 13 Sep, so
   the adjustment card's arrow reads the same `--po-drop-line`). The proper fix is upstream:
   these want names in `tokens/colors.css`. Flagged through `FROM-THE-BUILD.md`. */
.po-upload { margin: 0; }
.po-upload__scope { font-size: var(--text-fine); color: var(--fc-mute); margin: 0 0 12px; }

.po-drop {
  border: 2px dashed var(--po-drop-line); border-radius: 12px; padding: 26px 18px;
  text-align: center; cursor: pointer; background: var(--po-drop-bg);
  transition: background .15s, border-color .15s;
}
.po-drop:focus-visible { outline: 2px solid var(--fc-info); outline-offset: 2px; }
.po-drop--over { border-color: var(--fc-info); background: var(--po-drop-bg-over); }
.po-drop__lead { font-family: var(--font-body); font-weight: 700; font-size: 14.5px;
                 color: var(--fc-marine); }
/* ⚠ **Filled from `import_readable`, never written in the markup.** */
.po-drop__note { font-family: var(--font-mono); font-size: var(--text-fine);
                 color: var(--fc-mute); margin-top: 5px; }

/* The refusals, each with the parser's own reason. Quiet — a refused format is a fact about
   the product, not a problem with what the estimator just did. */
.po-refused { list-style: none; margin: 10px 0 0; padding: 0;
              font-size: var(--text-fine); color: var(--fc-mute); line-height: 1.5; }
.po-refused li { margin-top: 4px; }
.po-refused strong { font-family: var(--font-mono); color: var(--fc-steel); }

.po-reading { border: 1px solid var(--fc-line); border-radius: 12px; padding: 26px 18px;
              text-align: center; }
.po-reading[hidden] { display: none; }
.po-reading__name { font-family: var(--font-mono); font-size: 12.5px; color: var(--fc-steel); }
/* ⚠ **Indeterminate, not the mock's frozen 62%** — `Layout - Responsive - Print.md` says a bar
   held at a number nothing produces is worse than no bar. This one says "working", which is the
   only thing the browser actually knows during a parse it cannot observe. */
.po-bar { height: 4px; border-radius: 2px; background: var(--fc-line); margin: 12px auto 0;
          max-width: 320px; overflow: hidden; }
.po-bar i { display: block; height: 100%; width: 38%; border-radius: 2px;
            background: var(--fc-info); animation: po-bar-run 1.1s ease-in-out infinite; }
@keyframes po-bar-run { 0% { margin-left: -38%; } 100% { margin-left: 100%; } }
/* Respect a reader who has asked for no motion — the bar becomes a plain filled track. */
@media (prefers-reduced-motion: reduce) {
  .po-bar i { animation: none; width: 100%; margin-left: 0; }
}

.po-map__lead { font-size: var(--text-detail); color: var(--fc-steel); margin: 0 0 12px; }
.po-map__supplier { max-width: 340px; margin-bottom: 12px; }
.po-replacing { margin-bottom: 12px; }
.po-replacing[hidden] { display: none; }
.po-map { margin-top: 4px; }
.po-map td { vertical-align: middle; }
.po-map__pick { font-family: var(--font-mono); font-size: 12px; }
.po-map__sample { color: var(--fc-mute); font-size: var(--text-fine); }
/* Required, said on the row rather than by an asterisk with a legend somewhere else. */
.po-map__req { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-amber); }
.po-map__actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.po-done { margin-top: 4px; align-items: center; }
.po-done[hidden] { display: none; }

/* ── Screen 11 · Purchase orders ────────────────────────────────────────────────
   One card per order, because an order is a document rather than a row: it has a supplier, a
   date, a set of lines and a total, and squeezing that into a table row hides the lines, which
   are the part a supplier argues about. */
.pu-order { margin-bottom: 16px; }
.pu-head { display: flex; align-items: flex-start; justify-content: space-between;
           gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
.pu-amount { font-family: var(--font-mono); font-size: 17px; font-weight: 700;
             color: var(--fc-ink); }
/* A total that is not yet a total. **Never styled like the real one** — a provisional figure
   that looks final is the failure the unpriced-line rule exists to prevent. */
.pu-provisional { font-size: var(--text-meta); font-weight: 400; color: var(--fc-mute); }
.pu-notes { font-size: var(--text-meta); color: var(--fc-steel); margin: 0 0 12px; }
.pu-lines td { vertical-align: top; }
.pu-actions { margin-top: 12px; display: flex; gap: 8px; align-items: center; }
.pu-final { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); }

@media print {
  .pu-order { break-inside: avoid; border: none; }
}
.pu-paper { margin-top: 20px; }

/* ── Screen 11 · this job's suppliers — the mock's cards, read-only (Q-B, slot 2, 13 Sep) ──
   `PurchaseOrders.jsx` at its manifest pin: the h3 (`--h3`, `--tracking-h2`), the auto-fill
   200px grid, the white card on `--fc-line` with `--shadow-rest`, the dashed unresolved card on
   `--fc-paper` / `--fc-line-strong` (the same Ruling-1a shape as 03's `.mc-po-card--none`), the
   name at 14px/700/.02em, the count in mute, the value mono 13.5px/600 marine — every hex the
   mock writes inline is a token here. An empty value cell stays EMPTY (Q-A). */
.pu-h3 { font: var(--h3); letter-spacing: var(--tracking-h2); margin: 0 0 10px; }
.pu-suppliers { margin-bottom: 26px; }
.pu-sup { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.pu-sup:empty { display: none; }
.pu-sup__card {
  background: var(--surface-card); border: 1px solid var(--fc-line); border-radius: var(--radius-card);
  box-shadow: var(--shadow-rest); padding: 14px 15px; display: flex; flex-direction: column; gap: 10px;
}
.pu-sup__card--none {
  background: var(--fc-paper); border-style: dashed; border-color: var(--fc-line-strong);
  box-shadow: none; gap: 8px;
}
.pu-sup__name { font-family: var(--font-body); font-size: 14px; font-weight: 700; letter-spacing: .02em; }
.pu-sup__card--none .pu-sup__name { color: var(--fc-steel); }
.pu-sup__meta { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.pu-sup__n { font-size: 12px; color: var(--fc-mute); }
.pu-sup__value {
  font-family: var(--font-mono); font-size: 13.5px; font-weight: 600; color: var(--fc-marine);
  font-variant-numeric: tabular-nums;
}
.pu-sup__card--none .pu-sup__value { color: var(--fc-mute); }
.pu-sup__why { font-size: 12px; color: var(--fc-mute); line-height: 1.5; }
/* The live-order chip: the mock's unset button around a centred status chip. */
.pu-sup__chip { all: unset; cursor: pointer; display: block; }
.pu-sup__chip .fc-chip { display: flex; justify-content: center; width: 100%; box-sizing: border-box; }
.pu-sup__chip:focus-visible .fc-chip { outline: 2px solid var(--fc-info); outline-offset: 2px; }
.pu-sup__raise { align-self: flex-start; }
.pu-foot { font-size: 12px; color: var(--fc-mute); line-height: 1.6; margin: 12px 0 0; }
/* The rendered order, as text. Monospace because it IS text — the engine lays it out with
   spaces, and a proportional font would break the columns it aligned. */
.pu-text { font-family: var(--font-mono); font-size: 12px; line-height: 1.5;
           white-space: pre-wrap; margin: 0; color: var(--fc-ink); }

/* ── Screen 12 · Rules & overrides ────────────────────────────────────────────────
   A reference page. Everything on it comes off the wire, so the styling's only job is to make a
   rule and its reason read as one thing rather than two.
   Slot 2 of the FULL MOCK TRANSFER, 13 Sep 2026 — the mock's structure (`Rules.jsx` at its
   manifest pin) in the platform's tokens: every hex the mock writes inline is a token here
   (#173B53 --fc-marine · #5E6B75 --fc-steel · #8B8779 --fc-mute · #A9A499 --fc-faint · #E9E7E1
   --fc-line · #F1EFEA --fc-line-soft · #FAFAF8 --fc-paper · #F7F6F2 --fc-well · #FCF3E2
   --fc-amber-tint · #8A5A16 --fc-amber · #0F6A43 --fc-green · #B3401F --fc-red). Nothing here is
   a new visual language: the cards are `fc-card`, the heads `iq-kicker` / `iq-src`. The two
   flush cards keep `--pad-card-sm` and bleed their rows and the amber footer to the card edge
   (the `.mc-fab__banner` technique) under `overflow: hidden`, rather than a padding-0 card.
   Contract: `docs/ui-contracts/2026-09-13-rules-and-overrides-to-the-mock.md`. */
.ru-block { margin-bottom: 18px; }
/* The mock's `1fr 1fr` top grid, `gap 16`, `align-items: start`, `margin-bottom 20` — `auto-fit`
   so it stacks to one column when the viewport cannot hold two (the mock is a 1440 prototype). */
.ru-top { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
          gap: 16px; align-items: start; margin-bottom: 20px; }
.ru-card { overflow: hidden; }
.ru-h3 { font-family: var(--font-display); font-size: 14.5px; font-weight: 700;
         letter-spacing: var(--tracking-h2); color: var(--fc-ink); margin: 0 0 10px;
         display: flex; align-items: baseline; gap: 8px; }
/* The Bracing card's five rows: `26px 1fr` grid with the applies-to line under the name, row 0
   shaded and 700, the others 500; dividers between rows, none above the first; the rows bleed to
   the card edge. */
.ru-states { margin-inline: calc(var(--pad-card-x) * -1); }
.ru-state { display: grid; grid-template-columns: 26px 1fr; column-gap: 8px;
            padding: 9px var(--pad-card-x); font-size: 13px; font-weight: 500;
            border-top: 1px solid var(--fc-line-soft); }
.ru-state:first-child { border-top: 0; }
.ru-state--default { background: var(--fc-paper); font-weight: 700; }
.ru-state__code { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute);
                  padding-top: 2px; }
.ru-state__name { color: var(--fc-ink); }
.ru-state__applies { grid-column: 2; font-family: var(--font-mono); font-size: 11px;
                     font-weight: 400; color: var(--fc-mute); margin-top: 3px; }
/* The amber footer — the notice amber, bled to the card's edges and bottom. */
.ru-states__note { margin: 0 calc(var(--pad-card-x) * -1) calc(var(--pad-card-y) * -1);
                   padding: 10px var(--pad-card-x); background: var(--fc-amber-tint);
                   color: var(--fc-amber); font-size: 12px; line-height: 1.5; }
/* The Override governance card's numbered rows: `22px 1fr`, the mock's mono badge (white on
   marine, 20px square, radius 6), a 700 13px name and a 12px steel statement. */
.ru-gov { margin-inline: calc(var(--pad-card-x) * -1); }
.ru-gov__row { display: grid; grid-template-columns: 22px 1fr; column-gap: 10px;
               padding: 10px var(--pad-card-x); border-top: 1px solid var(--fc-line-soft); }
.ru-gov__row:first-child { border-top: 0; }
.ru-gov__badge { display: inline-flex; align-items: center; justify-content: center;
                 width: 20px; height: 20px; border-radius: 6px; background: var(--fc-marine);
                 color: var(--fc-white); font-family: var(--font-mono); font-size: 10.5px; }
.ru-gov__name { font-size: 13px; font-weight: 700; color: var(--fc-ink); }
.ru-gov__statement { font-size: 12px; color: var(--fc-steel); line-height: 1.5; margin-top: 2px; }
.ru-gov__source { font-family: var(--font-mono); font-size: 11px; color: var(--fc-faint); margin-top: 3px; }
/* The override log: the mock's DataTable inside a card, `margin-bottom 20`, its own scroll box. */
.ru-log { margin-bottom: 20px; padding: 0; overflow: hidden; }
.ru-log__scroll { overflow-x: auto; }
.ru-log__table th:nth-child(2) { width: 80px; }
.ru-log__table th:nth-child(3), .ru-log__table th:nth-child(4), .ru-log__table th:nth-child(5) { width: 140px; }
.ru-log__table th:nth-child(6) { width: 110px; }
.ru-log__table td { vertical-align: top; }
.ru-log__override { color: var(--fc-amber); font-size: 12.5px; }
.ru-log__final { font-weight: 600; }
.ru-log__block, .ru-log__flag { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); }
.ru-log__flag { color: var(--fc-amber); }
.ru-log__who { color: var(--fc-faint); }
/* The struck value — the ENGINE names which one lost; the browser only draws it. */
.ru-log__table td.is-struck { text-decoration: line-through; color: var(--fc-faint); font-weight: 400; }
.ru-log__note { margin: 0; padding: 12px var(--pad-card-x); font-family: var(--font-mono);
                font-size: 12px; color: var(--fc-mute); }
#ru_log_nojob { margin-bottom: 20px; }
/* The pricing chain as four layer cards: `auto-fit minmax(220px, 1fr)`, gap 12, `margin-bottom 20`. */
.ru-layers { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
             gap: 12px; margin-bottom: 20px; }
.ru-layer__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ru-layer__badge { display: inline-block; padding: 1px 7px; border-radius: 6px;
                   background: var(--fc-marine); color: var(--fc-white);
                   font-family: var(--font-mono); font-size: 10.5px; white-space: nowrap; }
.ru-layer__name { font-size: 13.5px; font-weight: 700; color: var(--fc-ink); }
.ru-layer__what { font-size: var(--text-meta); color: var(--fc-steel); margin: 6px 0 0; line-height: 1.5; }
.ru-facts { margin: 8px 0 0; padding-left: 18px; font-size: var(--text-meta);
            color: var(--fc-mute); line-height: 1.6; }
/* Pricing source integrity as the mock's tile grid: `auto-fit minmax(180px, 1fr)`, gap 10, tiles
   on the well, radius 10, padding 10px 12px; the status line green when live and red when never. */
.ru-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
.ru-tile { background: var(--fc-well); border-radius: 10px; padding: 10px 12px; }
.ru-tile__name { font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--fc-ink); }
.ru-tile__what { font-size: 12px; color: var(--fc-steel); margin-top: 3px; line-height: 1.45; }
.ru-tile__status { font-family: var(--font-mono); font-size: 11px; margin-top: 6px; }
.ru-tile--live .ru-tile__status { color: var(--fc-green); }
.ru-tile--never .ru-tile__status { color: var(--fc-red); }
/* Kept: the governing rules with their reasons, and the truth table. */
.ru-rule { padding: 12px 0; border-bottom: 1px solid var(--fc-line); }
.ru-rule:last-child { border-bottom: none; }
.ru-rule__head { display: flex; align-items: baseline; gap: 10px; }
.ru-rule__statement { font-size: var(--text-meta); color: var(--fc-steel); margin: 4px 0 0; }
/* The reason, beside the rule and not behind a disclosure. **A rule without its reason is the
   one somebody edits out.** */
.ru-rule__because { font-size: var(--text-meta); color: var(--fc-mute); margin: 4px 0 0; }
.ru-table td { vertical-align: top; }
.ru-inert { font-size: var(--text-fine); color: var(--fc-mute); }

/* ── Screen 13 · Gates ──────────────────────────────────────────────────────────
   ⚠ **Three cell states, and they must never look alike.** A price; a gap somebody should
   close; and a product that is not made. The third is hatched and holds no control at all —
   an empty box would invite somebody to find a price that cannot exist.

   Slot 2 of the FULL MOCK TRANSFER, 13 Sep — the mock's structure (`Gates.jsx` at its manifest
   pin, read beside the rendered bundle for its hexes) in the platform's tokens. Every hex the
   mock writes inline is a token here and was checked one by one: #1C242B --fc-ink · #173B53
   --fc-marine · #5E6B75 --fc-steel · #8B8779 --fc-mute · #A9A499 --fc-faint · #E9E7E1 --fc-line
   · #F1EFEA --fc-line-soft · #D9D6CE --fc-line-strong · #1E6E96 --fc-info · #E9F3F8
   --fc-info-tint · #FCF4F1 / #DC9F8C the attention field state. Nothing here is a new visual
   language: the band is `fc-notice--info`, the pills are `fc-filter` (the design system's
   FilterChip, its first product use), the button is `fc-btn`, the cards are `fc-card`. */
/* The mock's groups: a bare flex row, 22px apart, each a caption over a 7px-gapped pill row.
   The caption is `FieldKeyLegend`'s key (11px / 700 / uppercase / 0.06em / --fc-faint), which
   is the mock's caption to the byte. */
.ga-axes { display: flex; gap: 22px; flex-wrap: wrap; margin-bottom: 16px; }
.ga-axis__label { font-family: var(--font-body); font-size: 11px; font-weight: 700;
                  text-transform: uppercase; letter-spacing: 0.06em; color: var(--fc-faint);
                  margin-bottom: 7px; }
.ga-axis__opts { display: flex; gap: 7px; flex-wrap: wrap; }
/* The mock's chip carries `white-space: nowrap` and `.fc-filter` does not — a served label such
   as `Round top (RT)` would otherwise wrap inside its pill at phone width. Screen-scoped rather
   than a change to the component. */
.ga-axis__opts .fc-filter { white-space: nowrap; }
/* The `ƒ` band sits 16px above what follows, as the mock's `marginBottom`. */
.ga-band { margin-bottom: 16px; }
/* The editor — one card, `hidden` until a cell is picked. `fc-card` sets no `display`, so the
   attribute works unaided; the flex row is a child so the card can still hide. */
.ga-editor { margin-bottom: 16px; }
.ga-editor__row { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.ga-editor__what { flex: 1; min-width: 220px; }
.ga-editor__title { font-family: var(--font-display); font-size: 15px; font-weight: 700;
                    color: var(--fc-ink); }
.ga-editor__sub { font-size: var(--text-meta); color: var(--fc-steel); margin-top: 2px; }
/* The mock's 150px input. `fc-field__control--numeric` already makes it mono, right-aligned
   and tabular; the attention state (`#FCF4F1` / `#DC9F8C`) is the field component's. */
.ga-editor .fc-field__control { width: 150px; }
.ga-editor .fc-field { margin: 0; }
/* The matrix card. ⚠ `padding: 0` alone CLIPS the head: `.fc-card__head` pulls itself out by
   `-16px -18px` to cancel the card's own padding, so with none to cancel it lands outside the
   card and `overflow: hidden` cuts it. The head's margin is zeroed here for that reason. The
   scroll box is a body wrapper, not the card, so the band and the scroll never fight — a phone
   width scrolls the table in its own box, never the page. */
.ga-matrix-card { padding: 0; overflow: hidden; }
.ga-matrix-card > .fc-card__head { margin: 0; display: flex; align-items: baseline;
                                   justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.ga-matrix__sub { font-family: var(--font-body); font-size: var(--text-meta); font-weight: 400;
                  letter-spacing: 0; color: var(--fc-mute); margin-left: 8px; }
.ga-matrix__scroll { overflow-x: auto; }
.ga-made { font-family: var(--font-mono); font-size: 11px; font-weight: 400; letter-spacing: 0;
           color: var(--fc-faint); }
/* The design system's DataTable rule keeps money right-aligned (the mock's grid is
   left-aligned — recorded in the contract as Design's to settle, not picked silently). */
.ga-matrix td, .ga-matrix th { text-align: right; }
.ga-matrix tbody tr:last-child td { border-bottom: none; }
/* A cell is a button: the mock's `cursor: pointer`, the mock's `12px 14px`, a soft rule on its
   left. Money over the code, both mono. */
.ga-cell { padding: 12px 14px; cursor: pointer; border-left: 1px solid var(--fc-line-soft);
           vertical-align: top; }
.ga-cell:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.ga-cell__price { font-family: var(--font-mono); font-size: 13px; font-weight: 600;
                  font-variant-numeric: tabular-nums; }
.ga-cell__edited { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); }
.ga-cell__code { font-family: var(--font-mono); font-size: 10px; color: var(--fc-faint);
                 margin-top: 2px; }
/* The picked cell — the mock's `#E9F3F8`, which is --fc-info-tint. */
.ga-cell--picked { background: var(--fc-info-tint); }
/* A gap: amber, the same as an unpriced register line. **Not red** — a gap during onboarding is
   ordinary, and an error colour would make a new catalogue look broken. */
.ga-cell--gap { background: var(--fc-amber-tint); }
/* Not made. Hatched, inert, and it carries no control at all. The text is the mock's `not made`
   in its own `#C9C5BA` — ⚠ **a colour no token names** (the nearest, `--fc-line-strong`, is
   `#D9D6CE`), kept as one screen-scoped property with its reason rather than a literal in the
   rule: the `--po-drop-line` precedent above. Reaching past a token for a literal is how the
   segmented control once read as half-selected; naming the one copy is what keeps it one. */
.ga-cell--notmade { --ga-notmade-ink: #C9C5BA; cursor: default;
  background: repeating-linear-gradient(45deg, var(--fc-paper) 0 6px, var(--fc-line) 6px 7px); }
.ga-cell__notmade { font-family: var(--font-mono); font-size: 11px; color: var(--ga-notmade-ink); }
.ga-legend { margin-top: 12px; font-size: var(--text-meta); color: var(--fc-mute); }
.ga-key { display: inline-block; width: 14px; height: 10px; vertical-align: middle;
          border: 1px solid var(--fc-line); margin-right: 4px; }
.ga-key--priced { background: var(--fc-white); }
.ga-key--gap { background: var(--fc-amber-tint); }
.ga-key--notmade { background: repeating-linear-gradient(45deg,
  var(--fc-paper) 0 4px, var(--fc-line) 4px 5px); }
/* The mock's footer sentence: 12px --fc-mute, 10px under the card. */
.ga-note { margin-top: 10px; font-size: var(--text-meta); color: var(--fc-mute); }
.ga-gaps { margin-top: 20px; }
.ga-gaptable td { vertical-align: top; }

/* ── Screen 14 · Security ───────────────────────────────────────────────────────
   Plain by intent. A security page that looks like a dashboard invites skimming, and every
   control here changes who can get in. */
.se-block { margin-bottom: 16px; }
.se-note { font-size: var(--text-meta); color: var(--fc-mute); margin: 8px 0 0; line-height: 1.6; }
.se-state { margin: 8px 0 0; }
/* ── slot 2 of the FULL MOCK TRANSFER, 13 Sep — the mock's switch (`SecuritySettings.jsx` at
   its manifest pin) on the SAME `<input type="checkbox">`: `appearance: none`, a 52×28 pill,
   radius 999, a 22px knob at left 3 → 27, `transition .15s`, on `--fc-green` (the mock's
   `#0F6A43`). ⚠ The OFF track is the mock's `#C9C5BA`, which no token names — kept as a
   screen-scoped custom property exactly as `.po-upload` keeps `--po-drop-line` (the same value),
   because a near-miss is the failure this stylesheet has already paid for. Flagged upstream for a
   name in tokens/colors.css, with `.po-upload`'s. The row is the mock's flex row: the label sans
   14px 700, the read caption mono 11px mute as its next sibling. */
.se-toggle { --se-track-off: #C9C5BA; display: flex; align-items: center; gap: 14px;
             flex-wrap: wrap; margin-top: 12px; font-size: 14px; font-weight: 700;
             color: var(--fc-ink); }
.se-toggle__control { display: flex; align-items: center; gap: 14px; cursor: pointer; }
.se-toggle__control[hidden], .se-toggle__state[hidden], .se-toggle__logged[hidden] { display: none; }
.se-toggle input { appearance: none; -webkit-appearance: none; margin: 0; flex: none;
                   width: 52px; height: 28px; border: none; border-radius: 999px;
                   background: var(--se-track-off); position: relative; cursor: pointer;
                   transition: background .15s; }
.se-toggle input::after { content: ""; position: absolute; top: 3px; left: 3px;
                          width: 22px; height: 22px; border-radius: 50%;
                          background: var(--fc-white); transition: left .15s; }
.se-toggle input:checked { background: var(--fc-green); }
.se-toggle input:checked::after { left: 27px; }
.se-toggle input:focus-visible { outline: 2px solid var(--fc-info); outline-offset: 2px; }
.se-toggle__state { font-size: 14px; font-weight: 700; color: var(--fc-ink); }
.se-toggle__logged { font-family: var(--font-mono); font-size: 11px; font-weight: 400;
                     color: var(--fc-mute); }
/* Recovery codes at or below the engine's low-water mark. */
.se-low { color: var(--fc-amber); }
.se-never { margin: 8px 0 0; padding-left: 20px; font-size: var(--text-meta);
            color: var(--fc-mute); line-height: 1.7; }
.jt-edit { width: 100%; min-width: 90px; background: var(--field-input-bg);
           border-color: var(--field-input-border); font-size: var(--text-meta); }
.jt-sub .jt-edit { font-size: var(--text-fine); margin-top: 3px; }
.jt-refs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; align-items: center; }
.jt-ref { font-size: var(--text-fine); }
.jt-refx { border: none; background: none; cursor: pointer; padding: 0 0 0 4px;
           color: var(--fc-mute); font-size: var(--text-fine); }
/* The pixel plan — a photo or a bare grid. Its aspect ratio is set from the wire, because the
   design space is the engine's `CANVAS_W/H` and a literal here would be a fourth copy. */
/* ⚠ **No CSS grid here, and that is the point.** This carried a 40px background grid until
   19 Aug — 40 *device* pixels, on an element whose width follows the viewport, over a design
   space of 1180 x 700. So one square was 1.82 m on a laptop and 5.26 m on a phone, **same plan,
   same calibration**, changing when the phone was rotated.

   **A bare grid on a measuring screen reads as a measuring aid.** An estimator counting squares
   to sanity-check a length gets a confident wrong answer with nothing on screen contradicting
   them — the same family as a zero standing in for "no scale". The grid is drawn in the SVG at
   design-space coordinates now, in whole metres, and **only once a scale exists**. */
/* ⚠ **`--fc-paper-2` is a token nothing defines**, and the `var()` fallback is what hid it.
   The ground is a cool grey in a warm palette on purpose — it sits behind a satellite photo
   rather than beside body text — so it is written here, where the reason for it is, instead
   of being invented as a palette token the design never had. */
.sm-canvas { position: relative; width: 100%; background: #F1F4F5;
             border-radius: 6px; overflow: hidden; cursor: crosshair; }
.sm-canvas__photo { position: absolute; inset: 0; width: 100%; height: 100%;
                    object-fit: contain; }
.sm-canvas__svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.sm-plannote { margin: 6px 2px 0; font-size: var(--text-fine); color: var(--fc-mute); }
/* ⚠ **`[hidden]` needs its own rule wherever a class sets `display`.** The class beats the UA
   stylesheet, so the attribute does nothing and the element is silently always visible — the
   `.savebar[hidden]` lesson, and a sweep across every v2 screen enforces it. */
.sm-pixelbar { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; flex-wrap: wrap; }
.sm-pixelbar[hidden] { display: none; }
.sm-pixelbar__gap { flex: 1 1 auto; }
.sm-scalecard { margin-top: 10px; }
/* An excluded run stays drawn and reads as out of the quote — it is usually traced because it
   exists, not because it is being charged for. */
.sm-run--out { opacity: 0.55; }
.sm-run--out .sm-run__m { text-decoration: line-through; }
.sm-run__inc { font-size: var(--text-fine); color: var(--fc-mute); display: flex;
               align-items: center; gap: 4px; }

/* ── Costing summary (screen 03, inside the management-only block) ───────────────────────
 *
 * ⚠ **The markup is `ui/costing_summary.js`'s, reused verbatim** — the same module Classic
 * renders. It is formatting only; every figure arrives computed, and its own header gives the
 * reason: *"a third derivation is a third thing that can drift."* A second renderer here would
 * have been exactly that, so what is ported is the palette and nothing else.
 *
 * Classic's stylesheet is not loaded on a v2 page, so these are the same rules against v2's
 * tokens. **No `--alarm`/`--rust`/`--navy` survives** — those are Classic's names and would have
 * resolved to nothing here, which renders as unstyled rather than as an error.
 */
.sum-band {
  background: var(--fc-red-tint);
  border: 1px solid var(--fc-red);
  border-radius: 2px 2px 0 0;
  color: var(--fc-red);
  font: 700 11px/1 var(--font-mono);
  letter-spacing: 0.05em;
  padding: 10px 12px;
}

.sum-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  padding: 14px;
  background: var(--fc-white);
  border: 1px solid var(--fc-line);
  border-top: 0;
  border-radius: 0 0 2px 2px;
}

.sum-col h3 {
  font: 800 15px/1 var(--font-display);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--fc-marine-deep);
  margin: 0;
}
.sum-col h3 i { font-style: normal; color: var(--fc-amber); margin-right: 4px; }
.sum-sub { color: var(--fc-steel); font-size: 11px; margin: 4px 0 8px; }

.sum-body {
  border: 1px solid var(--fc-line);
  border-radius: 2px;
  padding: 4px 0;
}
.sum-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: baseline;
  padding: 5px 10px;
}
.sum-label { font-size: 12.5px; color: var(--fc-ink); }
.sum-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--fc-marine-deep);
  text-align: right;
}
.sum-detail {
  grid-column: 1 / -1;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fc-mute);
}
/* The one colour that survives: priced-lines status. Green when every line has a price, red
   when one does not — unmissable because nothing else on the block is coloured. */
.sum-value.is-ok, .sum-detail.is-ok { color: var(--fc-green); }
.sum-value.is-bad, .sum-detail.is-bad { color: var(--fc-red); }

.sum-bar {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  background: var(--fc-marine-deep);
  color: var(--fc-white);
  border-radius: 2px;
  margin-top: 8px;
  padding: 9px 10px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  font-weight: 600;
}


/* The ex-GST statement on the price-import mapping step. Not a warning colour: it is a
   condition of the input, true on every import, and a permanent red banner is one people stop
   reading. */
.po-map__gst {
  display: block;
  margin-top: 6px;
  font-size: var(--text-detail);
  color: var(--fc-steel);
}

/* ⚠ **The no-GST statement, and it is deliberately quiet.** It explains why Subtotal and Total
   are the same figure on an unregistered subscriber's document. Not a warning colour and not
   emphasised: it is an ordinary fact about the quote, and a client should read it once and be
   satisfied rather than alarmed. Right-aligned under the totals it explains. */
.cq-nogst {
  margin: 10px 0 0;
  text-align: right;
  font-size: var(--text-detail);
  font-style: italic;
  color: var(--fc-steel);
}

/* ── Multi-type fence-type blocks — D1a, ruled 25 Aug; mock pin A4 ──────────────────────
   One block per fence type, repeated inline. **Every value here is the mock's**, not a
   choice made in code: 3px marine top accent, 16px separation (which is `--gap-card`, the
   screen's standard card gap — "nothing special", per design), and #D8D4C8 for the disabled
   remove control.

   ⚠ `.iq-block` and `#iq_blocks` both re-declare the column flow because they sit between
   `.iq-col` and the cards, and `.iq-col`'s `gap` does not reach grandchildren. Same gap at
   both levels, so a single-block screen is spaced exactly as it was before blocks existed. */
#iq_blocks { display: flex; flex-direction: column; gap: var(--gap-card); }
.iq-block { display: flex; flex-direction: column; gap: var(--gap-card);
            border-top: 3px solid var(--fc-marine); padding-top: var(--gap-card); }
.iq-block__head { display: flex; align-items: baseline; gap: 10px; }
.iq-block__n { font-weight: 800; font-size: 12.5px; letter-spacing: .04em;
               text-transform: uppercase; color: var(--fc-marine); }
.iq-block__name { flex: 1; font-size: 12.5px; color: var(--fc-mute); }

/* ⚠ **Disabled, never hidden, on the last remaining block** — the gate-row pattern.
   "Hidden would read as a defect; disabled reads as a rule" (design, 25 Aug). The title
   attribute carries the reason, so the greying is never unexplained. */
.iq-block__del { all: unset; cursor: pointer; font-size: 12px; font-weight: 600;
                 color: var(--fc-red); padding: 2px 4px; border-radius: 4px; }
.iq-block__del:hover:not([disabled]) { background: var(--fc-well); }
.iq-block__del[disabled] { color: #D8D4C8; cursor: default; }
/* ⚠⚠ **`all: unset` SETS `display`, and that is why this rule exists.** `all` is the shorthand
   for every property; `unset` on the non-inherited `display` resolves to `initial`, which is
   `inline` — so the class beats the UA stylesheet's `[hidden] { display: none }` and the button
   would be **visible and clickable while carrying `hidden`**. The `.savebar[hidden]` defect for
   the third time, arriving without the word `display` anywhere in the rule.
   ⚠ `test_no_element_toggled_by_hidden_has_its_display_overridden` matched `display\s*:` and
   could not see this shape; it reads `all:` too now. */
.iq-block__del[hidden] { display: none; }

.iq-block-add { all: unset; display: block; width: 100%; box-sizing: border-box;
                cursor: pointer; text-align: center; padding: 12px;
                border: 1px dashed var(--fc-line-strong); border-radius: var(--radius-card);
                font-size: 12.5px; font-weight: 600; color: var(--fc-marine); }
.iq-block-add:hover { background: var(--fc-well); }
/* ⚠⚠ **Load-bearing, and the guard caught its absence.** `display: block` above beats the UA
   stylesheet's `[hidden] { display: none }`, so the add control would have been visible while
   carrying `hidden` — the `.savebar[hidden]` defect exactly, swept for by
   `test_no_element_toggled_by_hidden_has_its_display_overridden`. */
.iq-block-add[hidden] { display: none; }

/* ── Screen 03 · the no-quote scaffold (Q-A, Owner 13 Sep; slot 2) ──────────────────
   Every rule is scoped to `main.is-scaffold`, which `renderScaffold()` sets and nothing else
   does — the populated sheet's tints, the promise that a cell can be typed into, are untouched.
   `not yet quoted` reads muted, never as a figure. */
.is-scaffold .iq-src.is-unquoted, .is-scaffold .mc-addrow__status.is-unquoted,
.is-scaffold .mc-fab__from.is-unquoted, .is-scaffold .mc-br__v.is-unquoted {
  color: var(--fc-mute); font-weight: 500; font-style: italic; }
.is-scaffold .mc-sheet input:disabled { background: transparent; color: var(--fc-faint);
  cursor: default; }
.is-scaffold .fc-seg__opt:disabled { color: var(--fc-faint); cursor: default; }
.is-scaffold .mc-jh__v { color: var(--fc-faint); }

/* ── Screen 15 · Job folders ─────────────────────────────────────────────────
   The design's two-column master/detail: a 280px folder rail that sticks, and the open
   folder beside it. Nothing here is a new visual language — the chips are `fc-seg`, the
   folder body is `fc-card`, the status pills are `fc-chip`, and the tones are the ones
   `components.css` already defines. */

/* Slot 1 of the FULL MOCK TRANSFER, 13 Sep — the mock's row: search · `n of m` · the chips. */
.jf-toolbar { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 0 0 14px; }
.jf-search { min-width: 320px; margin: 0; }
.jf-count { font-family: var(--font-mono, monospace); font-size: 11px; color: var(--fc-mute); }
.jf-toolbar .jf-chips { margin: 0; }
.jf-chips { margin: 0 0 14px; flex-wrap: wrap; }
/* The count the design draws inside each chip. Mono so the numerals line up as they change. */
.jf-chip__n { font-family: var(--font-mono, monospace); font-size: 11px; font-weight: 600;
              opacity: .7; margin-left: 5px; }

.jf-split { display: grid; grid-template-columns: 280px 1fr; gap: 16px; align-items: start; }
/* One column under the rail's own breakpoint — the folder list above the open folder, which is
   the reading order on a narrow screen rather than a rail squeezed to nothing. */
@media (max-width: 900px) { .jf-split { grid-template-columns: 1fr; } }

.jf-list { display: flex; flex-direction: column; gap: 8px; position: sticky; top: 78px; }
@media (max-width: 900px) { .jf-list { position: static; } }

.jf-folder { all: unset; display: block; box-sizing: border-box; width: 100%; cursor: pointer;
             background: var(--fc-white); border: 1px solid var(--fc-line);
             border-radius: var(--radius-card); padding: 12px 14px; }
.jf-folder:hover { background: var(--fc-well); }
/* The design's selected folder: a marine hairline plus a ring, not a fill. */
.jf-folder.is-open { border-color: var(--fc-marine);
                     box-shadow: 0 0 0 1px var(--fc-marine); background: var(--fc-white); }
/* ⚠ **Dimmed, and it is the ONLY thing that marks an archived folder in the list.** The design
   fades its Archived sample to .72. It is not a status and must not read as one — the status
   chip beside it still says what the job is, which is B10's whole point. */
.jf-folder[data-archived] { opacity: .72; }
.jf-folder[hidden] { display: none; }

.jf-folder__top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.jf-folder__no { font-family: var(--font-mono, monospace); font-size: 13px; font-weight: 600;
                 white-space: nowrap; }
.jf-folder__client { display: block; font-size: 12.5px; font-weight: 600; margin-top: 3px; }
.jf-folder__meta { display: block; font-size: 11.5px; color: var(--fc-mute); }

.jf-moves { margin-top: 6px; }
.jf-empty { font-size: 12px; color: var(--fc-mute); font-style: italic; margin: 0; padding: 12px 0; }

/* State C · the empty book. ⚠ The mock writes the footnote colour as the literal `#8B8779`;
   `--fc-mute` already names that exact value, so the token is used — `test_design_tokens` refuses a
   raw literal a token has a name for, and it is right to: two spellings of one colour is how a
   palette drifts. The 12.5px is the mock's own and no token names it. */
.jf-book-empty { margin-bottom: 14px; }
.jf-book-empty__foot { font-size: 12.5px; color: var(--fc-mute); margin: 8px 0 0; }
/* State B's inline control, from the mock: `all: unset` plus an underline drawn by the border, so
   it reads as text the estimator can press rather than as a second button in the banner. */
.jf-clear-filter { all: unset; cursor: pointer; font-weight: 700; border-bottom: 1px solid currentColor; }

.jf-detail { display: flex; flex-direction: column; gap: 16px; }
.jf-card__head { display: flex; justify-content: space-between; align-items: flex-start;
                 gap: 12px; flex-wrap: wrap; }
.jf-card__sub { margin: 3px 0 0; font-size: 12.5px; color: var(--fc-mute); }
.jf-card__actions { display: flex; align-items: center; gap: 9px; }

/* The archived note. Neutral, not an alert — a folder out of the working view is a filing
   state, and colouring it like a problem would read as one. */
.jf-archived { margin: 12px 0 0; padding: 9px 12px; border-radius: 10px;
               background: var(--fc-well); font-size: 12px; color: var(--fc-mute); }

.jf-contact { margin-top: 14px; background: var(--fc-well); border-radius: 10px; padding: 10px 12px; }
.jf-contact__name { font-size: 13px; font-weight: 700; }
.jf-contact__ways { font-family: var(--font-mono, monospace); font-size: 11px;
                    color: var(--fc-marine); margin-top: 2px; }

.jf-revs__list { list-style: none; margin: 10px 0 0; padding: 0; }
.jf-revs__row { display: grid; grid-template-columns: 56px 92px 1fr; gap: 10px;
                padding: 9px 0; border-top: 1px solid var(--fc-line-soft); align-items: baseline; }
.jf-revs__row:first-child { border-top: none; }
.jf-revs__n { font-family: var(--font-mono, monospace); font-size: 12px; font-weight: 600;
              color: var(--fc-marine); }
.jf-revs__at { font-family: var(--font-mono, monospace); font-size: 11px; color: var(--fc-mute); }
.jf-revs__state { font-size: 12px; color: var(--fc-mute); }
/* **N-2 · DA r18 §20.25.2.** The current revision reads as the row's tail, not as a badge — the
   mock's own treatment. Only the weight and ink change; `historical` inherits the muted tail. */
.jf-revs__cur { font-weight: 600; color: var(--fc-ink); }
.jf-revs__broken { margin: 8px 0 0; font-size: 12px; color: var(--fc-amber); }
/* The delivery line sits UNDER its revision and is visibly subordinate to it — one grid cell
   spanning the row, indented to the label column, so a revision with two sends reads as one
   revision rather than as three rows of equal weight. */
.jf-revs__row--sent { display: block; border-top: none; padding: 0 0 6px 12px; }
.jf-revs__sent { font-size: 11.5px; color: var(--fc-mute); }
.jf-revs__sent::before { content: "└ "; opacity: .55; }

/* ⚠ **The blocked line is part of the screen, not a placeholder to delete later.** Naming what
   a folder cannot hold yet is the honest state; the alternative is a reader assuming the folder
   is complete because nothing said otherwise. */
.jf-blocked { margin: 0; font-size: 12px; color: var(--fc-mute); }

/* The header slot where the design's `+ New folder from RFQ` goes. **Deliberately not a
   control** — see the markup's note: a button that does nothing is refused by the dead-control
   sweep, and rightly. */
.jf-pending-action { margin: 0; font-size: 12px; color: var(--fc-mute); text-align: right;
                     max-width: 320px; }
/* ⚠⚠ **`.fc-pending` is `white-space: nowrap` and this sentence is not a label.** The component
   is built for short marks like "not built yet"; a sentence inside it laid out on one line and
   pushed the document to **1711px in a 1440px viewport**, so the whole page scrolled sideways.
   Found by measuring the built screen, not by any test — every source and browser test passed
   over it, because none of them reads layout.

   **Overridden here rather than in the component**, deliberately: the nowrap is correct
   everywhere else it is used, and widening it would fix this screen by loosening fourteen
   others. The narrower rule is the honest one. */
.jf-pending-action .fc-pending { white-space: normal; }

/* The typed RFQ intake card — B1 ruled (a), `DA r23 §20.30`. It reuses `jt-log__grid`, the
   `Log an enquiry` card's own field grid, so the two intake surfaces are laid out by one rule
   rather than by two that agree today. */
/* ⚠⚠ **`.fc-btn` is `display: inline-flex`, which BEATS the UA stylesheet's `[hidden]`.** So
   the Installer gate needs this rule or the button stays on screen for the one role BP-3 §7 says
   must not see it — with every source and browser test passing, because only a rendered page
   shows it. Fourth instance of that trap in this stylesheet. */
#jf_new[hidden] { display: none !important; }
.jf-rfq { margin-bottom: 14px; }
/* ⚠ **`[hidden]` needs `display: none !important` here.** A `display` on the element beats the
   UA stylesheet's `[hidden]`, which is how a save bar once announced unsaved changes that did
   not exist. `.fc-card` carries one, so the card would draw open at rest without this line —
   and every source test would still pass, because only a rendered page shows it. */
.jf-rfq[hidden] { display: none !important; }
.jf-rfq__lead { margin: 0 0 12px; font-size: 12.5px; color: var(--fc-mute); max-width: 70ch; }
.jf-rfq__actions { display: flex; gap: 10px; align-items: center; margin-top: 14px;
                   flex-wrap: wrap; }
/* The named refusal, where the enquiry form renders its own — BP-3 §8. ⛔ Empty draws nothing:
   `:empty` rather than a `hidden` attribute, because the element's content is the signal. */
.jf-rfq__refusal { margin: 12px 0 0; font-size: 12.5px; color: var(--fc-red); }
.jf-rfq__refusal:empty { display: none; }

/* The three RFQ facts on the open folder's header block — read-only, and absent when empty. */
.jf-card__rfq { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 6px 0 0; }
.jf-card__rfq div { display: flex; gap: 6px; align-items: baseline; }
.jf-card__rfq dt { margin: 0; font-size: 11px; color: var(--fc-mute); }
.jf-card__rfq dd { margin: 0; font-size: 12.5px; color: var(--fc-ink); }

/* ── SFR A2 · the no-quote card — DA r13 §20.22.1 ─────────────────────────────────────────────
   ⚠ **Every value is a token, and each matches the mock's inline style exactly** —
   `ClientQuote.jsx:244-248`: `#fff` is `--surface-card`, `#E9E7E1` `--fc-line`, `12px`
   `--radius-card`, the shadow `--shadow-rest`, `#8B8779` `--fc-mute`, `#1C242B` `--fc-ink`,
   `#5E6B75` `--fc-steel`, `#A9A499` `--fc-faint`. ⛔ Reaching past a token for a literal is how
   five chrome rules ended up invisible on a light ground. */
.cq-empty {
  background: var(--surface-card);
  border: 1px solid var(--fc-line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-rest);
  padding: 40px 42px;
  text-align: center;
}
.cq-empty__kicker {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--fc-mute);
}
.cq-empty__title {
  font-family: var(--font-display);
  font-size: 19px; font-weight: 700; letter-spacing: -0.01em;
  color: var(--fc-ink);
  margin: 10px 0 6px;
}
.cq-empty__body {
  font-size: 13px; line-height: 1.6;
  color: var(--fc-steel);
  max-width: 470px; margin: 0 auto;
}
.cq-empty__foot {
  font-family: var(--font-mono);
  font-size: 11px; color: var(--fc-faint);
  margin-top: 16px;
}

/* ── 16 Clients · 17 Client onboarding — card 65 / BP-2 ───────────────────────
   ⚠ **Every value here is a token or a value already used on a v2 screen.** A new literal
   colour is a second palette, which is what `tokens-v2.css` exists to prevent. */
.cl-table { width: 100%; }
.cl-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.cl-open-btn { all: unset; cursor: pointer; font-weight: 600; color: var(--fc-marine); }
.cl-open-btn:hover { text-decoration: underline; }
/* ⚠ `display` is set explicitly on the row rather than through `all:` — a shorthand that
   resolves `display` is what beat `[hidden]` on the save bar, twice. */
tr.cl-open { background: var(--fc-mist); }
.cl-empty { color: var(--fc-mute); }
.cl-card { margin-top: 14px; }
.cl-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
             gap: 10px 18px; margin: 12px 0 0; }
.cl-fields dt { font-size: 11.5px; color: var(--fc-mute); }
.cl-fields dd { margin: 2px 0 0; font-size: 13px; color: var(--fc-ink); }
/* The edit form reuses the read view's own grid, so the fields do not move when the control
   opens — a layout that shifts under an estimator reads as a different screen. */
.cl-edit { align-items: start; }
.cl-edit__actions { display: flex; align-items: center; gap: 10px; margin-top: 12px;
                    flex-wrap: wrap; }
.cl-edit__actions .fc-pending { flex: 1 1 220px; }
.cl-credit-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.cl-credit--check { background: var(--fc-amber-tint); color: var(--fc-amber); }
.cl-credit--approved { background: var(--fc-green-tint); color: var(--fc-green); }
.cl-credit--declined { background: var(--fc-red-tint); color: var(--fc-red); }
.cl-jobs { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 0; }
/* ── slot 2 of the FULL MOCK TRANSFER, 13 Sep — 24 Clients: the rendered mock's tiles, search
   and register card, in the platform's tokens. Read from `Clients.jsx` at its manifest pin (the
   bundle segment agrees). Nothing here is a new visual language: tiles are `fc-card`, the figure
   is `fc-money` (the tracker's tile treatment), the search is `fc-field--input`. Every colour is
   a token whose value is the mock's own hex (`--fc-marine` #173B53 · `--fc-green` #0F6A43 ·
   `--fc-amber` #8A5A16 · `--fc-mute` #8B8779). */
/* Three tiles: `.jt-tiles`' `minmax(170px, 1fr)`, so they sit on one row at 1440 as the mock's
   `repeat(4, 1fr)` does, and stack at 400px without a breakpoint. */
.cl-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
            gap: 12px; margin: 0 0 14px; }
.cl-tile .fc-money { color: var(--fc-marine); }
.cl-tile--ok .fc-money { color: var(--fc-green); }
.cl-tile--warn .fc-money { color: var(--fc-amber); }
/* The mock's search: full width, mono 12.5px (`--text-meta`). */
.cl-search { margin: 0 0 14px; }
.cl-search .fc-field__control { font-family: var(--font-mono); font-size: var(--text-meta); }
/* The register card: the table sits flush inside the mock's white 12px-radius box, so the card's
   own padding is zeroed; `overflow: hidden` clips the head's corners. ⚠ `[hidden]` is declared
   first and is load-bearing — `.fc-card` sets no `display`, but the rule is written so a later
   one cannot beat the attribute (the `.savebar[hidden]` defect, three times on this project). */
.cl-register[hidden] { display: none; }
.cl-register { padding: 0; overflow: hidden; }
/* The mock's `Nothing matches “…”`: centred, mono 12px, muted, 20px of padding, inside the card. */
.cl-nomatch { margin: 0; padding: 20px; text-align: center; font-family: var(--font-mono);
              font-size: 12px; color: var(--fc-mute); }
/* ⛔ The two boundary rules and the client-detail note rule are GONE with the copy they styled —
   DA §20.29 removed it from screens 16 and 17. A rule for a class nothing emits is the orphan the
   20 Aug sweep spent a day on; deleting it in the same edit is what stops the next one.
   ⚠ The selectors are not named here: a test asserts they are absent from this file, and a
   comment quoting one would be a check satisfied by its own explanation. */
.co-route { border-left: 3px solid var(--fc-marine); }
.co-notes { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
            gap: 12px; margin-top: 12px; }
.co-terms { display: flex; align-items: flex-start; gap: 8px; margin-top: 14px;
            font-size: 13px; }
.co-actions { margin-top: 16px; }
/* ⛔ The duplicate gate is a WARNING WITH A GATE, not an error: amber, not red. Red would say
   something went wrong, and nothing has — a second client of the same name is legitimate and
   the estimator is being asked to confirm it. */
.co-dup { margin-top: 14px; padding: 12px 14px; border: 1px solid var(--fc-amber-line);
          background: var(--fc-amber-tint); border-radius: 6px; }
.co-dup p { margin: 0 0 10px; font-size: 13px; color: var(--fc-ink); }
.co-dup[hidden] { display: none !important; }
/* A linked job's client name is read, not typed — card 65. `iq_job_number`'s treatment. */
.jt-client { margin: 0; }

/* ────────────────────────────────────────────────────────────── Screen 18 · Company docs
   Slot 3 of the FULL MOCK TRANSFER, 13 Sep 2026 — `CompanyDocs.jsx` at its r30 pin. The mock's
   values: `maxWidth 1080px`, the identity grid `repeat(auto-fit, minmax(240px, 1fr))`, the paper
   cards `repeat(auto-fit, minmax(420px, 1fr))` with 16px gaps, a row `1fr auto auto` at 14px gap
   and `10px 18px` padding with a `#F1EFEA` top rule (`--fc-line-soft`), the soon-row wash
   `#FFFDF6` (`--field-override-bg` — the tokens already name it), the mono 11px sub-line in
   `#8B8779` (`--fc-mute`). Tokens throughout, no literal. */
.cd-page { max-width: 1080px; }
.cd-identity { margin-bottom: 16px; }
.cd-empty { font-size: var(--text-meta); color: var(--fc-steel); line-height: 1.5; margin: 0 0 12px; }
.cd-id-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; align-items: end; }
.cd-id-label { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
               color: var(--fc-mute); margin-bottom: 6px; }
.cd-id-value { font-family: var(--font-mono); font-size: 13px; padding: 10px 12px; border: 1px solid var(--fc-line-strong);
               border-radius: var(--radius-badge); background: var(--surface-card); min-height: 1.4em; overflow-wrap: anywhere; }
.cd-id-value--blank { color: var(--fc-mute); }
.cd-id-actions { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-top: 14px; }
.cd-id-buttons { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.cd-logo { display: flex; align-items: center; gap: 10px; }
.cd-logo__img { max-height: 48px; max-width: 160px; border: 1px solid var(--fc-line); border-radius: var(--radius-badge);
                background: var(--surface-card); padding: 4px; }
.cd-logo__placeholder { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); border: 1px dashed var(--fc-line-strong);
                        border-radius: var(--radius-badge); padding: 12px 16px; }
.cd-logo__meta { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); }
.cd-logo-note { font-size: var(--text-fine); color: var(--fc-amber); margin: 10px 0 0; }
.cd-chip { all: unset; display: inline-flex; align-items: center; font-family: var(--font-mono); font-size: 11px;
           padding: 4px 9px; border-radius: 999px; background: var(--fc-amber-tint); color: var(--fc-amber);
           border: 1px solid var(--fc-amber-line); cursor: default; }
.cd-notice { margin-bottom: 14px; }
.cd-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 16px; align-items: start; }
@media (max-width: 900px) { .cd-grid { grid-template-columns: 1fr; } }
.cd-card { padding-bottom: 0; }
.cd-card > .iq-kicker-sub { margin-bottom: 0; }
.cd-card > .cd-empty { margin: 12px 0 0; }
.cd-rows { margin-inline: calc(var(--pad-card-x) * -1); }
.cd-row { display: grid; grid-template-columns: 1fr auto auto; gap: 14px; padding: 10px 18px; align-items: center;
          border-top: 1px solid var(--fc-line-soft); }
.cd-row--soon { background: var(--field-override-bg); }
.cd-row--expired { background: var(--fc-red-tint); }
.cd-row__name { font-size: 13px; font-weight: 700; display: block; }
.cd-row__meta { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); display: block; overflow-wrap: anywhere; }
.cd-row__file { color: var(--fc-mute); text-decoration: underline; text-underline-offset: 2px; }
.cd-row__control:empty { display: none; }
.cd-tender { padding-bottom: var(--pad-card-y); }
.cd-tender__body { display: flex; flex-direction: column; gap: 9px; font-size: 12.5px; color: var(--fc-steel); margin-top: 12px; }
.cd-tender__ready { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.cd-tender__names { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); }
.cd-tender__actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.cd-tender__why { font-size: 12px; color: var(--fc-mute); line-height: 1.5; margin: 0; }
.cd-foot { font-size: 12px; color: var(--fc-mute); line-height: 1.6; margin-top: 16px; }
.cd-foot a { color: inherit; }
@media (max-width: 640px) { .cd-row { grid-template-columns: 1fr; gap: 6px; } }

/* ── 19 Assets — slot 4 of the FULL MOCK TRANSFER, 14 Sep 2026 ─────────────────────────────
   The mock's two folder cards (`Assets.jsx` @ its r30 pin) in the platform's tokens: a card head
   with title, sub and `+ Add unit`, a table whose past-due cells take the attention colours the
   mock uses (`#B3401F` is `--fc-red`; the row wash is the attention well). Nothing here is a new
   visual language. */
.as-band { margin-bottom: 14px; }
.as-alert { margin-bottom: 14px; }
.as-folders { display: flex; flex-direction: column; gap: 16px; }
.as-folder { padding: 0; overflow: hidden; }
.as-folder__head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
                   padding: 13px 18px; border-bottom: 1px solid var(--fc-line); }
.as-folder__title { font-size: var(--text-detail); font-weight: 700; color: var(--fc-ink); }
.as-folder__sub { font-size: var(--text-fine); color: var(--fc-mute); margin-top: 2px; }
.as-table { width: 100%; }
.as-table th:nth-child(1) { width: 20%; }
.as-table th:nth-child(2) { width: 110px; }
.as-table th:nth-child(4), .as-table th:nth-child(5) { width: 128px; }
.as-table th:last-child { width: 250px; }   /* the assignment select + Assign, since 16 Projects */
.as-unit { font-weight: 700; color: var(--fc-ink); }
.as-rego { font-size: var(--text-fine); color: var(--fc-mute); }
.as-make { font-size: var(--text-meta); color: var(--fc-steel); }
.as-past { color: var(--fc-red); }
.as-row--past td { background: var(--fc-red-tint); }
.as-none td { color: var(--fc-mute); font-size: var(--text-meta); padding: 12px 18px; }
.as-add .fc-field__control { min-width: 0; width: 100%; }
.as-add__actions { white-space: nowrap; }
.as-add__actions .fc-btn + .fc-btn { margin-left: 6px; }
@media (max-width: 900px) {
  .as-table { display: block; overflow-x: auto; }
}

/* ── 20 Employee profiles — slot 4 of the FULL MOCK TRANSFER, 14 Sep 2026 ───────────────────
   The mock's two-column body (`EmployeeProfiles.jsx` @ its r30 pin): a 280px list of person cards
   with a status chip, the selected person's cards beside it. In the platform's tokens; the
   selected card takes the marine outline the mock draws, an expired row the attention tint. */
.ep-page { max-width: 1080px; }
.ep-alert { margin-bottom: 16px; }
.ep-new { margin-bottom: 16px; }
.ep-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 13px; margin-top: 10px; }
.ep-form__actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 14px; }
.ep-form__note { font-size: var(--text-fine); color: var(--fc-mute); }
.ep-field-ro { font-size: var(--text-detail); color: var(--fc-ink); padding: 6px 0; }
.ep-body { display: grid; grid-template-columns: 280px 1fr; gap: 16px; align-items: start; }
.ep-body[hidden] { display: none; }
.ep-list { display: flex; flex-direction: column; gap: 8px; position: sticky; top: var(--sticky-top); }
.ep-person { text-align: left; background: var(--fc-white); border: 1px solid var(--fc-line); border-radius: var(--radius-card);
             padding: 11px 13px; cursor: pointer; font: inherit; color: inherit; display: block; width: 100%; box-sizing: border-box; }
.ep-person.is-selected { border-color: var(--fc-marine); box-shadow: 0 0 0 1px var(--fc-marine); }
.ep-person.is-inactive .ep-person__name { color: var(--fc-mute); }
.ep-person__row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.ep-person__name { font-size: var(--text-detail); font-weight: 700; }
.ep-person__chip { font-size: var(--text-fine); padding: 2px 9px; }
.ep-person__sub { display: block; font-size: var(--text-fine); color: var(--fc-mute); margin-top: 2px; }
.ep-detail { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.ep-card__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.ep-card--flush { padding: 0; overflow: hidden; }
.ep-card__head--pad { padding: 14px 18px 8px; }
.ep-card__foot { display: flex; gap: 10px; align-items: center; padding: 10px 18px; border-top: 1px solid var(--fc-line); }
.ep-table { width: 100%; }
.ep-table th:nth-child(2) { width: 130px; }
.ep-table th:nth-child(3) { width: 110px; }
.ep-table th:nth-child(4) { width: 240px; }
.ep-lic__type { font-weight: 600; }
.ep-lic__number { font-size: var(--text-fine); color: var(--fc-mute); }
.ep-past { color: var(--fc-red); }
.ep-row--expired td { background: var(--fc-red-tint); }
.ep-login { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--fc-line-soft); }
.ep-login__field { min-width: 260px; }
.ep-login__field .fc-field__control { width: 100%; box-sizing: border-box; }
.ep-login__label { font-size: var(--text-meta); color: var(--fc-ink); }
.ep-request { margin-left: 8px; }
.ep-request-blocked { margin-left: 8px; }
.ep-requested { display: block; font-size: var(--text-fine); color: var(--fc-mute); margin-top: 4px; }
.ep-none { color: var(--fc-mute); font-size: var(--text-meta); padding: 12px 18px; }
.ep-none td { color: var(--fc-mute); font-size: var(--text-meta); padding: 12px 18px; }
.ep-add-licence .fc-field__control { min-width: 0; width: 100%; }
.ep-add-licence__actions { white-space: nowrap; }
.ep-add-licence__actions .fc-btn + .fc-btn { margin-left: 6px; }
.ep-docs { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; }
.ep-doc { display: flex; align-items: center; gap: 10px; background: var(--fc-well); border-radius: var(--radius-card);
          padding: 10px 12px; color: inherit; text-decoration: none; min-width: 0; }
.ep-doc__glyph { font-family: var(--font-mono); color: var(--fc-marine); }
.ep-doc__body { min-width: 0; }
.ep-doc__name { display: block; font-size: var(--text-meta); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ep-doc__meta { display: block; font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); }
@media (max-width: 900px) {
  .ep-body { grid-template-columns: 1fr; }
  .ep-list { position: static; }
  .ep-table { display: block; overflow-x: auto; }
}

/* ── Screen 21 · Ops schedule — slot 4 of the FULL MOCK TRANSFER, 14 Sep 2026 ──────────────
   The mock's card, board and roster in the platform's tokens. Bars sit on a CSS grid whose columns
   are the served window's days (`--os-cols`), placed by the served `start_index` / `length` —
   the browser reckons no date. `[hidden]` overrides on every class that sets `display`. */
.os-page { max-width: 1280px; }
.os-views { flex-shrink: 0; }
.os-blocks { margin-bottom: 16px; }
.os-blocks__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.os-jobpick { min-width: 260px; }
.os-jobpick .fc-field__control { width: 100%; box-sizing: border-box; }
.os-chips { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.os-pend { margin-top: 12px; }
.os-pend__actions { display: flex; gap: 8px; margin-top: 8px; }
.os-note { margin-top: 12px; }
.os-figs { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-top: 14px; }
.os-fig { background: var(--fc-well); border-radius: var(--radius-card); padding: 8px 12px; }
.os-fig--over { background: var(--fc-red-tint); }
.os-fig__lbl { display: block; font-size: var(--text-fine); color: var(--fc-mute); text-transform: uppercase; letter-spacing: 0.04em; }
.os-fig__val { display: block; font-family: var(--font-mono); font-size: var(--text-detail); font-weight: 700; color: var(--fc-ink); font-variant-numeric: tabular-nums; }
.os-fig--over .os-fig__val { color: var(--fc-red); }
.os-overrun { margin-top: 12px; }
.os-table-wrap { overflow-x: auto; margin-top: 14px; }
.os-table { width: 100%; }
.os-table th:nth-child(4) { width: 70px; }
.os-block { font-family: var(--font-mono); font-weight: 700; white-space: nowrap; }
.os-block .os-blocked, .os-block .os-pack { margin-left: 8px; font-weight: 500; font-size: var(--text-fine); white-space: nowrap; }
.os-nocrew { color: var(--fc-mute); }
.os-change { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.os-move__date { width: 150px; box-sizing: border-box; }
.os-days__input { width: 90px; box-sizing: border-box; }
.os-change__pair { display: inline-flex; gap: 6px; align-items: center; }
.os-crewpick { width: 150px; box-sizing: border-box; }
.os-none { color: var(--fc-mute); font-size: var(--text-meta); padding: 12px 18px; }
.os-none td { color: var(--fc-mute); font-size: var(--text-meta); padding: 12px 18px; }
.os-blocked { font-size: var(--text-fine); white-space: nowrap; }
.os-add { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; align-items: end; margin-top: 14px;
          background: var(--fc-well); border-radius: var(--radius-card); padding: 12px 14px; }
.os-add[hidden] { display: none; }
.os-add .fc-field__control { min-width: 0; width: 100%; box-sizing: border-box; }
.os-add__reason { grid-column: span 2; }
.os-add__members { grid-column: span 2; }
.os-add__actions { display: flex; gap: 8px; }
.os-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 14px; }
.os-evidence { font-size: var(--text-meta); color: var(--fc-steel); margin: 14px 0 0; line-height: 1.7; }
.os-view { margin-bottom: 16px; }
.os-view[hidden] { display: none; }
.os-foot { font-size: var(--text-fine); color: var(--fc-mute); margin: 10px 0 0; }
/* today */
.os-dispatch { padding: 0; overflow: hidden; }
.os-dispatch__row { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 12px; align-items: center; padding: 12px 18px;
                    border-bottom: 1px solid var(--fc-line-soft); }
.os-dispatch__row:last-child { border-bottom: 0; }
.os-dispatch__crew { font-weight: 700; color: var(--fc-ink); }
.os-dispatch__job { font-size: var(--text-fine); color: var(--fc-mute); }
/* works ahead */
.os-boardnote { margin-bottom: 12px; }
.os-tray { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.os-tray__lbl { font-size: var(--text-fine); font-weight: 700; color: var(--fc-mute); text-transform: uppercase; letter-spacing: 0.04em; }
.os-tray__job { font-family: var(--font-body); font-size: var(--text-fine); font-weight: 600; color: var(--fc-red);
                background: var(--fc-red-tint); border: 1px solid var(--fc-red-line); border-radius: var(--radius-control);
                padding: 5px 10px; cursor: pointer; }
.os-tray__job.is-picked { background: var(--fc-marine); color: var(--fc-white); border-color: var(--fc-marine); }
.os-tray__done { font-size: var(--text-fine); color: var(--fc-green); font-weight: 600; }
.os-tray__hint { font-size: var(--text-fine); color: var(--fc-mute); }
.os-board { padding: 0; overflow-x: auto; }
.os-board__weeks, .os-board__row, .os-board__days { --os-cols: 28; display: grid; grid-template-columns: 180px repeat(var(--os-cols), minmax(var(--os-day-min, 22px), 1fr)) 56px; align-items: stretch; min-width: 900px; }
.os-board__week { grid-column: span 7; font-size: var(--text-fine); font-weight: 700; color: var(--fc-steel); padding: 8px 6px; border-left: 1px solid var(--fc-line); }
.os-board__idle-h { font-size: var(--text-fine); font-weight: 700; color: var(--fc-steel); padding: 8px 6px; text-align: right; }
.os-board__row { border-top: 1px solid var(--fc-line-soft); }
.os-board__crew { padding: 8px 12px; display: flex; flex-direction: column; justify-content: center; }
.os-board__crewname { font-weight: 700; color: var(--fc-ink); font-size: var(--text-meta); }
.os-board__lead { font-size: var(--text-fine); color: var(--fc-mute); }
.os-board__grid { --os-lanes: 1; display: grid; grid-template-columns: repeat(var(--os-cols), minmax(var(--os-day-min, 22px), 1fr)); grid-template-rows: repeat(var(--os-lanes, 1), 38px); position: relative; grid-column: 2 / span var(--os-cols); }
.os-cell { grid-row: 1 / -1; border-left: 1px solid var(--fc-line-soft); cursor: pointer; }
.os-cell--weekend { background: var(--fc-line-soft); cursor: default; }
.os-cell--today { box-shadow: inset 2px 0 0 var(--fc-marine); }
.os-bar { grid-row: 1; margin: 6px 2px; padding: 0 8px; border-radius: 6px; font-size: var(--text-fine); font-weight: 600;
          line-height: 26px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; }
.os-bar--scheduled { background: var(--fc-info-tint); color: var(--fc-info); border: 1px solid var(--fc-line); }
.os-bar--in_progress { background: var(--fc-marine); color: var(--fc-white); }
.os-bar--completed { background: var(--fc-green-tint); color: var(--fc-green); border: 1px solid var(--fc-line); }
.os-board__idle { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); text-align: right; padding: 8px 12px; align-self: center; }
.os-board__day { font-family: var(--font-mono); font-size: 10px; color: var(--fc-mute); padding: 6px 2px; text-align: center; border-top: 1px solid var(--fc-line); }
.os-board__day--weekend { color: var(--fc-mist); }
/* CR-2: a one-day booking keeps its job number readable inside the existing scroller. */
@media (max-width: 640px) {
  .os-board { --os-day-min: 112px; display: flex; flex-direction: column; }
  .os-board__weeks { order: -2; }
  .os-board__days { order: -1; }
  .os-board__weeks, .os-board__row, .os-board__days {
    min-width: calc(236px + var(--os-cols) * 112px);
  }
}
.os-key { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; font-size: var(--text-fine); color: var(--fc-steel); margin-top: 10px; }
.os-key__lbl { font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--fc-mute); }
.os-key__sw { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-right: 5px; vertical-align: -2px; border: 1px solid var(--fc-line); }
.os-key__sw--scheduled { background: var(--fc-info-tint); }
.os-key__sw--in_progress { background: var(--fc-marine); }
.os-key__sw--completed { background: var(--fc-green-tint); }
.os-key__sw--gap { background: var(--fc-white); }
.os-key__sw--weekend { background: var(--fc-line-soft); }
.os-key__blocked { color: var(--fc-mute); }
/* day-by-day */
.os-fortnight { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
.os-fortnight__range { font-weight: 700; color: var(--fc-ink); margin-right: auto; }
.os-days { padding: 0; overflow-x: auto; }
.os-days__row { --os-crews: 1; display: grid; grid-template-columns: 200px repeat(var(--os-crews), minmax(180px, 1fr)); border-top: 1px solid var(--fc-line-soft); min-width: 600px; }
.os-days__row > div { padding: 8px 12px; font-size: var(--text-meta); }
.os-days__row--head { border-top: 0; background: var(--fc-well); font-weight: 700; }
.os-days__row--head > div { font-size: var(--text-fine); }
.os-days__sub { font-size: var(--text-fine); color: var(--fc-mute); font-weight: 500; margin-top: 4px; }
.os-days__row--weekend { background: var(--fc-line-soft); color: var(--fc-mute); }
.os-days__wk { grid-column: 2 / -1; font-size: var(--text-fine); }
.os-days__row--today > div:first-child { box-shadow: inset 3px 0 0 var(--fc-marine); }
.os-days__cell { display: flex; flex-direction: column; gap: 2px; }
.os-days__lead { font-weight: 600; color: var(--fc-ink); }
.os-days__job { font-size: var(--text-fine); color: var(--fc-info); font-weight: 600; }
.os-days__free { font-size: var(--text-fine); color: var(--fc-mute); }
/* crews */
.os-crews { margin-bottom: 16px; }
.os-crewtable { width: 100%; margin-top: 10px; }
.os-crewname { font-weight: 700; color: var(--fc-ink); }
@media (max-width: 900px) {
  /* Owner ruling 3: containment is confined to the narrow layout. */
  .iq-flat { min-width: 0; }
  #os_crews { overflow-x: auto; }
  .os-dispatch__row { grid-template-columns: 1fr; }
  .os-add__reason, .os-add__members { grid-column: span 1; }
}

/* ── Screen 22 · Installers folder — slot 4 of the FULL MOCK TRANSFER, 14 Sep 2026 ─────────
   The mock's packs row, crew line, papers list, source card and letterhead card in the platform's
   tokens. `[hidden]` overrides on every class that sets `display`. */
.if-page { max-width: 1180px; }
.if-blocked { font-size: var(--text-fine); white-space: normal; }
.if-packs { display: flex; gap: 10px; flex-wrap: wrap; margin: 10px 0 16px; }
.if-pack { text-align: left; background: var(--fc-white); border: 1px solid var(--fc-line); border-radius: var(--radius-card);
           padding: 10px 14px; min-width: 240px; max-width: 100%; box-sizing: border-box; cursor: pointer; font-family: var(--font-body); }
.if-pack.is-selected { border-color: var(--fc-marine); box-shadow: 0 0 0 1px var(--fc-marine); }
.if-pack__row { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.if-pack__job { font-weight: 700; color: var(--fc-ink); font-size: var(--text-meta); }
.if-pack__fence { display: block; font-size: var(--text-fine); color: var(--fc-steel); margin-top: 4px; }
.if-pack__crew { display: block; font-size: var(--text-fine); color: var(--fc-mute); margin-top: 2px; }
.if-crew { margin-bottom: 16px; }
.if-crew__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.if-crew__lbl { font-weight: 700; color: var(--fc-ink); font-size: var(--text-meta); }
.if-crew__chips { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.if-crew__from { font-size: var(--text-fine); color: var(--fc-mute); }
.if-crew__note { font-size: var(--text-fine); color: var(--fc-mute); margin: 10px 0 0; line-height: 1.7; }
.if-blocks { margin-top: 12px; overflow-x: auto; }
.if-blocktable { width: 100%; }
.if-block { font-family: var(--font-mono); font-weight: 700; white-space: nowrap; }
.if-nocrew { color: var(--fc-mute); }
.if-two { display: grid; grid-template-columns: 1.1fr 1fr; gap: 16px; align-items: start; }
/* `display: grid` beats the UA `[hidden]` rule — the `.savebar[hidden]` defect this file records three
   times; without this the no-selection state leaves the papers and the source control visible. */
.if-two[hidden] { display: none; }
.if-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.if-doc { padding: 10px 0; border-bottom: 1px solid var(--fc-line-soft); }
.if-doc:last-child { border-bottom: 0; }
.if-doc--blocked .if-doc__label { color: var(--fc-steel); }
.if-doc__row { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.if-doc__label { font-weight: 700; color: var(--fc-ink); font-size: var(--text-meta); }
.if-doc__sub { display: block; font-size: var(--text-fine); color: var(--fc-mute); margin-top: 3px; }
.if-seg { margin-top: 10px; }
.if-seg .fc-seg__opt[hidden] { display: none; }
.if-source__note { font-size: var(--text-fine); color: var(--fc-steel); margin: 10px 0 0; line-height: 1.7; }
.if-source__actions { margin-top: 10px; }
.if-papers { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin-top: 10px; }
.if-paper { display: flex; align-items: center; gap: 10px; background: var(--fc-well); border-radius: var(--radius-card);
            padding: 10px 12px; color: inherit; text-decoration: none; min-width: 0; }
.if-paper__glyph { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-marine); }
.if-paper__body { min-width: 0; }
.if-paper__name { display: block; font-size: var(--text-meta); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.if-paper__meta { display: block; font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); }
.if-none { color: var(--fc-mute); font-size: var(--text-meta); margin-top: 10px; }
.if-letterhead__row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.if-letterhead__logo { width: 72px; height: 72px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
                       background: var(--fc-well); border-radius: var(--radius-card); overflow: hidden; }
.if-letterhead__img { max-width: 100%; max-height: 100%; }
.if-letterhead__placeholder { font-size: var(--text-fine); color: var(--fc-mute); }
.if-letterhead__row > div:nth-child(2) { flex: 1; min-width: 200px; }
.if-foot { font-size: var(--text-fine); color: var(--fc-mute); margin: 16px 0 0; line-height: 1.7; }
@media (max-width: 900px) {
  .if-two { grid-template-columns: minmax(0, 1fr); }
  .if-doc__row .fc-chip { white-space: normal; max-width: 100%; box-sizing: border-box; }
}

/* ── Screen 23 · Projects — slot 4 of the FULL MOCK TRANSFER, 14 Sep 2026 ──────────────────
   The mock's two-column body (a 280px project list, the readiness card, the induction card, the
   assigned-units card) and the office-queue card above it, in the platform's tokens: the readiness
   glyphs take the mock's `#0F6A43` (`--fc-green`) / `#B3401F` (`--fc-red`) / `#8A5A16` (`--fc-amber`);
   the open row's wash is the attention well; the unit chip is the mock's marine pill. Nothing here
   is a new visual language. `[hidden]` overrides on every class that sets `display`. */
.pj-page { max-width: 1180px; }
.pj-card__head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.pj-card__head > div:first-child { min-width: 0; flex: 1; }
.pj-card__actions { display: inline-flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.pj-queue { margin-bottom: 16px; }
.pj-blocked { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.pj-blocked__chip { font-size: var(--text-fine); white-space: normal; }
.pj-blocked__note { font-size: var(--text-fine); color: var(--fc-mute); }
.pj-two { display: grid; grid-template-columns: 280px 1fr; gap: 16px; align-items: start; }
.pj-list { display: flex; flex-direction: column; gap: 8px; position: sticky; top: 78px; }
.pj-item { text-align: left; background: var(--fc-white); border: 1px solid var(--fc-line); border-radius: var(--radius-card);
           padding: 12px 14px; cursor: pointer; font-family: var(--font-body); box-shadow: 0 1px 3px rgba(28,36,43,0.05); }
.pj-item.is-selected { border-color: var(--fc-marine); box-shadow: 0 0 0 1px var(--fc-marine), 0 1px 3px rgba(28,36,43,0.05); }
.pj-item__row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.pj-item__job { font-family: var(--font-mono); font-size: var(--text-detail); font-weight: 600; white-space: nowrap; color: var(--fc-ink); }
.pj-chip { font-size: var(--text-fine); padding: 2px 8px; white-space: nowrap; }
.pj-item__client { display: block; font-size: var(--text-meta); font-weight: 600; margin-top: 3px; color: var(--fc-ink); }
.pj-item__line { display: block; font-size: var(--text-fine); color: var(--fc-mute); margin-top: 2px; }
.pj-item__status { display: block; font-size: var(--text-fine); color: var(--fc-steel); margin-top: 2px; }
.pj-detail { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.pj-rows { margin-top: 10px; }
.pj-row { display: grid; grid-template-columns: 22px max-content minmax(0, 1fr); gap: 12px; padding: 9px 0; align-items: baseline; border-top: 1px solid var(--fc-line-soft); }
.pj-row:first-child { border-top: 0; }
.pj-row__glyph { font-family: var(--font-mono); font-size: var(--text-detail); }
.pj-row--done .pj-row__glyph { color: var(--fc-green); }
.pj-row--open .pj-row__glyph, .pj-row--open .pj-row__label { color: var(--fc-red); }
.pj-row--open { background: var(--fc-red-tint); margin: 0 -18px; padding-left: 18px; padding-right: 18px; }
.pj-row--blocked .pj-row__glyph { color: var(--fc-amber); }
.pj-row--blocked .pj-row__label { color: var(--fc-steel); }
.pj-row__label { font-size: var(--text-detail); font-weight: 600; color: var(--fc-ink); }
.pj-row__detail { font-family: var(--font-mono); font-size: var(--text-fine); color: var(--fc-mute); text-align: right; justify-self: end; max-width: 100%; }
.pj-units { margin-top: 10px; }
.pj-units__chips { display: flex; gap: 8px; flex-wrap: wrap; }
.pj-unit { font-family: var(--font-mono); font-size: var(--text-meta); font-weight: 600; color: var(--fc-marine);
           background: var(--fc-info-tint); border: 1px solid var(--fc-sky-soft); border-radius: 999px; padding: 6px 14px; }
.pj-units__none { font-size: var(--text-meta); color: var(--fc-red); }
@media (max-width: 900px) {
  .pj-two { grid-template-columns: 1fr; }
  .pj-list { position: static; }
  .pj-row { grid-template-columns: 22px minmax(0, 1fr); }
  .pj-row__detail { grid-column: 2; text-align: left; justify-self: start; }
}
/* 19 Assets · the `Assigned to` cell once 16 Projects landed (14 Sep, Assets 2a). */
.as-assigned-cell { white-space: nowrap; }
.as-assigned { font-size: var(--text-fine); font-weight: 600; color: var(--fc-marine); }
.as-assigned--gone { color: var(--fc-mute); font-weight: 400; }
.as-unassigned { font-size: var(--text-fine); color: var(--fc-mute); }
.as-assign__ctl { display: inline-flex; gap: 6px; align-items: center; }
.as-assign__select { min-width: 0; width: 150px; font-size: var(--text-fine); }

/* Native disabled fieldset prevents edits before asynchronous quote restoration completes. */
.iq-loading-fields { display: contents; }

/* ── Manual pricing panel · Owner 2026-10-04 ──────────────────────────────────────────────────
   v2-native, built from the existing component system. The catalogue search reuses the `mc-*`
   classes above (same endpoint, same hit list); only the manual-editor layout is new here. */
/* The mode toggle hides these engine surfaces (and the manual panel) via [hidden]; each sets its
   own `display`, so — like `.iq-block-add[hidden]` — the attribute needs an explicit rule to win. */
#iq_blocks[hidden], #iq_spec_card[hidden], #iq_quantities_card[hidden],
#iq_typecards[hidden], #iq_manual_mode[hidden] { display: none; }

.mp-blocks { display: flex; flex-direction: column; gap: 16px; }
.mp-block__head { display: flex; justify-content: space-between; align-items: center; }
.mp-block__del, .mp-row__del {
  background: none; border: 0; cursor: pointer; color: var(--fc-steel);
  font-size: 18px; line-height: 1; padding: 0 4px; border-radius: 4px;
}
.mp-block__del:hover, .mp-row__del:hover { color: var(--fc-red); background: var(--fc-well); }
.mp-primary { display: grid; grid-template-columns: minmax(0, 1fr) 128px 128px; gap: 12px; margin-top: 12px; }
.mp-cost-head { margin-top: 18px; }
.mp-rows { margin-top: 10px; }
.mp-rows .mp-cell { width: 100%; min-width: 0; }
.mp-rows .mp-cell--num { text-align: right; }
.mp-rows td { vertical-align: middle; }
.mp-row__pending { display: inline-block; margin-left: 6px; font-size: 11px; color: var(--fc-red); white-space: nowrap; }
.mp-block__total { margin-top: 12px; font-size: var(--text-meta); color: var(--fc-steel); }
.mp-add { margin-top: 14px; }
.mp-totals { margin-top: 16px; }

@media (max-width: 560px) {
  .mp-primary { grid-template-columns: 1fr; }
}
