/* ==========================================================================
   TRU72 — SHARED DESIGN TOKENS  (Rev06 brand)
   --------------------------------------------------------------------------
   ONE source of truth for colour, type, spacing and shape on EVERY Tru72 site.

   COLOUR IS THE APPROVED FIVE AND NOTHING ELSE.
   Bethany confirmed this palette on 2026-09-09; it is what the Rev06 brand
   guide records. It is NOT the North Carolina build's as-shipped colour set —
   NC drifted (B8952A, F5F0E8 and a rust family that the rebrand dropped).
   The layouts, structure, components and typography come from NC, which is
   correct. The colour comes from here.

       INK            #3D3F3F
       SCENERY GREEN  #696859
       ANTIQUE GOLD   #C39954
       LINEN          #CBC0AA
       PAPER          #FFF6DC

   FIVE, PLUS ONE THAT DOES EXACTLY ONE JOB.                   (D83, 2026-09-22)

       GOLD-AGED      #A8854A   the italic display line, on PAPER, nowhere else

   D42 dropped GOLD-AGED and the palette ran at five. D83 puts it back, and
   deliberately puts it back NARROW: on the live T. Byrd's site the italic
   second half of a display headline is warm gold on cream, and that is the
   single most recognisable thing about the reference's typography. ANTIQUE
   GOLD cannot do that job — 2.43:1 on PAPER, a failure at every size, which
   no amount of making it bigger rescues. GOLD-AGED measures 3.18:1, which
   clears WCAG's 3.0 for LARGE text, so it does the job legitimately.

   It is NOT a general-purpose sixth colour, and the cage below is built so it
   cannot become one. There is still no rust, no B8952A and no F5F0E8, and
   there is no seventh colour. Identical on all four sites, no per-site
   variation. A contrast problem that cannot be solved inside these six gets
   REPORTED, not solved with another colour.

   ---------------------------------------------------------------------------
   THE GOLD-AGED CAGE — why it cannot be misused                          (D83)

   GOLD-AGED is declared ONCE, as --t72-gold-aged, and read ONCE, by
   --t72-display-italic-on-paper. Nothing else in any stylesheet on any of the
   four sites may name either one. tools/audit_phase5.py check 15 fails the
   build if anything does, and it also re-derives the two facts that make the
   colour legal:

     THE GROUND MUST BE PAPER.   Measured on each light step —
         PAPER        #FFF6DC   3.18:1   passes as large text
         PAPER-WARM   #F6ECD3   2.92:1   FAILS, even as large text
         PAPER-DEEP   #F0E7CE   2.78:1   FAILS, even as large text
         LINEN        #CBC0AA   1.91:1   FAILS
         SCENERY GRN  #696859   1.64:1   FAILS — and D83 says never on green
         INK          #3D3F3F   3.09:1   passes, but ANTIQUE GOLD is 4.04:1
                                         there and is already the display
                                         colour on ink, so ink keeps it
     The margin on PAPER is 0.18. A tint is enough to lose it. So the two
     LINEN-in-PAPER tints — .t72c-lite-warm and .t72c-lite-deep, which
     relevel() can put on ANY light band at build time — take the colour BACK
     to SCENERY GREEN, and so does every LINEN and green band.

     THE TYPE MUST BE LARGE.     3.18:1 clears AA for large text only: 24px,
     or 18.66px at weight 700. The italic line is Fraunces at weight 400, so
     the threshold is the full 24px. Only two rules in the system may read
     the role, and each is floored so its SMALLEST rendered size clears it:
         .t72c-h1 em   .82em of clamp(40px, 4.6vw, 62px)  ->  32.8px minimum
         .t72c-h  em   max(24px, .74em) of clamp(30px,…)  ->  24px minimum
     Never body text, never a label, never an eyebrow, never a button fill,
     never a border or a rule — ANTIQUE GOLD keeps every decorative job.

   THIS BLOCK IS THE SWITCH. Every colour on all four sites resolves through
   the six declarations below. Changing Bethany's mind — for example adopting
   NC's as-built values instead — is editing this one block, not a rebuild.

   ---------------------------------------------------------------------------
   THE CONTRAST RULES THAT FOLLOW FROM SIX COLOURS  (measured, WCAG 2.1)

     on PAPER          INK 12.1:1 ok · SCENERY GREEN 5.2:1 ok
                       ANTIQUE GOLD 2.4:1 FAILS at every size
     on INK            PAPER 9.7:1 ok · LINEN 5.7:1 ok
                       ANTIQUE GOLD 4.0:1 — large type only (24px+, or 18.7px
                       bold); never small type
     on SCENERY GREEN  PAPER 5.2:1 ok
                       LINEN 3.1:1 FAILS small · ANTIQUE GOLD 2.2:1 FAILS at
                       every size. There is NO gold type on green, ever.

   on LINEN          INK 5.9:1 ok
                     SCENERY GREEN 3.1:1 FAILS small · ANTIQUE GOLD 1.5:1 and
                     PAPER 1.7:1 FAIL at every size. On a LINEN band the type
                     is INK, all of it.

   So: gold is DECORATION as TYPE — rules, keylines, borders, hairlines — and a
   FILL only where D46's size goes with it. INK on ANTIQUE GOLD is 4.04:1, which
   clears AA large text, so a gold button is legal at 24px, or at 18.66px bold,
   and illegal below that. PAPER on ANTIQUE GOLD is 2.6:1 and is never used.

   ---------------------------------------------------------------------------
   THE TWO TINTS, AND WHY THEY ARE NOT A SEVENTH COLOUR

   A six-flat-colour site has no way to say "this band is not the last band".
   Rather than introduce a hue, the two light surfaces below are LINEN mixed
   into PAPER with color-mix(), so they carry no pigment that is not already in
   the palette and they move automatically if PAPER or LINEN move. The hex on
   the line above each is the pre-computed fallback for an engine without
   color-mix, and is the same mix arithmetic.

   No tint is ever used to solve a contrast problem, and no type colour is a
   tint — type is always one of the six.
   ========================================================================== */

