/* ============================================================================
   PACRON — component stylesheet
   ----------------------------------------------------------------------------
   WHAT  The CONSULATE components this application needs: the crown, the queue
         band, the page header, the canvas, the white box, section header
         bands, control strips, data grids, field rows and pagination.

   WHY   CONSULATE is a Locked design system. It ships tokens and a written
         specification, not a stylesheet, so each product writes the component
         rules itself against the shared tokens. These rules are transcribed
         from 01-CONSULATE-DESIGN-SYSTEM.md and consulate-reference.html.

   HOW   Every value below is a token from tokens.css. No raw hex, anywhere.
         Load-bearing rules, from 01 section 2:
           1. border-radius is 0 everywhere, forever.
           2. Two border weights only: 2px container, 1px item.
           3. A heading is a FILLED BAND, never floating bold text.
           4. Shadow belongs to the record window and the confirm only.
           5. Status colour is TEXT ONLY. No chips, badges, pills or fills.
   ============================================================================ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; border-radius: var(--radius); }

body {
  font-family: var(--font);
  font-size: var(--t-body);
  color: var(--c-ink);
  background: var(--c-canvas);
}

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
a { color: var(--c-link); text-decoration: underline; }
:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }

.app { min-height: 100vh; display: flex; flex-direction: column; }

/* ---------- CROWN ---------------------------------------------------------
   84px total: a 44px identity row and a 40px object-tab row. The active tab
   is 42px tall so it runs 2px past the crown's own border and merges into the
   white band below. That square notch is the signature form. ------------- */
.crown { background: var(--c-crown); border-bottom: 2px solid var(--c-crown-border); flex: none; }
.crown-r1 { height: var(--h-crown-row1); display: flex; align-items: center; padding: 0 var(--gutter); }
.crest {
  width: 26px; height: 26px; background: var(--c-gold); color: var(--c-ink-invert);
  display: grid; place-items: center; font-size: 12px; font-weight: 700; flex: none;
}
.wordmark {
  font-size: var(--t-wordmark); font-weight: 700; letter-spacing: 1.2px;
  color: var(--c-ink-invert); margin-left: 12px;
}
.crown-rule { width: 1px; height: 18px; background: var(--c-crown-rule); margin: 0 14px; }
.desk { font-size: var(--t-secondary); color: var(--c-crown-ink); }
.spacer { flex: 1; }
.util { margin-left: 20px; font-size: 12px; display: flex; align-items: center; gap: 8px; }
.util a { color: var(--c-ink-invert); }
.util .sep { color: var(--c-crown-rule); text-decoration: none; }

/* Card t_20aff642. The Margin lines tickbox sits in the crown's own utility
   row, so it needs the crown's secondary ink colour rather than .tickbox's
   ordinary one, which is tuned for the white canvas below. */
.navtoggle { display: inline-flex; align-items: center; }
.util .tickbox { color: var(--c-crown-ink); font-size: 12px; }
.util .tickbox input { margin: 0; }

.crown-r2 { height: var(--h-crown-row2); display: flex; align-items: stretch; padding: 0 var(--gutter); }
.otab {
  padding: 0 22px; display: grid; place-items: center;
  font-size: var(--t-tab-object); font-weight: 600; color: var(--c-crown-ink);
  text-decoration: none;
}
.otab:hover { background: var(--c-crown-hover); color: var(--c-ink-invert); }
.otab.on {
  background: var(--c-surface); color: var(--c-crown); font-weight: 700;
  height: 42px; align-self: flex-start; position: relative; z-index: 2;
}
.otab.off { color: var(--c-crown-rule); cursor: default; }
.otab.off:hover { background: none; color: var(--c-crown-rule); }

/* ---------- QUEUE TAB BAND ------------------------------------------------
   Level 2 navigation. Every queue visible at rest with a live count. A count
   of zero prints as nothing at all. ------------------------------------- */
.qband {
  height: var(--h-queue-band); background: var(--c-surface);
  border-bottom: 1px solid var(--c-border-container);
  display: flex; align-items: stretch; padding: 0 var(--gutter); flex: none;
}
.qtab {
  padding: 0 18px; display: flex; align-items: center; gap: 6px;
  font-size: var(--t-tab-queue); font-weight: 600; color: var(--c-ink-2);
  border-bottom: 3px solid transparent; text-decoration: none;
}
.qtab .n { font-weight: 400; color: var(--c-ink-3); }
.qtab:hover { color: var(--c-ink); }
.qtab.on { color: var(--c-ink); font-weight: 700; border-bottom-color: var(--c-gold); }

/* ---------- EVIDENCE DROPDOWN + CONFIDENCE SEGMENTED CONTROL ---------------
   Card t_833ba0b1 gave the evidence ladder its own qband, wrapped onto
   however many rows ten tabs needed, with the observed/derived toggle in a
   qband of its own underneath. Card t_db4ba6b4 puts both on the one queue
   band: a <select> (which cannot wrap the way tabs did, so it never needs
   to) beside a segmented control. Two qbands become one. */
.evidenceband { height: auto; min-height: var(--h-queue-band); align-items: center; gap: 14px; padding: 6px var(--gutter); }
.evidenceband-note { border-bottom: 1px solid var(--c-border-container); }

/* The confidence toggle. Same CONSULATE grammar as .atabs - a 2px container
   border, 1px dividers between segments, the active segment a filled band,
   zero border-radius throughout this design - just sized to sit inline
   beside the dropdown instead of spanning the page. */
.seg { display: inline-flex; border: 2px solid var(--c-border-container); height: var(--h-input); flex: none; }
.segopt {
  display: flex; align-items: center; gap: 6px; padding: 0 14px;
  font-size: var(--t-tab-queue); font-weight: 600; color: var(--c-ink-2);
  text-decoration: none; border-right: 1px solid var(--c-border-item);
}
.segopt:last-child { border-right: none; }
.segopt .n { font-weight: 400; color: var(--c-ink-3); }
.segopt:hover { background: var(--c-band-header); color: var(--c-ink); }
.segopt.on { background: var(--c-crown); color: var(--c-ink-invert); }
.segopt.on .n { color: var(--c-crown-ink); }

/* ---------- ASSUMPTIONS TABS — card t_5491e45c ----------------------------
   The Assumptions screen splits into two tabs that hold DIFFERENT KINDS of
   content - arithmetic on one, genuine judgement calls on the other - so
   this does not reuse the .qtab underline idiom above, which switches
   between slices of the SAME list. Filled colour bands instead, the same
   grammar as every section heading (.strip) on this screen: the active tab
   is a filled band, not a highlighted line. Two border weights - a 2px
   container border around the whole strip, a 1px item border between the
   two tabs - and zero border-radius, like everywhere else in this design. */
.atabs {
  display: flex; border: 2px solid var(--c-border-container);
  border-bottom: none; flex: none;
}
.atab {
  flex: 1; padding: 12px 18px; text-align: center;
  font-size: var(--t-header); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; text-decoration: none;
  background: var(--c-band-service); color: var(--c-ink-2);
  border-right: 1px solid var(--c-border-item);
}
.atab:last-child { border-right: none; }
.atab .n {
  font-weight: 400; text-transform: none; letter-spacing: normal;
  margin-left: 6px; color: var(--c-ink-3);
}
.atab:hover { background: var(--c-band-header); color: var(--c-ink); }
.atab.on { background: var(--c-crown); color: var(--c-ink-invert); }
.atab.on .n { color: var(--c-crown-ink); }
.atab-note .note { font-style: italic; color: var(--c-ink-2); }

/* ---------- PAGE HEADER ---------------------------------------------------- */
.phead {
  min-height: var(--h-page-header); background: var(--c-surface);
  border-bottom: 1px solid var(--c-border-container);
  display: flex; align-items: center; padding: 8px var(--gutter); gap: 12px; flex: none;
}
.ptile {
  width: 32px; height: 32px; background: var(--c-crown); color: var(--c-ink-invert);
  display: grid; place-items: center; font-size: 16px; font-weight: 700; flex: none;
}
.eyebrow {
  font-size: var(--t-eyebrow); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--c-ink-3);
}
.h1line { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
h1 { font-size: var(--t-h1); font-weight: 700; }
.countline { font-size: var(--t-secondary); color: var(--c-ink-2); }
.countline .good { color: var(--c-good); font-weight: 700; }

.btn {
  height: var(--h-button); padding: 0 16px; font-size: var(--t-body);
  background: var(--c-surface); border: 1px solid var(--c-border-control);
  display: grid; place-items: center; text-decoration: none; color: var(--c-ink);
}
.btn.primary {
  background: var(--c-crown); color: var(--c-ink-invert);
  border-color: var(--c-crown); font-weight: 600;
}

/* ---------- CANVAS + BOX --------------------------------------------------
   The whole product is ONE white rectangle on a tinted field. Never put
   content directly on the canvas. ---------------------------------------- */
.canvas { flex: 1; padding: 16px var(--gutter); }

/* Card t_68799fdd. Set on <body> by an inline script the instant the tiers-
   by-distance page decides it has a scroll offset to restore, and taken off
   again by app.js once that offset has been applied — so the page never
   paints at the top before jumping to where the reader actually was. */
.restoring-scroll { visibility: hidden; }

.box {
  background: var(--c-surface); border: 2px solid var(--c-border-container);
  display: flex; flex-direction: column; margin-bottom: 16px;
}

/* A heading is a filled band. Section strips use --c-band-header; column
   headers, group headers and pagination use --c-band-service. */
.strip {
  min-height: var(--h-section-strip); background: var(--c-band-header);
  display: flex; align-items: center; gap: 10px; padding: 0 var(--pad-box);
  font-size: var(--t-header); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--c-ink-on-band);
}
.strip .strip-note {
  font-size: var(--t-secondary); font-weight: 400; text-transform: none;
  letter-spacing: normal; color: var(--c-ink-2); margin-left: auto;
}

