/* ============================================================================
   site-header.css — the SHARED CabDesign suite chrome
   ----------------------------------------------------------------------------
   Two things live here, both driven ENTIRELY by the theme tokens that
   shared/theme.css owns (never a hardcoded palette hex — the fixed white bevel
   on the accent mark is the one deliberate, theme-independent effect and is
   annotated):

     1. .suite-brand  — the reusable brand mark used by BOTH headers:
                          the accent glyph tile + "CabDesign" (the primary
                          wordmark) + an optional section crumb, ON ONE LINE
                          since FD161 (2026-08-17), separated by a quiet "·".
                        CabDesign is always the loud segment. The header used to
                        carry a small "built by Bespoke Woodcraft Studio" credit
                        under it (.suite-brand-credit, still defined below and
                        still used by the planner for its front-door credit); the
                        founder's standing order (P668, 2026-08-15) took it out
                        of the CabDesign-suite page chrome, nothing replacing it
                        — the legal name still appears in every page's footer.
                        (Guarded by site-lint R7: "Bespoke Woodcraft Studio" is
                        never allowed as an un-demoted primary brand.)

     2. .topbar       — the dark-walnut app-bar SHELL that was copy-pasted
                        verbatim into 9 tool pages. Extracted here unchanged so
                        the app bar looks identical after the refactor; only the
                        brand text hierarchy is corrected.

   USAGE
     App tool page (dark bar):
       <link rel="stylesheet" href="/shared/site-header.css">
       <header class="topbar">
         <a class="suite-brand" href="/index.html"
            aria-label="CabDesign home · Materials Library">
           <span class="suite-brand-mark" aria-hidden="true"><svg viewBox="0 0 100 100" fill="currentColor" aria-hidden="true" focusable="false"><rect width="34" height="100" rx="5"/><rect x="40" width="60" height="42" rx="5"/><rect x="40" y="48" width="60" height="23" rx="5"/><rect x="40" y="77" width="60" height="23" rx="5"/></svg></span>
           <span class="suite-brand-text">
             <span class="suite-brand-name">CabDesign</span>
             <span class="suite-brand-sub">Materials Library</span>
           </span>
         </a>
         <nav data-tool-nav data-module="materials" aria-label="CabDesign modules"></nav>
         <div data-account-chip></div>
       </header>

     Marketing header (light bar) — keep the page's own .nav/.nav-links/.nav-cta
     layout, just drop the shared brand mark in (no .suite-brand-sub):
       <a class="suite-brand" href="#top"
          aria-label="CabDesign home">
         <span class="suite-brand-mark serif" aria-hidden="true"><svg viewBox="0 0 100 100" fill="currentColor" aria-hidden="true" focusable="false"><rect width="34" height="100" rx="5"/><rect x="40" width="60" height="42" rx="5"/><rect x="40" y="48" width="60" height="23" rx="5"/><rect x="40" y="77" width="60" height="23" rx="5"/></svg></span>
         <span class="suite-brand-text">
           <span class="suite-brand-name">CabDesign</span>
         </span>
       </a>

     Guide / content page (light breadcrumb) — drop the brand mark into the page's
     own slim nav bar as class="suite-brand suite-breadcrumb"; full recipe +
     verified contrast are documented at variant 3 near the end of this file.

   Any page-specific tweak (e.g. cabinet-designer.html spans the app bar across a
   CSS grid) stays on that page as a SCOPED, higher-specificity override
   (.page-layout > .topbar { … }) so it wins over this shared base regardless of
   stylesheet order — this file never needs to know about a page's layout.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. The reusable brand mark. Base styles = the LIGHT context (marketing nav):
      the wordmark inherits the page ink; the sub/credit use light-safe muted
      tokens. The dark app-bar context is layered on under `.topbar` below.
   --------------------------------------------------------------------------- */
/* The one stacking order (W7-01, R-29): every layer named below is declared in shared/layers.css,
   imported here so any page that links this file has the layers with no head edit. */
@import url("/shared/layers.css");

.suite-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
}
.suite-brand:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
  border-radius: 6px;
}

/* The accent glyph tile. --on-accent-fill / --on-accent are the theme's
   verified-AA fill+text pair for any accent-filled chip, so the mark reads on
   both the light nav and the dark bar with one rule. */
.suite-brand-mark {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 7px;
  display: grid;
  place-items: center;
  background: var(--on-accent-fill);
  color: var(--on-accent);
  font-size: 14px;
  line-height: 1;
}
/* A brand may supply its glyph as an inline SVG instead of a text character -
   StackDesign's mark is a stack of cases, and since 2026-08-21 CabDesign's is a
   cabinet elevation, neither of which any character says. Both inherit currentColor
   from the tile, so the one --on-accent pairing above still holds and neither brand
   needs its own mark rule (P868).

   ONE RULE SIZES THE DRAWING EVERYWHERE, and it is a percentage on purpose. `font-size`
   scales a text glyph and does nothing at all to an <svg>, so every variant that resizes
   this tile - the app bar at 38px, the guide breadcrumb at 22px, the narrow bar at 32px,
   the Designer flyout at 30px - used to need its own pixel pair beside it. Those pairs
   were a stored derived fact: nine of them, hand-rounded, drifting between 59.1% and
   62.5% of tiles they were supposed to track. The tile always has a definite width and
   height (every variant sets both), so 61.5% resolves against it and the inset is
   DERIVED at each size instead of retyped. Resizing a tile now needs one number, not
   two, and the mark's optical weight is identical on every surface by construction. */
.suite-brand-mark svg {
  width: 61.5%;
  height: 61.5%;
  display: block;
}

/* ONE LINE, EVERYWHERE (FD161 option A, founder 2026-08-17). This used to stack the
   wordmark over the section name, and the stack is what manufactured the doubled
   "StackDesign / StackDesign" corner he called out: two lines invite a page to type
   the product name a second time underneath the one that is already data-bound. The
   lockup now reads as a breadcrumb — wordmark, a quiet "·", the section — which is
   the guide-page recipe at variant 3 below, promoted from a one-off to the default.
   That variant now re-states only its own INK; it no longer owns a second layout.

   `min-width: 0` is deliberately gone with the column. A zero shrink floor on a
   one-line lockup means the text overflows its own box under pressure instead of
   wrapping, and this project never truncates or clips text. With the default
   `min-width: auto` the lockup keeps its natural width and the .topbar (flex-wrap)
   wraps around it. */
.suite-brand-text {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  gap: 0;
  line-height: 1.06;
  white-space: nowrap;
}

/* "CabDesign" — the primary wordmark, always the loudest segment.
   THIS IS THE ONE WORDMARK SIZE, and it is the whole of FD161's "one wordmark at one
   size": the wordmark used to ship at .96rem here, 1.06rem in the app bar and .95rem
   in the guide breadcrumb, so the same word resized itself as you walked the site. No
   variant below re-declares font-size on .suite-brand-name — the ink changes per
   surface (it must, for contrast), the SIZE does not.
   tests/page-shell.audit.cjs section 11 runs the cascade over this file and fails if a
   second size reaches the wordmark in any of the three variants. */
.suite-brand-name {
  font-weight: 800;
  font-size: .96rem;
  letter-spacing: -0.01em;
  color: inherit;
}

/* THE SECTION CRUMB — which tool or page this is, in one quiet segment after the
   wordmark. Same story as the wordmark: this was .72rem here, .8rem in the app bar and
   .82rem in the guide breadcrumb, three sizes for one word. .82rem is the value that
   carries evidence: it is the guide-breadcrumb size whose contrast was measured at AA
   for P606, and it holds the ~0.85 subordination ratio against the .96rem wordmark
   that the app bar's own note asked for ("subordinate via size, not an illegible tiny
   kicker"). shared/brand.js derives the TEXT from the tool registry — see its
   applySuiteBrand — so nothing here has to know a page's name. */