/* --------------------------------------------------------------------------
   THE DISPLAY FACE — self-hosted, one weight
   --------------------------------------------------------------------------
   Friz Quadrata Bold is the only weight that exists, so it is display and
   titles only; Fraunces carries everything smaller. Nothing here synthesises a
   lighter or heavier cut.

   `font-weight: 400 700` is not a claim that the file holds a regular — it is
   how you stop a browser faking one. A page asking for weight 400 in this
   family matches the descriptor, so the real Bold outlines are drawn instead
   of a synthesised thin. `font-synthesis: none` on the body is the belt to
   that braces.

   Full face, not subset: the .otf already carries only 209 glyphs (Latin plus
   the punctuation the sites use) and the woff2 is 17 KB, so subsetting would
   save a rounding error and risk dropping a character.
   -------------------------------------------------------------------------- */
/* THE OPTICAL SIZE CORRECTION — size-adjust                       (Phase 20)
   Bethany: our headlines render bigger and bolder than the reference's. They
   do, and it is not a size token, letter-spacing or a synthesised bold — BOTH
   h1s compute to 58.88px / weight 700 / .005em / line-height 55.936px. It is
   the FACE, and the reason is that the reference never had ours.

   The reference site ships NO font file and NO @font-face at all. Its display
   stack is `'Friz Quadrata Bold', Cinzel, serif`; it loads Cinzel (plus
   Fraunces and Geist Mono) from Google Fonts, Friz Quadrata resolves to
   nothing, and every display heading on it is therefore CINZEL. Ours is the
   real licensed face, self-hosted. Measured in the browser, `HAMBURGEFONS` at
   700/59px, ink pixels counted on a canvas:

       face                          cap height   width    ink px
       Friz Quadrata Bold (ours)        46px      547px    13,099
       Cinzel 700 (what the ref draws)  43px      528px     8,000
       Fraunces 700 (our fallback)      43px      517px     9,870

   7% taller and 64% more ink: "bigger and bolder", exactly.

   `size-adjust: 93.5%` (= 43/46) scales the outlines so the cap height lands
   on 43px, the reference's, and the measure comes to 512px against its 528px.
   The remaining stroke weight is the typeface and cannot be scaled away. Friz
   Quadrata is the brand display face (D43) and Cinzel is not a Tru72 face, so
   Cinzel is NOT being added to the stack to chase the last of it.

   IT ALSO FIXES THE SWAP. With `font-display: swap` the fallback paints first;
   before this the webfont arrived 7% bigger than Fraunces and the headline
   jumped. Now webfont (cap 43 / 512px) and fallback (cap 43 / 517px) are
   within 1%, so a late or failed webfont degrades to the same size — which is
   also what the reference's stack does, since its two entries never differ.
   Every line-height in the system is a unitless multiple of font-size, so no
   line box moves. Chrome 92+, Firefox 92+, Safari 17+; older engines ignore
   the descriptor and get today's rendering. */