/* ---------- THE SECTION BAND'S RIGHT-HAND SHELF — card t_6273cc11 ----------
   WHAT  A `.strip-note` that holds SEVERAL things instead of one sentence:
         the counts that state what the screen is a screen of, the standing
         caveat about a number on it, and the folded explanations that used
         to sit in bands of their own between the filters and the table.

   WHY   Joe, about the two bands under the /items filter row: "Also there are
         two boxes underneath. We have to find a different place for it, maybe
         on the side on the top. Make it shorter and put it on the top because
         that column heading should be right under the filter. It's taking up
         too much real estate space again."

         "On the side on the top" is this — the right-hand end of the section
         band that is already there. Nothing new is added to the page's
         height: the band existed, the space to its right was empty, and the
         two `.ghead` bands and two note bands that used to follow the filter
         row are gone from the flow altogether. That is what puts the table's
         column headings directly under the filter row.

         Everything on this shelf is ABOVE the pinned control strip, so it
         all scrolls away like ordinary page content and none of it is
         charged against the height the table gets once the strip pins.

   HOW   A flex row that WRAPS, right-aligned, pushed to the far end of the
         band by `margin-left: auto` exactly as a plain `.strip-note` is. It
         resets the band's uppercase, bold, letter-spaced heading type back
         to ordinary note type for everything inside it, so a sentence reads
         as a sentence and a disclosure arrow reads as a disclosure arrow.

         `min-width: 0` lets the shelf be squeezed narrower than its longest
         line and wrap inside itself rather than forcing the band wider than
         the window at 1280. `align-items: baseline` lines the arrows up with
         the sentences beside them rather than with the middle of the band.
   -------------------------------------------------------------------------- */
.strip .strip-shelf {
  margin-left: auto; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end;
  gap: 2px 16px; padding: 4px 0; text-align: right;
  font-size: var(--t-secondary); font-weight: 400; text-transform: none;
  letter-spacing: normal; color: var(--c-ink-2);
}

/* A disclosure ON the shelf. Closed — which is how it is at rest — it is one
   more item on a row that already existed and costs the screen no height at
   all, the same bargain `.cstrip .stripdisc` strikes on a control strip. The
   two lines are for the OPEN state: `max-width` stops the paragraph turning
   into one line the width of the window, and `min-width: 0` lets the item be
   squeezed rather than forcing the band wider. The body is left-aligned
   because a wrapped paragraph ragged on the left is very hard to read. */
.strip .strip-shelf .disc { max-width: 560px; min-width: 0; }
.strip .strip-shelf .disc-body { text-align: left; }
.ghead {
  min-height: var(--h-group-header); background: var(--c-band-service);
  display: flex; align-items: center; padding: 4px var(--pad-box);
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.6px; color: var(--c-ink-2);
}

/* ---------- CONTROL STRIP (filters) --------------------------------------- */
.cstrip {
  min-height: var(--h-control-strip); display: flex; align-items: center;
  gap: 10px; flex-wrap: wrap; padding: 6px var(--pad-box);
  border-bottom: 1px solid var(--c-border-item);
}
.cstrip label { font-size: var(--t-control); color: var(--c-ink-2); }
.cstrip .fieldgroup { display: flex; align-items: center; gap: 6px; }
/* The standard-vs-custom filter is the only MULTI-select control in the
   product, so its four tick boxes are enclosed in a 1px item border to read
   as one control rather than four loose ones. No new border weight, no
   radius: the same grammar as everything else. */
.cstrip .clsfilter {
  border: 1px solid var(--c-border-item); padding: 3px 8px; gap: 10px;
  background: var(--c-band-service);
}
.cstrip .clsfilter .tickbox { font-weight: 600; color: var(--c-ink); }

/* ---------- ONE STRIP, TWO JOBS — card t_1eff36c7 -------------------------
   WHAT  Two group boxes for a control strip that carries the FILTERS and the
         ACTIONS on one line. `.cactions` holds the action half; each
         `.cgroup` inside it is ONE action — its label, its control and its
         button — held together so a narrow window wraps whole actions rather
         than shedding a button onto a line away from the tickbox it belongs
         to. That is what happened at 1280 before `.cgroup` existed: "Show
         only items with a suggested match" finished one line and "22 items
         have one. Apply suggested match" began the next.

   WHY   /capacity-gaps had three control strips stacked vertically - the
         search, the capacity dropdown with its Apply, and the suggested-match
         tickbox with its Apply. Joe, scrolled down the queue: "when I scroll,
         everything is, and the whole filter is hidden, all the action items
         are hidden... I think we need to combine this together with the
         action, or at least close to it."

         Pinning three stacked strips to the top of the window would have
         answered the letter of that and cost him a third of the screen. So
         they are one strip, and the space to the right of the search - which
         was empty on every one of those three rows - is where the actions
         went. It costs the page no vertical space at all: three rows became
         one at 1920.

   HOW   `.cstrip` is already a wrapping flex row, so this adds nothing to the
         layout model. `.cactions` is a nested flex row that WRAPS, with the
         same gap as its parent so the controls sit at the same rhythm as the
         ones outside it; each `.cgroup` inside it does NOT wrap, which is what
         keeps one action whole. Both are layout only: no control changes what
         form it belongs to, what it posts, or where it falls in tab order by
         being inside them.

         Measured on the live screen: at 1920 the strip is 87px and the table
         gets 1,032 of a 1,080px window; at 1280 it is 127px and the table
         gets 712 of 800. Before this card the three strips scrolled away
         entirely and the table got 840 and 560.
   -------------------------------------------------------------------------- */
.cstrip .cactions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.cstrip .cgroup {
  display: flex; align-items: center; gap: 10px; flex-wrap: nowrap;
}
.sel, .inp {
  height: var(--h-input); border: 1px solid var(--c-border-control);
  background: var(--c-surface); padding: 0 8px;
  font-size: var(--t-control); font-weight: 600; color: var(--c-ink);
  font-family: var(--font);
}
.inp { font-weight: 400; }
/* Long customer names must not stretch the control strip off the page. */
.sel { max-width: 220px; }
.go {
  height: var(--h-input); padding: 0 16px; background: var(--c-gold);
  color: var(--c-ink-invert); font-size: var(--t-secondary); font-weight: 700;
}

/* ---------- THE CHECKBOX DROPDOWN — card t_4adc06ee, REVERSED BY t_297d0a26
   Joe: "Maybe the standard-or-custom and cost-of column should also be this
   new idea of having a dropdown with checkboxes next to it." Built once here
   and used for every checkbox-group filter on /items, /margin-lines and
   /tier-distance - freight type, custom cost, tier, evidence level, distance.

   The browser's own <details>/<summary>, the same idiom .disc already uses,
   so it needs no script to open or close and stays keyboard-operable. The
   closed <summary> ALWAYS states where the filter stands - "Tiers: all" or
   "Tiers: 3 of 6" - computed server-side from the filter actually in force,
   never from an unsubmitted tick, so it is correct with JavaScript off too.

   THE OPEN BODY FLOATS OVER THE PAGE - WHEN JAVASCRIPT IS RUNNING. It is
   absolutely positioned against the <details>, so opening or closing a filter
   moves nothing else on the screen by a single pixel. With JavaScript off it
   flows in the document instead and pushes the page apart, which is the older
   behaviour and is deliberate; the JAVASCRIPT OFF paragraph below says why.

   WHAT THIS COMMENT USED TO SAY, AND WHY IT IS NOW WRONG.
   Card t_4adc06ee wrote here that the body deliberately FLOWED IN THE
   DOCUMENT the way .disc-body does, and did not float. The stated reason:
   with ten filters on one control strip the strip wraps onto a second line
   at ordinary widths, so a floating panel opening under a first-line control
   would sit on top of whatever the SECOND line holds - on the margin grid,
   the Go button itself - and a reader who opened the Tier dropdown could no
   longer reach Go without closing it again. Growing the strip's own height
   instead pushed the wrapped line down rather than covering it.

   Joe overruled that in card t_297d0a26: "This new drop-down with the
   checkboxes - the whole screen jumps. It doesn't hover like a drop-down.
   When there's a regular drop-down it hovers, it doesn't move all the
   controllers." He is right, and a native <select> option list has floated
   over the page since the first browser that had one.

   The Go-button objection is answered rather than ignored, by the three
   close behaviours card t_297d0a26 added in app.js's wireCheckboxDropdowns():
   opening a second dropdown closes the first, clicking anywhere outside
   closes it, and Escape closes it. That is exactly what a native dropdown
   does, so a panel is never STUCK over the Go button - it goes away the
   moment the reader reaches for anything underneath it. The old objection
   was really an objection to a panel that could only be dismissed by going
   back to the control that opened it, and that panel no longer exists.

   JAVASCRIPT OFF - THE ONE PLACE THE OLD OBJECTION STILL BITES.
   Every one of those three close behaviours is script. Turn JavaScript off
   and a floating panel has none of them, so it is precisely the panel the old
   objection described: on /tier-distance and /margin-lines the control strip
   wraps, an Evidence level panel opened from the first row lands squarely on
   the Go button, and the only way out is to find the summary again and click
   it a second time. That was measured on the live site, not imagined - a real
   click on the Go button was intercepted by the panel and timed out.

   So the float is scoped under `.js`, a marker an inline script at the top of
   base.html's <head> puts on <html> as the page's very first act. No script,
   no marker, no float: the panel flows in the document exactly as it did
   before card t_297d0a26 and nothing is ever covered. Card t_297d0a26's brief
   allowed that fallback in as many words - "with JS off the panel may fall
   back to pushing the page apart - that is acceptable". The float is a trade:
   nothing on the page moves, paid for by close behaviours. Where the close
   behaviours cannot exist, the trade is not available and is not taken.

   z-index 50 is deliberate and has to stay above 2: the scrolling table
   pane's sticky column headers carry z-index 2 (see .tablescroll below) and
   the active object tab in the crown carries z-index 2 as well, and either
   of those would otherwise paint straight over an open panel. Nothing on the
   path from .ddown-body up to <body> creates a stacking context or clips
   overflow - measured, not assumed - so one z-index here is enough and no
   ancestor has to be given `overflow: visible` to let the panel out.

   No box-shadow: rule 4 in tokens.css reserves that for exactly two surfaces,
   and this dropdown is not one of them. The 2px container border is what
   gives the open list its edge instead. -------------------------------- */
