/* ── Estimate pricing block - THE formatting system (UI STANDARDS 2026-07-03) ──
   ONE source for the pricing rows on EVERY surface that renders a quote from the
   canonical: the customer estimate panel (#quoteSummary, customer projection) and
   the portal quote-requests inbox (manufacturer, full expansion). Scoped by
   .est-scope so any container can opt in. Moved here from product-page.css
   2026-07-03 (the portal inbox had silently drifted onto its own bespoke layout -
   the exact one-rendering-system violation CORE RULES now names).
   Token fallbacks: the portal pages do not load index.css, so every var() carries
   the index.css value as its fallback. */

.est-scope .est-block {
  grid-column: 1 / -1; /* no-op outside a grid context (e.g. the portal's normal block flow) */
  column-span: all; /* no-op outside a multi-column context (2026-07-09, #quoteSummary) */
  font-size: 0.76rem;
  line-height: 1.65;
  margin-bottom: 8px;
}

.est-scope .est-block .est-section,
.est-scope .est-block .est-child {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.est-scope .est-block .est-section {
  color: var(--dark, #1c2426);
  /* 2026-07-09: was 500, lighter than .est-branch's 700 below it - once a customer-mode branch
     (Compartment 1/2, Mounting Panels) got the same est-branch weight as the manufacturer view
     (this session's Phase 1 fix), the section header above them read LESS prominent than its own
     children, an inverted hierarchy Tim caught live. Section is the top tier and must be the
     heaviest weight on the panel. */
  font-weight: 700;
  /* ── THE SIZE STEP, MOVED HERE FROM THE PORTAL (2026-08-15, queue Task 1) ──────────────────
     This lived in an injected <style> block inside portal-shell.js, scoped .pv2-inbox-detail-body,
     so the portal's quote-requests breakdown had a size hierarchy and the quote document did not.
     Measured on the same morphd quote, before this change:

         portal            section 700/17px     leaf 300/12.16px
         quote document    section 700/12.16px  leaf 300/12.16px

     which is Tim's report exactly: "every line price in the document renders at one weight and
     size, so depth is not legible from the number". It was never a missing value, it was the rule
     living on one surface. CORE RULES names this precise drift ("the portal quote-requests view
     silently drifting from the customer estimate panel's formatting system"), so the fix is to move
     the rule rather than to copy it into a third place.

     var(--t-body) with the literal as its fallback: the portal pages define that token and keep
     using it, and quote-document.html (which does not load portal.css) gets the same number. One
     value, one rule, every surface. */
  font-size: var(--t-body, 1.0625rem);
}

.est-scope .est-block .est-child {
  font-weight: 300;
  color: var(--dark, #1c2426);
  padding-left: 1.2em; /* ONE indent step per depth below the section row */
}

.est-scope .est-block .est-child .est-label {
  color: var(--mid, #4a5c5e);
}

/* Phase E2: second indent depth - per-compartment component sub-lines */
.est-scope .est-block .est-child.est-child2 {
  padding-left: 2.4em;
}
/* Manufacturer nested build view: third depth - a grouped component's own cost lines
   (Compartment N > Drawer x2 > Sheet Metal). */
.est-scope .est-block .est-child.est-child3 {
  padding-left: 3.6em;
}
/* A compartment container row and a grouped-component row sit above their leaves, so they
   carry a touch more weight than a bare leaf. */
.est-scope .est-block .est-child.est-node {
  /* ── A GROUP MUST NEVER READ THE SAME AS THE LINES INSIDE IT, AT ANY DEPTH ──────────────────
     (2026-08-17, queue Task 2 reopened.)

     This was font-weight 400 with NO size step, so a node and its own children differed by one
     100-unit weight increment at an identical 12.16px and an identical colour. Numerically distinct,
     visually one thing. On the toolbox, which nests four deep, that made the bottom two levels read
     as a single flat list:

         Door (2160W x 890H mm)   $590.00     400 / 12.16px
           Labour (Lift-Up Door)  $360.00     300 / 12.16px
           Drawing                 $25.00     300 / 12.16px
           Hinges                  $18.00     300 / 12.16px

     THE EARLIER GATE PASSED ON THIS, three times, because it compared a SECTION against a LEAF and
     found them different. Aggregates hide it: 26 nodes at 400 and 133 leaves at 300 look like a
     working ladder while any given group and its own children are indistinguishable. The gate now
     compares every group with ITS OWN CHILDREN, which is the only comparison that can fail here.

     So the node tier gets a real SIZE step as well as a bigger weight step, and every parent/child
     pair on the platform now differs on size, not on weight alone:

         section  700 / 17px
         branch   600 / 17px
         node     500 / 13.6px
         leaf     300 / 12.16px

     The size is `em` against .est-block's 0.76rem base, so it tracks that base rather than pinning a
     second absolute. It does not compound with depth: a nested row is a SIBLING of the summary
     inside <details>, not a descendant of it, so it inherits the block base and not this. */
  font-weight: 500;
  font-size: 1.12em;
}
.est-scope .est-block .est-child.est-node .est-label {
  color: var(--dark, #1c2426);
}
/* Branch-level subtotal (2026-07-08): a real tree branch or compartment container - "Internal
   Finish", "Compartment 1" - aggregating its own children's costs, distinct from a repeating-
   component group's own line (Drawer Powder Coat x2, still .est-node, not bold) or a bare leaf.
   Comes after .est-node so it wins at equal specificity. Customer side reuses this same class on
   a pinned (branch-collapsed-to-one-line) row - same concept, one class. */
.est-scope .est-block .est-child.est-branch {
  /* 2026-07-09: was 700 (equal to .est-section above) - dropped one step so the hierarchy reads
     section (700) > branch (600) > node (400) > leaf (300), section always the heaviest. */
  font-weight: 600;
  /* Moved here from portal-shell.js's injected block with .est-section above (2026-08-15). A branch
     is a real subtotal - "Compartment 1", "Internal Finish" - and reads at the section's size, one
     weight below it. Everything under a branch stays at the leaf size, so the number's size says
     "this is a subtotal" and the number's weight says which kind. */
  font-size: var(--t-body, 1.0625rem);
}
.est-scope .est-block .est-child.est-branch .est-label {
  color: var(--dark, #1c2426);
}
/* 2026-07-09: a superseded baseline (e.g. the whale when central is chosen) used to render here
   struck-out at its would-be cost - Tim reversed that: it's omitted entirely at the source now
   (canonical.js), never reaches the renderer, so .est-struck never gets applied. Left in place as
   a general-purpose affordance (estimate-render.js's row.struck is always false, documented there)
   rather than deleted, in case a future row kind legitimately needs it again. */
.est-scope .est-block .est-child.est-struck .est-label,
.est-scope .est-block .est-child.est-struck .est-val {
  text-decoration: line-through;
  color: var(--muted, #8a9e9f);
}

/* ── DEPTH, THREE WAYS AT ONCE (2026-09-28 16:26 WITA, Tim's decisions added to the top of THE
   QUEUE "The manufacturer view gets Size, Qty, Unit and Total columns on every row, groups
   included") ─────────────────────────────────────────────────────────────────────────────────

   "WEIGHT groups bold, items regular. SIZE three steps only: L0, L1, L2-and-deeper. INDENT one
   fixed step per level, same at every depth. GUIDE a thin line down the left from each group to
   its last child, so a group inside a group reads as its child however deep."

   SCOPED TO .est-mfg-nest, NOT the base .est-child/.est-node/.est-branch/.est-section rules above:
   this decision is on the manufacturer-columns item, whose "Rest fold in" note already scoped the
   whole item to "MANUFACTURER VIEW ONLY. Not the manufacturer email ... and not the customer
   estimate." The customer's own live configurator panel (#quoteSummary, product-page.js) shares
   this same CSS system and never asked for this redesign, so it keeps the ladder above exactly as
   it read before. .est-mfg-nest is stamped by quote-document.js on the manufacturer breakdown AND
   on the Specification list beside it (Tim: "follows the SAME tree and the same rules"), never on
   the customer document or the customer panel.

   CHECKED FOR AN EARLIER DEPTH-BASED STYLE BEFORE WRITING THIS ("the middle weights used to exist
   and have disappeared"): none is on record. `git log -S"pv2-inbox-detail-body"` and the "final
   scale pass" commit (efff605) that built the ladder above both describe it as KIND-based from the
   start - "leaf < group < branch < section < product name/header total < product total < grand
   total" - never depth-based, and no CSS anywhere in history keys off `data-est-depth`. So this is
   a new scheme, not a restored one; the finding is recorded here rather than assumed away.

   WEIGHT: two tiers, not the four above. `.est-node`/`.est-branch`/`.est-section` are the group
   tiers (a row gets one of them whenever it has children - see estimate-render.js), so bolding all
   three to one weight and leaving `.est-child` alone at its own is "groups bold, items regular"
   exactly. */
/* ── SUPERSEDED FOR THIS SCOPE 2026-09-30: ONE TREE-TABLE, TWO SIZES, THE SAME AT EVERY DEPTH (THE
   QUEUE "Manufacturer view, full rethink: simple for a tradie, bigger text, groups closed by default
   and clicked open, Specification answers using the space") ────────────────────────────────────────

   Tim: "Everything feels so tiny on the page. The item's text is so small. The jump in text size from
   the top pricing group to the next pricing group is large. Remember thinking of a tradie with grubby
   hands going into this." Then, 10:38: "the SAME size at every depth. The indentation is already
   large and carries the depth on its own; do not shrink text as it nests." And 10:48: the
   Specification is "the same tree-table code as the price breakdown: same row height, same font size,
   same weight and boldness, same indent step, same guide line, same open/close behaviour."

   SO THE THREE SIZE STEPS ABOVE (L0, L1, L2-and-deeper) ARE GONE. MEASURED before this on the demo
   Toolbox (ac705138) at 1600px:

       price   top group 17px/700   depth-1 group 13.6px/700   depth-2 line 11.6px/400
       spec    top group 17px/700   depth-1 group 11.6px/700   answer 11.6px/400

   five sizes across two trees, and the smallest text on the page was exactly the detail a tradie opens
   a group to read. Now there are two, and a row's depth never changes either of them:

       a GROUP row   --est-mfg-group   16px / 700
       a LINE row    --est-mfg-line    15px / 400

   A GROUP IS A ROW THAT OPENS: a <summary>, or a row the presenter stamps `est-grp` (estimate-render.js,
   GRP). Not a kind class, because .est-section also marks a top-level ungrouped LINE and
   .est-branch/.est-node mean other things on the customer's panel.

   EVERYTHING ON THE ROW TAKES THE ROW'S SIZE: the name, the category and size words, the figures, a
   spec answer and its count. "Two sizes" is then true of the text, not only of the labels. The column
   captions and the product total are not rows and keep their own.

   INDENT AND GUIDE ARE IN rem. They were `em` of whatever the row was set in, so a depth-1 step and a
   depth-2 step were different widths, and the guide line (on the <details>, at the block's 12.16px)
   sat at a third. And the step landed TWICE: `[style*="--d"]` matched the <details> as well as its
   rows, so a nested group's rows were indented by the details AND by themselves. It is on the rows
   alone now, one --est-mfg-step per depth, and the guide runs down the middle of its group's caret,
   which is a fixed --est-mfg-caret wide on every row so every name at one depth starts on one line.

   THE ROW IS A TARGET: 6px above and below the text makes a one-line row 33px, clickable anywhere
   along its width (the <summary> is the whole row), with a quiet ground under the pointer. */
.est-scope.est-mfg-nest {
  --est-mfg-group: 1rem;
  --est-mfg-line: 0.9375rem;
  --est-mfg-step: 1.25rem;
  --est-mfg-caret: 1rem;
  --est-mfg-pad: 6px;
}
.est-scope.est-mfg-nest .est-block {
  font-size: var(--est-mfg-line);
  line-height: 1.4;
  margin-bottom: 0;
}
.est-scope.est-mfg-nest .est-block [data-est-depth] {
  font-size: var(--est-mfg-line);
  font-weight: 400;
  padding-top: var(--est-mfg-pad);
  padding-bottom: var(--est-mfg-pad);
}
.est-scope.est-mfg-nest .est-block summary[data-est-depth],
.est-scope.est-mfg-nest .est-block .est-grp[data-est-depth] {
  font-size: var(--est-mfg-group);
  font-weight: 700;
}
/* The words and figures beside a name are the row's size, never a step smaller (the category and
   size columns were 0.86em, a third and fourth size on every row). */
.est-scope.est-mfg-nest .est-block .est-cols-mfg .est-col-category,
.est-scope.est-mfg-nest .est-block .est-cols-mfg .est-col-size {
  font-size: inherit;
}
.est-scope.est-mfg-nest .est-block [data-est-depth][style*="--d"] {
  padding-left: calc(var(--d, 0) * var(--est-mfg-step));
}
/* By category is drawn without --d (spend-breakdown.js, shared with the portal's inbox, which keeps its
   own markup) and its lines carry no caret spacer, so a line there starts where a caret would: one
   step in, plus the caret, which is where every line's NAME starts in the tree above it. */
.est-scope.est-mfg-nest .est-block .est-child[data-est-depth]:not([style*="--d"]) {
  padding-left: calc(var(--est-mfg-step) + var(--est-mfg-caret) + 0.3rem);
}
.est-scope.est-mfg-nest .est-caret {
  width: var(--est-mfg-caret);
  margin-right: 0.3rem;
  font-size: 0.9rem;
  text-align: center;
  color: var(--mid, #4a5c5e);
}
.est-scope.est-mfg-nest details.est-det > summary {
  border-radius: 4px;
}
.est-scope.est-mfg-nest details.est-det > summary:hover {
  background: var(--est-row-hover, rgba(0, 0, 0, 0.04));
}
.est-scope.est-mfg-nest details.est-det > summary:focus-visible {
  outline: 2px solid var(--muted, #8a9e9f);
  outline-offset: -2px;
}
/* A NAME THAT WRAPS HANGS UNDER ITS OWN FIRST WORD, not under its caret. MEASURED at 1100px and on
   paper: "Shell Sheet Metal (including Doors)" and the spec's "Compartment Layout" took a second line
   that started one caret to the LEFT of the first, back under the parent's column, and read as a row
   of its own. The caret sits in the hanging indent; a label with no caret (By category's) has none. */
.est-scope.est-mfg-nest .est-block [data-est-depth] > .est-label:has(> .est-caret) {
  padding-left: calc(var(--est-mfg-caret) + 0.3rem);
  text-indent: calc(-1 * (var(--est-mfg-caret) + 0.3rem));
}
.est-scope.est-mfg-nest .est-caret { text-indent: 0; }
/* GUIDE: a thin line down the left of each group, from under its own row to its last child, down the
   middle of that group's caret. On the <details>, which carries the same --d as its summary; every
   <details> in the tree starts at the tree's left edge (none of them is indented), so `left` is the
   same arithmetic as the row's own indent. Top-level groups included: they are groups too. */
.est-scope.est-mfg-nest .est-det {
  position: relative;
}
.est-scope.est-mfg-nest .est-det::before {
  content: "";
  position: absolute;
  top: calc(var(--est-mfg-group) * 1.4 + 2 * var(--est-mfg-pad));
  bottom: var(--est-mfg-pad);
  left: calc(var(--d, 0) * var(--est-mfg-step) + var(--est-mfg-caret) / 2);
  width: 1px;
  /* A step darker than the rows' hairline (#dfe6e7), which MEASURED invisible down a 16px caret
     column at 1600px: the guide is how a tradie follows a group to its last line. A neutral grey. */
  background: var(--est-guide, #c5d0d2);
}
/* ── A FOLDED ROW KEEPS ITS OWN CELLS (Tim, 2026-10-06, supersedes "a closed group shows its name and its total
   only", 10:38) ────────────────────────────────────────────────────────────────────────────────────────────
       Folding only hides the CHILDREN. A group's own Category, Size, Qty, Unit, Cost, Margin and Sell stay on its
       row open or closed, so "Drawer  2  $520" reads the same folded as it does open. The rule that blanked Category,
       Qty and Unit on a closed group is deleted. A hidden Size used to wrap and make a closed row taller; nothing
       is hidden now, so that guard went with it. */
/* A closed group has no children to guide to; the line would be a stub under a row that wrapped. */
.est-scope.est-mfg-nest details.est-det:not([open])::before { display: none; }

/* NARROWER SHEETS AND PAPER, ONE STEP SMALLER, STILL TWO SIZES. At 1100px the eight fixed proportions
   give Category about 110px, and "Purchased parts" at 15px is wider than that: every purchased line
   took two lines (MEASURED on the demo Toolbox). 14px and 15px fit it on one, and on A4 landscape they
   print at about 10.5pt and 11pt, which reads at arm's length. The same breakpoint the money tracks
   narrow at, below. */
@media print, (max-width: 1180px) {
  .est-scope.est-mfg-nest {
    --est-mfg-group: 0.9375rem;
    --est-mfg-line: 0.875rem;
    --est-mfg-pad: 5px;
  }
}
@media print {
  .est-scope.est-mfg-nest { --est-mfg-pad: 3px; }
}

/* ── THE CUSTOMER'S TREE-TABLE: THE SAME ROWS, ITEM AND PRICE ONLY (2026-10-03, THE QUEUE) ──────────
   Tim, on the demo Gullwing Canopy: "The estimate panel's breakdown ... looks different from the
   quote's manufacturer view. Make it the same: the same lines, text sizes, weights, indent and guide
   lines." MEASURED BEFORE, at 1500 wide, the panel against the manufacturer view of the same rows:

       panel           a group 17px/700 at the edge, a sub-group 17px/600 one 20px step in, no caret
                       column, no guide, "$u x q" beside a line
       manufacturer    a group 16px/700, a line 15px/400, a 20px step per depth after a 16px caret
                       column, a 1px guide down each open group, 6px above and below every row

   The panel's rows are drawn by the same presenter now (estimate-render.js, CUSTOMER_TREE) inside an
   .est-mfg-nest scope, so every rule above this block that sizes, weighs and indents a manufacturer row
   sizes, weighs and indents a panel row too. Nothing here restates them.

   WHAT IS HERE IS THE GUIDE, because the panel has no <details> to hang it on. Its rows are siblings
   (the More detail fold reads them in order), so each row paints the guide of every group it sits in:
   one 1px line per depth above it, at that group's caret middle, which is the arithmetic of
   .est-det::before's `left` above. --ge (set by the fold, product-page.js) is how many of those lines
   carry on below this row; the rest end here, a pad short of its foot, where the manufacturer's guide
   ends on a group's last child. Without the fold's script every line simply carries on (--ge = --d). */
.est-scope.est-mfg-nest .est-block > .est-cols-cust[data-est-depth] {
  --ge: var(--d, 0);
  --est-guide-cols: repeating-linear-gradient(to right,
    transparent 0 calc(var(--est-mfg-caret) / 2),
    var(--est-guide, #c5d0d2) calc(var(--est-mfg-caret) / 2) calc(var(--est-mfg-caret) / 2 + 1px),
    transparent calc(var(--est-mfg-caret) / 2 + 1px) var(--est-mfg-step));
  background-image: var(--est-guide-cols), var(--est-guide-cols);
  background-repeat: no-repeat;
  background-size: calc(var(--ge) * var(--est-mfg-step)) 100%,
    calc((var(--d, 0) - var(--ge)) * var(--est-mfg-step)) calc(100% - var(--est-mfg-pad));
  background-position: 0 0, calc(var(--ge) * var(--est-mfg-step)) 0;
}
/* An open group turns its caret, as an open <summary>'s does. The class is the fold's (product-page.js). */
.est-scope.est-mfg-nest .est-block > .est-cols-cust.est-row--open > .est-label > .est-caret { transform: rotate(90deg); }
/* A group the customer can open is pressed anywhere along its row, with the manufacturer's quiet ground
   under the pointer. A group the projection keeps to one line has no control and no ground. */
.est-scope.est-mfg-nest .est-block > .est-cols-cust.est-grp:has(> [data-est-detail-toggle]) { cursor: pointer; border-radius: 4px; }
.est-scope.est-mfg-nest .est-block > .est-cols-cust.est-grp:has(> [data-est-detail-toggle]):hover {
  background-color: var(--est-row-hover, rgba(0, 0, 0, 0.04));
}
/* A note ("X - priced on application") sits where a depth-1 name starts. */
.est-scope.est-mfg-nest .est-block > .est-note { padding-left: calc(var(--est-mfg-step) + var(--est-mfg-caret) + 0.3rem); }

/* ── THE SPECIFICATION'S TWO COLUMNS: QUESTION | ANSWER (2026-09-30, same item) ─────────────────────
   Tim: "ITS COLUMNS question | answer (instead of the money columns); a group row shows its answer
   count where the price breakdown shows its total", and "Specification answers should be further to
   the right, making the most of the space it has."

   A GRID LIKE THE PRICE ROWS', NOT A FLEX ROW. The spec was .est-child's flex space-between inside a
   `width: max-content` list, so every answer sat a fixed 2.5em after the LONGEST question and the rest
   of the column stood empty. Now the answer has a track of its own at the right of the column, the
   same on every row at every depth, and its words are left-aligned in it the way the price table's
   Category and Size are: an answer is text, not a figure.

   --est-col-answer is the track. 45% here; quote-document.css restates it in cqw on the manufacturer's
   spec, for the reason the money tracks are in cqw (FIXED PROPORTIONS, below): a percentage resolves
   against the row's own content box, which narrows by one indent step per depth, so the answers would
   step right as the tree nests. A count is quiet (muted, regular) because it is not an answer. */
.est-scope .est-block .est-cols-spec {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--est-col-answer, 45%);
  align-items: baseline;
  gap: var(--est-col-gap, 8px);
}
.est-scope .est-block .est-cols-spec .est-col-answer {
  min-width: 0;
  text-align: left;
  overflow-wrap: anywhere;
  color: var(--dark, #1c2426);
}
.est-scope .est-block .est-cols-spec .est-col-count,
.est-scope .est-block .est-cols-spec .est-col-answer.est-nil {
  color: var(--muted, #8a9e9f);
  font-weight: 400;
}

/* Progressive disclosure (manufacturer view): every expandable branch/compartment/component group
   is a native <details>. The default open state mirrors the customer's pin state; Luke expands any
   node on demand. A leading caret marks expandable rows; leaves reserve the same width for
   alignment. No JS - <details>/<summary> does the interaction. */
.est-scope details.est-det { margin: 0; }
.est-scope details.est-det > summary {
  list-style: none;
  cursor: pointer;
  outline: none;
}
.est-scope details.est-det > summary::-webkit-details-marker { display: none; }
.est-scope details.est-det > summary::marker { content: ""; }
.est-scope .est-caret {
  display: inline-block;
  width: 0.85em;
  margin-right: 0.15em;
  color: var(--muted, #8a9e9f);
  font-size: 0.72em;
  line-height: 1;
  transition: transform 0.12s ease;
}
.est-scope .est-caret::before { content: "\25B8"; }        /* right-pointing triangle */
.est-scope .est-caret.est-caret-empty::before { content: ""; } /* leaf: spacer, no triangle */
.est-scope details.est-det[open] > summary .est-caret { transform: rotate(90deg); }
.est-scope details.est-det > summary:hover .est-label { color: var(--dark, #1c2426); }

.est-scope .est-block .est-val {
  white-space: nowrap;
  flex-shrink: 0;
  text-align: right;
}

.est-poa {
  font-style: italic;
  color: var(--red, #c0392b);
  font-weight: inherit; /* never bold - weight comes from the row, style from here */
}

.est-scope .est-block .est-note {
  font-size: 0.68rem;
  color: var(--muted, #8a9e9f);
  font-style: italic;
  padding-left: 1.2em; /* notes sit at the depth of the items they name */
  margin-top: 2px;
  line-height: 1.5;
}

/* Manufacturer-only: the muted driver/quantity gloss after a leaf label.

   THE GLOSS FOLLOWS ITS OWN ROW (2026-08-17, queue Task 2 reopened). This was 0.68rem - an ABSOLUTE
   size - so "$1,126 x 1" on a section, "$182 x 1" on a node and "$237 x 1" on a leaf all rendered at
   exactly 10.88px. The left-hand gloss was flat at every depth while the right-hand number carried
   the hierarchy, so the two halves of the same row disagreed about how deep it was.

   `em` makes it relative to the row it sits in, so it inherits the ladder for free rather than
   needing a rule per tier. 0.895em is the existing relationship preserved exactly where the gloss
   already lived: 0.895 x 12.16px = 10.88px on a leaf, unchanged to the pixel. A node's gloss now
   reads 12.17px, matching its heavier row. Sections and branches carry no gloss, so raising the
   ratio there is not a live case. */
.est-scope .est-block .est-driver {
  font-size: 0.895em;
  color: var(--muted, #8a9e9f);
  font-weight: 300;
}

/* Grand-total row for non-panel surfaces (the panel keeps its own .est-total
   with the large Cormorant figure; the inbox total is a plain bold row). */
/* ── AND A PRODUCT TOTAL OUTWEIGHS EVERY GROUP INSIDE IT (2026-09-21, THE QUEUE) ───────────────
   Tim: "THE PRODUCT TOTAL is larger and heavier than a group subtotal."

   MEASURED on demo quote d3596a9e at Full breakdown, before this, at 1600 / 900 / 720 / 560 / 420:

       section  700 / 17px
       branch   600 / 17px
       node     500 / 13.6px
       leaf     300 / 12.16px
       PRODUCT TOTAL  700 / 12.16px

   So it was not merely level with a group subtotal, it was the SMALLEST row on the page, five
   pixels under the branch it sums. Uppercase and a rule were carrying the whole of its rank.

   THE TIER ALREADY EXISTS AND IS NOT NEW HERE. The portal's inbox drew this same ladder in
   2026-07-08 - "leaf < branch < section < a product's own total (pv2-product-total) < the
   document's ONE grand total" - and sized its product total at var(--t-lead). This row is the
   quote document's pv2-product-total, so it reads at the same token rather than at a number
   invented for it. 800 against the section's 700 is the weight half of the same step.

   THE FIGURES FIT BECAUSE THE TRACKS WERE SIZED FOR THE BIGGEST THING IN THEM, which is what the
   column block below already says in those words; at 20px the total's own "4.5% ($376.00)" becomes
   that biggest thing, so the tracks moved with it rather than the row being kept small to suit
   them. A 2026-09-20 attempt at 0.86rem was reverted for exactly the overflow that not moving them
   causes, and the numbers it was measured at are in quote-document.css. */
.est-scope .est-grand {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border-lt, #eaf0f1);
  font-size: var(--t-lead, 1.25rem);
  font-weight: 800;
}
.est-scope .est-grand .est-label {
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* ── AND THE TOTAL IS IN THE SAME TABLE (2026-09-19, THE QUEUE) ────────────────────────────────

   Tim: "ONE SET OF COLUMNS for every row: lines, groups, By category and the total."

   MEASURED: with the columns fixed in rem, every row landed on 1155 / 1267 / 1411 and the total
   stayed at 1029 / 1211 / 1444. It carries `.est-cols`, so it looked like it should have taken the
   grid - and it did not, because that rule is `.est-scope .est-block .est-cols` and a grand total
   row is NOT inside `.est-block`. It stayed `display: flex` with space-between, which spreads three
   figures across the width and lines up with nothing.

   SO THE GRID IS STATED FOR IT TOO, reading the same three properties, not a fourth set of
   numbers. justify-content is reset because space-between means something different to a grid: it
   distributes the TRACKS, which would undo the template it has just been given. */
.est-scope .est-grand.est-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
  justify-content: unset;
  align-items: baseline;
  gap: var(--est-col-gap);
}
.est-scope .est-grand.est-cols .est-val {
  text-align: right;
  white-space: nowrap;
}

/* Total row: label left; big figure right with the small GST suffix inline;
   every further note (inc-GST equivalent, POA gloss) stacks BELOW the figure -
   never an inline flex sibling of it. */
.est-scope .est-total {
  grid-column: 1 / -1; /* no-op outside a grid context (e.g. the portal's normal block flow) */
  column-span: all; /* no-op outside a multi-column context (2026-07-09, #quoteSummary) */
  display: flex;
  justify-content: space-between;
  /* THE LABEL SITS ON THE FIGURE'S LINE, NOT LOW AND DETACHED [Tim, 2026-10-05]: baseline, so "ESTIMATED TOTAL" reads on the
     same line as the number to its left, and the inc GST note stacks under the figure as before. */
  align-items: baseline;
  gap: 8px;
  margin-top: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--border-lt, #eaf0f1);
}

.est-scope .est-total .est-total-label {
  font-family: var(--font-body, "Jost", sans-serif);
  font-size: 0.6rem;
  font-weight: 500;
  color: var(--muted, #8a9e9f);
  letter-spacing: 2px;
  text-transform: uppercase;
  padding-bottom: 6px; /* align to the bottom of the large figure */
}

.est-scope .est-total .est-total-right {
  text-align: right;
}

.est-scope .est-total .est-total-gst {
  font-size: 0.68rem;
  color: var(--muted, #8a9e9f);
  font-weight: 400;
}

.est-scope .est-total .est-total-note {
  display: block;
  font-size: 0.68rem;
  color: var(--muted, #8a9e9f);
  font-weight: 400;
  margin-top: 2px;
  line-height: 1.5;
}

/* AN EDITED LINE SAYS SO, QUIETLY (2026-08-13, brief Task 5).
   A revision line whose price or quantity Luke typed carries `edited` in the stored result, and
   estimate-render.js maps that to this class on the leaf. Without it a quote that no longer matches
   what the system computed is indistinguishable from one that does.
   Quiet is the requirement: a small lowercase word after the label, not a badge. It must not compete
   with POA, which is the marker that means somebody still has to act. */
.est-edited .est-label::after {
  content: "edited";
  margin-left: 7px;
  font-size: 0.68em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.5;
  font-weight: 600;
}

/* AN OVERRIDDEN GROUP IS VISIBLY ONE (2026-08-18, queue Task 12). "A silent override is the same
   fault class as a control that reports success and does nothing", so this is deliberately not
   subtle: it sits beside the group's name, in the same amber the POA badge uses for "this number
   needs a human", and it names the figure the items actually add up to. */
.est-override {
  font-size: 0.895em; font-weight: 600; color: #d97706; white-space: nowrap;
}
.est-overridden > .est-label { font-style: normal; }

/* ── COST, MARGIN AND SELL ON EVERY MANUFACTURER ROW (2026-09-19, THE QUEUE) ──────────────────────

   Tim: "In the manufacturer view, the margin column needs to be everywhere, not just on category."

   A ROW IS FLEX WITH space-between, which is right for one value and wrong for three: the three
   would spread across the row and nothing would line up down the column. So a columned row is a
   GRID, and only a columned row: `.est-cols` is added by the presenter when `columns` is passed,
   which no customer surface passes.

   THE MARGIN COLUMN IS THE WIDEST because it carries two things, "$376 (20%)". Cost and sell carry
   one figure each.

   THE COLUMNS ARE THE SAME AT EVERY DEPTH. The label cell absorbs the indent (padding-left is on
   the row, so the first grid column simply starts further in), which is what keeps the three
   figures in a straight line from the section heading down to a leaf five levels under it.

   ── AND THEY WERE SIZED IN em, WHICH IS WHY THEY DID NOT LINE UP (2026-09-19, THE QUEUE) ───────

   Tim: "The columns don't line up. Perhaps we increase the column widths to suit the big text and
   then ensure everything is in a single column, apart from the item and group names."

   MEASURED ON A DEMO GULLWING QUOTE, and it is three different tables in one column of figures:

     the header and the LEAF rows   cost 1207   margin 1306   sell 1424
     a SECTION or BRANCH row        cost 1091   margin 1227   sell 1388
     the TOTAL row                  cost 1029   margin 1211   sell 1444

   `em` IS THE ROW'S OWN FONT. A section heading is set larger and a total larger again, so 7.5em
   and 9em resolve to a different number of pixels on every tier, the fixed columns get wider, and
   the 1fr label cell gives up the difference - which slides the whole set LEFT on exactly the rows
   whose figures are biggest. Nothing was misaligned by a rule; they were three different grids.

   ONE TEMPLATE, IN rem, DECLARED ONCE. rem is the root font and does not care what a row is set
   in, so every tier resolves to the same pixels. It is a custom property rather than three
   repeated values because the header, the rows and the total all have to read the same number and
   a fourth surface will read it next. WIDER THAN THE OLD em VALUES AT LEAF SIZE, which is Tim's
   "increase the column widths to suit the big text": the widest thing in any of them is a section's
   "$376 (4.5%)" at the section font, and that is what these are sized for. */
/* ── AND THE MARGIN TRACK IS SIZED FOR THE PRODUCT TOTAL NOW (2026-09-21, THE QUEUE) ───────────
   The paragraph above sizes these for "the widest thing in any of them ... a section's
   '$376 (4.5%)' at the section font". The product total is now the biggest row on the page, so the
   widest thing moved to it: MEASURED, its "4.5% ($376.00)" is 155px against a 136px track, which is
   the same overflow that reverted the 2026-09-20 attempt at this.

   THE WIDTH IS TAKEN FROM THE CATEGORY TRACK, NOT FROM THE LABELS. 8rem was a guess at what a
   category needs; MEASURED, the longest one on this quote ("Purchased parts" at 0.86em) is 78px, so
   6.75rem still holds it with room and the four tracks together grow by 16px rather than 36px. The
   labels keep very nearly the width they had, which is what stops this from wrapping "Engineering
   Drawings $400 x 1" on a 820px sheet to buy the total its figures.

   10.75rem AND NOT 10: a right-aligned cell that almost fills its track leaves only the 8px gap
   between itself and the figure before it, so at 155px of text in a 160px track the total read
   "$8,293.00 4.5% ($376.00)" with 5px between the two numbers. The extra 0.75rem is white space
   INSIDE the margin track, which is the only place it can go on a right-aligned grid. */
.est-scope {
  --est-col-cost: 6.5rem;
  --est-col-margin: 10.75rem;
  --est-col-sell: 6.5rem;
  --est-col-category: 6.75rem;
  --est-col-gap: 8px;
}
.est-scope .est-block .est-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
  justify-content: unset;
  align-items: baseline;
  gap: var(--est-col-gap);
}

.est-scope .est-block .est-cols .est-val {
  text-align: right;
  white-space: nowrap;
  flex-shrink: 0;
}

/* THE HEADING ROW, emitted by the surface rather than the presenter: only a surface knows it is
   showing the manufacturer a table. It uses the same grid so it cannot drift from the rows under
   it. */
.est-scope .est-colhead {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
  align-items: baseline;
  gap: var(--est-col-gap);
  padding: 0 0 4px;
  font-size: 0.78em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #8a9e9f);
  border-bottom: 1px solid var(--border-lt, #eaf0f1);
  margin-bottom: 4px;
}

.est-scope .est-colhead span + span { text-align: right; }

/* ── A CATEGORY TRACK BEFORE THE THREE FIGURES (2026-09-21, THE QUEUE) ─────────────────────────
   "Every row says which bucket it lands in." Added only when the surface asks for it (the presenter
   stamps `est-cols-cat`), so a columned tree WITHOUT one keeps its four tracks exactly. It is a
   fourth custom property and the same one template on the head, the rows and the total, because a
   heading at one width and its rows at another is the fault the columns above were built to end.
   The words are left-aligned and quiet, and cut with an ellipsis rather than wrapping: a wrapped
   category makes one row taller than the rows beside it and the columns stop reading as a table. */
.est-scope .est-block .est-cols.est-cols-cat,
.est-scope .est-colhead.est-cols-cat,
.est-scope .est-grand.est-cols.est-cols-cat {
  grid-template-columns: minmax(0, 1fr) var(--est-col-category) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
}
.est-scope .est-cols-cat .est-col-category {
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.86em;
  color: var(--muted, #8a9e9f);
}
.est-scope .est-colhead.est-cols-cat span:nth-child(2) { text-align: left; }

/* ── SIZE, QTY, UNIT, AND EITHER COST/MARGIN/SELL OR TOTAL, ON EVERY MANUFACTURER ROW
   (2026-09-28, THE QUEUE "The manufacturer view gets Size, Qty, Unit and Total columns on every
   row, groups included", decisions added to the top 16:26 WITA the same day) ─────────────────────

   ONE GRID, NOT TWO. The item's first build kept this separate from .est-cols (cost/margin/sell),
   because Tim's own table for it showed neither cost nor margin. His later decision supersedes
   that: "COLUMNS every product, margin or not: ITEM | SIZE | QTY | UNIT | COST | MARGIN | SELL." So
   `.est-cols-mfg` is now the ONE manufacturer grid; `.est-cols-mfg-margin` (stamped alongside it,
   never instead of it) widens the template to add the three money tracks in place of the single
   TOTAL track. `.est-cols` stays exactly as it was for the one surface that still uses it alone
   (By category, which has no size/qty/unit of its own to show - a category is a reading across a
   whole product, not one physical thing).

   EVERY ROW HAS A UNIT PRICE NOW (see sizeQtyUnitTotal's own comment in estimate-render.js: "Blank
   on a group that sums different things" was WITHDRAWN), so QTY and UNIT are rarely empty any more
   - a blank cell still holds its track regardless, which is what keeps the TOTAL/SELL column in one
   straight line down the page either way.

   SIZE NEVER TRUNCATES (Tim: "Size stays its OWN column and never truncates... NOT under the item
   name, it leaves gaps"). The first build cut it with an ellipsis at a fixed 8.5rem, which is
   exactly what a screenshot of a 1081.5W x 135H x 100D compartment caught mid-cut. Widened and set
   to WRAP rather than clip: a taller cell still shows everything, which is what "never truncates"
   asks for, without the extra machinery of measuring text to decide when to fall back to sitting
   beside the item name instead. */
/* ── ONE ORDER, EVERY COLUMN, EVERY PRODUCT (2026-09-29, THE QUEUE "Manufacturer view: every column
   always shows, text columns left and money right, Size inside its column, and the Specification
   beside the drawing") ──────────────────────────────────────────────────────────────────────────

   Tim: "the columns show even when they are empty", "reorder: category and size to the left, all
   the pricing to the right", and the layout he signed off:

       ITEM | CATEGORY | SIZE | QTY | UNIT | COST | MARGIN | SELL
       text on the left              money on the right

   SO THERE IS ONE TEMPLATE, NOT FOUR. The block above built a base grid and three modifiers
   (-margin, -cat, and both) because a quote without a margin had no category or money tail. Every
   manufacturer row now carries all seven cells (estimate-render.js, mfgCellsHtml), so the rows and
   the heading read ONE template and cannot drift apart by quote. It reads the same custom
   properties as .est-cols, so the money tail lines up with By category and the product total below
   it: all three grids end in cost, margin and sell at the same widths.

   CATEGORY AND SIZE ARE TEXT: left-aligned, and they WRAP rather than run on. Size used to carry
   .est-val, whose nowrap outranked the size rule's own `white-space: normal`, so "570W x 900H mm"
   on a group row, set at the group's larger size, ran out of its track and over Qty. It is no
   longer an .est-val, and `min-width: 0` lets the grid hold it to its track. 9.5rem rather than
   11rem: a leaf's three-dimension size fits one line at the leaf size, and the width returned goes
   to the item names. */
.est-scope {
  --est-col-size: 9.5rem;
  --est-col-qty: 3rem;
  --est-col-unit: 6.5rem;
  /* 7.25rem, not 6.5rem: every product total is columned now, so a no-margin quote's own figure
     meets these tracks at the total's 20px, and "$12,204.00" is 114px there (MEASURED on demo quote
     d3596a9e by verify-the-product-total-outweighs-...). The narrower sheets below set their own. */
  --est-col-cost: 7.25rem;
  --est-col-sell: 7.25rem;
}
.est-scope .est-block .est-cols-mfg,
.est-scope .est-colhead-mfg {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--est-col-category) var(--est-col-size) var(--est-col-qty)
    var(--est-col-unit) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
  justify-content: unset;
  align-items: baseline;
  gap: var(--est-col-gap);
}
.est-scope .est-block .est-cols-mfg .est-val {
  text-align: right;
  white-space: nowrap;
  flex-shrink: 0;
}
.est-scope .est-block .est-cols-mfg .est-col-category,
.est-scope .est-block .est-cols-mfg .est-col-size {
  min-width: 0;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  font-size: 0.86em;
  color: var(--muted, #8a9e9f);
  /* Regular weight on every row: a section or group row is bold for its NAME and figures, and the two
     text columns beside them read as quiet labels, not as headings (MEASURED on an ungrouped line,
     where "Uncategorised" printed in the section's bold). */
  font-weight: 400;
}
.est-scope .est-colhead-mfg span:not(:first-child) { text-align: right; }
/* The empty margin, centred: see nilCls in estimate-render.js. Every grid that carries the margin
   track, not only this one, because By category and the product total print the same dash. */
.est-scope .est-val.est-nil[class*="est-col-"] { text-align: center; color: var(--muted, #8a9e9f); }
/* THE TEXT COLUMNS' DASH (Category, Size) stays under its left-aligned heading; only a figure's dash
   is centred, because that is the one that could be misread as a minus. */
.est-scope .est-block .est-cols-mfg .est-nil:not(.est-val) { text-align: left; color: var(--muted, #8a9e9f); }
.est-scope .est-colhead-mfg span:nth-child(2),
.est-scope .est-colhead-mfg span:nth-child(3) { text-align: left; }

/* ── FIXED PROPORTIONS, SO NOTHING FLOATS (2026-09-30, THE QUEUE "Manufacturer view: a dash in every
   empty cell, and fixed column widths so nothing floats") ──────────────────────────────────────
   Tim, from a screenshot of the demo Gullwing Canopy's manufacturer view: "there is a big gap to the
   left of Category ... let's spread everything out a little bit". The Item track was the 1fr leftover,
   so every column beside it was a fixed number of rem and the name took whatever the sheet had left:
   on a wide panel that was a wide empty stretch before Category.

       ITEM 28%   CATEGORY 12%   SIZE 14%   QTY 6%   UNIT / COST / MARGIN / SELL 10% each   (Tim's)
       ITEM 28%   CATEGORY 11%   SIZE 13%   QTY 6%   UNIT 10%  COST 10%  MARGIN 12%  SELL 10%   (built)

   MARGIN TOOK 2% FROM CATEGORY AND SIZE, the "adjust if something truncates" Tim allowed: MEASURED on
   the demo Gullwing Canopy, a group's "4.3% ($387.00)" at its larger type is 130px and the product
   total's at 20px is 155px, against 135px at 10% of a 1352px sheet, so the total ran out of its track
   and the group's margin touched its cost. 12% is 162px there; a narrower sheet wraps the margin at
   its space rather than cutting it.

   Item is still the one 1fr track (the seven others are declared, and the seven gaps come out of the
   Item's share), so it is 28% less the gaps rather than a track that can overflow the sheet.

   IN cqw, NOT %, AND THAT IS THE POINT. A row's indent is padding-left on the row itself (see
   [style*="--d"] above), so a percentage track resolves against a content box that is narrower by
   one indent step per level and the money would slide left down the tree. cqw is a percentage of the
   SCOPE's width (a size container, below), the same number on every row at every depth, and on the
   heading, By category and the product total, which read these same custom properties, so all of
   them still end in cost, margin and sell at the same widths.

   BEHIND @supports and out of the phone layout: a browser with no container units keeps the rem
   tracks above, and a phone keeps its own two-line grid (below) with its own rem tracks. */
@supports (width: 1cqw) {
  @media (min-width: 721px) {
    .est-scope.qd-breakdown.est-mfg-nest,
    .est-scope.qd-product-total,
    .est-scope.qd-spend {
      container-type: inline-size;
      --est-col-category: 11cqw;
      --est-col-size: 13cqw;
      --est-col-qty: 6cqw;
      --est-col-unit: 10cqw;
      --est-col-cost: 10cqw;
      --est-col-margin: 12cqw;
      --est-col-sell: 10cqw;
    }
    /* A margin reading is two figures with a space between them ("16.7% ($387.00)"). At 10% of a
       narrow sheet, or at a group's larger type, it may not fit on one line, and a nowrap figure
       would run left over the cost beside it. It wraps at the space instead: nothing is cut off. */
    .est-scope.qd-breakdown.est-mfg-nest .est-cols-mfg .est-col-margin,
    .est-scope.qd-product-total .est-grand.est-cols .est-col-margin { white-space: normal; }
  }
}

/* ── NARROWER TRACKS WHERE THE SHEET IS NARROWER: THE LANDSCAPE PDF, AND A LAPTOP'S PANEL ─────────
   A4 landscape leaves 1017px inside its 14mm margins, and the portal frames this document in a
   panel as wide as the window. At the screen widths above, eight tracks take 872px and leave the
   item names 145px, which is what wrapped "Engineering Drawings" onto two lines in the landscape
   PDF. These give the names about 280px at 1017px. ONE set of properties, so the heading, the rows,
   By category and the product total all narrow together and stay one table.

   THE PRODUCT TOTAL'S MARGIN MAY TAKE TWO LINES here, the same allowance a phone already has below:
   it is the one cell on the row with two readings of the money in it, and the track that fits it
   on one line at the total's size is the width the names cannot spare. */
@media print, (max-width: 1180px) {
  .est-scope {
    --est-col-category: 6rem;
    --est-col-size: 8.75rem;
    --est-col-qty: 2.5rem;
    --est-col-unit: 5.75rem;
    --est-col-cost: 6.5rem;
    --est-col-sell: 6.5rem;
    --est-col-margin: 9rem;
  }
  /* 1.1rem, not 1.25rem: at 20px the total's "4.3% ($387.00)" is 155px and would have to wrap in a
     144px track. 17.6px at weight 800 still outranks the 17px/700 section heading above it. */
  .est-scope .est-grand.est-cols { font-size: 1.1rem; }
  .est-scope .est-grand.est-cols .est-col-margin { white-space: normal; }
}

@media (max-width: 720px) {
  .est-scope {
    --est-col-size: 6.2rem;
    --est-col-qty: 2.2rem;
    --est-col-unit: 4.4rem;
  }
  .est-scope .est-block .est-cols-mfg,
  .est-scope .est-colhead-mfg {
    font-size: 0.94em;
  }
}

/* A NARROW SCREEN KEEPS THE COLUMNS, NARROWER. Dropping to one column would put three figures on
   one line with nothing saying which is which, which is worse than a tight table. */
@media (max-width: 720px) {
  /* NARROWER, AND STILL ONE TEMPLATE: the same three properties re-declared on the scope, so a
     phone cannot end up with a header at one width and its rows at another. */
  .est-scope {
    --est-col-cost: 5rem;
    --est-col-margin: 6.2rem;
    --est-col-sell: 5rem;
    --est-col-category: 5.6rem;
    --est-col-gap: 5px;
  }
  .est-scope .est-block .est-cols,
  .est-scope .est-colhead {
    font-size: 0.94em;
  }
  /* ── AND THE TWO HEAVIEST TIERS WERE NOT NARROWING WITH THEM (2026-09-21) ─────────────────
     The rule above is three classes. `.est-child.est-branch` and `.est-child.est-node` are FOUR,
     so a branch subtotal and a component group kept their full 17px and 13.6px inside a table
     whose tracks had just been cut to 4.6 / 6.2 / 4.6rem. MEASURED at 420px on the demo gullwing:
     Fabrication's "$7,707.00" ran straight over the margin cell beside it and the sell figure was
     clipped at the right edge of the sheet, which is visible in
     scripts/price-breakdown-with-category-420.png as committed at f1d6621a.
     Stated at four classes so the narrow ladder actually reaches them, and in `em` against
     .est-block so the tiers keep their proportions rather than being flattened to the leaf. */
  .est-scope .est-block .est-child.est-branch.est-cols { font-size: 1.12em; }
  .est-scope .est-block .est-child.est-node.est-cols { font-size: 1.05em; }
  /* ── AND THE TOTAL SHRINKS WITH THEM (2026-09-20, THE QUEUE) ──────────────────────────────
     The rows and the heading were narrowed here and the grand total was not, because at the time
     it appeared on one surface and only when a quote carried a margin. It is now the last row of
     EVERY manufacturer breakdown, so it meets a phone.

     MEASURED on the demo gullwing at 620px before this: the three columns are 74 / 99 / 74 px and
     the total's widest cell, "$376.00 (4.5%)", was 95px of text in the 99px margin track - 4px of
     headroom on a nowrap row, which a longer percentage spends. The rows beside it had 11px.

     0.715rem rather than 0.94em: this row is sized in rem (0.76) and em here would resolve against
     the scope rather than against itself, which is the exact mistake the column widths were moved
     off em for. 0.715rem lands on 11.44px, the same pixel size as the rows above it, which is what
     "ONE SET OF COLUMNS for every row" asks for.

     ── AND A PHONE STILL HAS TO SAY WHICH ROW IS THE PRODUCT TOTAL (2026-09-21) ────────────────
     0.715rem made it the same pixel size as a leaf, which is the fault this item is about, reached
     by a different route: at 420px the biggest number on the page was a branch subtotal and the
     product total was the smallest row under it. 0.95rem sits above the narrow branch tier
     (1.12em of 0.76rem = 13.6px) by the same step 1.25rem sits above 17px on a wide screen, and
     the tracks below are sized for its margin cell at that size. Still rem, for the reason above.

     0.89rem, NOT the 0.95rem this was first written at, and the difference was measured rather than
     chosen: at 15.2px the total's "$8,293.00" is 78px in a 74px cost track, so the figure it exists
     to show would have been the one thing clipped. 14.24px lands at 73px and clears the narrow
     branch tier (12.77px) by weight 800 against 600 as well as by size. */
  /* 0.84rem and 5rem tracks (2026-09-29), from 0.89rem and 4.6rem: every product total carries the
     three columns now, margin or not, and a five-figure total ("$12,204.00") needed 81px in a 74px
     track at 0.89rem (MEASURED at 420px on demo quote d3596a9e). 13.4px at weight 800 still outranks
     the narrow branch tier (12.77px at 600). */
  .est-scope .est-grand.est-cols { font-size: 0.84rem; }
  /* AND ITS MARGIN CELL IS THE ONE THING ALLOWED TO TAKE TWO LINES. Every other cell on the row is
     a single figure that cannot break anywhere; this one is two readings of the same money with a
     space between them, so a phone gets "4.5%" over "($376.00)" instead of 119px of text driven
     through a 99px track and over the sell figure beside it. Wide screens keep it on one line,
     where the track is sized for it. */
  .est-scope .est-grand.est-cols .est-col-margin { white-space: normal; }
  /* THE CATEGORY COLUMN GOES ON A PHONE (2026-09-21). MEASURED at 420px: label + category + the three figures do
     not fit, SELL is clipped at the right edge and the labels wrap to two lines. The three figures are the
     table; the category is a second reading of the same money, and By category under the tree says it in full,
     so the narrow layout keeps the three figures and drops the extra track rather than shrinking all four. */
  .est-scope .est-block .est-cols.est-cols-cat,
  .est-scope .est-colhead.est-cols-cat,
  .est-scope .est-grand.est-cols.est-cols-cat {
    grid-template-columns: minmax(0, 1fr) var(--est-col-cost) var(--est-col-margin) var(--est-col-sell);
  }
  .est-scope .est-cols-cat .est-col-category,
  .est-scope .est-colhead.est-cols-cat span:nth-child(2) { display: none; }
  /* ── AND ON A PHONE THE MANUFACTURER ROW TAKES TWO LINES (2026-09-29) ──────────────────────
     Eight tracks cannot share 330px with a name: MEASURED at 420px on demo quote b1aadce6 with
     every column on every product, the sheet scrolled sideways to 611px and "Body" wrapped one
     word per line. So a phone reads each row as the name and its size on the first line and the
     five figures under it, on tracks that are the same on every row. Category goes, as it does on
     every other phone table (By category says it in full).

     THE INDENT MOVES FROM THE ROW TO THE NAME here, because the figures are no longer behind a
     1fr name that absorbs it: left on the row, each depth would shift its figures right by one
     step and no column would line up. The group's guide line reads --d from .est-det, not from
     this padding, so it does not move. */
  .est-scope .est-block .est-cols-mfg,
  .est-scope .est-colhead-mfg {
    grid-template-columns: var(--est-col-qty) repeat(4, minmax(0, 1fr));
    grid-template-areas: "label label label size size" "qty unit cost margin sell";
    row-gap: 1px;
  }
  .est-scope .est-block .est-cols-mfg { margin-top: 5px; } /* two-line rows need air between them */
  .est-scope.est-mfg-nest .est-block .est-cols-mfg[style*="--d"] { padding-left: 0; }
  .est-scope .est-block .est-cols-mfg > .est-label { grid-area: label; padding-left: calc(var(--d, 0) * var(--est-mfg-step, 1.2em)); }
  .est-scope .est-block .est-cols-mfg > .est-col-size { grid-area: size; text-align: right; }
  .est-scope .est-block .est-cols-mfg > .est-col-qty { grid-area: qty; }
  .est-scope .est-block .est-cols-mfg > .est-col-unit { grid-area: unit; }
  .est-scope .est-block .est-cols-mfg > .est-col-cost { grid-area: cost; }
  .est-scope .est-block .est-cols-mfg > .est-col-margin { grid-area: margin; white-space: normal; }
  .est-scope .est-block .est-cols-mfg > .est-col-sell { grid-area: sell; }
  .est-scope .est-block .est-cols-mfg .est-val { font-size: min(1em, 0.8rem); }
  .est-scope .est-colhead-mfg > span:nth-child(1) { grid-area: label; }
  .est-scope .est-colhead-mfg > span:nth-child(3) { grid-area: size; text-align: right; }
  .est-scope .est-colhead-mfg > span:nth-child(4) { grid-area: qty; }
  .est-scope .est-colhead-mfg > span:nth-child(5) { grid-area: unit; }
  .est-scope .est-colhead-mfg > span:nth-child(6) { grid-area: cost; }
  .est-scope .est-colhead-mfg > span:nth-child(7) { grid-area: margin; }
  .est-scope .est-colhead-mfg > span:nth-child(8) { grid-area: sell; }
  .est-scope .est-block .est-cols-mfg .est-col-category,
  .est-scope .est-colhead-mfg span:nth-child(2) { display: none; }
}

/* ── THE PHONE, IN THE TREE-TABLE'S TERMS (2026-09-30, THE QUEUE "Manufacturer view, full rethink") ──
   Still two sizes, set so a figure fits its track: the five figures share about 330px, and the rows'
   figures were already capped at 0.8rem (above) for exactly that. So a LINE is 0.8rem, the cap, and
   every figure on the page is the line size; a GROUP's name is one step up at 0.83rem, which keeps it
   under the product total's 0.84rem, the tier the product total must outrank.

   AND By category TAKES THE TREE'S TWO-LINE ROW. Its rows are the four-track .est-cols grid, whose name
   track is what is left after three money tracks: with the tree's indent on the row it was 17px, and
   "Labour" ran under "$3,201.00" (MEASURED at 420px). Name on the first line, the three figures on the
   second under the tree's own Cost, Margin and Sell. */
@media (max-width: 720px) {
  .est-scope.est-mfg-nest {
    --est-mfg-group: 0.83rem;
    --est-mfg-line: 0.8rem;
    --est-mfg-pad: 4px;
    --est-mfg-step: 1rem;
  }
  .est-scope.est-mfg-nest .est-block .est-cols-mfg { margin-top: 0; }
  .est-scope.est-mfg-nest .est-block .est-cols-mfg > .est-label:has(> .est-caret) {
    padding-left: calc(var(--d, 0) * var(--est-mfg-step) + var(--est-mfg-caret) + 0.3rem);
  }
  .est-scope.est-mfg-nest .est-block .est-cols {
    grid-template-columns: var(--est-col-qty) repeat(4, minmax(0, 1fr));
    grid-template-areas: "label label label label label" ". . cost margin sell";
    row-gap: 1px;
  }
  .est-scope.est-mfg-nest .est-block .est-cols > .est-label { grid-area: label; }
  .est-scope.est-mfg-nest .est-block .est-cols > .est-col-cost { grid-area: cost; }
  .est-scope.est-mfg-nest .est-block .est-cols > .est-col-margin { grid-area: margin; white-space: normal; }
  .est-scope.est-mfg-nest .est-block .est-cols > .est-col-sell { grid-area: sell; }
  .est-scope.est-mfg-nest .est-block .est-cols .est-val { font-size: min(1em, 0.8rem); }
  /* NO GUIDE LINE ON A TWO-LINE PRICE ROW: its second line starts at the left edge with Qty, and a
     nested group's guide ran straight through the quantities ("2" under Hinges, MEASURED at 420px).
     The indent of the names still carries the depth. The Specification's one-line rows keep theirs. */
  .est-scope.est-mfg-nest .est-det:has(> summary.est-cols-mfg)::before,
  .est-scope.est-mfg-nest .est-det:has(> summary.est-cols)::before { display: none; }
  /* Its indent moves from the row to the name, as the tree's does above, so the figures line up. */
  .est-scope.est-mfg-nest .est-block .est-cols.est-child[data-est-depth]:not([style*="--d"]) { padding-left: 0; }
  .est-scope.est-mfg-nest .est-block .est-cols.est-child[data-est-depth]:not([style*="--d"]) > .est-label {
    padding-left: calc(var(--est-mfg-step) + var(--est-mfg-caret) + 0.3rem);
  }
}

/* ── THE CUSTOMER'S TREE-TABLE ON A PHONE KEEPS THE LAPTOP SIZES (2026-10-03) ────────────────────────
   The manufacturer's phone sizes above (13.3px / 12.8px) are what lets FIVE figures share 330px. The
   customer's row has ONE figure, so it keeps the sizes the narrower sheets use (15px / 14px) and its
   20px step, rather than shrinking to make room for columns it does not have. A DECISION, reversible:
   delete this block and the panel follows the manufacturer's phone sizes too. */
@media (max-width: 720px) {
  .est-scope.est-mfg-nest.est-cust-tree {
    --est-mfg-group: 0.9375rem;
    --est-mfg-line: 0.875rem;
    --est-mfg-pad: 5px;
    --est-mfg-step: 1.25rem;
  }
}

/* ── HOW A LINE WAS PRICED: AN (i) BESIDE THE UNIT, MANUFACTURER ROWS ONLY (2026-10-08, THE QUEUE "Manufacturer
   breakdown: a small info mark on each priced line ...") ───────────────────────────────────────────────────────
   Light grey and only on the row being hovered; AMBER and always shown when a logged-jobs price was clamped, in the
   same amber the POA badge and the override use for "this number needs a human". Never printed. */
/* OUT OF THE FLOW [Tim, 2026-10-08]: "I want the text still to align the same as it did before without the (i) rather than
   shifting to the left." The mark hangs just past the right edge of the unit figure, so the figure keeps its exact place. */
.est-col-unit { position: relative; }
.est-how { position: absolute; left: 100%; top: 50%; transform: translateY(-50%); margin-left: 2px; line-height: 1;
  color: #9aa5a8; font-size: 0.85em; cursor: help; visibility: hidden; }
.est-how--clamped { color: #d97706; visibility: visible; font-weight: 700; }
.est-cols-mfg:hover .est-how, .est-cols-mfg:focus-within .est-how { visibility: visible; }
@media print { .est-how, .est-how--clamped { display: none !important; } }

/* ── THE MANUFACTURER BREAKDOWN ON A PHONE: CARDS, NOT A TABLE (2026-10-10, THE QUEUE "Portal on a phone", page type 3) ──────────────────
   Tim's screenshot: eight columns (item, category, size, qty, unit, cost, gross margin, sell) in 390px. Size floated, Gross margin wrapped,
   the numbers dropped under the names, "PRODUCT TOTAL" printed on top of its figure. A phone has no columns to keep, so each row becomes
   a two-tier line, the shape of Tim's sketch:

       Body                                   $2,923       the name, and the SELL (the money that counts)
       Sheet Metal . 1 x $1,888 . cost $1,888 . margin 20%  what it is made of, only the parts that have a value

   A figure with nothing to say (a dash) is not drawn; the Unit that repeats the total at quantity one and the Cost that equals the sell with no
   margin are not drawn. Each remaining figure carries its own small label, because without a column header nothing else says what it is.
   The header row goes. Above 640px, and in print, nothing here applies, so the columns are exactly as they were. */
@media (max-width: 640px) {
  .est-scope .est-colhead { display: none; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)),
  .est-scope .est-grand.est-cols {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 1px 10px;
  }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-label,
  .est-scope .est-grand.est-cols > .est-label { flex: 1 0 calc(100% - 8.5rem); min-width: 0; order: 1; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-sell,
  .est-scope .est-grand.est-cols > .est-col-sell { order: 2; flex: 0 0 auto; margin-left: auto; font-weight: 700; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-category,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-size,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-qty,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-unit,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-cost,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-margin,
  .est-scope .est-grand.est-cols > .est-col-cost,
  .est-scope .est-grand.est-cols > .est-col-margin {
    order: 3; flex: 0 0 auto; text-align: left; font-size: 0.8125rem; font-weight: 400; color: var(--muted, #6b7376);
    white-space: normal; max-width: 100%; text-indent: 0;
  }
  /* the small figures start under the NAME, not under the caret: a spacer the width of the row's indent, first on the second line */
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec))::before {
    content: ''; order: 3; flex: 0 0 calc(var(--d, 0) * var(--est-mfg-step, 1.25rem) + var(--est-mfg-caret, 1rem) + 0.3rem - 10px); height: 0;
  }
  /* THE BREAK BETWEEN THE TWO TIERS: a zero-height full-width item, ordered after the sell and before the small figures, so the figures
     always start the second line and never sit beside the sell. */
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec))::after,
  .est-scope .est-grand.est-cols::after { content: ''; order: 2; flex: 0 0 100%; height: 0; }
  /* nothing to say: not drawn (the sell keeps its dash or its POA) */
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-nil:not(.est-col-sell),
  .est-scope .est-grand.est-cols > .est-nil:not(.est-col-sell) { display: none; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-unit--same,
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-qty:has(+ .est-col-unit--same) { display: none; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-cost:has(+ .est-col-margin.est-nil),
  .est-scope .est-grand.est-cols > .est-col-cost:has(+ .est-col-margin.est-nil) { display: none; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-qty::before { content: 'Qty '; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-unit::before { content: 'Unit '; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-cost::before,
  .est-scope .est-grand.est-cols > .est-col-cost::before { content: 'Cost '; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-margin::before,
  .est-scope .est-grand.est-cols > .est-col-margin::before { content: 'Margin '; }
  .est-scope .est-block :is(.est-cols-mfg, .est-cols:not(.est-cols-spec)) > .est-col-size::before { content: 'Size '; }
}

/* THE WHY BUTTON (2026-10-13, line-why.js): an orange (i) on every POA or capped manufacturer line that opens the quote document's
   panel on a click. Sits where the hover mark sat, beside the unit. Never printed. */
.est-why { position: absolute; left: 100%; top: 50%; transform: translateY(-50%); margin-left: 2px; padding: 0 2px; border: 0;
  background: none; font: inherit; font-size: 0.85em; line-height: 1; color: #d97706; font-weight: 700; cursor: pointer; }
.est-why:hover, .est-why:focus-visible { color: #b45309; outline: 1px solid currentColor; outline-offset: 1px; border-radius: 2px; }
@media print { .est-why { display: none !important; } }
