/* ============================================================================
   SLAB TOOL HEADER — THE ONE COPY.
   ----------------------------------------------------------------------------
   WHY THIS FILE EXISTS. Every page in Slab carried its own hand-pasted copy of
   `.main-hdr`, `.pg-title`, `.hdr-right`, `.hdr-icon`, `.hdr-upd` … and they had
   drifted into SEVEN different `.main-hdr` rules across sixteen pages (some with
   `gap:16px`, some with none; two with `min-height:86px`; one with `flex-wrap`).
   Each copy carried a comment claiming it matched the others. That is the same
   shape of bug that made four hand-copied money rules disagree and understate the
   invoices register, so the header is now ONE file that every page links.

   WHY A LINKED STYLESHEET AND NOT nav.js. The header's look USED to arrive as CSS
   injected at runtime: `.hdr-status` / `.hdr-primary` from nav.js, `.hdr-icon` from
   nav.js's notification layer, `.hdr-disc` from slab-comments.js — and
   slab-comments.js only injected on `SlabComments.mount()`, which the proposal page
   called from `boot()`, i.e. AFTER a Promise.all of three API calls. Measured on
   production 2026-08-23: nav.js finished at 273ms but `/api/v2-projects` answered at
   2671ms, so the Discussion button sat there as a raw unstyled browser <button> for
   ~2.4 SECONDS. "the discussion button and the project drop down take a long time to
   load" (Adrienne). A <link> in <head> is render-blocking, so the header is styled on
   the FIRST paint and no script can be late with it.

   ADDING A RULE HERE? It applies to every page. Page-specific chrome belongs in the
   page. test/tool-header-shared.test.js fails if a page or a script re-declares any
   selector this file owns.
   ============================================================================ */

/* ---- THE ONE NUMBER THE TOP OF THE APP IS BUILT FROM ---------------------
   ⚠ `--slab-topbar-h` is the height of BOTH blocks along the top edge: the sidebar's
   BFB·SLAB logo block and the page header beside it. They each draw a 1px bottom rule, and
   those two rules are read as ONE CONTINUOUS LINE across the top of the app — so a
   one-pixel disagreement is more obvious than no line at all.

   THEY DID DISAGREE. Measured in Chrome at 1440x900 on 2026-08-23: the logo block was 68px
   (padding 20/16 around its content) and the takeoff header 58px (padding 12/12) — a 10px
   step in the middle of the line. "make the nav height the same height as the BFB SLAB
   section, i want the lines to line up" (Adrienne). Neither number was written down
   anywhere; each was the arithmetic of somebody's padding, in sixteen separate copies.

   NOW: one token, two consumers, and NEITHER of them may set a height of its own.
   test/tool-header-shared.test.js fails if a page declares `.sb-logo` or `.main-hdr` again,
   and asserts both consumers read this token. 68px because the logo block's content sets the
   floor — the header had the room to grow and the logo did not.

   min-height, not height: nothing on this line is allowed to be CLIPPED to hold it. The
   token is the floor both blocks stand on, and content that genuinely needs more room still
   gets it — see the doc-state token below for how much room that is before the line moves.

   ---- AND THE SECOND NUMBER, FOR WHEN A DOCUMENT IS OPEN ------------------
   ⚠ THE LINE USED TO BREAK INSIDE A DOCUMENT, and it was left that way on purpose once:
   "the builder header is legitimately taller (it has a second row), and its rule is meant to
   sit below the logo's." Measured on the deployed site 2026-08-23: sidebar 68, estimate and
   proposal document headers 95 — a 27px step in the middle of the one line Adrienne asked to
   line up. She was not objecting to a tall builder header. She was objecting to a BROKEN LINE,
   and a broken line does not read as a considered exception; it reads as a mistake, which is
   why it came back as a bug report rather than as a design note.

   THE THREE WAYS OUT, and why this one:
     · ACCEPT THE STEP and dress it up. There is no dressing. The eye reads two 1px rules at
       the same colour, 27px apart, meeting at the sidebar's edge as one line that failed.
     · MOVE THE SECOND ROW OUT of the bar, so the bar stays 68 in both states. That is a real
       design and it is not available here: the document's NAME lives on that second row, and
       test/tool-header-shared.test.js §12 exists because a header with no name in it shipped
       once already. It also means editing all three builders' markup at once.
     · GROW THE SIDEBAR'S BLOCK TO MATCH — this one. The whole top band of the app grows as
       ONE unit when a document opens, and the line stays whole in both states. Nothing moves
       independently of anything else: the header was already growing 27px and pushing the
       page down with it, so the sidebar joining it is the only part that was missing.

   AND IT IS THE SAME MECHANISM AS THE LINE ABOVE, deliberately: one token, two consumers,
   neither computing a height of its own. The alternative — nav.js measuring the header and
   writing a pixel value onto the sidebar — is the runtime-injected chrome this whole file
   exists to get rid of, and it would paint one frame at the wrong height every time.

   WHY 27px IS NOT A MAGIC NUMBER. It is the band the back row adds, named as that. And the
   document header no longer ARRIVES at 95 by padding arithmetic: it is 75px of content
   centred inside a 95px floor (measured, 2026-08-23), so a document action can grow by 20px
   before either block moves — and when it finally does, BOTH move, because both read this. */
:root{--slab-topbar-h:68px;--slab-docband-h:27px;
      --slab-topbar-doc-h:calc(var(--slab-topbar-h) + var(--slab-docband-h));
      /* ---- AND THE THIRD NUMBER, FOR TABLET AND BELOW ------------------------
         The two-band form's height, declared rather than arrived at. 33px of rail on band 1,
         38px of buttons on band 2, 10px between them and 10px of air top and bottom = 101,
         rounded up to 106 so a button that grows two pixels does not move the whole bar.
         IT IS A FLOOR THAT IS ALSO A CEILING IN PRACTICE, and that is the point: at a given
         viewport every page's header is the SAME height, whether it carries three controls
         or none. A header that is 101px on Bills and 68px on Settings is the churn this
         block was written to stop. See THE SETTLED TIERS at the foot of this file. */
      --slab-topbar-stack-h:106px;}

/* ---- THE SIDEBAR'S LOGO BLOCK — the left half of that line ---------------
   Sixteen pages carried a byte-identical copy of this rule. It is here because it is half
   of the line `.main-hdr` has to meet, and a shared line cannot have one end owned by
   sixteen files. Horizontal padding stays with it; the VERTICAL size comes from the token,
   and the content is centred in it rather than pushed around by padding arithmetic. */