.ddfilter { display: inline-flex; align-items: flex-start; gap: 6px; }
/* The panel's positioning parent. It is the <details> rather than the
   surrounding .ddfilter span so that a right-aligned panel lines up with the
   right edge of its own SUMMARY, not with the far edge of the ↺ Clear link
   sitting beside it. Left unscoped rather than put under `.js`: with the
   panel in flow there is nothing absolutely positioned inside a .ddown for
   this to be the parent of, so with JavaScript off the line changes nothing
   whatsoever. */
.ddown { position: relative; }
.ddown > summary.ddsum {
  height: var(--h-input); border: 1px solid var(--c-border-control);
  background: var(--c-surface); padding: 0 10px; display: flex;
  align-items: center; gap: 6px; white-space: nowrap; cursor: pointer;
  list-style: none; font-size: var(--t-control); font-weight: 600;
  color: var(--c-ink); font-family: var(--font);
}
.ddown > summary.ddsum::-webkit-details-marker { display: none; }
.ddown > summary.ddsum::after { content: "\25BE"; color: var(--c-ink-3); font-size: 10px; }
.ddown[open] > summary.ddsum { border-color: var(--c-crown); }
.ddown[open] > summary.ddsum::after { content: "\25B4"; }
.ddown > summary.ddsum:focus-visible {
  outline: var(--focus-ring); outline-offset: var(--focus-offset);
}
/* The open panel itself. Everything here - its size, its own scrolling, its
   2px container border, its lack of a shadow - is what it looks like in BOTH
   worlds. Only where it sits differs, and that is the .js rule below. This
   base rule is the JavaScript-off one: `margin-top: 2px` and no positioning,
   so the panel flows in the document and grows the control strip, exactly as
   it did before card t_297d0a26. */
.ddown-body {
  margin-top: 2px;
  min-width: 220px; max-width: 340px; max-height: 240px; overflow-y: auto;
  background: var(--c-surface); border: 2px solid var(--c-border-container);
  padding: 8px 10px; display: flex; flex-direction: column; gap: 6px;
}
/* THE FLOAT, and it only happens when JavaScript is actually running. `.js`
   is put on <html> by a one-line inline script at the top of base.html's
   <head> - see the comment there - so it is already on the element before the
   first paint and there is never a flash of the in-flow layout. See the
   JAVASCRIPT-OFF paragraph in the block above for why the float is tied to
   the script this way. */
.js .ddown-body {
  position: absolute; top: calc(100% + 2px); left: 0; z-index: 50;
  margin-top: 0;
}
/* Right-aligned to its summary instead of left-aligned, for a filter sitting
   close enough to the right-hand edge that a left-aligned panel would hang
   off the window. app.js measures each panel as it opens and adds this class
   only when it is actually needed; see flipPanelIfOffscreen(). Scoped under
   .js as well, because only a floating panel can hang off anything and only
   JavaScript ever adds the class. */
.js .ddown-body.ddown-body-right { left: auto; right: 0; }
/* A WIDER panel, for the one dropdown whose contents are sentences rather
   than tick boxes: the sample questions on /ask (card t_5147e2fd). The shared
   rule's 340px was measured against option labels like "Pickup" and "Tier 3";
   a seven-entry list of whole questions inside it wraps three times an entry
   and turns a glance into a reading exercise. This is a modifier rather than
   an edit to .ddown-body because three other screens depend on that width and
   none of them wants this one. max-height goes up with it for the same
   reason — seven questions, each with its own "what this one answers" fold,
   is more than 240px of list — but it is still capped and still scrolls, so
   the panel can never grow past the window.

   The width is set by min-width, not by max-width, and that is not an
   oversight. A floating panel is sized shrink-to-fit inside its positioning
   parent, and the parent here is the .ddown itself — barely wider than the
   summary — so the available width the browser offers the panel is tiny and
   max-width never comes into play at all. min-width is the only one of the
   pair that can make this panel wider; max-width stays as the guard that
   keeps it off a small window. Measured, not assumed: at min-width 320px the
   panel came out exactly 320px wide and the seventh question sat below the
   fold. */
.ddown-body.ddown-body-wide {
  min-width: 480px; max-width: 520px; max-height: 420px;
}
/* The list sits directly under the paragraph above it, which already carries
   the gap the flex column gives every child; the extra top margin .asklist
   has for its life on a page would double it. */
.ddown-body-wide .asklist { margin-top: 0; }
.ddown-body-wide p { margin: 0; }
.ddown-body .tickbox { font-weight: 400; color: var(--c-ink); white-space: nowrap; }
/* The per-group clear. Only ever rendered when that one group is actually
   filtering - see web/templates/_furniture.html's checkbox_dropdown() macro -
   so its presence on screen already means something without reading its
   text. The word "Clear" sits beside the circular-arrow icon rather than the
   icon alone, because a bare circular arrow reads as "reload the page" to a
   lot of people; Joe picked this icon off a sheet of five. */
.ddclear {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: var(--t-control); font-weight: 600; color: var(--c-link);
  text-decoration: underline; white-space: nowrap;
}
.ddclear-icon { font-size: 12px; text-decoration: none; }
/* "Clear all filters", plain text at the end of the row, visible only when
   at least one filter anywhere on the screen is on - see any_filters_on() in
   web/app.py. */
.clearall { font-size: var(--t-control); font-weight: 600; color: var(--c-link); }

/* ---------- DATA GRID -----------------------------------------------------
   No zebra striping and no vertical column rules: both were considered and
   removed by the design system. Rows are 44px on purpose. ---------------- */
.grid { width: 100%; border-collapse: collapse; }
.grid thead th {
  height: var(--h-column-header); background: var(--c-band-service);
  font-size: var(--t-column); font-weight: 700; color: var(--c-ink-2);
  text-align: left; padding: 0 10px; white-space: nowrap;
}
.grid tbody td {
  height: var(--h-row); padding: 4px 10px; font-size: var(--t-body);
  border-bottom: 1px solid var(--c-border-item); vertical-align: middle;
}
.grid tbody tr:hover { background: var(--c-band-service); }
.grid .r { text-align: right; font-variant-numeric: tabular-nums; }
.grid .strongcell { font-weight: 600; }
.grid td.sub { font-size: var(--t-secondary); color: var(--c-ink-2); }
.grid .this-line td { background: var(--c-band-header); font-weight: 600; }

/* ---------- SELECT ALL, IN THE TABLE HEADER — card t_b2f5c626 -------------
   WHAT  The tick column's header cell: a "Select all" tickbox with its label,
         and under it the running count of how many rows are ticked.

   WHY   Joe: "when you click Select All, actually selects all that is on the
         screen." Putting it at the top of the column of ticks is what makes
         that self-evident — a box at the head of a column can only mean the
         column beneath it. The old control floated above the capacity dropdown
         and had to be read to be understood.

         The count matters because the capacity gaps screen no longer shows a
         confirmation before it writes. How many rows are about to be acted on
         has to be legible at the moment the button is pressed, not after.

   HOW   The column is widened from the 28px a bare tickbox needed to fit the
         two words beside it, and the label wraps the tickbox so clicking
         either works. The header cell is `white-space: normal` because the
         grid's default nowrap would otherwise force the count onto the same
         line as the label. Nothing here changes the sticky header: the cell is
         still a plain `th` inside `.tablescroll`, so the rule at the foot of
         this file still pins it.
   -------------------------------------------------------------------------- */