/* --------------------------------------------------------------------------
   THE DISPLAY FACE — CINZEL, self-hosted            Bethany, 2026-09-28
   --------------------------------------------------------------------------
   This is what tbyrdhvac.com has rendered all along. Its CSS asks for
   `"Friz Quadrata Bold", Cinzel, serif` and ships NO font file, so every
   headline on the site Bethany has been matching is Cinzel, served from
   Google. We shipped Friz, so we rendered what T. Byrd's only ever asked for
   — 22% more ink in 10% less width, which is what she kept calling too thick.

   Self-hosted, so the estate still makes ZERO external font requests. Cinzel
   is SIL OFL; the licence ships beside it as cinzel-OFL.txt. One variable
   file, 400-900, latin subset, 25.9 KB.

   IT IS AN ALL-CAPS FACE — lowercase renders as small caps. That is safe here
   and checked, not assumed: 21 of 24 headings on a sample Oregon page already
   compute `text-transform: uppercase`, as do all three on the reference. The
   three that do not are the Fraunces italic accent phrases, which take the
   body face anyway. */
@font-face {
  font-family: 'Cinzel';
  src: url('../fonts/cinzel.woff2') format('woff2');
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Friz Quadrata Bold';
  src: url('../fonts/friz-quadrata-bold.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  size-adjust: 93.5%;
}

/* --------------------------------------------------------------------------
   THE LABEL FACE — GEIST MONO, self-hosted                    (D95, Phase 21)
   --------------------------------------------------------------------------
   D43 said two brand faces and only two, and the label voice — the wide,
   tracked, uppercase small type on nav, the utility strip, eyebrows, numbered
   labels and buttons — was Fraunces with tracking standing in for a mono.

   It does not stand in. Measured on the reference at 1280px, every one of
   those roles is 'Geist Mono': `.utility` 10.5px/.18em, `.nav-links`
   12px/.2em/500, `.eyebrow` 11px/.22em/500, `.intent-num` 10px/.26em/600,
   `.btn` 10.5px/.16em/600. Our sheets already carry those exact sizes and
   that exact tracking — only the FACE was wrong, so a serif was being asked
   to do a monospaced job and the texture never arrived. Bethany, Phase 21:
   "Geist Mono is a THIRD typeface and supersedes the two-face rule (D43) for
   this UI role."

   It is a THIRD FACE WITH ONE ROLE, not a free-for-all. Display stays Friz
   Quadrata Bold; prose, decks, italics and card bodies stay Fraunces. Geist
   Mono is reached only through --t72-font-label / --t72-font-mono, which is
   the name every label rule in the system already uses.

   SELF-HOSTED, like the display face, and for the same reason: the reference
   pulls it from fonts.googleapis.com and we do not add a third-party font
   request. Geist Mono 1.401 is SIL OFL 1.1 (openfontlicense.org), which
   permits redistribution; the file is the upstream variable woff2 subset to
   Latin-1 plus the marks the sites use (·, ★, →, —, curly quotes) — 31 KB,
   one weight axis 100-900, so 400/500/600/700 are all real cuts and nothing
   is synthesised. Source of record: assets/_system/fonts/geist-mono.woff2.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Geist Mono';
  src: url('../fonts/geist-mono.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   THE BODY FACE - FRAUNCES, self-hosted                      (D98, Phase 22)
   --------------------------------------------------------------------------
   Fraunces is the prose voice: decks, card bodies, paragraphs, the gold italic
   display line, and the fallback behind Friz Quadrata. It was the last face
   still fetched from a third party - all 208 source pages linked
   fonts.googleapis.com for it, with a preconnect pair to match. Three external
   requests per page, on every one of the five builds, for a face we are
   licensed to host.

   Fraunces is SIL OFL 1.1 (assets/_system/fonts/fraunces-OFL.txt, shipped
   beside the files as the licence requires). Geist Mono's licence, which
   Phase 21 named but never shipped, is now beside it as geist-mono-OFL.txt.

   BUILT TO MATCH WHAT GOOGLE WAS SERVING, EXACTLY. The link these pages
   carried was

     family=Fraunces:opsz,ital,wght@9..144,0,300..900;9..144,1,300..900

   so what the browser actually received was the upstream variable font with
   SOFT and WONK pinned at their defaults (SOFT 0, WONK 1 - Fraunces ships
   `wonky` ON) and opsz 9..144 plus wght 100..900 left live, declared to CSS as
   `font-weight: 300 900`. These two files are the same upstream source,
   instanced the same way, subset to Latin-1 plus the punctuation the sites
   use, and they declare the same `300 900`, so no weight that used to resolve
   to a real cut now clamps to a different one.

   VERIFIED, not asserted. Both files were diffed against the woff2 that
   fonts.gstatic.com was serving for that URL: 1,176 glyph outlines each
   (28 characters x 7 weights 300-900 x 6 optical sizes 9-144), 0 differing,
   plus 435 advance widths each and every vertical metric - upem, ascender,
   descender, line gap, win ascent/descent, x-height, cap height - identical.
   The opsz axis is KEPT, so `font-optical-sizing: auto` still reshapes the
   face by size the way it did; pinning it would have been smaller and wrong.

   TWO FILES, because we use true italics. The gold display line, the pull
   quotes and every `em` in a deck are Fraunces italic, and a single roman file
   would have let the browser synthesise a slant instead of drawing the real
   italic - a different face, not a tilted one. 70 KB roman + 82 KB italic,
   against the 66 KB + 80 KB Google was sending for the same coverage.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Fraunces';
  src: url('../fonts/fraunces-roman.woff2') format('woff2');
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Fraunces';
  src: url('../fonts/fraunces-italic.woff2') format('woff2');
  font-weight: 300 900;
  font-style: italic;
  font-display: swap;
}

:root {
  /* ======================= THE APPROVED FIVE ============================ */
  --t72-paper:        #FFF6DC;   /* PAPER         — the page                */
  --t72-ink:          #3D3F3F;   /* INK           — type, dark surfaces     */
  --t72-green:        #696859;   /* SCENERY GREEN — muted type, mid band    */
  --t72-linen:        #CBC0AA;   /* LINEN         — type on ink, hairlines  */
  --t72-gold:         #C39954;   /* ANTIQUE GOLD  — decoration only         */
  /* ====================================================================== */

  /* ---- THE SIXTH, CAGED (D83) -------------------------------------------
     Declared here, read on the next line, and named nowhere else on any of
     the four sites. Do not reach for --t72-gold-aged in a stylesheet; reach
     for the role, and only from a rule that is on PAPER and 24px or larger.
     Audit check 15 enforces both halves of that sentence. */
  --t72-gold-aged:    #A8854A;   /* GOLD-AGED — 3.18:1 on PAPER, large only */
  --t72-display-italic-on-paper: var(--t72-gold-aged);

  /* Surface tints — LINEN in PAPER. No new pigment. Never used for type. */
  /* PAPER WARM IS NOW AN ALIAS OF PAPER DEEP, not a colour of its own.
     Measured: #F6ECD3 on #F0E7CE is 1.049:1, which is no seam at all -- the
     two were one tint with two names. Bethany dropped it from the palette on
     2026-09-29. The NAME survives because roughly forty rules across four
     stylesheets read it, and repointing it is safer than editing all of them;
     every one of those rules now paints the single tint. Do not give this a
     value of its own again. */
  --t72-paper-warm:   #F0E7CE;
  --t72-paper-warm:   var(--t72-paper-deep);   /* alias — see above */
  /* 28%, not 34%. At 34% this tint measured #EDE4CB, and SCENERY GREEN type on
     it is 4.45:1 — under the 4.5:1 AA needs for normal text, by a hair. The fix
     stays inside the five (D42): the same two colours, mixed a little lighter.
     At 28% the pair measures 4.57:1 and INK measures 8.59:1. Do not darken it
     again without re-measuring green on it. */
  --t72-paper-deep:   #F0E7CE;
  --t72-paper-deep:   color-mix(in srgb, var(--t72-linen) 28%, var(--t72-paper));

  /* Rules, drawn from INK and the gold at low alpha. */
  --t72-rule:         rgba(61,63,63,.14);
  --t72-rule-strong:  rgba(61,63,63,.32);
  --t72-rule-on-ink:  rgba(203,192,170,.24);
  --t72-rule-gold:    rgba(195,153,84,.55);  /* the hairline, ANTIQUE GOLD   */
  --t72-rule-gold-soft: rgba(195,153,84,.22); /* the nested keyline          */

  /* A PANEL NESTED ON A DARK SURFACE.                            (Phase 14)
     The reference site's "pick your market" cards are a TRANSLUCENT dark laid
     over the SCENERY GREEN booking card at about a third opacity, and the
     translucency is part of the effect: the panel reads as a recess in the
     green rather than as a second, unrelated block. A solid fill does not look
     the same. It is reproduced with INK at 34%, rather than with the second
     charcoal the reference reaches for, so it adds no pigment that is not
     already in the five.
     Over SCENERY GREEN it resolves to about #5A5A50, on which PAPER measures
     6.46:1 — better than PAPER on flat green (5.23:1), so nesting the panel
     improves legibility rather than costing it. */
  --t72-panel-on-dark: rgba(61,63,63,.34);
  --t72-panel-on-dark: color-mix(in srgb, var(--t72-ink) 34%, transparent);

  /* Semantic roles — what to reach for, so no page has to re-derive the
     contrast table above. */
  --t72-type-on-paper:       var(--t72-ink);      /* 12.1:1                */
  --t72-type-on-paper-muted: var(--t72-green);    /*  5.2:1                */
  --t72-type-on-ink:         var(--t72-linen);    /*  5.7:1                */
  --t72-type-on-ink-strong:  var(--t72-paper);    /*  9.7:1                */
  --t72-type-on-green:       var(--t72-paper);    /*  5.2:1 — the ONLY one */
  --t72-display-on-ink:      var(--t72-gold);     /*  4.0:1 — 24px+ only   */

  /* ---- Type ------------------------------------------------------------- */
  /* TWO BRAND FACES, AND ONLY TWO. Friz Quadrata for display and titles,
     Fraunces for accents and secondary. The two decorative faces Oregon used
     to load locally are retired permanently, and so are the two non-brand
     webfonts the NC build reached for. None of them is a Tru72 face, and none
     of them is loaded anywhere any more. docs/PHASE3B_OR_RESKIN_REPORT.md
     names them.

     Friz Quadrata Bold is licensed for web use and SELF-HOSTED from the site's
     own assets — no third-party font CDN. The @font-face below serves the
     woff2 built from the supplied .otf; the .otf stays in assets/fonts/ as the
     source of record. The .ttf copy in that folder is NOT used: it is missing
     the middot and the accented glyphs and renders empty boxes.

     THREE, since D95. The label voice — the wide, uppercase, tracked small
     type on nav, the utility strip, eyebrows, numbered labels and buttons —
     is GEIST MONO, self-hosted, and it is the third face. It used to be
     Fraunces with tracking; the reference has always drawn it in a mono and
     the substitution was visible at every size. See the @font-face above for
     why this supersedes D43 and how narrowly it is scoped. */
  /* PHASE 26 — THE DISPLAY FACE IS FRAUNCES 600, AND IT IS ONE SWITCH.
     Bethany: "the title font is still thicker than tbyrds". It is, and the
     reason is that tbyrdhvac.com asks for `'Friz Quadrata Bold', Cinzel,
     serif` and SHIPS NO FRIZ FILE — so it has always drawn Cinzel. We ship
     the real Friz, so we draw what it only ever asked for. Measured in the
     browser, "Heritage. Standard. Method." at 48px, ink pixels on canvas:

         Fraunces 400            5,677 ink   615px wide
         Fraunces 500            6,498 ink   628px
         Fraunces 600            7,356 ink   640px
         Cinzel 700 (the ref)    8,076 ink   759px
         Fraunces 700            9,175 ink   665px
         Friz Quadrata Bold      9,866 ink   686px

     Friz is 22% more ink than Cinzel in 10% less width — ~35% denser per unit
     of measure. It is a single bold master; there is no lighter cut to pick
     and `font-synthesis: none` (correctly) forbids faking one. Cinzel is not
     an approved Tru72 face, so the closest shippable match to what the
     reference actually renders is FRAUNCES 600 — an approved face, already
     self-hosted as a 300-900 variable, so weight 600 costs no new request.
     Friz Quadrata Bold stays in the stack and the file still ships. */
  /* CINZEL, at the reference's own weight. Friz and Fraunces stay in the
     stack and both files still ship, so either is one edit away. */
  --t72-display-weight: 700;
  --t72-font-display: 'Cinzel', 'Friz Quadrata Bold', 'Fraunces', Georgia,
                      'Iowan Old Style', 'Times New Roman', 'DejaVu Serif', serif;
  /* THE ACCENT'S DEFAULT GOLD, declared here because a HAND-WRITTEN band
     declares no ground and therefore no `--t72c-gold-ok`. Ten display italics
     on the ported NC pages resolved to nothing and fell back to their
     heading's own colour -- ink on cream, paper on dark -- while every
     component heading went gold. Cream is the default ground, so the default
     gold is the one legal on cream: GOLD-AGED at 3.18:1. Bands that paint
     something else restate it (see tru72-components.css). Declared in THIS
     sheet, which is the one place D83's cage exempts from its reader count. */
  --t72c-gold-ok:     var(--t72-display-italic-on-paper);
  --t72-font-body:    'Fraunces', Georgia, 'Iowan Old Style',
                      'Times New Roman', 'DejaVu Serif', serif;
  /* The mono fallbacks are the platform monos, NOT the serif stack: if the
     webfont is slow or blocked the label voice must still degrade to a
     monospace texture, not back to Fraunces, or the swap moves every label. */
  --t72-font-label:   'Geist Mono', ui-monospace, 'SFMono-Regular', 'Menlo',
                      'Consolas', 'DejaVu Sans Mono', monospace;
  --t72-font-mono:    var(--t72-font-label);   /* the name is right again    */

  --t72-track-display:    .005em;   /* inscriptional faces want POSITIVE    */
  --t72-track-mono:       .2em;
  --t72-track-mono-tight: .16em;

  /* ---- Spacing + shape --------------------------------------------------- */
  /* THE SHEET AND ITS GUTTERS, IN ONE PLACE.                     (Phase 18)
     Measured off the live reference: `.page { max-width: 1440px }` on a dark
     body, and every band `padding: <v> 56px`, so the measure is 1440-112=1328.
     At 1280px of viewport (1265 once the scrollbar is off) that resolves to
     1153, which is what the reference computes — and now what we compute too.
     --t72-page-max is the SHEET; --t72-page-measure is what is left of it
     inside the gutters. Change the page width here and the sheet, every band's
     gutter and every component's measure follow. */
  --t72-page-max:      1440px;
  --t72-page-measure:  1328px;      /* 1440 - two 56px gutters               */
  --t72-gutter:        56px;
  --t72-gutter-mobile: 22px;
  --t72-radius:        0;           /* the system is square-cornered         */
  --t72-shadow-soft:   0 6px 28px rgba(61,63,63,.10);
  --t72-shadow-deep:   0 12px 48px rgba(61,63,63,.18);
  --t72-shadow-drop:   0 28px 64px -24px rgba(61,63,63,.35);
}