.suite-brand-sub {
  font-weight: 600;
  font-size: .82rem;
  color: var(--accent-text);
}

/* The inline "·" separators, kept OUT of the markup so the link's accessible name
   stays "CabDesign home · Materials Library" (built by brand.js from the parts) rather
   than swallowing punctuation the eye reads but a screen reader should not have to.
   A suppressed segment is [hidden] — display:none — so its separator never orphans. */
.suite-brand-sub::before,
.suite-brand-credit::before {
  content: "·";
  margin: 0 .5em;
  font-weight: 400;
}

/* P668 (2026-08-15): the founder's standing order took the "built by Bespoke Woodcraft
   Studio" credit out of the page chrome, nothing replacing it — the legal name still
   appears in every page's footer. ONE page still emits the span, and it is not a
   leftover: the planner (modules/festool-systainer-cabinet-width/systainer-bank.html)
   carries <span class="suite-brand-credit" data-brand-credit>, which shared/brand.js
   fills with the front door's own credit — "part of the CabDesign platform" on the
   CabDesign door, and EMPTY (so brand.js hides the span) on both StackDesign doors,
   which is what keeps that sentence off the StackDesign product. Do not use it in new
   markup — see the USAGE recipes above, none of which reference it. */
.suite-brand-credit {
  font-weight: 600;
  font-size: .62rem;
  letter-spacing: .01em;
  color: var(--ink-soft);
  white-space: nowrap;
}

.suite-brand:hover .suite-brand-name {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   2. The dark-walnut app-bar shell (extracted verbatim from the 9 tool pages).
      Uses the walnut / gold header aliases, all first-visit-safe in theme.css.
   --------------------------------------------------------------------------- */
.topbar {
  background: linear-gradient(160deg, var(--walnut) 0%, var(--walnut-2) 100%);
  color: var(--header-text);
  border-bottom: 3px solid var(--gold);
  padding: 12px 22px;
  display: flex;
  align-items: center;
  /* 12px, not 18px (2026-08-22). This gap is what separates Tools from the account
     chip on the eight app-bar pages, and 18px was the widest of the five different
     values that pair took across the suite (Designer 4, Outputs 12, Systainer Bank
     14, here 18). The suite's own spec caps a paired-control gap at 16px, so this
     was the one over it; 12px is the value the Outputs bar already shipped and the
     one .cd-topband-right now floors at, so the pair reads the same everywhere. */
  gap: 12px;
  flex-wrap: wrap;
  position: sticky;
  top: 0;
  z-index: var(--layer-docked);
}

/* ---------------------------------------------------------------------------
   The brand mark inside the dark bar: scaled up, cream wordmark, gold kicker.
   Scoping under `.topbar` means every app page gets the dark treatment for free
   just by dropping <a class="suite-brand"> inside its .topbar.
   --------------------------------------------------------------------------- */
.topbar .suite-brand {
  /* The brand no longer takes the free space: with BOTH the brand and the account chip
     on auto margins, Tools floated to wherever each page's own controls left room -
     measured 523px of page-to-page drift (257 to 780) at 1440, defeating the very
     Fitts's-law muscle memory tool-nav.js documents for itself. Now the brand holds a
     fixed footprint and the TOOLS NAV takes the free space (rules below), so the
     launcher lands in the same spot on every topbar page (design authority, 2026-08-13
     - fix proven in-browser before shipping).

     WHICH SIDE it takes that space on is decided by whether the bar carries an account
     chip, and that is the founder's call of 2026-08-21: "throughout both sites, the
     'Tools' button should be just to the left of the 'Account' button... The Designer
     page has the correct location of the 'Tools' Button." On a bar WITH a chip the nav
     takes the free space on its LEFT, so the pair lands together at the right end the
     way .cd-topband-right builds it on the Designer. On a bar without one it keeps the
     space on its right, exactly as the 2026-08-13 fix shipped it. */
  margin-right: 0;
  min-width: 248px;
  gap: 12px;
}
/* No account chip on this page: Tools keeps the free space to its right, sitting by the
   brand. The marketing bar's siblings are in this state, and dragging them along with the
   fix below would push Tools past their own controls.
   FD277 (2026-08-27) took the free tool pages OUT of this state - gola-configurator,
   spec-to-cutlist, the hinge calculator and both calculators now carry a chip, so the rule
   below is what applies to them and their units pickers moved to the launcher's left to
   keep the pair adjacent. This rule stays for the bars that genuinely have no chip. */
.topbar > nav[data-tool-nav] {
  margin-right: auto;
}
/* P1040 - THE MOUNT IS A FLEX ROW, so the return chip and the Tools pill share one centre line.
   Founder, 2026-08-18, with a screenshot of stackdesign.app/materials.html: "the stack design
   button doesn't line up with the tools button properly, so that stuff needs to line up and
   look correct."

   ROOT CAUSE, measured in-browser at 1440x900 before this rule. The mount is a <nav>, so it was
   display:block, and BOTH controls inside it are inline-flex boxes sharing ONE line box. The
   chip declares `vertical-align: middle` (2b below) and .bws-switch takes the initial
   `baseline`, so the two 40px boxes hung off different reference points: chip top 16.63 against
   pill top 12.00, a 4.63px step, and a line box grown to 44.63px that pushed .topbar itself to
   71.63px where every other app bar measures 67px. Both defects on materials.html AND
   hardware.html, both themes.

   WHY FLEX AND NOT A MATCHING vertical-align. A flex container has no line box, so there is no
   strut, no baseline, and no third value for a future control to disagree with - alignment
   becomes a property of the ROW rather than an agreement two components have to keep. This is
   the same correction designer-outputs.html made page-locally on 2026-08-21 (its own header
   comment) and cut-list-optimizer.html made page-locally under P804, which explicitly deferred
   "the systemwide correction (every page that mounts the switcher)" to a shared pass. This is
   that pass; both page-local rules are now redundant and harmless.
   Pinned by tests/topbar-control-alignment.test.cjs. */
.topbar > nav[data-tool-nav] {
  display: flex;
  align-items: center;
}
/* A chip follows somewhere in this bar: hand the free space to the nav's LEFT margin so
   the launcher travels across to the chip instead of the chip travelling away from it.
   Measured at 1440x900 on 2026-08-21: 858 to 872px of empty bar between the two before
   this rule, and the bar's own `gap` after - 18px then, 12px since the gap was brought
   into line on 2026-08-22 - on every .topbar page carrying a chip.
   Pinned by tests/tools-position.test.cjs. */
.topbar > nav[data-tool-nav]:has(~ [data-account-chip]) {
  margin-right: 0;
  margin-left: auto;
}
/* shared/account-chip.js gives every chip `margin-left: auto` so it can self-align to the
   far right of a flex bar. Once the nav above has claimed the free space, that second auto
   margin SPLITS it with the first and re-opens the gap - two autos on a main axis share the
   slack equally. Zero it on the chip that directly follows the launcher; a chip anywhere
   else in the bar still self-aligns as before. */
.topbar > nav[data-tool-nav] + [data-account-chip] {
  margin-left: 0;
}
.topbar .suite-brand-mark {
  width: 38px;
  height: 38px;
  border-radius: 9px;
  font-size: 20px;
  /* fixed white top-bevel highlight on the accent mark — a deliberate,
     theme-independent sheen, matching the original hand-copied topbar. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
/* INK ONLY. The app bar used to bump the wordmark to 1.06rem here; FD161 ended that
   (see the one-size note on .suite-brand-name above), so this rule now says only what
   is genuinely surface-specific: cream on the dark walnut gradient. */
.topbar .suite-brand-name {
  line-height: 1.05;
  color: var(--header-text);
}
/* The section crumb on the dark bar — the most load-bearing "which tool is this?" cue
   for a first-time visitor. It reads subordinate to the cream wordmark via the gold
   hue and the lighter weight; the SIZE is the shared .82rem, no longer a third value
   invented here, and the .04em tracking went with it so the crumb is one treatment on
   every surface. */
.topbar .suite-brand-sub {
  line-height: 1.2;
  color: var(--gold-soft);
}
.topbar .suite-brand-credit {
  margin-top: 0;
  font-size: .58rem;
  line-height: 1.15;
  letter-spacing: .04em;
  /* Muted: the bright --header-text at 72% alpha, composited over the app-bar gradient.
     Verified WCAG AA >=4.5:1 on BOTH gradient stops (--walnut AND --walnut-2) in both
     themes. Re-measured for P606 against the two that survived the ten-theme collapse:
     worst case 5.93:1 (linen-ink at the lighter --walnut-2 stop), best 8.63:1
     (midnight-oak at --walnut). The prior ad-hoc 58% mix landed sub-AA at the
     --walnut-2 stop, which is why the alpha is pinned by number here. */
  color: color-mix(in srgb, var(--header-text) 72%, transparent);
}

/* ---------------------------------------------------------------------------
   Mobile: ONE shared wordmark / app-bar scale-down for the narrow viewport.
   Consolidated here because it had been independently reinvented per page
   (systainer-bank.html at 768px, cut-list-optimizer.html at 720px, with
   divergent sizes; the other dark-topbar pages had none). 760px is the single
   suite breakpoint; page-local @media rules no longer touch the wordmark.
   Placed after the base .topbar rules so, at equal specificity, source order
   lets it win inside the query.
   --------------------------------------------------------------------------- */
@media (max-width: 760px) {
  .topbar { padding: 10px 14px; gap: 12px; }
  /* the wordmark's .98rem override lived here and is gone: FD161 gives the wordmark ONE
     size on every surface, and a fourth value hiding in a media query is exactly how the
     first three accumulated. The mark and the bar padding still tighten. */
  .topbar .suite-brand-mark { width: 32px; height: 32px; font-size: 17px; }
}

/* ---------------------------------------------------------------------------
   2b. THE RETURN CHIP (P114, D16 section 4) — "← Back to the Designer · <project>".
       Rendered ONCE by shared/tool-nav.js into every app page's [data-tool-nav]
       mount, adjacent to the Tools launcher, whenever the Designer recorded a
       fresh working project. A QUIET ghost-pill that borrows the launcher's own
       --header-text grammar (color-mix tints) so the two controls read as one
       family on the dark bar, in every theme, with zero new hex. The full label
       truncates a long project name with ellipsis (the title attribute + the
       homepage resume chip both carry the full name); below 720px it collapses
       to just "← Designer" so the bar never overflows.
   --------------------------------------------------------------------------- */
.bws-return-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  vertical-align: middle;
  margin-right: 10px;                 /* separate from the Tools pill that follows */
  text-decoration: none;
  font-family: inherit;
  font-weight: 700;
  font-size: .82rem;
  line-height: 1;
  color: var(--header-text);
  background: color-mix(in srgb, var(--header-text) 2%, transparent);
  border: 1px solid var(--accent);
  /* P1040 - ONE GEOMETRY SOURCE WITH THE LAUNCHER. These three used to be typed here (9px
     radius, 8px/11px padding, min-height:40px) while .bws-switch-btn in shared/tool-nav.js read
     --tnav-radius / --tnav-padx / --tnav-h off its host through var() fallbacks. Two boxes that
     merely agreed on 40px today, and either could drift. Reading the SAME three tokens means a
     host that resizes the launcher resizes the chip with it, and no host can size one without
     the other. Every host that overrides them either hides this chip (.mktbar-tools) or never
     renders one (the Designer's 34px band is its own front door), so today this resolves to the
     same 40px box - 1px wider padding, nothing else moves.
     HEIGHT, NOT min-height, matching what designer-outputs.html worked out page-locally on
     2026-08-21: min-height beats height (the trap page-layout.md section 4 records), and a long
     project name must never grow the chip past the pill. */
  border-radius: var(--tnav-radius, 9px);
  padding: 0 var(--tnav-padx, 12px);
  height: var(--tnav-h, 40px);
  min-height: 0;
  max-width: 340px;
  white-space: nowrap;
  transition: background .12s, border-color .12s;
}
.bws-return-chip:hover {
  background: color-mix(in srgb, var(--header-text) 5%, transparent);
  border-color: var(--accent);
}
.bws-return-chip:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}
.bws-return-ico { flex: none; font-size: .95rem; opacity: .9; }
.bws-return-full { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; }
.bws-return-lead { flex: none; }
.bws-return-proj {
  min-width: 0;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  opacity: .82;
}
/* the " · " separator, kept out of the markup so the link's accessible name stays clean */
.bws-return-proj::before { content: "·"; margin-right: 6px; opacity: .6; font-weight: 400; }
.bws-return-short { display: none; }
@media (max-width: 719px) {
  .bws-return-chip { max-width: none; }
  .bws-return-full { display: none; }
  .bws-return-short { display: inline; }
}