.grid thead th.tickcol {
  width: 86px;
  white-space: normal;
  line-height: 1.25;
  vertical-align: middle;
}
.grid thead th.tickcol .tickall {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--t-column); font-weight: 700; color: var(--c-ink-2);
  cursor: pointer;
}
.grid thead th.tickcol .tickall-count {
  display: block;
  font-size: var(--t-secondary); font-weight: 700; color: var(--c-crown);
}

/* ---------- PRODUCT GROUPING INSIDE ONE PANE — card t_e1bd743a -------------
   WHAT  The heading row that names a product on the tiers-by-distance report,
         and the totals row at the foot of a summary table.

   WHY   That report is read product by product, and each product carries its
         own tiers and its own distance bands underneath it. Drawing one table
         per product would have given the page a scrolling pane and a sticky
         header per product; one table with a filled heading row keeps a single
         pane, a single column header and one reachable sideways scrollbar.

   HOW   A heading is a FILLED BAND in this design system, so the row takes the
         section-header fill and the rule above it is the container weight —
         the heavier of the two — because it separates products rather than
         rows. No radius, no third border weight, no new colour: both rules use
         tokens that were already here. */
.grid tbody tr.prodhead td {
  background: var(--c-band-header);
  border-top: 2px solid var(--c-border-container);
  font-weight: 600;
}
.grid tbody tr.prodhead:hover td { background: var(--c-band-header); }
.grid tbody tr.prodhead .sub { font-weight: 400; }

.grid tfoot tr.totalrow td {
  background: var(--c-band-service);
  border-top: 2px solid var(--c-border-container);
  font-weight: 700;
}

/* Status colour is TEXT ONLY, and it is spent sparingly.
   A negative true margin is the exception worth colouring, so it prints in
   --c-urgent. An ordinary positive margin stays in ordinary ink: a column of
   green numbers is noise, and it would spend the one signal the eye has on
   the rows that are fine. --c-good is reserved for a single terminal
   statement - the coverage headline, and the margin on a drill-down. */
.pos { color: var(--c-good); }
.neg { color: var(--c-urgent); }
.grid .pos { color: var(--c-ink); }
.muted { color: var(--c-ink-3); }
.note { font-size: var(--t-secondary); color: var(--c-ink-2); padding: 10px var(--pad-box); }
.note.tight { padding: 6px var(--pad-box); }
.derived { font-size: var(--t-eyebrow); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--c-urgent); }
.actual { font-size: var(--t-eyebrow); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--c-ink-3); }

/* ---------- PAGINATION ----------------------------------------------------- */
.pag {
  min-height: var(--h-pagination); border-top: 2px solid var(--c-border-container);
  background: var(--c-band-service); display: flex; align-items: center;
  gap: 6px; flex-wrap: wrap; padding: 4px var(--pad-box);
  font-size: 12px; color: var(--c-ink-2);
}
.pg {
  border: 1px solid var(--c-border-control); background: var(--c-surface);
  padding: 3px 9px; font-size: 12px; font-variant-numeric: tabular-nums;
  text-decoration: none; color: var(--c-ink);
}
.pg.on { background: var(--c-crown); color: var(--c-ink-invert); border-color: var(--c-crown); font-weight: 700; }
.pagination-count { margin-right: 8px; }

/* ---------- FIELD ROWS (component library 5.1) ----------------------------
   Label column 180px, 13px/400, RIGHT-ALIGNED, 12px gutter. The label is not
   above the value; that is Dynamics inheritance and it is deliberate. ---- */
.fields { padding: 0 var(--pad-box); }
.c-field {
  display: flex; align-items: baseline; min-height: 34px; padding: 6px 0;
  border-bottom: 1px solid var(--c-border-field);
}
.c-field:last-child { border-bottom: none; }
.c-field__label {
  width: 220px; margin-right: 12px; flex: none; text-align: right;
  font-size: 13px; font-weight: 400; color: var(--c-ink-2);
}
.c-field__value { flex: 1 1 auto; font-size: var(--t-body); }
.c-field__value .big { font-size: 15px; font-weight: 700; }
.c-help { font-size: 13px; color: var(--c-ink-3); }

/* ---------- ARITHMETIC BLOCK ----------------------------------------------
   The sum, shown as a sum. There is no monospace anywhere in this product,
   so the working is set in the product face with tabular figures. ------- */
.sum { padding: 10px var(--pad-box); }
.sum .line { display: flex; align-items: baseline; gap: 10px; padding: 6px 0;
  border-bottom: 1px solid var(--c-border-field); flex-wrap: wrap; }
.sum .line:last-child { border-bottom: none; }
.sum .lbl { width: 220px; flex: none; text-align: right; font-size: 13px; color: var(--c-ink-2); }
.sum .calc { font-size: var(--t-body); font-variant-numeric: tabular-nums; }
.sum .calc .eq { font-weight: 700; }
.sum .why { font-size: 13px; color: var(--c-ink-3); width: 100%; padding-left: 232px; }