.sb-logo{
  min-height:var(--slab-topbar-h);box-sizing:border-box;
  padding:0 18px;border-bottom:1px solid var(--surface-card);
  display:flex;align-items:center;gap:10px;flex-shrink:0;
}
/* ⚠ AND IT FOLLOWS THE HEADER INTO A DOCUMENT. Same token, same floor, so the two rules stay
   one line in the state Adrienne found broken.
   THE `:has()` IS LOAD-BEARING, NOT DECORATION. `body.slab-doc-open` alone is the wrong
   condition: it says "a document is open", and PUNCHLIST opens a document without its header
   changing shape at all — one row, 68px, in both states. Keyed on the body class alone the
   sidebar would grow to 95 beside a 68px punchlist header and this fix would ship a NEW break
   on the fourth tool. The condition the sidebar actually wants is "the header next to me is in
   its two-row form", and that is what this asks. `.hdr-2row` is the marker for a header that
   HAS a builder form; `body.slab-doc-open` is whether it is in it. Both, or neither. */
body.slab-doc-open:has(.main-hdr.hdr-2row) .sb-logo{min-height:var(--slab-topbar-doc-h);}

/* ---- THE BAR -------------------------------------------------------------
   ONE LINE. A tool's MAIN page puts everything on a single row — "all main tool pages
   should match the layout of the takeoff tool page, all items on one line" (Adrienne,
   2026-08-23). The two-row form is for a BUILDER and is opted into below. */
.main-hdr{
  min-height:var(--slab-topbar-h);box-sizing:border-box;
  padding:0 28px;border-bottom:1px solid var(--surface-card);background:var(--surface-topbar);
  display:flex;align-items:center;justify-content:space-between;
  flex-shrink:0;gap:16px;
}
/* `.hdr-tall` IS RETIRED (2026-08-23). projects.html and v2-budget.html each pinned
   `min-height:86px` so their header would not shrink when the sub-line under the title was
   empty — a floor, and a reasonable one at the time. The token above is now that floor for
   every page, so the only thing 86px still did was make those two headers 18px taller than
   the sidebar's logo block and put a visible step in the rule that runs across the top of the
   app. Measured at 1440px: delta 18px on both, 0 on the other eight.
   NOTHING IS CLIPPED BY REMOVING IT — `min-height` is a floor, not a height, so a header
   whose content genuinely needs more room still grows. The two pages no longer carry the
   class and the rule is gone rather than left lying around for somebody to re-apply. */

.pg-title{font-size:20px;font-weight:700;color:var(--text-strong);}
.main-hdr .pg-title{white-space:nowrap;}
.pg-sub{font-size:15px;color:var(--text-muted);margin-top:2px;}

.hdr-left{display:flex;align-items:center;gap:14px;min-width:0;flex:1 1 auto;}
.hdr-left>div{min-width:0;}
.hdr-right{display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:flex-end;}

/* the "updated …" line a few register pages still carry */
.hdr-upd{font-size:16px;color:var(--text-muted);display:flex;align-items:center;gap:6px;cursor:default;}
.hdr-upd i{font-size:var(--ico-md);}
.hdr-upd.hdr-upd-link{cursor:pointer;transition:color .12s;}
.hdr-upd.hdr-upd-link:hover{color:var(--accent-green);}

/* ---- THE ROW COUNT FOLLOWS THE BACK BUTTON, and nothing else decides it ---
   ⚠ THIS IS NOT A PER-PAGE CHOICE. A BUILDER has a document to go back from, so its back
   button gets a row of its own above the document's name; a MAIN page has no back button,
   so there is nothing to put on a second row and everything sits on one line:

     main      X Tool                       Choose Project First   New X +   [bell]
     builder   ← Back To All X                                     Discussion
               name / title  ·  chip        …document actions…     New X +   [bell]

   `body.slab-doc-open` is the ONE signal, and every tool already had the fact under a
   private name — `tk-open`, `est-open`, `prop-open`. Each page's existing single writer of
   its own class now writes this one alongside it, so there is no second source of truth
   about whether a document is open, and the row count cannot be set page by page.

   It got set page by page once and this is what that cost: estimate and proposal both
   declared themselves two-row unconditionally, so their MAIN pages rendered the bell alone
   on one line with the title and the buttons dropped to the next — which is what Adrienne
   was looking at when she wrote "all main tool pages should match the layout of the takeoff
   tool page, all items on one line". */
/* ⚠ DECLARATION ORDER IS LOAD-BEARING HERE. `.hdr-row` and `.hdr-row-back` are both single
   class selectors, so their specificity is IDENTICAL and the LAST one wins. Written the other
   way round — which it was, for one measurement — `.hdr-row{display:flex}` overrode
   `.hdr-row-back{display:none}`, the back row stayed in the layout as an empty flex item, and
   on a MAIN page it took half the bar's width with nothing in it. Found by measuring the
   rendered box in Chrome, not by reading the file. `.hdr-row` FIRST; the state rules after. */
/* ⚠ `.hdr-2row` IS NOT AN ASSERTION THAT THE HEADER HAS TWO ROWS RIGHT NOW, and the name has
   already misled one reader into filing "all the main pages carry hdr-2row while rendering one
   row — clean that up" as a bug (2026-08-23). It is not a bug and there is nothing to clean:
   the class is a STATIC marker meaning "this header has a builder form available", and
   `body.slab-doc-open` is the only thing that says whether it is in it. The pair is deliberate
   — that is what §9 above is about, and what makes the row count impossible to set page by
   page. On a main page the class does exactly one thing, the rule directly below: it makes the
   single visible row GROW to fill the bar. Measured with it applied, on all three builders'
   main pages at 1180–1920: header 68px, sidebar 68px, delta 0.
   IT CANNOT BE APPLIED "ONLY IN THE DOCUMENT STATE" without a script toggling it, which would
   put a second source of truth beside `body.slab-doc-open` — the exact thing this file removed.
   Punchlist, which has no builder form at all, correctly does NOT carry it. */
.hdr-row{display:flex;align-items:center;justify-content:space-between;gap:16px;min-width:0;}
/* On a MAIN page the bar is one row-direction flex container with a single visible .hdr-row
   in it, so that row must GROW to fill the bar. In the BUILDER the bar is a column and the
   rows must not grow, or the two would split the height between them. */
.main-hdr.hdr-2row > .hdr-row{flex:1 1 auto;min-width:0;}
/* ⚠ THE HEIGHT COMES FROM THE TOKEN, NOT FROM THE PADDING. It used to be `padding:10px 28px`
   with `gap:8px` around 66px of rows, which ARRIVED at 95px — and 95px was nowhere on disk, so
   the sidebar had nothing to agree with and the line broke (see --slab-topbar-doc-h above).
   Now the floor is the token and the rows are CENTRED in it, which renders identically —
   measured on v2-estimate.html at 1512px, rows at y=10 and y=50 before and after, to the
   pixel — and leaves 20px of slack (75px of content in a 95px box) before a taller document
   action moves the line. When it does move it moves BOTH blocks, because both read the token.
   The horizontal padding stays 28px: it is the same gutter the one-row form uses. */