/* THE MOBILE GUTTER IS A REDEFINITION OF THE SAME TOKEN, not a second rule per
   band. The reference drops every band from 56px to 22px at ONE breakpoint,
   720px — `.utility, .nav, .hero, .trust-band, … { padding-left: 22px;
   padding-right: 22px }` — so the token moves and everything that reads it
   moves with it: the shared bands, the brand hub's `.band` family, and the
   Oregon/Nevada `.container`. One gutter, one breakpoint, and no band left
   holding a stale value. --t72-gutter-mobile stays because the hub and the
   component sheet still name it in places, and the two must agree. */
@media (max-width: 720px) {
  :root { --t72-gutter: var(--t72-gutter-mobile); }
}

/* ===========================================================================
   THE PAGE-FAMILY HEADING SCALE                                  (Phase 25a)
   ===========================================================================
   STOP ASSIGNING ONE HERO SIZE GLOBALLY. `.t72c-h1` used to be a single
   clamp(40px, 4.6vw, 62px) for every page that renders the shared hero, which
   put 58.9px on 41 of North Carolina's 49 routes. That is the HOMEPAGE size.
   It is not what the reference does and it is why a financing page shouted as
   loudly as the front door.

   EVERY NUMBER BELOW IS READ OFF TByrds_Launch, not guessed. The declarations
   were extracted from the reference's own page sources and then CONFIRMED by
   loading both trees in Chromium and reading getComputedStyle at four
   viewports. Reference <h1>, measured:

     family           390px    1024px   1280px   1440px   reference declaration
     home              42.0     47.1     58.9     64.0    clamp(42px, 4.6vw, 64px)
     city-landing      48.0     55.3     69.1     77.8    clamp(48px, 5.4vw, 80px)
     equipment         34.0     51.2     64.0     68.0    clamp(44px, 5vw,   68px)
     seasonal          32.0     47.1     58.9     60.0    clamp(40px, 4.6vw, 60px)
     membership        38.0     45.1     56.3     60.0    clamp(38px, 4.4vw, 60px)
     schedule          36.0     46.1     57.6     60.0    clamp(36px, 4.5vw, 60px)
     repair-metro      31.2     43.0     53.8     58.0    clamp(36px, 4.2vw, 58px)
     careers           36.0     45.1     56.0     56.0    clamp(36px, 4.4vw, 56px)
     sitemap           40.0     45.1     56.0     56.0    clamp(40px, 4.4vw, 56px)
     service-index     30.0     38.9     48.6     52.0    clamp(34px, 3.8vw, 52px)
     faqs              31.2     36.9     46.1     48.0    clamp(32px, 3.6vw, 48px)
     suburb-landing    28.0     36.9     46.1     48.0    clamp(32px, 3.6vw, 48px)
     repair-suburb     31.2     36.9     46.1     48.0    clamp(32px, 3.6vw, 48px)
     about             27.3     34.8     43.5     44.0    clamp(30px, 3.4vw, 44px)
     contact           31.2     34.8     43.5     44.0    clamp(30px, 3.4vw, 44px)
     service-areas     27.3     34.8     43.5     44.0    clamp(30px, 3.4vw, 44px)
     legal             31.2     34.8     43.5     44.0    clamp(32px, 3.4vw, 44px)
     program           27.3     32.8     41.0     42.0    clamp(28px, 3.2vw, 42px)

   Bethany's three cited figures are correct to the tenth: About 43.5px,
   financing 41.0px, the services index 48.6px, all at 1280px.

   The 390px column is a SECOND clamp the reference sets under its own 720px
   breakpoint — the mobile heads are not just the desktop clamp's minimum. It
   is reproduced below.

   The family comes from `pages/<site>/pages.json` and is stamped onto <body>
   by build.py's ensure_family_class(). A page with no family keeps the old
   default.

   PHASE 25B: brand, or and nv now declare one too, so all 258 pages of all
   five builds take a family scale and NO page is left on the 62px global
   default. The default below is therefore dead code that exists only so a
   newly added route renders while its family is being decided — and audit
   check 21 fails the build if one is ever left that way. */