/* ---------- TWO COLUMNS OF BOXES ------------------------------------------ */
.cols { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.cols > * { flex: 1 1 420px; min-width: 0; }

/* ---------- A ROOMIER PAIR OF COLUMNS — card t_5247398f -------------------
   WHAT  The same two-column helper above, with a wider point at which the
         boxes give up and stack.

   WHY   The 420px basis is right for a box of field rows, which reads fine in
         a narrow column. A box holding a data TABLE needs more room than that
         before splitting the screen is an improvement rather than a squeeze.
         /tiers is the first screen to put two table boxes side by side.

   HOW   One number changes: the flex basis. Two 700px columns plus the 16px
         gap need 1416px, so on a 1920 window the pair sits side by side and on
         a 1280 window it wraps and stacks in DOM order. Nothing else about the
         helper changes and there is no second grid system. ---------------- */
.cols.roomy > * { flex: 1 1 700px; }

/* ---------- /distance: THE TALL COLUMN COMES DOWN — card t_638d3434 --------
   WHAT  `.cols.roomy.levelfit` makes a two-column row whose height is set by
         the FIRST column alone, and fits the second column into exactly that
         height, letting the table inside it scroll. Two helper classes go with
         it: `.levelfit-driver` is the column that sets the height, and
         `.levelfit-follower` is the column that is fitted to it.

   WHY   Joe, of /distance: "take this box… and move it up right under the box
         which distance — it's empty space. And the box next to it, make the
         same height, it should end the same place where this new box is going
         to end, so it looks good on the screen, doesn't look lopsided."

         On /distance the TALL column is the right one — the band ladder is
         about 1045px of rows, and the left column is about 440px even after
         the no-band box moves into it. So the plain answer, `align-items:
         stretch`, is the WRONG answer here: it makes every column as tall as
         the tallest, which would stretch the short left column down and leave
         some six hundred pixels of dead white inside it. The ends would match
         and the screen would look worse. Joe asked for the opposite — the
         bands box comes DOWN to where the left column ends.

         This is why /tiers must NOT use this modifier. There the tall column
         is the left one, it is full of controls, and the right box growing to
         meet it costs nothing; /tiers uses `.levelstretch` below.

   HOW   Three parts, and no fixed pixel or `vh` height anywhere — a hard-coded
         `calc(100vh - 240px)` on a whole column is the exact bug that broke
         /tier-distance and it is not coming back.

           1. A CONTAINER QUERY, not a media query. `.cols.roomy` gives each
              child `flex: 1 1 700px`, so the row is side by side only while
              the wrapper itself is at least 700 + 16 + 700 = 1416px wide, and
              below that it wraps and stacks. Asking the wrapper its own width
              tests exactly that condition, whatever the page gutter happens to
              be, which a window-width media query can only approximate.
           2. Inside the query the row becomes a stretch container, and the
              follower column's box is taken OUT OF FLOW (`position: absolute;
              inset: 0` inside a `position: relative` column). An out-of-flow
              box contributes no height, so the flex line's height is the
              driver column's own content height, and the follower is then
              stretched to exactly that and its box fills it.
           3. The follower's `.tablescroll` gives up the shared height ceiling
              and becomes the one part of that box that flexes, so the ladder
              scrolls INSIDE the box and the last band and the "Above the
              bands" row stay reachable.

         Outside the query — i.e. as soon as the row wraps — every one of those
         rules is off. Nothing is absolutely positioned, nothing is fitted, the
         boxes are full width in document order and the pane goes back to the
         ordinary ceiling. A browser too old for container queries lands in the
         same place, which is today's screen. ------------------------------- */
.cols.roomy.levelfit { container-type: inline-size; container-name: levelfit; }

/* The driver column is an ordinary stack of boxes. Its last box drops its
   bottom margin only in the fitted layout, so the two columns finish on the
   same pixel rather than 16px apart. */
.levelfit-driver > .box:last-child { margin-bottom: 16px; }

@container levelfit (min-width: 1416px) {
  .levelfit-driver > .box:last-child { margin-bottom: 0; }

  /* `align-self` and not `align-items` on the wrapper. A container query can
     only restyle the container's DESCENDANTS - the container itself is off
     limits, because a rule that changed its own size would be circular - so
     the stretch has to be asked for by the column that wants it. It is the
     same result: the follower is given the flex line's height, and the line's
     height is the driver's, because an out-of-flow box contributes none. */
  .levelfit-follower { position: relative; align-self: stretch; }
  .levelfit-follower > .box {
    position: absolute; inset: 0; margin: 0;
  }
  /* Everything except the scrolling pane keeps its natural height; the pane
     takes whatever is left. `min-height: 0` is what lets a flex child actually
     shrink below its content — without it the box would grow past the column
     it is supposed to match. */
  .levelfit-follower > .box > * { flex: 0 0 auto; }
  .levelfit-follower > .box > .tablescroll {
    flex: 1 1 auto; min-height: 0; max-height: none;
  }
}

/* ---------- /tiers: THE SHORT COLUMN GROWS — card t_638d3434 ---------------
   WHAT  `.cols.levelstretch` makes every column of the row as tall as the
         tallest one, so a row of two boxes finishes level at the bottom.

   WHY   Joe, of /tiers: "or stretch this box a little bit". On /tiers the tall
         column is the LEFT one — the band editor, which is all controls — and
         the right one, the tiers in force, is a short table. Growing the short
         table to meet the editor adds a little quiet space under five rows and
         costs nothing.

         /distance must NOT use this modifier. There the tall column is the
         band ladder and stretching would leave hundreds of pixels of dead
         white in the short column, which is the opposite of what was asked
         for. /distance uses `.levelfit` above.

   HOW   One line: `align-items: stretch` in place of the `flex-start` that
         `.cols` sets. No container query is needed here and none is used. Once
         the row wraps there is one box per flex line, a line is only as tall
         as the box on it, and stretching a box to its own height changes
         nothing — so the narrow layout is untouched by construction rather
         than by a second rule. ------------------------------------------- */
.cols.levelstretch { align-items: stretch; }

/* ---------- A TABLE THAT DOES NOT JUSTIFY ITS COLUMNS — card t_5247398f ---
   WHAT  Two modifiers on `.grid`. `.grid.hug` makes a table only as wide as
         its own cells need, instead of stretching to its container.
         `.grid.hug-last` keeps the table full width but lets the LAST column
         swallow every spare pixel, so each column before it is sized to its
         own content and they sit together at the left.

   WHY   `.grid` is `width: 100%`, which is right for a table of many columns
         filling a screen and wrong for a table of three. On /tiers the band
         editor's three cells were justified across 1880px: the tier name at
         the far left, the "From" box near the middle of the screen and the
         "Up to" box out at the right edge, with a blank gutter of several
         hundred pixels between them. Joe: "It's too much empty space on this
         page." The empty space was INSIDE the boxes, not around them, so
         halving the box width on its own would have made it worse.

   HOW   `width: auto` hands the table back to the browser's automatic layout,
         which sizes each column to its content and then stops. `hug-last`
         gets the same effect for a table whose final column is a sentence
         rather than a figure: a cell asked for 100% of the table takes
         whatever is left once the other columns have taken what they need,
         which pulls the narrow columns together at the left and gives the
         prose the remainder. Numbers are untouched by either rule — they keep
         their right alignment and their tabular figures, and nothing here can
         make a figure fold. ---------------------------------------------- */
.grid.hug { width: auto; }
.grid.hug-last th:last-child,
.grid.hug-last td:last-child { width: 100%; }
/* The row's NAME is not spare space. Asking the last column for 100% makes the
   browser squeeze every other column towards its narrowest possible width, and
   the first column is the one with a name in it — "Tier 1" came out folded onto
   two lines and "In no tier" onto three. Holding the name on one line hands
   that column its natural width back. A `.sub` note underneath the name is a
   sentence and is allowed to fold as sentences do. */
.grid.hug-last th:first-child,
.grid.hug-last td:first-child { white-space: nowrap; }
.grid.hug-last td:first-child .sub { white-space: normal; }

.breadcrumb { font-size: var(--t-secondary); color: var(--c-ink-2); }
.breadcrumb a { color: var(--c-link); }

/* ---------- PHASE 2 COMPONENTS --------------------------------------------
   Everything below is built from the same tokens and the same five rules as
   the components above: no radius, two border weights, headings are filled
   bands, no shadow outside the record window, and status colour is text only.
   Nothing here introduces a chip, a pill or a coloured row. -------------- */

/* A refusal is a heading band like any other heading band. The only thing
   that marks it out is the ink, because status colour in CONSULATE is text. */
.strip.refused { color: var(--c-overdue); }
.bigrefusal { font-size: 15px; font-weight: 700; color: var(--c-ink); }

/* ---------- ITEMS SCREEN --------------------------------------------------
   Rows here are taller than a normal 44px data row because each one carries
   two editable values, both of their originals, and an audit trail. The row
   height token still governs the minimum; the content decides the rest. */
.grid.items tbody td { vertical-align: top; padding-top: 10px; padding-bottom: 10px; }
.grid.items tbody tr:hover { background: none; }

.valuenow { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* "your correction" is a sentence fragment in accent ink, not a badge. */
.yours {
  font-size: var(--t-eyebrow); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.7px; color: var(--c-gold); margin-left: 8px;
}
.grid td .sub { font-size: var(--t-secondary); color: var(--c-ink-2); }
.grid td .sub.warn { color: var(--c-ink-3); }

/* An edit control sits inside the cell it edits, on its own line. */
.editrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.editrow .go:disabled,
.editrow .inp:disabled { opacity: 0.45; cursor: not-allowed; }
.tickbox { font-size: var(--t-control); color: var(--c-ink-2); display: flex; align-items: center; gap: 4px; }
.btn.small { height: var(--h-input); padding: 0 12px; font-size: var(--t-secondary); }

/* ---------- ASSUMPTIONS ---------------------------------------------------
   A field row per rule. The label column carries the rule's own title, so it
   is a little wider than the standard 220px and reads left-aligned. */
.c-field.assumption { align-items: flex-start; padding: 10px 0; }
.c-field.assumption .c-field__label {
  width: 280px; text-align: left; font-weight: 700; color: var(--c-ink);
}
.c-field.assumption .c-field__label .sub {
  font-size: var(--t-eyebrow); font-weight: 400; color: var(--c-ink-3);
  text-transform: uppercase; letter-spacing: 0.7px;
}
.ruledetail { margin-top: 6px; }

/* ---------- PHASE 3: THE QUESTION BOX -------------------------------------
   Same five rules as everything above: no radius, two border weights,
   headings are filled bands, no shadow outside the record window, status
   colour is text only. Nothing here is a chat bubble — an answer is rendered
   in the product's own field rows, arithmetic blocks and data grids, because
   the answer IS the product's data and should look like it. */

/* The question field is the one control on the page, so it takes the room the
   filter controls elsewhere have to share. */
.askfield { flex: 1 1 520px; }
.askfield .inp { width: 100%; min-width: 240px; }

/* The example questions and the follow-ups are a plain list of links. They are
   not buttons: pressing one is asking a question, which is navigation. */
.asklist { padding-left: 18px; margin-top: 6px; }
.asklist li { padding: 3px 0; }

/* The fact packet, printed in full. Whitespace is preserved because it is JSON
   and its shape carries meaning, but the face is the product's own: there is
   no monospace font anywhere in this product and this is not the exception. */
.packet {
  font-family: var(--font);
  font-size: 12px;
  line-height: 1.5;
  color: var(--c-ink-2);
  background: var(--c-band-service);
  border-top: 1px solid var(--c-border-item);
  padding: 12px var(--pad-box);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 460px;
  overflow: auto;
}

/* ---------- SCREEN 8: THE RAW TABLE BROWSER --------------------------------
   Built from the same five rules as everything above. Nothing new is
   invented here: the grid is the product's grid, the filter row sits inside
   the column-header band because it belongs to the columns, and the only
   genuinely new thing is horizontal scrolling — a table with 32 columns has
   to go somewhere, and the alternative is hiding columns, which is exactly
   what this screen must not do. */

/* The table picker holds every table in the database, so it needs more room
   than the 220px a filter select gets. */
.sel.wide { max-width: 420px; }

/* The capacity dropdown on the gaps screen. Card t_046c6208 change 2 put the
   code, its description from Pacron's capacity sheet and its units per truck
   in every option, which is a long line; at 420px the description was cut off
   at the very moment it was added, which would have defeated the point of the
   change. */
.sel.widest { max-width: 640px; }

/* One table may be wider than the screen. The box does not scroll; the grid
   inside it does, so the section band, the control strip and the pagination
   all stay put. The rules that make that pane work — the fixed height, the
   column header that stays put, the second scrollbar above it — are shared by
   every screen and live in the SCROLLING TABLE PANE section at the foot of
   this file. */

/* Column headings carry the real database name under the readable one. The
   heading is a sort link, so it is underlined like every other link in the
   product. */
.grid.raw { --rawhead-h: 46px; }
.grid.raw thead th {
  vertical-align: top; padding-top: 6px; padding-bottom: 6px;
  /* A fixed height, not `auto`, because the filter row underneath is pinned
     at exactly this offset when the pane is scrolled. See the SCROLLING TABLE
     PANE section at the foot of this file. */
  height: var(--rawhead-h);
}
.grid.raw thead th a { color: var(--c-ink-2); }
.colname {
  font-size: var(--t-eyebrow); font-weight: 400; color: var(--c-ink-3);
  text-transform: none; letter-spacing: normal; margin-top: 2px;
}
.sortmark { color: var(--c-gold); font-weight: 700; }

/* The filter row is part of the column-header band, not part of the data. */
.grid.raw .filterrow td {
  background: var(--c-band-service); height: auto; padding: 4px 6px;
  border-bottom: 1px solid var(--c-border-container); white-space: nowrap;
}
.sel.tiny, .inp.tiny { height: 24px; font-size: 12px; padding: 0 4px; }
.sel.tiny { max-width: 118px; }
.inp.tiny { width: 118px; }

/* Raw rows are single-line so a wide table stays scannable. The full text of
   anything trimmed is in the cell's tooltip. */
.grid.raw tbody td {
  height: auto; padding: 7px 10px; white-space: nowrap;
  max-width: 520px; overflow: hidden; text-overflow: ellipsis;
}
.grid.raw tbody td.r { font-variant-numeric: tabular-nums; }

/* A numeric column's heading is right-aligned, so its filter control belongs
   under the heading rather than at the far left of the same cell. */
.grid.raw .filterrow td.r { text-align: right; }

/* ---------- SCREEN 8: THE COLUMN PICKER -----------------------------------
   Same five rules as everything else. The picker is part of the page, not a
   drawer and not a modal, because CONSULATE has neither. It is a list of
   items, so it gets the 1px item border and nothing heavier. */

.picker {
  max-height: 460px; overflow-y: auto;
  border-bottom: 1px solid var(--c-border-container);
}
.pickrow {
  display: flex; align-items: center; gap: 10px;
  padding: 5px var(--pad-box);
  border-bottom: 1px solid var(--c-border-field);
  font-size: var(--t-secondary);
}
.pickrow:last-child { border-bottom: none; }
/* A column that is currently out of the way is shown in secondary ink while
   "Show hidden" is on, so the eye can tell it from the ones already in play.
   Status colour in CONSULATE is text, never a fill, so this is ink only. */
.pickrow.was-hidden .pickname { color: var(--c-ink-3); }
.pickname { font-weight: 600; min-width: 220px; }
.picktype { color: var(--c-ink-3); font-size: var(--t-eyebrow);
  text-transform: uppercase; letter-spacing: 0.7px; min-width: 190px; }
.picknote { color: var(--c-ink-2); }

/* The filter list for columns that are not in the grid. Same controls as the
   inline filter row, laid out as a wrapping list rather than a table row. */
.offfilters {
  display: flex; flex-wrap: wrap; gap: 8px 16px;
  padding: 8px var(--pad-box);
  border-bottom: 1px solid var(--c-border-item);
}
.offrow { display: flex; align-items: center; gap: 6px; }
.offname { font-size: var(--t-control); color: var(--c-ink-2); font-weight: 600; }

/* A statement that something is affecting the numbers off screen. Status
   colour is TEXT ONLY - no fill, no chip, no coloured row. */
.warnline { color: var(--c-overdue); }
.warnline strong { color: var(--c-overdue); }

/* The list of reasons a save was refused - card t_68f81c25. One line per
   fault, each naming the exact numbers that are wrong. Status colour is TEXT
   ONLY, in keeping with the rule above: no fill, no chip, no coloured row. */
.reasons { margin: 6px 0 0; padding-left: 18px; }
.reasons li { margin: 3px 0; }

.sep-dot { color: var(--c-ink-3); }

/* ---------- BREADCRUMB TRAIL (change 2) -----------------------------------
   WHAT  The row of clickable steps across the top of every entity screen,
         sitting between the page header and the canvas.
   WHY   The client asked to walk down from an order line into a Bill of
         Lading, an item, a capacity record, a salesperson or a price tier and
         climb back out the way he came. The trail is the path he actually
         took, so it has to sit where a path belongs: above the content, on
         every one of those screens, at rest.
   HOW   Built from the tokens like everything else. It is a service band, the
         same pale surface the pagination and the column headers use, with a
         1px item rule under it. No radius, no chip, no fill behind the
         current step - the step you are standing on is simply heavier ink,
         because in CONSULATE state is expressed as type, never as colour. */
.crumbs {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0 8px;
  padding: 7px var(--pad-box);
  background: var(--c-band-service);
  border-bottom: 1px solid var(--c-border-item);
  font-size: var(--t-secondary);
}
.crumbs .crumb { color: var(--c-ink-2); }
.crumbs a.crumb { color: var(--c-link); }
.crumbs .crumb.on { color: var(--c-ink); font-weight: 700; }
.crumb-sep { color: var(--c-ink-3); }

/* A step in the address that could not be used says so, in the same service
   band, in status ink. Never a fill, and it never repeats the bad text back. */
.crumb-note {
  padding: 6px var(--pad-box);
  background: var(--c-band-service);
  border-bottom: 1px solid var(--c-border-item);
  font-size: var(--t-secondary);
  color: var(--c-overdue);
}

/* The "open the thing this figure came from" link that sits in a heading
   band. It is a link in a band, so it inherits the band's type and only the
   colour changes. */
.strip-note a { color: var(--c-link); }

/* ---------- SCROLLING TABLE PANE ------------------------------------------
   WHAT  Turns any data table wrapped in `.tablescroll` into a pane with its
         own scrollbars: the column header stops at the top of the pane and
         stays there while the rows slide underneath it, and a horizontal
         scrollbar sits at the foot of the pane instead of at the foot of the
         page.

   WHY   The client asked for exactly this. On a wide table — the source-table
         browser can be fifty-odd columns — he lost the column heading as
         soon as he scrolled, so he could no longer tell which column he was
         reading, and the only way to go left or right was to scroll past
         several hundred rows to reach the bottom of the page. Both of those
         are the page scrolling when the TABLE should have been scrolling.

   HOW   Three parts, all CSS, none of them a JavaScript table library:
           1. `.tablescroll` gets a height ceiling and `overflow: auto`, which
              makes it a scroll container in both directions. The rows scroll
              inside it; the section band, the control strip and the
              pagination around it never move.
           2. `position: sticky; top: 0` on every header cell pins the header
              to the top of that pane. Sticky is pinned on the TOP axis only,
              so when the pane is scrolled sideways the header travels with
              the columns and stays over its own data. That is the whole
              reason for using sticky rather than a floating copy of the row.
           3. A sticky cell is painted over live rows, so it needs a solid
              fill of its own or the text underneath shows through it. Every
              header cell gets the service band colour explicitly, and its
              bottom rule is drawn as an inset shadow because a collapsed
              table border belongs to the table, not to the cell, and would
              be left behind when the cell sticks.

         The ceiling here is expressed against the viewport so the pane is as
         tall as the screen allows, minus the crown, the queue band, the page
         header and the box furniture above it. Nothing is clipped: everything
         the pane cannot show is reachable by scrolling the pane, and when the
         pane runs out of rows the wheel carries on scrolling the page as
         usual.

         240px is a rough allowance and it is only good enough for the small
         secondary panes on these screens — a five-row tier ladder, a history
         list. The one big grid on a screen is marked `.screenpane` instead
         and gets a measured ceiling; see the block immediately below.
   -------------------------------------------------------------------------- */
.tablescroll {
  overflow: auto;
  max-height: calc(100vh - 240px);
  /* A sideways flick on a trackpad must not be read as a "go back" gesture
     once the pane has run out of columns. Vertical chaining is deliberately
     left alone so the page still scrolls on when the rows run out. */
  overscroll-behavior-x: contain;
}

/* ---------- THE PANE THE SCREEN IS BUILT AROUND — card t_191d4a18 ---------
   WHAT  The one big grid on a screen. It is an ordinary `.tablescroll` pane —
         rows scroll inside it, its column header pins to its own top, its
         horizontal scrollbar is at its own foot — with a ceiling MEASURED
         from the two things that have to share the window with it: the
         control strip that pins above it, and the pagination below it. The
         result is that the pane ends before the bottom of the window and the
         pager sits under it, in view, at the same time as the column header.

   WHY   Joe, 1 September 2026, on four screens in a row: "This screen has a
         problem because it doesn't have a button. I have to scroll to the
         bottom until I reach the bottom part, which is a problem. This should
         have been visible, like the other screens." And on the margin grid,
         which still worked: "See, you see the part of the footer and the
         header you see on the same screen. I'm just rolling within this
         window. That's how it needs to be done. So if I roll here, then I go
         outside of the rolling part, I can move up and down this part so I
         can quickly reach the top part."

         What he is describing is a pane with a ceiling. The four screens had
         lost theirs: `.pagepane`, which card t_da7aabcc pass 2 introduced,
         took the ceiling off so the WINDOW did the scrolling and the document
         grew as tall as the rows — 11,345px on /items. The pager was then
         ten thousand pixels below the fold. This class is the ceiling coming
         back, and `.pagepane` is gone.

         Pass 1 of t_da7aabcc is the OTHER way to get this wrong, and it is
         ruled out here as firmly. It sized the pane so the whole page fitted
         the window exactly, which froze the top of the page: "Nothing scrolls
         up. The header doesn't scroll up. I want it to scroll up until where
         my line is." So the pane is NOT sized to what is above it today; it
         is sized to what must still be above it once the page has scrolled as
         far as it goes.

   HOW   One declaration and two measured numbers, both written by app.js and
         neither of them a constant:

           --top-h   from the TOP of the pinned control strip to the top of
                     the pane. That is the strip's own height plus anything
                     between the two, and it is 0px on a screen that has no
                     pinned strip. It is what must still be on screen when the
                     strip has reached the top of the window.

           --foot-h  from the foot of the pane to the foot of the furniture
                     underneath it — the pager, and the page-size chooser
                     where there is one. That is what must be on screen under
                     the pane.

         The pane is `100vh` minus the two. The arithmetic that falls out of
         that is the whole point of the class, so it is worth writing down.
         Call A the height of the page title and summary ABOVE the strip:

           - the document ends up exactly `100vh + A` tall, so the page
             scrolls by A and by nothing more;
           - scrolled to that end, the strip is at the top of the window, the
             title has gone, the pane fills everything between the strip and
             the pager, and the pager is on the bottom edge. Nothing overlaps
             and nothing is wasted.

         Both of Joe's requirements are then true at once, and they are true
         because of each other: the page scrolls exactly as far as its own
         title band, which is what "I can move up and down this part so I can
         quickly reach the top part" asks for, and the pane is exactly as tall
         as the window leaves, which is what "you see the part of the footer
         and the header on the same screen" asks for.

         A is not free. Every pixel of pane height above "the whole page fits
         the window" is a pixel the page has to scroll before the footer comes
         into view, and there is no third option: a document that is exactly
         one window tall is a document with a frozen top. So on a screen whose
         title band is taller than its pager, the pager starts just below the
         fold and one short scroll — a third of a screen, against the ten
         thousand pixels it replaces — settles the page for good. That is
         measured per screen in REPORT_t_191d4a18.md.

         Both fall back to something sane, because neither is load-bearing:
         with JavaScript off `--top-h` is 0px and `--foot-h` is 240px, which
         is the plain `.tablescroll` ceiling above, and the pane behaves as it
         did before this class existed.
   -------------------------------------------------------------------------- */
.tablescroll.screenpane {
  max-height: calc(100vh - var(--top-h, 0px) - var(--foot-h, 240px));
}

/* The control strip that pins. Sticky, not fixed: before the page has
   scrolled that far the strip sits in the flow exactly where it always did,
   which is the whole difference between this and pass 1 of card t_da7aabcc.
   Its z-index of 40 is above the header cells' 2, so rows pass UNDER it
   rather than through it; the floating filter panels of card t_297d0a26 are
   inside it and so travel with it. */
.cstick {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--c-surface);
}

.tablescroll > .grid > thead > tr > th,
.tablescroll > .grid > thead > tr > td {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--c-band-service);
  box-shadow: inset 0 -1px 0 var(--c-border-item);
}