body.slab-doc-open .main-hdr.hdr-2row{min-height:var(--slab-topbar-doc-h);
  flex-direction:column;align-items:stretch;justify-content:center;padding:0 28px;gap:8px;}
body.slab-doc-open .main-hdr.hdr-2row > .hdr-row{flex:0 0 auto;}
/* THE BACK ROW EXISTS ONLY WHILE A DOCUMENT DOES. Hidden rather than emptied: an empty flex
   row is still a flex ITEM, so the container's `gap` would keep reserving space for it and
   the main row would sit 8px lower on every main page than it does in the builder. */
.hdr-row-back{display:none;}
body.slab-doc-open .hdr-row-back{display:flex;}
/* THE IDENTITY CLUSTER — what this is, and the one chip that says whether it is
   safe, on ONE line. flex-GROW is load-bearing: Chrome sizes a wrap container's
   intrinsic width from its widest ITEM, not the sum, so without it the cluster
   asks for its own width and wraps the chip onto a line of its own with empty
   header beside it (measured on takeoff.html, 2026-08-21). */
.hdr-id{display:flex;align-items:center;gap:10px;min-width:0;flex:1 1 auto;flex-wrap:wrap;}
/* ⚠ THE `margin-top:0` EXCEPTION THAT USED TO LIVE HERE IS GONE, and so is the margin it
   was cancelling. `.hdr-id .hdr-status{margin-top:0}` existed because the chip carried a 5px
   top margin for a STACKED header, and `.hdr-id` was the one cluster that had already gone
   inline. Every cluster is inline now (see "THE CHIP SITS ON THE TITLE'S LINE" at the foot of
   this file), so there is no stacked header left for that margin to serve and no exception
   left to make: the property is simply not set. */

/* ---- THE STATE CHIP ------------------------------------------------------
   Sits in the old subtitle slot. It is NOT decoration: it is how a page says the
   store answered, or did not. Pages that drop it from their home list must have
   somewhere else that says so in words (takeoff's homeNote, proposals' prNote). */
.hdr-status{display:inline-flex;align-items:center;gap:6px;font-size:14px;font-weight:600;
  color:var(--text-muted);background:var(--surface-card);border:1px solid var(--border);border-radius:999px;padding:3px 10px;
  letter-spacing:.01em;white-space:nowrap;}
.hdr-status i{font-size:var(--ico-sm);color:var(--accent-green-bright);}
.hdr-status.live{color:var(--accent-green-bright);border-color:var(--action-primary);background:color-mix(in srgb, var(--action-primary) 14%, transparent);}
.hdr-status.offline{color:var(--accent-red);border-color:color-mix(in srgb, var(--accent-red-deep) 42%, transparent);background:color-mix(in srgb, var(--accent-red-deep) 8%, transparent);}

/* ---- THE PRIMARY (green pill, icon on the RIGHT — the house rule) ---------
   EVERY MAIN TOOL PAGE HAS ONE. "ALL need a primary green button" (Adrienne, 2026-08-23) —
   punchlist's New Punchlist was an outlined `.btn`, which read as a secondary on a page
   whose only action it is. */
.hdr-primary{display:inline-flex;align-items:center;gap:6px;background:var(--action-primary);border:1px solid var(--action-primary);border-radius:10px;
  color:var(--on-action);font-family:inherit;font-size:16px;font-weight:700;padding:8px 14px;cursor:pointer;
  transition:background .12s,border-color .12s,color .12s;letter-spacing:.01em;text-decoration:none;white-space:nowrap;}
.hdr-primary:hover{background:var(--action-primary-hover);border-color:var(--action-primary-hover);}
/* ⚠ DISABLED IS A DIMMED GREEN, NOT A GREY. It used to go #1E2A3F/#5A6880, which is the
   secondary palette — so takeoff's New Takeoff, correctly gated until a project is chosen,
   did not read as "the primary action, not yet available" but as "some grey control".
   Adrienne saw it as a missing primary. The gate itself is right and stays; what changed is
   that the button still looks like what it is while it is off. The `title` says why — every
   page that disables this writes one, and test/button-help.test.js holds them to it. */
.hdr-primary:disabled,.hdr-primary[disabled]{
  background:color-mix(in srgb, var(--action-primary) 30%, transparent);border-color:color-mix(in srgb, var(--action-primary) 85%, transparent);color:var(--on-action-dim);
  cursor:not-allowed;}
.hdr-primary:disabled:hover,.hdr-primary[disabled]:hover{background:color-mix(in srgb, var(--action-primary) 30%, transparent);border-color:color-mix(in srgb, var(--action-primary) 85%, transparent);}
.hdr-primary:disabled i,.hdr-primary[disabled] i{opacity:.7;}
.hdr-primary i{font-size:var(--ico-md);}

/* ---- THE BACK BUTTON — AND THE ONE PLACE THE ICON GOES ON THE LEFT --------
   ⚠ DELIBERATE EXCEPTION TO THE HOUSE RULE. Every other button in Slab puts its
   icon to the RIGHT of the label (Adrienne's standing preference, and
   test/tool-header-shared.test.js enforces it on the header's other buttons).
   A BACK ARROW IS THE EXCEPTION: it points left, and an arrow pointing left that
   sits to the right of the words it is about reads backwards. "icon needs to move
   to left side" (Adrienne, 2026-08-23) — she asked for this after driving it.
   DO NOT "fix" this back to icon-right. The markup puts the <i> first and this
   rule is only here to say why. */
.hdr-back{}
.hdr-back i{order:-1;}

/* ---- THE BELL ------------------------------------------------------------ */
.hdr-icon{width:33px;height:33px;background:var(--surface-card);border:1px solid var(--border);border-radius:8px;
  display:flex;align-items:center;justify-content:center;cursor:pointer;transition:border-color .12s;}
.hdr-icon:hover{border-color:var(--action-primary);}
.hdr-icon i{font-size:var(--ico-md);color:var(--text-muted);}
.hdr-icon[data-slabn]{position:relative;}

/* ---- THE DISCUSSION BUTTON ----------------------------------------------- */
.hdr-disc{position:relative;display:inline-flex;align-items:center;gap:7px;background:var(--surface-card);
  border:1px solid var(--border);border-radius:8px;color:var(--accent-neutral);font-family:inherit;font-size:16px;
  font-weight:600;padding:7px 13px;cursor:pointer;transition:border-color .12s,color .12s;white-space:nowrap;}