body.t72-fam-home           { --t72c-h1-size: clamp(42px, 4.6vw, 64px); }
body.t72-fam-city-landing   { --t72c-h1-size: clamp(48px, 5.4vw, 80px); }
body.t72-fam-equipment      { --t72c-h1-size: clamp(44px, 5vw,   68px); }
body.t72-fam-seasonal       { --t72c-h1-size: clamp(40px, 4.6vw, 60px); }
body.t72-fam-membership     { --t72c-h1-size: clamp(38px, 4.4vw, 60px); }
body.t72-fam-schedule       { --t72c-h1-size: clamp(36px, 4.5vw, 60px); }
body.t72-fam-repair-metro   { --t72c-h1-size: clamp(36px, 4.2vw, 58px); }
body.t72-fam-careers        { --t72c-h1-size: clamp(36px, 4.4vw, 56px); }
body.t72-fam-sitemap        { --t72c-h1-size: clamp(40px, 4.4vw, 56px); }
body.t72-fam-service-index  { --t72c-h1-size: clamp(34px, 3.8vw, 52px); }
body.t72-fam-faqs           { --t72c-h1-size: clamp(32px, 3.6vw, 48px); }
body.t72-fam-suburb-landing { --t72c-h1-size: clamp(32px, 3.6vw, 48px); }
body.t72-fam-repair-suburb  { --t72c-h1-size: clamp(32px, 3.6vw, 48px); }
body.t72-fam-legal          { --t72c-h1-size: clamp(32px, 3.4vw, 44px); }
body.t72-fam-about          { --t72c-h1-size: clamp(30px, 3.4vw, 44px); }
body.t72-fam-contact        { --t72c-h1-size: clamp(30px, 3.4vw, 44px); }
body.t72-fam-service-areas  { --t72c-h1-size: clamp(30px, 3.4vw, 44px); }
body.t72-fam-program        { --t72c-h1-size: clamp(28px, 3.2vw, 42px); }