/* ---------------------------------------------------------------------------
   3. GUIDE-PAGE BREADCRUMB variant  ( class="suite-brand suite-breadcrumb" )
      ----------------------------------------------------------------------
      The THIRD documented recipe, alongside the app-tool (.topbar) and the
      marketing-nav variants above. It is a LIGHT, single-line, NON-sticky,
      NO-gold-border brand lockup for the content / guide pages, which carry
      their own slim horizontal nav bar (home link + section anchors) rather
      than the dark .topbar app shell. Drop it in as the FIRST child of the
      guide page's existing <nav>, replacing the old back-link + page-title:

        <a class="suite-brand suite-breadcrumb" href="/index.html"
           aria-label="CabDesign home · LEGRABOX install">
          <span class="suite-brand-mark" aria-hidden="true"><svg viewBox="0 0 100 100" fill="currentColor" aria-hidden="true" focusable="false"><rect width="34" height="100" rx="5"/><rect x="40" width="60" height="42" rx="5"/><rect x="40" y="48" width="60" height="23" rx="5"/><rect x="40" y="77" width="60" height="23" rx="5"/></svg></span>
          <span class="suite-brand-text">
            <span class="suite-brand-name">CabDesign</span>
            <span class="suite-brand-sub">LEGRABOX install</span>
          </span>
        </a>

      CabDesign is the bold PRIMARY segment; the guide name follows inline,
      demoted by the shared crumb size and a lower opacity of the SAME ink — so
      its contrast tracks the nav's own (already-AA) text colour rather than a
      fixed token, and stays legible on BOTH light and dark guide bars. Since
      FD161 (2026-08-17) this inline shape is the DEFAULT lockup rather than this
      variant's own trick, so the rules below carry ink and nothing else — the
      row, the sizes and the "·" separators live in section 1. (P668, 2026-08-15: the recipe
      used to add a third, smaller "built by Bespoke Woodcraft Studio" segment
      here — .suite-brand-credit, styled below and still defined for the pages
      outside this sweep's scope — the founder's standing order took it out of
      every page this recipe drives.)

      Ink source: the lockup defaults to --text (the 5 light guide bars). The 2
      guide bars that paint a dark --header-bg background (drawer-fridge-options,
      quiet-workshop-fridge) add ONE scoped line in their own <style>:
        nav .suite-brand.suite-breadcrumb { color: var(--header-text); }
      which out-specifies the default (0,2,1 > 0,2,0) so the ink flips to cream.

      The double-class colour selector (0,2,0) intentionally out-specifies each
      guide nav's own `.nav a` / `nav a` link colour (0,1,1 / 0,0,2) so the
      wordmark reads at full strength, not the nav's muted link ink.

      Verified: the guide name at .82 opacity clears WCAG AA 4.5:1 on every guide
      bar in both themes, worst case 7.15:1 (linen-ink) / 9.92:1 (midnight-oak),
      re-measured for P606.
   --------------------------------------------------------------------------- */