.hdr-disc:hover{border-color:var(--action-primary);color:var(--text-2);}
.hdr-disc i{font-size:var(--ico-md);color:var(--text-muted);}
.hdr-disc:hover i{color:var(--accent-neutral);}
.slabc-badge{min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:var(--accent-red);color:var(--on-action);
  font-size:14px;font-weight:700;line-height:18px;text-align:center;display:inline-block;}
.slabc-badge[hidden]{display:none;}

/* ---- KEEPING THE ONE LINE ONE LINE ---------------------------------------
   A main tool header is `Title … picker · primary · bell`, and at a narrow viewport that
   ran out of room and wrapped to three lines — measured on punchlist at 1000px, where the
   offline warning chip is long: header 80px against the logo block's 68px, so the shared
   rule stepped 12px in the middle. Sixteen pages each pin `min-width:200px` on .filter-sel,
   which is a floor the header cannot afford at that width.

   WHAT GIVES, IN ORDER, and none of it is a truncated warning: the TITLE ellipsizes first
   (it is the one thing on the bar you can still read from context), then the picker shrinks
   from its 200px preferred width. The state chip, the primary and the bell never shrink —
   a chip that says "Offline — Changes Not Saved" is the whole reason that chip is allowed to
   exist on punchlist, so it is the last thing that may be squeezed.
   Scoped to `.main-hdr` so the same select on a page's body keeps its own floor. */
.main-hdr .filter-sel,.main-hdr .csel-btn{min-width:0;}
.main-hdr .pg-title{overflow:hidden;text-overflow:ellipsis;min-width:0;}
.main-hdr .hdr-status,.main-hdr .hdr-primary,.main-hdr .hdr-icon{flex:0 0 auto;}
/* ⚠ ON A MAIN PAGE NOTHING WRAPS, and this is the rule that actually enforces "all items on
   one line". The two clusters were BOTH `flex:0 1 auto` with `flex-wrap:wrap`, so at 1000px
   the identity cluster took 309px of an available 772, the right-hand cluster was squeezed to
   375px against 410px of content, and it wrapped INTERNALLY — the bell dropped to a second
   line and the header grew to 80px against the logo block's 68px. Measured, not guessed:
   there was 72px of free space in the bar at the time. Nothing was too big; the wrong cluster
   was giving.
   The CONTROLS never shrink — they are all a person can act on. The identity cluster gives,
   and its title ellipsizes.
   SCOPED TO A MAIN PAGE. A BUILDER's second row carries several document actions and may
   genuinely need to wrap at a narrow viewport; forcing nowrap there would push them out of
   the bar instead.
   ⚠ THE CARVE-OUT IS THE TWO-ROW FORM, NOT THE DOC STATE. Written as `body:not(.slab-doc-open)`
   alone it also let go on a header that has NO second row to wrap into — punchlist opens a
   document and keeps its single row, so from the moment it started writing the shared signal
   it would have lost the nowrap it needs and could have wrapped to 80px beside a 68px logo
   block. `.main-hdr:not(.hdr-2row)` is the honest condition for "this header is one row and
   always will be", and it holds in both of that page's states. */
.main-hdr:not(.hdr-2row) .hdr-right,
body:not(.slab-doc-open) .main-hdr .hdr-right{flex:0 0 auto;flex-wrap:nowrap;}
.main-hdr:not(.hdr-2row) .hdr-id,.main-hdr:not(.hdr-2row) .hdr-left,
body:not(.slab-doc-open) .main-hdr .hdr-id,
body:not(.slab-doc-open) .main-hdr .hdr-left{flex-wrap:nowrap;flex:0 1 auto;min-width:0;}

/* ---- THE PROJECT PICKER, AND ITS HONEST LOADING STATE --------------------
   A picker whose menu is still empty must not look ready. `.proj-dd[data-loading]`
   is set in the markup and cleared by the page the moment the project list lands;
   while it is set the control is disabled and says "Loading Projects…" — a real
   loading state, not a painted control that fills in later. nav.js's .csel twin
   copies the select's classes onto itself, so this reaches the styled button too. */
.filter-sel[data-loading],.csel-btn.proj-dd[data-loading],
.csel:has(> select[data-loading]) .csel-btn{opacity:.6;cursor:progress;}

/* ── THE BAR WRAPS BEFORE IT CLIPS ───────────────────────────────────────────────────
   Added 2026-08-29, after a full responsive audit found this failing at 600 and 375 on
   EVERY desktop page — the only remaining failure at those widths once the grids were fixed.

   The rule above is right and stays right: "the CONTROLS never shrink — they are all a person
   can act on. The identity cluster gives, and its title ellipsizes." That holds until the
   controls alone are wider than the bar. Measured on projects.html at a 600px viewport:
   .main-hdr is 536 wide, its two children need 554, the identity cluster is already at its
   min-content 359 and cannot give any more, and .hdr-right is flex:0 0 auto at 195. The
   overflow was 62px, and `.main{overflow:hidden}` CLIPPED it — so the controls did not
   shrink, they silently left the page.

   Clipping loses a control entirely; wrapping keeps every one of them reachable, one row
   lower. That is the trade, and below 760px wrapping is plainly the better half. The nowrap
   above still governs every width where the bar actually fits, which is the case it was
   written for.

   Selectors are repeated verbatim rather than loosened: `body:not(.slab-doc-open) .main-hdr
   .hdr-right` is (0,3,1), a media query adds no specificity, and a shorter selector here
   would simply lose. Same selectors + later source order is what makes this win. */
/* ⚠ THE RULES THAT USED TO BE HERE ARE NOW AT THE FOOT OF THIS FILE, in THE SETTLED TIERS.
   Nothing above is withdrawn: "wrapping keeps every control reachable, clipping loses one" is
   still the trade, and it is still what the stacked form is built on. What changed on
   2026-08-31 is that the wrap stopped being a FALLBACK and became a DESIGN — "have them come
   up with a better stacked design for tablet and below sizes. lets get this pinned down for
   all screens so we can stop messing with the top nav already" (Adrienne). Three things moved
   with it, and each is stated where the new block declares it:
     · the breakpoint went 760 → 1024 — the width the design system already steps at, where
       the sidebar rail collapses to its 64px icon form;
     · the controls band is right-aligned under the name rather than left-aligned, so it reads
       as a toolbar and not as spillage;
     · the bell and Settings left the controls band for a RAIL that stays in the top-right
       corner at every width, so the two icons never move.
   Keeping one copy of the rules matters more than keeping them next to their history: two
   media queries that both size `.hdr-right` is exactly the drift this whole file exists to
   stop. */