/* The 404. Its own family because it IS its own reference page: TByrds_Launch
   ships `404.html` at 51.2px at 1280, between the sitemap and the legal pages,
   and North Carolina's `.error-hero h1` already spells exactly that clamp
   inline. Oregon's and Nevada's 404s were taking the legal family's 43.5px
   only because nothing had given them a family of their own. */
body.t72-fam-error          { --t72c-h1-size: clamp(34px, 4vw,   52px); }

/* ---------------------------------------------------------------------------
   OREGON AND NEVADA'S OWN FAMILIES                                (Phase 25b)
   ---------------------------------------------------------------------------
   Six kinds of page North Carolina does not have. NONE of them gets a size of
   its own invention: each takes the scale of the nearest reference family,
   paired by ROLE and recorded in tools/assign_families.py.

     ours               nearest reference page        its 1280px <h1>
     neighborhood       hvac-kannapolis-nc            46.1   (suburb-landing)
     manual-j           faqs                          46.1   (faqs)
     diagnostic-first   about-charlotte               43.5   (about)
     reviews            about-charlotte               43.5   (about)
     rebates            savings-charlotte             41.0   (program)
     pricing            savings-charlotte             41.0   (program)

   `neighborhood` deserves the note. Oregon's neighbourhood leaf and its
   service-area leaf are the SAME DOCUMENT — the same components in the same
   order, the same "HVAC service in <place>." heading — and the reference has
   no third geographic rung under its suburb landing. Giving the neighbourhood
   a smaller step would be inventing a size the reference does not have, so
   both leaves take the leaf's size and the difference between them stays in
   the copy, which is where it belongs. */