/* ---------- TWO HEADER ROWS, BOTH PINNED ---------------------------------
   WHAT  The source-table browser is the one grid with TWO header rows: the
         column names and, under them, the filter controls that belong to
         those columns. Both stick to the top of the pane, one under the
         other.

   WHY   Card t_451d2114. On a 57-column table a reader who has scrolled a
         few rows down is looking at columns of bare values with nothing
         naming them, which is unreadable. Joe: "when you scroll up, that part
         becomes sticky together with this."

   HOW   The filter row stops one column-name row's height down. `--rawrow-h`
         is that row's own measured height, written by app.js on load, on
         resize and whenever a fold on the page opens or shuts — the three
         things that change it. `--rawhead-h` is the fallback for a reader
         with JavaScript off, and it is the same 46px the row is drawn at, so
         nothing moves if the measurement never arrives.

         Both rows are measured against the PANE, because the pane is a scroll
         container again — card t_191d4a18 gave it its ceiling back. That is
         why `--strip-h`, `--rail-h` and the `.nopan` clipping this rule used
         to be tangled up with have all gone: they existed to pin these rows
         to the WINDOW while the pane was as tall as the whole document, and
         the pane no longer is.
   -------------------------------------------------------------------------- */
.tablescroll > .grid.raw > thead > tr.filterrow > td {
  top: var(--rawrow-h, var(--rawhead-h));
  z-index: 1;
  box-shadow: inset 0 -1px 0 var(--c-border-container);
}