/* The identity cluster's comment above says "its title ellipsizes". It did not: `.pg-title`
   was `white-space:nowrap` with no `overflow` and no `text-overflow`, so instead of shortening
   it simply ran out of the bar. Stated globally rather than inside the media query, because a
   title that fits is unaffected either way — ellipsis only engages when there is not room, and
   that is exactly the case the comment was describing. Found 2026-08-29. */
.pg-title{min-width:0;overflow:hidden;text-overflow:ellipsis;}

/* ── THE IDENTITY CLUSTER IS NOT ALWAYS CALLED .hdr-left ─────────────────────────────
   Added 2026-08-30, after a sweep of every nav page at 1440/1280/1024/900/820/768/600/430/375.

   The two rules above — "the CONTROLS never shrink, the identity cluster gives" and the
   ≤760 wrap — are both written against `.hdr-id` and `.hdr-left`. FOURTEEN of the twenty-six
   pages that render this header do not use either class: they open the bar with a bare
   `<div>` holding the title and the state chip (bids, bills, index, finances, timeclock,
   directory, people, settings, account-stores, v2-bills, v2-budget, v2-change-orders,
   v2-invoices, v2-invoices-list). Those pages were getting NONE of the shrink behaviour the
   comments above describe, and the failure is silent: a bare div is `flex:0 1 auto` like
   `.hdr-left`, but `min-width:auto` means its floor is its MIN-CONTENT width, so it never
   actually gives.

   ⚠ THE FAILURE ABOVE WAS RE-MEASURED ON 2026-08-31 AND COULD NOT BE REPRODUCED. This
   comment used to state as fact that on bids.html at 900px the right-hand cluster was pushed
   85px past the end of the page and clipped — "New Bid and the notification bell were simply
   not on the screen". Measured against live prod, which does NOT carry this fix: at 900 the
   bar is 836 wide (matching) but `.hdr-right` ends at 872, INSIDE the bar, and New Bid is on
   screen; same at 430. The original was most likely measured in a page state with a much
   longer identity string than the default.

   The rule below is kept regardless, and on its own merits: `min-width:0` only LOWERS a
   floor, it cannot shrink anything that was already fitting, and a bare flex child defaulting
   to `min-width:auto` genuinely cannot give. That is correct CSS for a bar that must survive
   an iPad. But it is hygiene, not a fix for a confirmed on-screen failure, and this file has
   already cost a day to a comment that described intent as if it were measurement.

   ONE DECLARATION, NOT FOURTEEN EDITS. `min-width:0` on the bar's direct children is the
   single property those pages were missing, and it is the property the named-class rules
   above already set. Adding the class to fourteen files would fix the same bug fourteen
   times and leave the fifteenth page to rediscover it. This is also why it is `> *` and not
   a list: a page written next week gets it without knowing the rule exists.

   IT CANNOT SHRINK ANYTHING THAT WAS FITTING. `min-width:0` lowers a floor; it does not set
   a width. Verified by re-measuring all nine widths on all nineteen app pages before and
   after — the only boxes that changed are the ones that were overflowing. */
.main-hdr > *{min-width:0;}

/* ── AND THE SUBTITLE ELLIPSIZES, THE SAME WAY THE TITLE DOES ────────────────────────
   `.pg-title` was given `overflow:hidden;text-overflow:ellipsis` for exactly this reason;
   `.pg-sub` sits beside it in the same cluster and never was. It is a full sentence on
   v2-bills ("AI-coded in Slab · written to QuickBooks once you approve · paid in QB"), so
   instead of shortening it WRAPPED, and a wrapped subtitle makes the bar taller — 78px at
   1280 against the sidebar's 68px logo block, which is the one thing --slab-topbar-h exists
   to prevent. Measured: v2-bills stepped the shared top rule at 1280, 1024, 900 and 820,
   the only page that did so above 760.

   SCOPED TO THE ONE-ROW FORM. A builder's stacked header has a row to spend on this and may
   legitimately want the whole line; `.hdr-2row` is the same "this header has a builder form"
   marker the rules above key off, so the carve-out is stated the same way rather than as a
   new condition. */