body.t72-fam-neighborhood     { --t72c-h1-size: clamp(32px, 3.6vw, 48px); }
body.t72-fam-manual-j         { --t72c-h1-size: clamp(32px, 3.6vw, 48px); }
body.t72-fam-diagnostic-first { --t72c-h1-size: clamp(30px, 3.4vw, 44px); }
body.t72-fam-reviews          { --t72c-h1-size: clamp(30px, 3.4vw, 44px); }
body.t72-fam-rebates          { --t72c-h1-size: clamp(28px, 3.2vw, 42px); }
body.t72-fam-pricing          { --t72c-h1-size: clamp(28px, 3.2vw, 42px); }

/* The reference's own mobile step, at its own 720px breakpoint. Families the
   reference does not step (home, city-landing, membership, careers, schedule,
   sitemap) are absent on purpose — their desktop clamp's minimum IS their
   mobile size, which is what the reference measures at 390px. */
@media (max-width: 720px) {
  body.t72-fam-equipment      { --t72c-h1-size: clamp(34px, 8vw,   44px); }
  body.t72-fam-seasonal       { --t72c-h1-size: clamp(32px, 8vw,   42px); }
  body.t72-fam-repair-metro   { --t72c-h1-size: clamp(30px, 8vw,   42px); }
  body.t72-fam-repair-suburb  { --t72c-h1-size: clamp(30px, 8vw,   42px); }
  body.t72-fam-service-index  { --t72c-h1-size: clamp(30px, 7.5vw, 38px); }
  body.t72-fam-faqs           { --t72c-h1-size: clamp(26px, 8vw,   34px); }
  body.t72-fam-suburb-landing { --t72c-h1-size: clamp(28px, 7vw,   38px); }
  body.t72-fam-legal          { --t72c-h1-size: clamp(26px, 8vw,   34px); }
  body.t72-fam-contact        { --t72c-h1-size: clamp(26px, 8vw,   34px); }
  body.t72-fam-about          { --t72c-h1-size: clamp(26px, 7vw,   34px); }
  body.t72-fam-service-areas  { --t72c-h1-size: clamp(24px, 7vw,   32px); }
  body.t72-fam-program        { --t72c-h1-size: clamp(24px, 7vw,   32px); }
  /* Phase 25b — each one steps exactly as the family it was paired with. */
  body.t72-fam-neighborhood     { --t72c-h1-size: clamp(28px, 7vw,   38px); }
  body.t72-fam-manual-j         { --t72c-h1-size: clamp(26px, 8vw,   34px); }
  body.t72-fam-diagnostic-first { --t72c-h1-size: clamp(26px, 7vw,   34px); }
  body.t72-fam-reviews          { --t72c-h1-size: clamp(26px, 7vw,   34px); }
  body.t72-fam-rebates          { --t72c-h1-size: clamp(24px, 7vw,   32px); }
  body.t72-fam-pricing          { --t72c-h1-size: clamp(24px, 7vw,   32px); }
}