/* ---------- THE SCROLLBARS THEMSELVES --------------------------------------
   WHAT  Draws the scrollbars on the table panes, and on the strip above them,
         as permanently visible grey bars in the product's own palette.

   WHY   Several browsers now default to an OVERLAY scrollbar: an outline that
         floats over the content, takes up no room and fades away when you
         stop scrolling. That is the wrong behaviour for this screen — a bar
         you cannot see is a bar the client cannot find, and the whole point of
         the change is that he can see, at a glance, that the table goes
         further left and right than the window does. Asking for a classic
         scrollbar makes the strip above the table honest: its bar shows both
         where he is in the table and how much of it he is looking at.

   HOW   `scrollbar-width` and `scrollbar-color` are the standard properties;
         the `::-webkit-scrollbar` rules do the same job in Chrome and Edge,
         where naming the pseudo-element is also what switches the overlay
         scrollbar off. Both are built from existing tokens: the track is the
         service band the column header sits on and the bar is the control
         border grey, so nothing new enters the palette and nothing gains a
         radius or a shadow.
   -------------------------------------------------------------------------- */
.tablescroll {
  scrollbar-width: auto;
  scrollbar-color: var(--c-border-control) var(--c-band-service);
}
.tablescroll::-webkit-scrollbar { width: 14px; height: 14px; }
.tablescroll::-webkit-scrollbar-track { background: var(--c-band-service); }
.tablescroll::-webkit-scrollbar-thumb {
  background: var(--c-border-control);
  border: 3px solid var(--c-band-service);
}
.tablescroll::-webkit-scrollbar-corner { background: var(--c-band-service); }

/* ---------- DISCLOSURE — card t_90896ad6 -----------------------------------
   WHAT  The little arrow that folds an explanation away, leaving a one-line
         headline that still carries the meaning. Built on the browser's own
         <details>/<summary>, styled into the CONSULATE language.

   WHY   Joe: "there's a lot of words there... maybe it should be a little
         arrow where you can click and see what's under that." The words are
         WHY the client can trust the number, so none of them are deleted -
         they are folded. Fewer words at rest, not fewer words.

   HOW   No radius, no shadow, no animation - just show and hide. The marker
         is a plain triangle drawn in text, rotated 90 degrees when open, so
         no image and no icon font enters the product. The headline reads at
         --t-secondary in secondary ink like every other note on these
         screens, and the folded body is set off by a single 1px item-weight
         rule on its left edge, which is the existing grammar for "these are
         items inside something", not a new one.

         The default browser marker is switched off in both dialects
         (list-style on the summary, and the WebKit pseudo-element) so there
         is never a second arrow beside ours.
   -------------------------------------------------------------------------- */
.disc { margin: 0; }
.disc + .disc { margin-top: 2px; }

.disc > summary.disc-head {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--t-secondary);
  color: var(--c-ink-2);
  padding: 4px 0;
}
.disc > summary.disc-head::-webkit-details-marker { display: none; }
.disc > summary.disc-head:hover { color: var(--c-ink); }
.disc > summary.disc-head:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* The marker. A text triangle, so it inherits ink and size and needs no asset.
   `flex: none` keeps it from being squeezed when the headline wraps. */
.disc > summary.disc-head::before {
  content: "\25B8";
  flex: none;
  color: var(--c-ink-3);
  font-size: 11px;
  line-height: 1.4;
  transform: translateY(-1px);
}
.disc[open] > summary.disc-head::before { content: "\25BE"; }
.disc[open] > summary.disc-head { color: var(--c-ink); font-weight: 600; }

/* The folded half. Indented past the marker and ruled on the left so it reads
   as belonging to the headline above it rather than as the next paragraph. */
.disc-body {
  font-size: var(--t-secondary);
  color: var(--c-ink-2);
  border-left: 1px solid var(--c-border-item);
  padding: 2px 0 6px 12px;
  margin: 0 0 0 5px;
}
.disc-body p { margin: 0 0 6px 0; }
.disc-body p:last-child { margin-bottom: 0; }