.main-hdr:not(.hdr-2row) .pg-sub{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* ── THE TOP RULE, WHERE THE TWO HALVES CANNOT AGREE ─────────────────────────────────
   Everything at the top of this file is about ONE continuous 1px line across the top of the
   app: `.sb-logo`'s bottom border and `.main-hdr`'s, both standing on --slab-topbar-h, both
   68px, no step. That holds at every width down to 768 — measured, 0px delta on all
   nineteen app pages.

   BELOW 760 IT CANNOT HOLD, and the rule that breaks it is the wrap block above, which is
   correct and stays. Once the bar wraps, its height is however many rows the CONTROLS need:
   78px at 600, 166px at 430 and 375 on projects.html, 227px on v2-bills. `.sb-logo` is a
   flex item inside `.sb`, and `.main-hdr` is a flex item inside `.main`; they are in
   different subtrees, so there is no CSS by which the logo block can learn a height that is
   only decided by what the header wrapped to. A token cannot help — there is no single
   number to put in it.

   THE THREE WAYS OUT, and why this one:
     · MATCH THE HEIGHT. Not available, per above, without a script measuring the header and
       writing a pixel onto the sidebar — the runtime-injected chrome slab-header.css exists
       to remove, painting one frame at the wrong height on every resize.
     · HIDE THE LOGO BLOCK below 760. It is the whole "go to Overview" link; losing a control
       to tidy a border is the wrong trade.
     · TAKE AWAY THE SECOND LINE — this one. A broken line reads as a mistake (that is the
       finding the doc-state note above is built on). One line that starts at the rail's edge
       does not: it reads as the header's own rule, which is what it is. The BFB mark and its
       Overview link stay exactly where they were.

   BOUNDED TO THE WRAPPING RANGE. The same 760px the wrap block uses, so at 768 and above the
   line is untouched and still measures 68/68 on every page. */
/* ⚠ SUPERSEDED, SAME AS ABOVE — both rules now live in THE SETTLED TIERS at the foot of
   this file, at the 1024 breakpoint rather than 760. The reasoning below is unchanged and is
   why they still exist at all.
{
  .sb-logo{border-bottom:0;}

  /* ── THE WARNING IS THE ONE THING THAT MUST NOT BE LOST ────────────────────────────
     The nowrap block above says the state chip "never shrinks — a chip that says
     'Offline — Changes Not Saved' is the whole reason that chip is allowed to exist on
     punchlist, so it is the last thing that may be squeezed." That is right, and it had
     exactly one gap: NOT SHRINKING is only protection while the bar is wide enough to hold
     it. Past that, `white-space:nowrap` inside `.main{overflow:hidden}` does not squeeze the
     chip, it CLIPS it — and the longer the message, the more of it disappears.

     MEASURED on bids.html at a 600px viewport, where `#save-note` was carrying a real error
     rather than "Saved Locally": the chip rendered 650px wide inside a 536px bar and ran to
     right=742, so the reader saw "warning Couldn't Read The Bid Board. These Car" and the
     rest — what to do about it — was cut off the page. The one element the rule above was
     written to protect was the one element being destroyed.

     Same trade as the wrap block: clipping loses the content, wrapping keeps all of it, one
     row lower. Bounded to the same 760px, so at every width where the bar fits its one line
     the chip is still a single nowrap pill and nothing about it changes.
  .hdr-status{white-space:normal;}
} */

/* ── THE CHIP SITS ON THE TITLE'S LINE, ON EVERY PAGE ────────────────────────────────
   Adrienne, 2026-08-31: "i would rather that chip sat inline beside 'Overview'. this is
   occurring on every page, all of them need to be fixed."

   WHAT WAS WRONG. The identity content is written three ways across the twenty-six pages
   that render this header:
     · `.hdr-id` holding the title and the chip as siblings — projects, punchlist,
       v2-estimate, v2-proposal. Already one line: `.hdr-id` is a flex row.
     · a BARE `<div>` opening the bar, holding the two as siblings — fourteen pages (bids,
       bills, index, finances, timeclock, directory, people, settings, account-stores,
       v2-bills, v2-budget, v2-change-orders, v2-invoices, v2-invoices-list).
     · `.hdr-left` wrapping a bare `<div>` that holds the two — the seven design-system
       pages. `.hdr-left` is a flex row, but the div INSIDE it is not.
   The last two are block containers holding two block children, so the chip stacked under
   the title and read as a subtitle for the page rather than as a statement about where the
   page's numbers come from. That is the same finding projects.html fixed for itself on
   2026-08-29 by reaching for `.hdr-id`; twenty-one pages never got it.

   ONE DECLARATION, NOT TWENTY-ONE MARKUP EDITS. The condition is not "which class did this
   page happen to use" — it is "this element holds a page title and a state chip as its own
   children", and `:has()` can say exactly that. Adding `.hdr-id` to twenty-one files would
   fix the same thing twenty-one times and leave the twenty-second page to rediscover it;
   this is the same reasoning as `.main-hdr > *{min-width:0}` above, and for the same reason
   a page written next week gets it without knowing the rule exists.

   IT MATCHES `.hdr-id` TOO, harmlessly and on purpose: the properties are the ones `.hdr-id`
   already sets to the same values, so the three shapes cannot drift apart again.

   WHAT IT DELIBERATELY DOES NOT MATCH. v2-bills opens the bar with a title and a `.pg-sub`
   SENTENCE ("AI-coded in Slab · written to QuickBooks once you approve · paid in QB"). That
   is a subtitle, not a chip, and it belongs under the title — so the second `:has()` is the
   guard, and that page is untouched.

   WHAT GIVES WHEN THE BAR RUNS SHORT. The chip does not shrink — `.main-hdr .hdr-status`
   is `flex:0 0 auto` and the block above says why — so on a nowrap row the only thing left
   to give is the TITLE, and it gives all the way. MEASURED on timeclock.html at 768px: the
   bar is 704 wide, `.hdr-right` is 406 of it (a 200px project picker, Add Punch, the bell),
   the chip is 206, and title + gap + chip needs 312 in the 282 that is left. The title was
   squeezed to 9.9px — the page's NAME was gone, replaced by an ellipsis, while the chip kept
   every pixel. That is a worse page than the stacked one Adrienne asked me to fix, and
   test/tool-header-shared.test.js §12 exists because a header with no name in it shipped
   once already.

   SO THE PAIR WRAPS BEFORE THE NAME DISAPPEARS. Inline is what it does when the two fit;
   when they do not, the chip drops back UNDER the title — which is exactly the geometry the
   page had before this rule, so the fallback is by definition no worse than the old
   behaviour. It costs nothing on the shared top rule either: title 30 + 4 + chip 29 = 63,
   inside the 68px floor. Measured on timeclock: inline at 1440/1280/1024/900, wrapped at
   820/768, header 68px in BOTH states.

   ⚠ `flex:1 1 auto` IS PART OF THE WRAP, NOT A SEPARATE TWEAK. Chrome sizes a wrapping flex
   container's intrinsic width from its WIDEST ITEM rather than the sum, so a `flex:0 1 auto`
   wrap container asks for its own width and wraps immediately — the chip would have gone to
   a line of its own at 1440 with half the bar standing empty beside it. That is the same
   trap `.hdr-id` documents above, and the same escape: grow into the free space. Verified
   inline at 1440 with it, and only from 820 down does it wrap.

   AND IT IS SCOPED OFF `.hdr-id` / `.hdr-left`. Those two are the clusters the nowrap block
   above deliberately governs, in both the main and the builder form; this selector is more
   specific than that block, so declaring flex-wrap for them here would silently take that
   away from takeoff, estimate, proposal and punchlist. They were already inline and are left
   exactly as they were — the only pages this touches are the NINETEEN that were actually stacked:
   twelve of the fourteen bare-div pages (directory carries no chip at all, and v2-bills carries a
   `.pg-sub` sentence rather than a chip) plus all seven design-system pages. */
.main-hdr :has(> .pg-title):has(> .hdr-status){display:flex;align-items:center;gap:10px;min-width:0;}

/* ── WHAT GIVES, SETTLED: THE CHIP GIVES. THE PAGE'S NAME STAYS A NAME. ────────────────
   Promoted from a projects.html-only override on 2026-08-31, because its reasoning was
   never page-specific:

     "Projects" is ~60px against a chip that can be 360px. Shrinking the 60px item cannot
     recover the overflow — it only turns the page's name into "Pr…" while the chip stays
     whole.

   The older shared rule said the opposite (chip fixed, title ellipsizes). That was written
   for the STACKED header, where the title had the row to itself and giving cost nothing.
   Side by side it is simply wrong, and it produced the measured 9.9px title on timeclock.
   One inline layout, one answer, on every page — the thing this file keeps being bitten for
   not having.

   THE CHIP HAS A FLOOR. `min-width:7ch` so a failure chip can never ellipse down to
   meaninglessness — "QuickBooks unreachable" must not become "Qu…". Below that floor the
   pair wraps instead (the ≤1024 two-band form), which is where a long chip belongs anyway.
   The full text stays on `title` either way.

   NOT punchlist's offline warning: that is a `.pill`, a different component, and this page
   hides `.hdr-status.store-live` outright. Checked before promoting — there is no conflict. */
.main-hdr :has(> .pg-title):has(> .hdr-status) > .hdr-status{flex:0 1 auto;min-width:7ch;}
.main-hdr :has(> .pg-title):has(> .hdr-status) > .hdr-status > .hdr-status-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.main-hdr :has(> .pg-title):has(> .hdr-status) > .pg-title{flex:0 0 auto;}
.main-hdr :not(.hdr-id):not(.hdr-left):has(> .pg-title):has(> .hdr-status){
  flex:1 1 auto;flex-wrap:wrap;row-gap:4px;}

/* ════════════════════════════════════════════════════════════════════════════════════
   THE SETTLED TIERS — the top nav at every width, decided once.
   ------------------------------------------------------------------------------------
   Adrienne, 2026-08-31: "have them come up with a better stacked design for tablet and
   below sizes. lets get this pinned down for all screens so we can stop messing with the
   top nav already."

   THE HEADER HAS EXACTLY TWO FORMS. Not four, not one per page.

     ONE BAND  (≥1024px — desktop and laptop)          height 68px
       name · chip ……………………………… page controls · [⚙ 🔔]

     TWO BANDS (<1024px — tablet and phone)            height 106px
       name · chip ………………………………………………… [⚙ 🔔]
       ………………………………………… page controls, right-aligned

   WHAT IS GUARANTEED AT EVERY WIDTH, and these are the three things that kept breaking:
     1. EVERY CONTROL IS ON SCREEN. Nothing is clipped by `.main{overflow:hidden}` and
        nothing runs past the right edge. That is what the bands buy — see the note above
        about wrapping beating clipping.
     2. THE PAGE'S NAME IS LEGIBLE. `.pg-title` ellipsizes rather than overflowing, and it
        is never squeezed to nothing: below 1024 it has a band to itself (minus the rail),
        and above 1024 the chip beside it is short enough to leave it room. A previous pass
        measured the timeclock title crushed to 9.9px at 768; the two-band form is the
        answer to that, not a longer nowrap row.
     3. THE HEIGHT IS ONE OF TWO NUMBERS. 68 or 106, and which one you get depends on the
        VIEWPORT, never on which page you are on or how many buttons it happens to carry.

   WHY THE BREAK IS AT 1024 AND NOT 760. 1024 IS NOT A NEW NUMBER — it is the width the
   design system already steps at, where nav.js collapses the sidebar rail to its 64px icon
   form (`@media (max-width:1024px)`, one source, emitted twice; see /design-system
   §Breakpoints). The header and the rail are the two halves of the same top-left corner, so
   they change together or the corner changes twice on the way down. 760 was chosen as "where
   the bar finally overflows", which is a symptom of the content and goes stale the moment a
   page gains a button; 1024 is the tier, and the tier is where the navigation yields.
   768–1023 was a tablet running the desktop bar: one nowrap row holding a title, a chip, a
   200px project picker, one or two buttons and the bell, fitting only by giving it all back
   out of the title. THE PHONE GUTTER IS AT 700, the design system's phone tier, for the same
   reason and not a pixel of its own.

   WHY THE ICONS SIT IN A RAIL. The bell used to be the last item inside `.hdr-right`, so
   when that cluster dropped to a second row the bell went with it — Adrienne's exact
   complaint about projects at tablet width, where row 2 read "Project Review · New Project
   · 🔔". Notifications and Settings are not page controls; they are the app's controls, and
   an app control that moves to a different corner depending on the viewport is a control
   you have to hunt for. THE RAIL IS THE TOP-RIGHT CORNER OF THE PAGE, at every width, in
   both forms and on every page. nav.js builds it (there is one, and it is built in one
   place), and it sits BESIDE `.hdr-right` rather than inside it, which is what lets it
   stay on the name's band while the page's own controls drop below.

   WHAT IS DELIBERATELY NOT CHANGED. A BUILDER with a document open (`.hdr-2row` +
   `body.slab-doc-open`) keeps the two-row form it already has — that layout was designed
   for a document and is documented at the top of this file. The rail rides along in its
   row; the tier rules below do not touch it, exactly as the nowrap block above does not.
   ════════════════════════════════════════════════════════════════════════════════════ */

/* ---- THE RAIL ------------------------------------------------------------
   Two icon buttons, 33px each, 8px apart — narrower than `.hdr-right`'s 12px because these
   two are a PAIR and the page's controls are not. `flex:0 0 auto` for the same reason the
   chip and the primary are: it is a control, and controls do not give. */
.hdr-rail{display:flex;align-items:center;gap:8px;flex:0 0 auto;}
/* Settings is an <a>; the bell is a <div>. They must not look like two different things. */
a.hdr-icon{text-decoration:none;color:inherit;}
/* YOU ARE ALREADY HERE. The rail is on the Settings page too — taking it away there would
   move the bell one slot left on exactly one page, which is the kind of small inconsistency
   this block exists to end — so it says so instead. */
.hdr-icon[aria-current="page"]{border-color:var(--action-primary);}
.hdr-icon[aria-current="page"] i{color:var(--accent-green-bright);}
/* ⚠ THE RAIL IS APP CHROME, SO IT GOES WHERE `.hdr-right` GOES. punchlist's public share
   view hides the app's controls with `body.share-mode .sb,body.share-mode .hdr-right
   {display:none!important}` — a client or a sub on a share link must not be handed a way
   into Settings or into somebody else's notifications. The rail is a SIBLING of
   `.hdr-right` (that is what puts it on the name's band), so it has to be named in the
   same breath. Stated here rather than in punchlist.html because the next page that grows
   a public view must not have to rediscover it. */
body.share-mode .hdr-rail{display:none!important;}

/* ---- TABLET AND BELOW: THE TWO-BAND FORM ---------------------------------
   ⚠ THE SELECTORS ARE REPEATED VERBATIM from the nowrap block above, and that is not
   redundancy: `body:not(.slab-doc-open) .main-hdr .hdr-right` is (0,4,0), a media query
   adds no specificity, and a shorter selector here would simply lose to it. Same
   selectors + later source order is the whole mechanism. */
@media (max-width:1024px){
  /* THE BAR WRAPS. 10px of breathing room above and below so the two bands are not welded
     to the rule; the horizontal gutter is untouched. */
  .main-hdr:not(.hdr-2row),
  body:not(.slab-doc-open) .main-hdr{flex-wrap:wrap;row-gap:10px;padding-top:10px;padding-bottom:10px;
    min-height:var(--slab-topbar-stack-h);align-content:center;}
  /* On a BUILDER's main page the wrap container is the `.hdr-row` inside the bar, not the
     bar itself — the bar holds rows. Both need to wrap or takeoff/estimate/proposal would
     keep the desktop bar at tablet width. */
  .main-hdr .hdr-row{flex-wrap:wrap;row-gap:10px;}

  /* BAND 1 — the name and its chip, growing into whatever the rail does not take.
     `flex:1 1 auto` and NOT `1 1 100%`: the rail has to fit beside it. */
  .main-hdr:not(.hdr-2row) .hdr-id,.main-hdr:not(.hdr-2row) .hdr-left,
  body:not(.slab-doc-open) .main-hdr .hdr-id,
  body:not(.slab-doc-open) .main-hdr .hdr-left{flex:1 1 0;flex-wrap:wrap;min-width:0;}
  /* …and the same for the FOURTEEN pages that open the bar with a bare <div> instead of a
     named cluster. Same reasoning as `.main-hdr > *{min-width:0}` above: state it once
     against the shape rather than fourteen times against the class list.
     ⚠ IT ASKS FOR THE PAGE'S NAME, NOT FOR "NOT THE OTHER TWO". Written as
     `> :not(.hdr-right):not(.hdr-rail)` it also caught punchlist's `#langMount` — an EMPTY
     div that only fills on the public share view — and handed it an equal share of the bar,
     which put four items on a line that fits three and wrapped the rail off it. Measured at
     900 before this: header 138px against the 106 it should be. The identity cluster is
     "the thing holding the page name", and that is what this says. */
  .main-hdr > :has(.pg-title),
  .main-hdr .hdr-row > :has(.pg-title){flex:1 1 0;min-width:0;}

  /* THE RAIL STAYS ON BAND 1, pinned right. `order` is what does it: in the DOM the rail
     comes after `.hdr-right` (it is inserted next to it), and without this it would follow
     the controls down. */
  .hdr-rail{order:1;}
  /* ⚠ `flex-basis:0` ON THE IDENTITY IS WHAT KEEPS THE RAIL ON BAND 1, and it is not a
     tidy-up. Flexbox breaks a line on HYPOTHETICAL sizes and only shrinks what is left
     afterwards, so with `flex:1 1 auto` the cluster asked for title + chip at full width,
     the rail no longer fit beside it, and the rail wrapped to a line of ITS OWN — measured
     on bids at 375: title y=10, chip y=44, rail y=83, three bands where there should be
     two, and the two icons no longer in the corner they are supposed to live in. With a
     zero basis the rail is placed first and the name takes whatever is left.
     ⚠ AND THE SAME BASIS HAS TO BE STATED AGAINST THE `:has()` SELECTOR TOO. Nineteen of the
     twenty-six pages open the bar with a bare <div>, and the block at the foot of this file
     ("THE CHIP SITS ON THE TITLE'S LINE") reaches them with
     `.main-hdr :not(.hdr-id):not(.hdr-left):has(> .pg-title):has(> .hdr-status)` — five
     classes, which beats the four-class selector above no matter how late it is declared.
     Measured before this line existed: Projects (which uses `.hdr-id`, so it took the rule
     above) had the rail in the corner at 430; Overview, Bids and Finances (bare divs, so they
     took the `:has()` rule's `flex-basis:auto`) had it wrapped onto a third band at the LEFT
     edge. Same selector, repeated verbatim and later — the convention this file already uses
     everywhere the nowrap block has to be beaten. NOTHING ELSE about that block is changed:
     its `display:flex`, its gap and its `flex-wrap:wrap` all still apply, and above 1023 it
     is untouched. */
  .main-hdr :not(.hdr-id):not(.hdr-left):has(> .pg-title):has(> .hdr-status){flex:1 1 0;}

  /* BAND 2 — the page's own controls, on a line of their own, right-aligned.
     `flex-basis:100%` is what forces the break: an item whose base size is the full line
     cannot share one, so the band is a DESIGN and not an overflow that happened. */
  .main-hdr:not(.hdr-2row) .hdr-right,
  body:not(.slab-doc-open) .main-hdr .hdr-right{
    order:2;flex:1 1 100%;flex-wrap:wrap;justify-content:flex-end;row-gap:10px;}
  /* A PAGE WITH NOTHING TO ACT ON IS STILL THIS TALL, and that is deliberate. Overview and
     Settings carry no header control but the rail, so their `.hdr-right` is an empty
     zero-height flex item and their bar would come out ~35px shorter than Bills' — a header
     that changes height as you move between pages. The stack token is a floor for exactly
     that reason, and nothing detects the empty case: no `:has()`, no class, no JS. */

  /* THE WARNING IS NEVER CLIPPED. Measured on bids at 600px before this: the chip rendered
     650px wide inside a 536px bar, so "Couldn't Read The Bid Board. These Car" was all you
     got. Wrapping keeps all of it, and it is the right thing to spend the pixels on.
     THREE THINGS MAY EXCEED THE 106px STACK, all of them measured and all of them named in
     /design-system: this chip when it needs two lines; a phone-width page carrying three or
     more header controls (takeoff, 194px at 375); and v2-bills, whose band 1 holds a `.pg-sub`
     SENTENCE rather than a chip and runs 121px at tablet. Nothing else. */
  .hdr-status{white-space:normal;}

  /* THE TOP RULE CANNOT MEET A TWO-BAND HEADER. `.sb-logo` is a flex item in `.sb` and
     `.main-hdr` is a flex item in `.main` — different subtrees, no CSS by which one learns
     the other's height, and no single number to put in a token. A broken line reads as a
     mistake; one line that starts at the rail's edge reads as the header's own rule. */
  .sb-logo{border-bottom:0;}
}

/* ---- PHONE: the gutter comes in ------------------------------------------
   28px of padding is 15% of a 375px screen spent on nothing. 16px is the same gutter the
   app's cards use. The bands, the rail and the order are all unchanged — this tier is the
   tablet design at a smaller size, which is the point of having two forms and not four. */
@media (max-width:700px){
  .main-hdr{padding-left:16px;padding-right:16px;}
  /* A PICKER THAT WOULD RATHER TAKE A ROW OF ITS OWN. Sixteen pages pin `min-width:200px`
     on `.filter-sel`; the rule near the top of this file already lowers that floor inside
     the bar, but a floor is not a BASIS — flexbox still asks for the select's content width
     when it decides where the line breaks, so on Bills at 375 the picker and Upload Bill
     took a band each with 130px of the second one empty. A 120px basis lets them share the
     band and then grow into it. `.csel` is nav.js's styled twin of the same control and
     has to be named beside it or only the invisible half would give. */
  .main-hdr .filter-sel,.main-hdr .csel{flex:1 1 120px;min-width:0;}
}