.suite-brand.suite-breadcrumb {
  gap: 9px;
  margin-right: auto;                 /* brand left, the nav's section links to the right */
  color: var(--text);                 /* light-bar default ink; dark bars flip it per-page */
  white-space: nowrap;
  padding: 0;                          /* neutralise any `nav a` pill padding on the guide bars */
  opacity: 1;                          /* neutralise the dark fridge bars' `nav a{opacity:.7x}` dimming */
}
/* the wordmark is the brand, not a section link: no guide-nav hover-pill behind it. */
.suite-brand.suite-breadcrumb:hover { background: none; }
.suite-breadcrumb .suite-brand-mark {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  font-size: 12px;
  box-shadow: none;                   /* the white bevel is a .topbar-only sheen */
}
/* FD161: the one-line row, the wordmark size, the crumb size and the inline "·"
   separators all moved OUT of this variant and INTO the base above, because the base is
   now this shape everywhere. What is left here is what is genuinely specific to a light
   content bar: the ink follows the nav's own already-AA text colour through `inherit`
   plus an opacity step, instead of the accent/gold tokens the app bar uses. */
.suite-breadcrumb .suite-brand-text {
  line-height: 1;
}
.suite-breadcrumb .suite-brand-name {
  color: inherit;
}
.suite-breadcrumb .suite-brand-sub {
  color: inherit;
  opacity: .82;
}
.suite-breadcrumb .suite-brand-credit {
  margin-top: 0;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: 0;
  white-space: nowrap;
  color: inherit;
  opacity: .72;
}
@media (max-width: 760px) {
  /* keep the wordmark legible but tighten the inline credit on the smallest bars */
  .suite-breadcrumb .suite-brand-credit { display: none; }
}