/* ---------- A DISCLOSURE ON A CONTROL STRIP — card t_da7aabcc -------------
   WHAT  The one arrow that sits ON a control strip, in the row of controls,
         rather than in a note band of its own underneath it.

   WHY   Joe, about the two bands that used to sit under this strip: "some
         more words which I don't want there... You can put it after the
         Apply, between Apply and Open. There's enough space to push in. In
         other words you don't need to use space for the screen." Closed —
         which is how it is at rest — the arrow and its headline are one more
         item on a row that already existed, so the explanation costs the
         screen no height whatsoever. That was the whole point of the move.

   HOW   Nothing at all is needed to make the closed state work: the strip is
         a wrapping flex row and a <details> is just another item on it. The
         two lines below are for the OPEN state. `max-width` stops the
         paragraph from stretching to the width of the window and turning
         into one long line, and `min-width: 0` lets the item be squeezed
         narrower than its longest word rather than forcing the strip wider
         than the box.

         When it is opened the row gets taller, exactly as it would if a
         filter wrapped onto a second line, so the pinned strip above the
         grid gets taller too. Card t_191d4a18: the grid's own column header
         is pinned to the top of the PANE, which starts below the strip
         whatever height it is, so nothing has to be re-offset for that. What
         IS re-measured on every fold toggle is `--top-h` — a taller strip
         leaves the pane less room — see `.screenpane` above.
   -------------------------------------------------------------------------- */
.cstrip .stripdisc { max-width: 520px; min-width: 0; }

/* A disclosure sitting inside a .note block must not inherit the note's own
   padding a second time, or every folded section would step further right
   than the sentence above it. */
.note > .disc, .note > .disc-body { padding-left: 0; }

/* ---------- THE TIERS-BY-DISTANCE GRID — card t_0f3ca42b --------------------
   WHAT  The grid the client drew: the price tiers across the top with Outlier
         and Total beside them, the distance bands down the side, and inside
         every box a stack of figures — Margin %, Revenue, Orders, Quantity —
         with the names of those figures printed ONCE in a narrow gutter at the
         left edge of the row rather than repeated inside all seven boxes.

   WHY   The screen this replaced was a list of 2,356 rows repeating "Tier 1 …
         Tier 5" under every distance band. The client: "the tiers are gonna be
         the columns … and then on the side you're gonna have the distance …
         you made everything in rows."

         The gutter is what makes a box readable without a label in it. Every
         box on a row holds the same lines in the same order, so once the eye
         has read "Margin %, Revenue, Orders, Quantity" down the left edge it
         can read the numbers straight across without re-reading a word.

   HOW   Nothing new enters the palette and nothing gains a radius. The stacked
         lines inside a box are plain blocks at the secondary type size with a
         fixed line height, so a box for a tier a product never shipped in —
         which prints an em dash on every line — is exactly as tall as the box
         beside it and the rows stay level.

         Rows are `height: auto` here, unlike every other grid in the product:
         a box holding four figures cannot fit the fixed 44px data row, and
         forcing it would clip the fourth line. The 44px minimum is kept, so a
         one-figure grid looks like every other table on the screen.

         The Total column is separated by the CONTAINER border weight — the
         heavier of the two — because it is a different kind of thing from the
         tier columns beside it, not another tier. That is the existing
         grammar, not a new one, and no third weight is introduced.
   -------------------------------------------------------------------------- */
.grid.tdgrid tbody td { height: auto; min-height: var(--h-row); vertical-align: top;
  padding-top: 8px; padding-bottom: 8px; }
.grid.tdgrid thead th { vertical-align: bottom; }

/* One figure inside a box, and one label in the gutter. Both use the same line
   height so the labels sit exactly on their numbers. */
.grid.tdgrid .mline {
  font-size: var(--t-body); line-height: 20px; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.grid.tdgrid .mline.none { color: var(--c-ink-3); }
.grid.tdgrid .mlabel {
  font-size: var(--t-control); color: var(--c-ink-2); text-align: left;
  font-variant-numeric: normal;
}

/* The whole box is the link to the order lines behind it, so the click target
   is the figures rather than a word beside them. It is still a link, so it is
   still underlined — on hover only, because seven underlined boxes on every
   row would be a wall of rules. */
.grid.tdgrid a.cellwrap { display: block; color: var(--c-ink); text-decoration: none; }
.grid.tdgrid a.cellwrap:hover { text-decoration: underline; color: var(--c-link); }

/* The two left-hand columns are given fixed widths so the gutter sits at the
   LEFT EDGE of the row, right beside the band name, rather than being pushed
   across the page by a long item description. Everything left over goes to the
   tier columns, whose figures are right-aligned and so stay level whatever
   width they end up at. */
.grid.tdgrid .tdname { width: 300px; max-width: 300px; }
.grid.tdgrid .tdname .sub { white-space: normal; }
.grid.tdgrid .tdgutter { width: 118px; white-space: nowrap; }
.grid.tdgrid .tdband { padding-left: 34px; color: var(--c-ink-2); font-weight: 600; }
.grid.tdgrid .tdtotal { border-left: 2px solid var(--c-border-container); }

/* The fold control on a product's summary line. It is a form button because it
   WRITES — the open products are remembered against the reader — so it must
   not be a link the browser may prefetch. It is drawn as the triangle the
   disclosures elsewhere in the product use, and nothing else. */
.grid.tdgrid .foldform { display: inline; }
.grid.tdgrid .fold {
  border: 1px solid var(--c-border-control); background: var(--c-surface);
  color: var(--c-ink-2); font-family: var(--font); font-size: 12px;
  width: 22px; height: 22px; line-height: 1; padding: 0; margin-right: 6px;
  vertical-align: baseline;
}
.grid.tdgrid .fold:hover { background: var(--c-band-service); color: var(--c-ink); }

/* ---------- THE SIGN-IN SCREENS — card t_81a598e6 --------------------------
   WHAT  The login screen, the "forgotten your password" screen and the
         "choose a password" screen. One box, centred on the canvas, carrying
         the crest and almost nothing else.

   WHY   Joe asked for "a regular login screen — username and password", and
         the card's design rule was that it must look like it belongs to this
         application: same crest, same palette, almost no words. So these
         screens use the same navy, the same cyan crest tile, the same square
         corners and the same two border weights as everything else. They do
         NOT use the crown, because the crown is a navigation bar for screens
         a stranger may not see.

   HOW   Every value below is a token. No raw hex, no radius, no third border
         weight — the same rules that govern the rest of this file. The box is
         2px --c-border-container because it is a container, and the strip
         across its top is the crown navy, so the login screen reads as the
         top of the application rather than as a different product.
   -------------------------------------------------------------------------- */
.gatepage {
  min-height: 100vh; background: var(--c-canvas);
  display: flex; align-items: center; justify-content: center;
  padding: var(--gutter);
}
.gatebox {
  width: 380px; max-width: 100%;
  background: var(--c-surface); border: 2px solid var(--c-border-container);
}
.gatecrown {
  height: var(--h-crown-row1); background: var(--c-crown);
  border-bottom: 2px solid var(--c-crown-border);
  display: flex; align-items: center; padding: 0 var(--pad-box);
}
.gateform { padding: var(--pad-window); }
.gatelabel {
  display: block; font-size: var(--t-control); color: var(--c-ink-2);
  margin-bottom: 4px;
}
.gateinp { width: 100%; margin-bottom: 14px; }
.gatego { width: 100%; margin-top: 4px; }
/* Status colour is TEXT ONLY here, exactly as everywhere else in the product:
   no fill, no chip, no coloured row. */
.gateproblem {
  font-size: var(--t-body); font-weight: 700; color: var(--c-urgent);
  margin-bottom: 14px;
}
.gatenote { font-size: var(--t-body); color: var(--c-ink); margin-bottom: 14px; }
.gatehelp { font-size: var(--t-secondary); color: var(--c-ink-2); margin-top: 14px; }

/* Sign out sits in the crown's utility row, so it is drawn as one of that
   row's small white links rather than as a button — but it IS a button,
   because signing out is a POST and must not be a link a browser may
   prefetch or another site may embed. */
.signout {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: var(--font); font-size: 12px; color: var(--c-ink-invert);
  text-decoration: underline;
}
.signout:hover { color: var(--c-gold); }

/* ---------------------------------------------------------------------------
   THE STAGING BAND — card t_cd17aaa3.

   WHAT  A full-width amber strip across the very top of every page on the
         staging copy of the system.

   WHY   Staging carries a copy of production data and looks identical to the
         live site. The client is sent the staging URL to review changes, so
         somebody is always one tab away from editing the wrong system. Amber
         on black is used nowhere else in CONSULATE, which is the point: it
         cannot be mistaken for part of the product.

   HOW   Static, not fixed: it occupies real space at the top of the document
         and pushes the navy crown down rather than covering it. Fixed
         positioning would need the page to reserve a matching offset, and any
         offset would be a guessed number.

         There is deliberately NO height property. The band is one line of text
         with padding around it, so on a narrow window the sentence wraps and
         the band grows to hold it. A fixed height would clip the wrapped line
         and the warning would be the thing that broke. Joe's standing rule:
         never a fixed pixel height in CSS.

         This rule ships in production's stylesheet too and costs nothing
         there, because production never renders an element with this class —
         the markup is absent, not hidden.
   --------------------------------------------------------------------------- */
.staging-banner {
  background: #f0a500;
  color: #1a1a1a;
  font-family: var(--font);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-align: center;
  padding: 8px 16px;
  border-bottom: 3px solid #1a1a1a;
}
.staging-banner strong {
  font-weight: 800;
  letter-spacing: 0.08em;
}