/* ---------------------------------------------------------------------------
   3a. THE SKIP LINK  ( class="skip" )  — P847
      ----------------------------------------------------------------------
      WCAG 2.4.1 Bypass Blocks. The live audit counted it on 5 of 13 pages;
      re-measured on 2026-08-17 it was missing from 12 of 17 public pages,
      including every legal page and the Materials Library — so a keyboard or
      screen-reader user had to tab the whole top bar, the Tools switcher and
      the account chip on every single page load before reaching the content.

      It lives here because it is page chrome, and because six pages had each
      written their own copy of the same eight declarations. Those copies still
      win where they exist (a page <style> follows this <link> at equal
      specificity), so nothing moves on a page that already had one — this is
      what a page WITHOUT one now inherits for free:

        <body>
          <a class="skip" href="#main">Skip to the main content</a>
          ...
          <main class="wrap" id="main"> … </main>

      Off-screen until focused, never display:none — a hidden skip link is one
      that cannot be reached by the only user it exists for.
   --------------------------------------------------------------------------- */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: var(--layer-docked-skip);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 0 0 8px 0;
  padding: 12px 18px;
  font-weight: 700;
  text-decoration: none;
}
.skip:focus {
  left: 0;
}
.skip:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   3b. THE SHARED MARKETING BAR  ( class="mktbar" )  — P868
      ----------------------------------------------------------------------
      Founder, 2026-08-16: "I want standardization across both sites for layouts
      so that users have a smooth transition."

      WHAT WAS WRONG. Both products already agreed on what a marketing bar LOOKS
      like — a sticky cream pill with the wordmark, a link row and a sign-up pair
      — and neither shared one line of code for it. index.html had its own copy;
      the five StackDesign doors had a second copy, typed out five times; and
      CabDesign's own about / team / faq / free-tools pages had no marketing bar
      at all, because they were written from the app-page scaffold and inherited
      the dark tool .topbar. So a visitor walking CabDesign home → About changed
      sites mid-funnel, and the same walk on StackDesign did not. That is the
      exact shape section 2 of docs/design-system/page-layout.md already tells
      about headers: nobody drifted on purpose, there was just nothing to share.

      WHY A MARKETING BAR AND NOT THE APP BAR, on these pages. The app bar's job
      is workflow — switch tools, reach your account. A marketing page's job is
      conversion, and its bar has to carry the story links and the way in. The
      homepage already won this argument once (page-layout.md section 2c:
      "painting the marketing front door in the dark tool app bar would have been
      a downgrade wearing consistency's clothes"); About and FAQ are the same
      class of page as the front door they sit behind, so they get the same bar.

      THE RECIPE. One <nav>, the shared lockup, a link row, a CTA slot:

        <nav class="mktbar" aria-label="Primary">
          <div class="mktbar-in">
            <a class="suite-brand" href="/index.html" aria-label="CabDesign home">
              <span class="suite-brand-mark" aria-hidden="true"><svg viewBox="0 0 100 100" fill="currentColor" aria-hidden="true" focusable="false"><rect width="34" height="100" rx="5"/><rect x="40" width="60" height="42" rx="5"/><rect x="40" y="48" width="60" height="23" rx="5"/><rect x="40" y="77" width="60" height="23" rx="5"/></svg></span>
              <span class="suite-brand-text">
                <span class="suite-brand-name">CabDesign</span>
              </span>
            </a>
            <div class="mktbar-links">
              <a href="/free-tools.html">Free tools</a>
              <a href="/faq.html">FAQ</a>
            </div>
            <details class="mktbar-more">
              <summary class="mktbar-more-btn"><span>More</span><svg class="mktbar-more-chev" …/></summary>
              <div class="mktbar-more-menu">
                <a href="/about.html" aria-current="page">About</a>
              </div>
            </details>
            <div class="mktbar-cta">
              <nav class="mktbar-tools" data-tool-nav data-module="" aria-label="CabDesign modules"></nav>
              <a class="mktbar-btn" href="/signin.html">Sign in</a>
              <a class="mktbar-btn mktbar-btn-primary" href="/signin.html?signup=1">Start free</a>
            </div>
          </div>
        </nav>

      NO .suite-brand-sub HERE. The lockup on a marketing page is the wordmark
      alone; the page says which page it is in its <h1> and its breadcrumb, and a
      section crumb in the bar would be a third place saying the same word.

      THE TOOLS MOUNT IS CABDESIGN-ONLY, and that is P626, not an oversight: the
      StackDesign doors carry no CabDesign switcher, no account chip and no
      auth.js, because that host serves one product. The class is still defined
      here so the remap below lives in one place if that ever changes.

      TWO TRAPS THIS VARIANT SETS, both already paid for elsewhere:

        • --header-text IS NEAR-WHITE IN EVERY THEME, because the shared .topbar
          is dark walnut. shared/tool-nav.js paints its pill from it, so a mount
          on this cream bar renders cream-on-cream and passes every structural
          check while being invisible. .mktbar-tools remaps the four header
          tokens at that scope — the same fix the Designer's light .cd-topband
          and index.html's own nav already carry (page-layout.md section 3).

        • THE BUTTONS ARE .mktbar-btn, NOT .btn. Every page this bar lands on
          already defines its own .btn / .btn-primary in a page <style> that
          loads AFTER this file, so a shared `.btn` rule would tie on
          specificity (0,1,0) and lose on source order — correct-looking CSS
          doing nothing, the same trap shared/site-footer.js documents for
          `.site-foot`. Own class names cannot be shadowed by a page's own.

      SIZING. The CTA buttons carry min-height 44px (the comfortable pointer
      target; WCAG 2.2 AA 2.5.8 asks 24px, this clears it with room). The link
      pills compute ~36px tall, also above that floor. Nothing here truncates:
      .mktbar-in wraps rather than clipping, because this suite never truncates
      text and the supported floor is ~768px (desktop and tablet only, P455).
   --------------------------------------------------------------------------- */
.mktbar {
  position: sticky;
  top: 14px;
  z-index: var(--layer-docked);
  margin: 14px auto 0;
  max-width: var(--maxw, 1180px);
  padding: 0 14px;
  display: flex;
}
.mktbar-in {
  flex: 1;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 16px;
  background: color-mix(in srgb, var(--surface) 84%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 8px 8px 8px 16px;
  box-shadow: var(--shadow);
}
/* The lockup keeps the section-1 base (light context) treatment; only its
   spacing is this bar's business. */
.mktbar .suite-brand {
  gap: 10px;
  padding: 2px;
  color: var(--text);
}
.mktbar-links {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-left: 2px;
}
.mktbar-links a {
  display: inline-flex;
  align-items: center;
  padding: 9px 13px;
  border-radius: 999px;
  font-size: .89rem;
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.mktbar-links a:hover {
  color: var(--text);
  background: var(--surface-hi);
}
/* The page you are on, said once, in the bar. aria-current carries it to a
   screen reader; this is the same fact for the eye. */
.mktbar-links a[aria-current="page"] {
  color: var(--text);
  background: var(--surface-hi);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* THE "MORE" DISCLOSURE — FD234 (2026-08-22), card P1205.
   Founder: "Have a more button, it will contain Team, About, Contact, and Blog...
   Put the Guides button on the menu."

   WHAT MOVED AND WHY IT FITS NOW. The flat row is four items (How it works,
   Pricing, Free tools, FAQ) and five live in here. scripts/nav-items.cjs carries
   the arithmetic this replaces: a seven-item row plus an eighth overflowed the
   CabDesign bar by 68px at 1024 and 44px at 1280, because that bar also carries
   the Modules launcher and the sign-up pair. Four items is well inside it.

   IT IS A NATIVE <details>, NOT A BUTTON PLUS A SCRIPT, for the three reasons
   scripts/nav-items.cjs states at renderMore(): the marketing nav is the crawl
   path and must be static <a href>; a new shared/*.js needs an engine-group row
   in dev-dashboard-data.js (tests/dev-dashboard.audit.cjs, "P281-S3"); and
   <summary> promises exactly what it delivers — implicit button role, a
   browser-managed aria-expanded, Enter and Space — which is the same argument
   page-layout.md section P629 makes against role="menu".

   THE MENU OVERLAYS, IT NEVER REFLOWS. position:absolute, so opening it moves
   nothing (the no-layout-shift rule, NORTH-STAR section 4). Nothing on this bar
   sets overflow, and .mktbar itself is --layer-docked, so it floats clear of the
   page under it. .mktbar-in sets backdrop-filter, which makes it the containing
   block for FIXED descendants — that is why there is no full-viewport
   click-away backdrop here, and it is written down rather than rediscovered.

   THE TRIANGLE HAS TO GO TWICE. list-style:none kills the marker in Firefox and
   Chrome, ::-webkit-details-marker in Safari; the chevron replaces it and turns
   over when the menu is open.

   IT DECLARES ITS WHOLE BOX, AND THAT IS NOT TIDINESS — IT IS A MEASURED FIX.
   This is the first component in the shared bar built out of BARE ELEMENTS
   (<details>, <summary>) rather than out of class names, and three of the pages
   that carry the bar style those elements for their own FAQ accordion in a page
   <style> that loads AFTER this file: index.html has
   `details{border-bottom:1px solid var(--line);padding:18px 0}` and
   stackdesign-faq.html has `details{...;padding:4px 0}`. Specificity settles a
   property this rule DECLARES (.mktbar-more is 0,1,0 and beats a bare `details`
   at 0,0,1) and settles nothing about a property it leaves out — so before this
   block the control measured 77px tall inside a 40px row on the homepage, and
   the bar stood 95px instead of 60px. §7 records the identical trap for
   `.site-foot` and §6c's drift guard already watches the `.mktbar*` class names;
   a bare element name is the hole neither of them covers.
   Pinned by tests/mktbar-more-disclosure.test.cjs, which DERIVES the list: every
   property any bar page sets on an unscoped `details`/`summary` rule must be a
   property one of these two rules also sets. */
.mktbar-more {
  position: relative;
  /* STRETCHED TO THE ROW, so the menu hangs off the BAR and not off a 34px button
     floating inside it. The offset below is measured from this box's bottom edge;
     with align-self:center that edge sat 13px above the bar and the menu tucked 2px
     UNDER the bar's own border. Stretching also gives the control the same hit
     height as the pills either side of it. */
  align-self: stretch;
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  font: inherit;
}
.mktbar-more > summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0;
  padding: 9px 13px;
  border: 0;
  border-radius: 999px;
  background: none;
  box-shadow: none;
  font-family: inherit;
  font-size: .89rem;
  font-weight: 600;
  /* 1.55 IS THE PILLS' OWN LINE HEIGHT, MEASURED, and it is here because 1.1 was not.
     Founder, 2026-08-22: the More label "needs to use the exact same text as everything
     else so that it doesn't stand out any differently."

     The TYPE was already identical and re-measured so: 14.24px / 600 / Inter /
     rgb(86,93,102) / padding 9px 13px, byte-identical to all four .mktbar-links pills at
     1280. THE BOX WAS NOT. `.mktbar-links a` declares no line-height, so it inherits the
     bar's 1.55 and computes 22.07px of line box for a 40.1px pill; this rule said 1.1,
     computed 15.66px, and stood 33.7px - 6.4px shorter than every item beside it.

     At rest that is invisible, because a link pill paints no background until you touch
     it. It is not invisible in the two states that DO paint one: :hover, and the
     aria-current outline this control wears on the pages whose own entry lives inside the
     menu (five of the sixteen) - where a 33.7px ring sat between 40.1px ones. The tier
     below already says the intent out loud: "the disclosure is a link pill with a chevron
     - it tracks its row-mates down the tiers or it stands 2px taller than every item
     beside it." The 1.1 was copied from .mktbar-btn, where it is load-bearing for a
     different reason (it is what lets min-height:44px win over the content box). This
     control has no min-height, so all 1.1 ever did here was shrink it.

     AND IT IS `inherit` RATHER THAN A NUMBER, which the first fix here got wrong and the
     browser said so within one measurement. The pills declare NO line-height, so what they
     compute is whatever the PAGE hands down, and the pages do not agree: index.html hands
     down 1.55 and the pill stands 40.1px, team.html hands down `normal` and the same pill
     stands 35px. A literal 1.55 here matches on one of those pages and is 5px too tall on
     the other. `inherit` takes the same value from the same ancestor the pills take it
     from, so the control matches its row-mates on every page whatever that page does - and
     it still DECLARES the property, which is what tests/mktbar-more-disclosure.test.cjs
     requires of every property a page's bare summary rule might set. */
  line-height: inherit;
  color: var(--text-muted);
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
  transition: none;
}
.mktbar-more > summary::-webkit-details-marker { display: none; }
.mktbar-more > summary::marker { content: ''; }
.mktbar-more > summary:hover {
  color: var(--text);
  background: var(--surface-hi);
}
.mktbar-more > summary:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
}
.mktbar-more[open] > summary {
  color: var(--text);
  background: var(--surface-hi);
}
/* The page you are on lives in here on five of the sixteen pages, and the eye
   needs the same crumb the accessibility tree gets from aria-current. Derived by
   the browser from the menu's own markup - there is no second per-page flag. */
.mktbar-more:has(a[aria-current="page"]) > summary {
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border);
}
.mktbar-more-chev {
  flex: none;
  transition: transform .15s ease;
}
.mktbar-more[open] .mktbar-more-chev { transform: rotate(180deg); }
.mktbar-more-menu {
  position: absolute;
  /* 9px of that clears the bar's own padding and border (the wrapper is stretched to
     the bar's inner edge); the rest is the gap you see. */
  top: calc(100% + 17px);
  left: 0;
  z-index: var(--layer-menu);
  min-width: 190px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  /* OPAQUE, unlike the bar itself. .mktbar-in is a translucent tint over a
     backdrop blur, which is right for a bar and unreadable for a panel with page
     text behind it. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
  /* IT CANNOT RUN OFF THE BOTTOM OF THE SCREEN (P1271, card half two).
     The design-system measurement that said this menu fits was taken when it held FOUR
     rows. It ships SEVEN on both products now and stands about 296px, which still fits
     today - so this is the insurance the card asks for before an eighth row, and it is
     the same defect BUG-2026-08-04-04 already cost us once on the Modules panel: a
     position-out-of-flow list with no height cap does not clip with a scrollbar, its
     last rows are simply unreachable.

     WHY 160px OF SUBTRAHEND, derived rather than picked. The menu hangs off the sticky
     bar, so its own top is the bar's lower edge plus the 17px offset above. Measured in
     Chrome across both products at 768 to 1440 (the supported band): the bar's lower
     edge is 76px while the row fits on one line and 126.1px once it wraps to two, which
     is the widest this menu's top can ever start at (~143px). 160px leaves that worst
     case its full height plus a 17px breath at the bottom of the viewport, and on a
     normal 900px-tall window the cap resolves to 740px - far above the 296px the seven
     rows need, so nothing scrolls today and the eighth row degrades instead of
     disappearing.

     overscroll-behavior:contain stops a scroll gesture that reaches the end of this
     list from chaining to the page underneath, the same guard the Modules panel uses. */
  max-height: calc(100vh - 160px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.mktbar-more-menu a {
  display: block;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: .9rem;
  font-weight: 600;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.mktbar-more-menu a:hover {
  color: var(--text);
  background: var(--surface-hi);
}
.mktbar-more-menu a[aria-current="page"] {
  color: var(--text);
  background: var(--surface-hi);
  box-shadow: inset 0 0 0 1px var(--border);
}

.mktbar-cta {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
/* THE LOAD-BEARING REMAP — see the trap note above.
   WHICH OF THE FOUR ARE ACTUALLY READ, as of 2026-08-21: --header-text carries the pill's ink,
   its edge and its focus-ring default (and the return chip's ink and edge at :309), and
   --header-line carries that chip's edge. --header-bg and --header-bg-2 are read by nothing in
   this subtree any more - the pill's fill became a translucent tint that composites on whatever
   the bar paints, so it needs no bar token. They stay: they cost nothing, a future child of this
   mount would want them, and tests/page-shell.audit.cjs:632 pins --header-bg here by name. */
.mktbar-tools {
  display: inline-flex;
  align-items: center;
  --header-text: var(--text);
  --header-bg: var(--surface);
  --header-bg-2: var(--surface);
  --header-line: var(--border);
  /* AND THE SHAPE AND TYPE, which are this bar's call rather than the component's (2026-08-21,
     the Tools redesign). .mktbar-btn below is a 44px 999px pill at .92rem, so the shared
     launcher matches its row-mates here instead of landing as a 40px rectangle at .82rem among
     pills; on the dark app bar and the Designer's 34px band it keeps the compact defaults its
     neighbours wear there. shared/tool-nav.js reads all five through var() fallbacks.
     --tnav-ring names the focus ring for a bar that knows it is light: --accent-text measures
     7.28:1 in Linen and 8.25:1 in Midnight Oak against this surface. */
  --tnav-radius: 999px;
  --tnav-h: 44px;
  --tnav-padx: 16px;
  --tnav-fs: .92rem;
  --tnav-ring: var(--accent-text);
}
/* tool-nav.js renders a "Back to the Designer · <project>" chip beside the
   launcher on any page that is not the Designer. A marketing page is a funnel
   surface, not a workflow surface, and the chip runs up to 340px wide - it
   would push the sign-up pair off this bar. Suppressed here, and nothing is
   lost: the switcher itself still lists the Designer. */
.mktbar-tools .bws-return-chip { display: none; }
.mktbar-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  /* THE THREE PILLS IN THIS ROW ARE ONE ROW. Measured at 1280 and 1440 before this
     line existed: Tools 44.0px, Sign in 46.8px, Start free 46.8px, all on the same
     centre - one visibly shorter pill between two taller ones, on every marketing
     page, in both themes. .mktbar-tools above pins --tnav-h: 44px exactly, while
     this rule had min-height:44px and no line-height, so 14.72px text at `normal`
     grew the box past its own floor: 14.72 x ~1.2 = 17.7, + 22 padding + 2 border
     = 41.7 in theory and 46.8 as the font actually renders. At 1.1 the content box
     is 16.2 + 24 = 40.2, the min-height wins, and all three measure 44.0.
     tests/tool-nav-launcher-tokens.test.cjs derives that arithmetic from these
     declarations, so a padding or type change that breaks it again says so. */
  line-height: 1.1;
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-weight: 700;
  font-size: .92rem;
  text-decoration: none;
  white-space: nowrap;
}
.mktbar-btn:hover {
  border-color: var(--accent);
  background: var(--surface-hi);
}
.mktbar-btn-primary {
  background: var(--on-accent-fill);
  color: var(--on-accent);
  border-color: var(--on-accent-fill);
}
.mktbar-btn-primary:hover {
  background: var(--on-accent-fill);
  color: var(--on-accent);
  border-color: var(--on-accent-fill);
  filter: brightness(1.06);
}
.mktbar a:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
  border-radius: 999px;
}
/* THE COMPACTION TIER, INHERITED FROM A MEASUREMENT, NOT INVENTED HERE. P837 was
   a launch-blocker on the StackDesign doors: at 1024 the bar ran wider than the
   viewport, scrolled the page sideways and clipped "Start free trial" off the
   edge, and hiding the Sign in ghost below 1080 did not fix it. What did fix it
   was shrinking every part of the bar TOGETHER between 1200 and 900. Those five
   page-local copies are gone now, so the ladder lives here - dropping it with
   them would have re-opened a defect somebody already paid to close. */
@media (max-width: 1200px) {
  .mktbar-in { gap: 8px 10px; padding: 7px 7px 7px 12px; }
  .mktbar-links { margin-left: 0; gap: 0; }
  .mktbar-links a { padding: 8px 9px; }
  /* the disclosure is a link pill with a chevron - it tracks its row-mates down
     the tiers or it stands 2px taller than every item beside it */
  .mktbar-more > summary { padding: 8px 9px; }
  .mktbar-cta { gap: 6px; }
  .mktbar-btn { padding: 10px 14px; font-size: .87rem; }
  /* the launcher tracks its row-mates down the tiers too, or the type ladder this bar just
     got straightened bends again below 1200 (2026-08-21, the Tools redesign). */
  .mktbar-tools { --tnav-fs: .87rem; }
}
/* The tablet floor. Below 900 the bar WRAPS to two rows rather than dropping a
   link or clipping a word: a hidden marketing link is a page nobody can reach by
   hand, which is the failure tests/reachability.cjs exists to catch, and this
   suite never truncates text. */
@media (max-width: 900px) {
  .mktbar-in { border-radius: 22px; padding: 10px 12px; }
}

/* ---------------------------------------------------------------------------
   3c. THE ANCHOR OFFSET UNDER THE STICKY BAR  — P1277 (CabDesign) + P1299 (StackDesign)
      ----------------------------------------------------------------------
      WHAT WAS WRONG, measured. Not one marketing page on either product set an
      anchor offset, and the bar above them is sticky. So every jump landed its
      target UNDER the bar:

        • the site's OWN nav did it. "How it works" and "Pricing" are top-bar
          destinations from every page, and clicking them scrolled to a screen
          where the heading you clicked for was hidden behind the thing you
          clicked it with.
        • every SKIP LINK did it, which is the worse half. A skip link exists to
          land a keyboard user at the START of the content; landing them 76px past
          it, with the first heading cut in half, is worse than having no skip link
          at all (WCAG 2.4.1 is satisfied on paper and defeated in practice).

      THE MEASUREMENTS THIS VALUE COMES FROM. Chrome, both products, the supported
      band only (768 and up - desktop and tablet, P455), bar scrolled and stuck:

        >= 937px   bar lower edge  76.0px   (74.0 in the 1200 compaction tier)
        <= 936px   bar lower edge 126.1px   worst case, where .mktbar-in wraps to two rows

      936px is where the first bar wraps today. The tier below is set at 1023px
      rather than at 936 on purpose: it is a real tablet boundary, and it leaves
      ~87px of headroom so that adding one more nav item - which pushes the wrap
      point UP - cannot silently re-open this bug. The cost of that margin is up to
      66px of extra clearance between 937 and 1023, which reads as a slightly
      generous gap. Erring long is the right direction here: too much offset is
      cosmetic, too little is the defect.

      WHY :where() TWICE, AND WHY THAT IS THE LOAD-BEARING PART.

        • :where(:root:has(nav.mktbar)) scopes this to pages that actually carry
          the sticky marketing bar. The dark app .topbar is a different surface with
          its own story, and silently adding 92px to every scrollIntoView() on the
          Designer and the dashboard is not a fix, it is a second bug.
        • :where([id]) rather than a list of section selectors, because the targets
          are whatever a page happens to link to: sections, the <main id="main"> a
          skip link aims at, a heading, an FAQ row.
        • BOTH are wrapped in :where(), so the whole selector has ZERO specificity
          and ANY page rule beats it. That is what makes this safe to ship across
          all 75 bar pages at once: the legal pages already declare
          `section{scroll-margin-top:88px}` (0,0,1) and guides/guide.css declares
          `section[id]{scroll-margin-top:92px}` (0,1,1), and both still win. Pages
          that already solved this keep their own value; only the pages that never
          had one inherit this. A higher-specificity selector - or scroll-padding-top
          on the container, which ADDS to a page's scroll-margin instead of being
          overridden by it - would have doubled those pages to 164px and 168px.

      A browser without :has() drops this rule and lands exactly where it does
      today, so the degradation is the current behaviour rather than a new one.
   --------------------------------------------------------------------------- */
:where(:root:has(nav.mktbar)) :where([id]) {
  scroll-margin-top: 92px;      /* 76px bar + 16px breathing room */
}
@media (max-width: 1023px) {
  :where(:root:has(nav.mktbar)) :where([id]) {
    scroll-margin-top: 142px;   /* 126px two-row bar + 16px */
  }
}

/* ---------------------------------------------------------------------------
   3d. THE MARKETING TYPE + RHYTHM SCALE  ( class="mkt-page" )  — P1297
   ---------------------------------------------------------------------------
   WHAT THIS REPLACES. The seven StackDesign marketing pages each typed their own
   heading sizes and their own vertical beat into their own <style> block, so the
   same role carried different values on pages one click apart. The finding is
   docs/research/marketing-audit-2026-08-22/audit-web-stackdesign.md F8; the rule
   it breaks is docs/design-system/page-layout.md §8, "one spacing scale, one type
   scale". Pricing and Contact were the outliers, and they are the two pages a
   buyer reads hardest.

   WHY THESE NUMBERS. They are not a new scale. Every value below is the one six of
   the seven pages had already converged on by the time this was built, read out of
   their own stylesheets rather than redesigned - so applying it moves the two
   stragglers onto the majority and moves nothing else. Layout only: not one colour
   is named here, so FD166's palette lock is untouched.

   ONE PHYSICAL SOURCE, SINCE P2039. --mkt-h1/-h2/-lede (and their -lh partners) on
   the base scale below now read shared/theme.css's --type-h1-size etc instead of
   holding their own literal - they were already the same rem values as
   docs/design-system/page-layout.md §10's table, so this changed the SOURCE, not
   the rendered size. `--mkt-sec`, `--mkt-band-h2`/`--mkt-band-p` and `.mkt-door`'s
   own step-up stay literal here; none of the three has a role in that table to
   resolve to. docs/design-system/foundations-type.md has the full reconciliation.

   HOW A PAGE USES IT. <body class="mkt-page">, then every heading, lede and
   section rule on that page reads var(--mkt-*) instead of restating a number.
   The tokens live on the BODY, not on :root, on purpose: :root is <html> and the
   class is on <body>, so a page-local `:root{--sec:...}` could not read them. Every
   consumer is a descendant of body, which is why the page rules read them directly.

   THE ONE DELIBERATE EXCEPTION is the front door, and it says so out loud by
   carrying a second class. A landing page is allowed to open louder than the pages
   behind it; what is not allowed is seven pages each picking their own size. So the
   step-up is ONE named tier, in this file, applied to exactly one page - and
   tests/stackdesign-mkt-scale.audit.cjs fails if a second page claims it.

   NOT IN SCOPE, and it matters that it is not: --prose-max. All seven already agree
   on the 1180px reading column (the 840/900px split F8 recorded was closed on
   2026-08-23), so there is nothing here to promote and this block does not restate it.
   --------------------------------------------------------------------------- */
.mkt-page{
  /* P2039: h1/h2/lede (+ their -lh partners) are the shared type scale now
     (shared/theme.css, "THE TYPE SCALE") — they were already the same
     numbers as docs/design-system/page-layout.md §10's table, so
     re-pointing them here changes the SOURCE, not the rendered size.
     docs/design-system/foundations-type.md has the full reconciliation. */
  --mkt-h1:var(--type-h1-size);      --mkt-h1-lh:var(--type-h1-lh);
  --mkt-h2:var(--type-h2-size);      --mkt-h2-lh:var(--type-h2-lh);
  --mkt-lede:var(--type-lede-size);  --mkt-lede-lh:var(--type-lede-lh);
  --mkt-sec:66px;                          /* the vertical beat, with a hairline at every seam */
  /* THE CLOSING CTA BAND is one component, copied. It arrived on the home page and the
     FAQ as two blocks that were byte-identical apart from these two numbers - 1.95/2.7
     against 1.9/2.55, and 1.08 against 1.06 - which is a difference nobody can read as
     intentional and nobody would have chosen. One component, one value, here. */
  --mkt-band-h2:clamp(1.95rem,3.4vw,2.7rem);
  --mkt-band-p:1.08rem;
}
/* The beat tightens once the band stops being full width. This lived in two of the
   seven pages and nowhere else, which is the same divergence one level down. */
@media (max-width:900px){
  .mkt-page{ --mkt-sec:50px }
}
/* THE FRONT DOOR, one step up. stackdesign-home.html only. */
.mkt-page.mkt-door{
  --mkt-h1:clamp(2.4rem,4.4vw,3.35rem);   --mkt-h1-lh:1.03;
  --mkt-h2:clamp(2rem,3.4vw,2.75rem);     --mkt-h2-lh:1.08;
  --mkt-lede:1.16rem;                     --mkt-lede-lh:1.6;
}

/* ---------------------------------------------------------------------------
   3d. THE SEARCH-RESULT TRAIL (P1317) — one breadcrumb, shared, not copied.

      Breadcrumbs render in a search result in place of the raw address, and
      they tell an assistant where a page sits. Twenty-eight pages gained one
      on 2026-08-29; the rest are being brought over now.

      THIS BLOCK EXISTS BECAUSE THE COMPONENT HAD ALREADY FORKED. `.crumb` was
      declared page-locally, byte-identical, in fifteen separate <style> blocks,
      and `.crumbs` lives in support/support.css for the thirteen support
      articles. Adding forty more page-local copies would have made it fifty-five
      places for one trail to drift. Both class names are matched here so the
      support tree and the marketing tree resolve to ONE ruleset, and every page
      that links this stylesheet gets the trail for free.

      Markup, and it is the whole component — a real <nav> with a real list, so
      a screen reader announces "Breadcrumb, list, 2 items" and the last step
      says it is the page you are on:

        <nav class="crumb" aria-label="Breadcrumb">
          <ol>
            <li><a href="/index.html">CabDesign</a></li>
            <li><span aria-current="page">Terms of Service</span></li>
          </ol>
        </nav>

      The final step is a <span>, never a link: a link to the page you are
      already on is a control that does nothing.

      On a page served at BOTH front doors the first step carries
      data-brand-home / data-brand-name so shared/brand.js rewrites it, because
      a hand-typed "CabDesign" is wrong on the StackDesign door (the newsletter
      page learned that one the hard way).

      `.crumbs` keeps support's reading-column measure; `.crumb` sits flush in
      the page band, which is where the fifteen marketing pages already had it.
      The separators differ for the same reason and are deliberately preserved:
      "/" in the band, "›" in the reading column. */
.crumb ol,
.crumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  margin: 24px 0 18px;
  padding: 0;
  font-size: .86rem;
  color: var(--text-muted);
}
.crumb li,
.crumbs li { display: flex; align-items: center; gap: 9px; }
.crumb li + li::before { content: "/"; color: var(--border); }
/* The trail's LINKS had no colour of their own here, only the list, the separator and the
   current page did - so on any page without a compensating bare-`a` rule they fell through
   to the UA's rgb(0,0,238), which is 1.91:1 on the Midnight Oak background. Eight pages
   were rendering that blue. --accent-text (not --text-muted, which `.crumbs` uses for its
   quieter reading-column trail) is what the ~50 pages that DO carry a bare-`a` rule already
   paint, so this rule matches what the suite looks like today rather than restyling it. */
.crumb a { color: var(--accent-text); }
.crumb a:hover { color: var(--text); }
.crumb [aria-current="page"],
.crumbs [aria-current="page"] { color: var(--text); }

/* THE QUIET TRAIL — [data-crumb-echo], written by scripts/build-breadcrumbs.cjs.
   ------------------------------------------------------------------------------
   On an app-tool page the sticky .topbar lockup already prints this trail's last
   step, one line above it, and the <h1> often prints it a third time: hardware.html
   said "Hardware Library" at y=25, y=119 and y=157 before this rule. The bar is the
   copy that stays when the reader scrolls (`position: sticky`, section 2) and
   section 2's own note calls its sub "the most load-bearing 'which tool is this?'
   cue", so the trail is the copy that goes.

   IT GOES QUIET, NOT AWAY. This is the standard visually-hidden recipe, so the nav
   keeps its accessible name, its position in the a11y tree and its JSON-LD: a screen
   reader still gets the trail, a crawler still gets the BreadcrumbList, and nothing
   a sighted reader could have clicked is lost (the "CabDesign" step goes where the
   wordmark directly above it goes). The generator decides per page, by reading that
   page's own lockup — see its `trailEchoesTheStickyBar` note for the whole argument.
   No `focusable` exception is needed: the last step is a <span>, and the ancestor
   links duplicate the wordmark, so nothing here is a keyboard-only path.
   Guide-page lockups take the OTHER half of this settlement — shared/brand.js blanks
   their `.suite-brand-sub` and their trail stays visible, because that bar does not
   stick. */
.crumb[data-crumb-echo] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* A page with no content wrapper of its own to be the first child of gets this band.
   The seven module articles put their content straight in <body> behind a hero that
   paints itself in the DARK header tokens, so a trail dropped inside that hero would
   have rendered muted-ink on a near-black gradient - present, and unreadable. The band
   puts it above the hero instead, on the page background, at the same measure and the
   same 32px side padding those pages give everything else. */
.crumb-band { max-width: var(--maxw, 1180px); margin: 0 auto; padding: 0 32px; }

/* The support tree's reading-column variant: crumbs, article and foot share one
   centred measure, so the page resolves to a single text edge instead of a trail
   floating out at band width. */
.crumbs { max-width: var(--prose-max); margin-inline: auto; }
.crumbs ol { margin: 26px auto 4px; gap: 6px; font-size: .92rem; }
.crumbs li { gap: 6px; align-items: baseline; line-height: 1.6; }
.crumbs li + li::before { content: "\203A"; color: var(--text-muted); }
.crumbs a { color: var(--text-muted); }
.crumbs a:hover { color: var(--text); }
.crumbs [aria-current="page"] { font-weight: 600; }

/* ---------------------------------------------------------------------------
   4. PRINT IDENTITY BLOCK (P88, D9 OQ5) — the shop's own name/contact/logo on
      printed outputs. Populated by shared/print-identity.js from the
      company-standards doc (shared/bws-company.js); shared here so the cut
      list print, the sheet-layout print, and the Designer's own drawing
      print all carry the identity in the SAME place, in the SAME style,
      instead of three divergent headers.

      Markup (one line, wherever a page wants its printed masthead):
        <div class="print-identity" id="print-identity" hidden></div>
      print-identity.js fills it and clears the [hidden] attribute ONLY when
      a name is actually on file — so a signed-out visitor, a local-only
      session, or a shop that never filled in Company Standards prints with
      NO identity chrome at all (never an empty box).

      Screen-invisible always (this is print-only content); shown only
      under @media print, and only when not [hidden]. */
  .print-identity { display: none; }
/* lint-ok-hex-start: content=print print media renders on physical white paper for every theme,
   matching the existing per-page print blocks (cabinet-designer.html,
   designer-outputs.html, cut-list-optimizer.html) — black ink on white regardless
   of the on-screen theme. */
@media print {
  .print-identity:not([hidden]) {
    display: block;
    margin: 0 0 18px;
    padding-bottom: 10px;
    border-bottom: 1.5px solid var(--print-rule, #888);
  }
  .print-identity-row { display: flex; align-items: center; gap: 12px; }
  .print-identity-logo { width: 44px; height: 44px; object-fit: contain; flex: none; }
  .print-identity-text { min-width: 0; overflow-wrap: anywhere; }
  .print-identity-name { font-weight: 800; font-size: 15px; color: var(--print-ink, #000); }
  .print-identity-meta { font-size: 11px; color: var(--print-ink-muted, #333); margin-top: 2px; }
}
/* lint-ok-hex-end */
