/* ============================================================================
   R1 . SITE SIGN — design system, extracted verbatim from the APPROVED
   reference build `design/directions/R1-sitesign/index.html` (2026-07-24).

   This is the real R1: a layout + component system, not a band-tinting layer.
   It defines its own grounds (bone / cream / warm / gun / near-black / orange),
   hard 0-blur offset shadows, an Oswald display face, and the component
   vocabulary the reference uses (.proj .stat .cert .step .srow .diff .field).

   TOKEN CLASHES with the live sheet — 5, all cosmetic:
       --ink   #131313 (ref)  vs #111111 (live)
       --cream #ece4d5 (ref)  vs #f4f1ea (live)
       --orange-deep #a5330f  vs #b8390f
       --paper #ffffff        vs #fff
   Reference values are authoritative for R1 surfaces, so this file MUST be
   scoped so it cannot repaint the other 93 legacy pages.
   ========================================================================== */

/* ============================================================
   D5  ·  STEEL-MAJOR  ·  DeHart Roofing
   COLOR-PRESENCE variant of D3-dual. Layout / copy / images / components /
   scripts are identical to D3 — only the gunmetal token VALUES and the
   SURFACES that use them changed.
   Color-blocked Makita-industrial. Orange #cf4214 = star.
   TWO dark grounds, two semantic jobs:
     WARM BITUMEN (--warm*) -> product / craft     -> SERVICES
     GUNMETAL     (--gun*)  -> process / technical -> HERO PANEL + PROCESS
   D4 CHANGE 1: gunmetal raised from ~207deg/23%/15% to ~203deg/46%/20% so it
   reads as a genuinely distinct COOL ground — bluer than the old teal-leaning
   steel, so it never slides back toward teal.
   D4 CHANGE 2: second, high-visibility placement — the HERO content panel.
   The two darks are NEVER adjacent. Ground sequence:
     nav(near-black) -> HERO PANEL(gun) -> spec strip(FULL ORANGE) ->
     services(WARM) -> orange rule -> differentiators(near-black) ->
     projects(cream) -> process(GUN) -> reviews(bone) -> CTA(orange) ->
     footer(near-black)
   Orange separates hero-gun from services-warm; cream + bone flank process-gun.
   Every color / size / spacing value comes from a token.
   TEXT RULE: light grounds -> ink. Colored + dark grounds -> WHITE
   (--on-color / --on-color-2). No ink text on orange anywhere.
   ============================================================ */
:root{
  /* ---- Orange (star) ---- */
  --orange:#cf4214; --orange-h:#b53811; --orange-deep:#a5330f;

  /* ---- Warm darks ---- */
  --near-black:#0e0e0e; --ink:#131313; --char:#1c1c1c; --warm-dark:#191310;

  /* ---- DUAL SECONDARY GROUNDS (D3 — both D2 grounds kept, each given a job) ---- */
  /* WARM BITUMEN · product / craft ground — SERVICES */
  --warm:#1c3a4c; --warm-2:#24485c; --warm-line:#3a6a84;
  /* GUNMETAL · process / technical ground — HERO PANEL + PROCESS.
     D4: turned UP to ~203deg hue / ~46% sat / ~20% L. Chroma and lightness
     raised so it reads as a clearly distinct COOL ground, while the hue is
     pushed BLUER than the old teal-leaning steel so it cannot drift to teal.
     White = 11.94:1, --on-color-2 = 8.59:1 on --gun (both clear AA). */
  --gun:#241a13; --gun-2:#33251a; --gun-line:#4a3728;

  /* ---- Warm neutrals (light grounds) ---- */
  --bone:#f4f1ea; --cream:#ece4d5; --stone:#ddd5c5; --paper:#ffffff;

  /* ---- Text on LIGHT grounds ---- */
  --line:#d8d4cb; --muted:#5a5650;

  /* ---- Text on COLORED / DARK grounds (white only) ---- */
  --on-color:#ffffff;
  --on-color-2:rgba(255,255,255,.82);   /* AA on near-black, warm 11.7:1, D4 gunmetal 8.59:1 */

  /* ---- Dark hairlines / rgba layers ---- */
  --dark-line:#2c2c2c; --dark-line-soft:#262626; --dark-line-hi:#3a3a3a;
  --hair-on-dark:rgba(255,255,255,.18);
  --scrim:rgba(14,14,14,.58);
  /* D4: hero content panel ground = --gun (28,58,76) at 92%. Alpha raised from
     .90 to .92 so less of the warm photo bleeds through and muddies the blue.
     Worst-case composite (white photo pixel) still gives white 11.13:1 and
     --on-color-2 8.08:1 — AA with headroom on every possible pixel. */
  --scrim-panel:rgba(28,58,76,.92);
  --scrim-solid:rgba(14,14,14,.92);
  --ring-focus:0 0 0 3px rgba(207,66,20,.28);
  --ring-error:0 0 0 3px rgba(165,51,15,.22);
  --sh-corner:-3px -3px 0 rgba(14,14,14,.4);

  /* ---- Type families ---- */
  --f-display:'Oswald', 'Arial Narrow', sans-serif;
  --f-body:'IBM Plex Sans', system-ui, sans-serif;
  --f-mono:'IBM Plex Mono', ui-monospace, monospace;

  /* ---- Type scale ---- */
  --fs-nano:.625rem;       /* 10 */
  --fs-micro:.6875rem;     /* 11 */
  --fs-eyebrow:.75rem;     /* 12 */
  --fs-label:.8125rem;     /* 13 */
  --fs-small:.875rem;      /* 14 */
  --fs-body:1.0625rem;     /* 17 */
  --fs-body-lg:1.1875rem;  /* 19 */
  --fs-h4:1.25rem;         /* 20 */
  --fs-brand:1.35rem;      /* 21.6 */
  --fs-card-h:1.5rem;      /* 24 */
  --fs-h3:clamp(1.35rem,1rem + 1.6vw,1.75rem);
  --fs-h2:clamp(1.75rem,1.2rem + 2.6vw,2.6rem);
  /* #80 (2026-10-12): six display steps. stat/rating/step/cta were four near-identical sizes (52-60px): one 56px step;
     the differentiator lines share the H2 size; the H1 is the largest type on every page (the H2 "mega" was 76 vs 72) */
  --fs-num:clamp(2.25rem,1.4rem + 3.6vw,3.5rem);
  --fs-stat:var(--fs-num);
  --fs-rating:var(--fs-num);
  --fs-step:var(--fs-num);
  --fs-diff:var(--fs-h2);
  --fs-cta:var(--fs-num);
  --fs-h1:clamp(2.5rem,1.3rem + 6vw,5rem);         /* oversized moment 1 */
  --fs-mega:clamp(2.15rem,1rem + 5vw,4rem);        /* oversized moment 2, always under the H1 */

  /* ---- Letter-spacing scale ---- */
  --ls-display:.005em; --ls-tight:.004em; --ls-head:.02em;
  --ls-nav:.08em; --ls-cap:.05em; --ls-btn:.1em;
  --ls-mono:.12em; --ls-wide:.14em; --ls-eyebrow:.18em; --ls-hero:.2em;

  /* ---- Spacing (8px grid; #46 2026-10-12: the old 6/10/14px half-steps now land on the 4/8 grid) ---- */
  --s0:2px; --s1:4px; --s1h:8px; --s2:8px; --s2h:8px; --s3:12px; --s3h:16px;
  --s4:16px; --s4h:20px; --s5:24px; --s5h:28px; --s6:32px; --s7:48px;
  --s8:64px; --s9:96px; --s10:128px;
  /* #45 (2026-10-12): band padding steps up at 641px and 1100px (BATCH 2 PASS), always on the 8px grid */
  --band-pad:48px;
  --band-pad-lg:64px;
  --strip-pad:32px;
  --sec-pad:40px;
  --pad-x:clamp(16px,4vw,40px);
  --pad-hero-top:clamp(40px,6vw,84px);
  --pad-hero-bot:clamp(48px,6vw,88px);
  --pad-panel:clamp(24px,3vw,40px);
  --pad-card:clamp(20px,3vw,30px);
  --feat-h:clamp(260px,32vw,420px);
  --media-max:420px;
  --gap-grid:clamp(18px,2.2vw,28px);
  --gap-wide:clamp(24px,3.5vw,48px);
  --wrap:1200px;
  --nav-h:68px;
  --btn-py:16px; --btn-px:24px;
  --field-py:12px; --field-px:16px;
  --plate-top:40px; --cert-top:40px;
  --thumb:104px;
  --tap:44px;
  --icon-sm:14px; --icon-arrow-w:20px; --icon-arrow-h:12px;
  --stars-w:150px; --stars-h:28px; --stars-w-sm:96px; --stars-h-sm:18px;
  --logo-h:34px;

  /* ---- Border widths ---- */
  --bw:2px; --bw-hair:1px; --bw-rule-md:3px; --bw-rule:4px; --bw-rule-lg:6px;

  /* ---- Hard offset shadows (0 blur) ---- */
  --sh-ink:6px 6px 0 var(--ink);
  --sh-ink-sm:4px 4px 0 var(--ink);
  --sh-ink-lg:8px 8px 0 var(--ink);
  --sh-orange:6px 6px 0 var(--orange);
  --sh-orange-lg:8px 8px 0 var(--orange);
  --sh-black:6px 6px 0 var(--near-black);
  --sh-black-sm:4px 4px 0 var(--near-black);
  --lift:translate(-4px,-4px);
  --lift-sm:translate(-2px,-2px);
}

/* ---- reset ---- */
*,*::before,*::after{box-sizing:border-box;border-radius:0}
html{-webkit-text-size-adjust:100%}
/* U-16: overflow-x:hidden on html AND body stops body's overflow reaching the viewport, so body became the sticky
   .nav's scroll container and the header never stuck on / and /es/ (the other 90 pages repair it with an inline
   html,body{overflow-x:clip}). clip clips the same without making a scroll container; hidden stays as the fallback
   for browsers without clip, which keep today's behaviour. */
html,body{margin:0;padding:0;overflow-x:hidden;overflow-x:clip;max-width:100%}
body{
  font-family:var(--f-body); font-size:var(--fs-body); line-height:1.6;
  color:var(--ink); background:var(--bone);
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}
h1,h2,h3,h4,p,ul,dl,dd,figure,blockquote{margin:0}
ul{list-style:none;padding:0}

/* ---- focus visibility (AA, visible on every ground) ---- */
:focus-visible{outline:var(--bw-rule-md) solid var(--orange);outline-offset:var(--s0)}
.band-orange a:focus-visible,.band-orange button:focus-visible,
.band-orange input:focus-visible,.rating a:focus-visible,
.proj-note a:focus-visible{outline-color:var(--near-black)}
/* orange only clears 3:1 comfortably on light + near-black grounds; on BOTH
   secondary grounds the ring switches to white (11.94:1 on D4 gunmetal,
   17:1 on warm) so the indicator always passes 1.4.11.
   D4: the HERO PANEL is now a gunmetal ground too — orange there measures only
   2.27–2.53:1, so its ring switches to white as well (10.74–11.94:1). */
.band-warm a:focus-visible,.band-warm button:focus-visible,
.band-warm-2 a:focus-visible,.band-warm-2 button:focus-visible,
.band-gun a:focus-visible,.band-gun button:focus-visible,
.band-gun-2 a:focus-visible,.band-gun-2 button:focus-visible,
.hero__panel a:focus-visible,.hero__panel button:focus-visible{outline-color:var(--on-color)}

.skip{position:absolute;left:-999px;top:0;background:var(--ink);color:var(--on-color);
  padding:var(--s3) var(--s4);z-index:200;font-family:var(--f-mono);font-size:var(--fs-label)}
.skip:focus{left:var(--s3);top:var(--s3)}

/* ---- shared helpers ---- */
.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--pad-x)}
.band{padding-block:var(--band-pad)}
.band--lg{padding-block:var(--band-pad-lg)}
.mono{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;letter-spacing:var(--ls-mono)}
.eyebrow{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-eyebrow);font-size:var(--fs-eyebrow);line-height:1.3}
.display{font-family:var(--f-display);font-weight:700;line-height:1.02;
  text-transform:uppercase;letter-spacing:var(--ls-display)}
.dot{color:var(--orange)}                                  /* orange period, display size only */
/* orange is too dim on both secondary grounds. D4: the hero panel joined them —
   orange there is 2.53:1, under even the 3:1 large-text floor — so it goes white. */
.band-warm .dot,.band-warm-2 .dot,
.band-gun .dot,.band-gun-2 .dot,
.hero__panel .dot{color:var(--on-color)}
.section-head{max-width:56ch;margin-bottom:var(--s7)}
.section-head .eyebrow{color:var(--orange-deep);margin-bottom:var(--s3);display:block}
.section-head h2{font-family:var(--f-display);font-weight:600;font-size:var(--fs-h2);
  line-height:1.04;text-transform:uppercase;letter-spacing:var(--ls-display)}
.lead{font-size:var(--fs-body-lg);color:var(--muted);margin-top:var(--s4);max-width:52ch}

/* eyebrow CHIP: on dark/colored grounds the eyebrow becomes a solid orange block
   with white text (white-on-orange = 4.73:1 AA) — orange as area, never as small text */
.band-dark .section-head .eyebrow,.band-warm .section-head .eyebrow,
.band-warm-2 .section-head .eyebrow,.band-gun .section-head .eyebrow,
.band-gun-2 .section-head .eyebrow,.certs__head .eyebrow,
.feat__aside .eyebrow,.hero__eyebrow{
  background:var(--orange);color:var(--on-color);display:inline-block;
  padding:var(--s1h) var(--s3);line-height:1.25}
.band-dark .lead,.band-warm .lead,.band-warm-2 .lead,
.band-gun .lead,.band-gun-2 .lead{color:var(--on-color-2)}
.band-dark .section-head h2,.band-warm .section-head h2,
.band-warm-2 .section-head h2,.band-gun .section-head h2,
.band-gun-2 .section-head h2{color:var(--on-color)}

/* section base colors (#81: the bone band renders white, see the bright pass) */
.band-bone{background:var(--paper);color:var(--ink)}
.band-cream{background:var(--cream);color:var(--ink)}
.band-warm{background:var(--warm);color:var(--on-color)}       /* craft ground */
.band-warm-2{background:var(--warm-2);color:var(--on-color)}
.band-gun{background:var(--gun);color:var(--on-color)}         /* technical ground */
.band-gun-2{background:var(--gun-2);color:var(--on-color)}
.band-dark{background:var(--near-black);color:var(--on-color)}
.band-orange{background:var(--orange);color:var(--on-color)}
.rule-orange{height:var(--bw-rule-lg);background:var(--orange)}

/* ============ BUTTONS ============ */
.btn{display:inline-flex;align-items:center;gap:var(--s2);
  font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-btn);font-size:var(--fs-label);
  padding:var(--btn-py) var(--btn-px);border:var(--bw) solid transparent;
  line-height:1;white-space:nowrap;
  transition:transform .12s ease, box-shadow .12s ease, background-color .12s ease}
.btn--primary{background:var(--orange);color:var(--on-color);border-color:var(--orange);
  box-shadow:var(--sh-ink-sm)}
.btn--primary:hover{background:var(--orange-h);border-color:var(--orange-h);
  transform:var(--lift-sm);box-shadow:var(--sh-ink)}
.btn--primary:active{transform:translate(0,0);box-shadow:var(--s0) var(--s0) 0 var(--ink)}
.btn--ink{background:var(--near-black);color:var(--on-color);border-color:var(--near-black);
  box-shadow:var(--sh-black-sm)}
.btn--ink:hover{background:var(--ink);transform:var(--lift-sm);box-shadow:var(--sh-black)}
.btn--ghost{background:transparent;color:var(--ink);border-color:var(--ink)}
.btn--ghost:hover{background:var(--ink);color:var(--on-color)}
.band-dark .btn--ghost,.band-warm .btn--ghost,.band-warm-2 .btn--ghost,
.band-gun .btn--ghost,.band-gun-2 .btn--ghost,
.band-orange .btn--ghost,.hero .btn--ghost{color:var(--on-color);border-color:var(--on-color)}
.band-dark .btn--ghost:hover,.band-warm .btn--ghost:hover,.band-warm-2 .btn--ghost:hover,
.band-gun .btn--ghost:hover,.band-gun-2 .btn--ghost:hover,
.band-orange .btn--ghost:hover,.hero .btn--ghost:hover{background:var(--on-color);color:var(--ink)}
.band-orange .btn--ink{box-shadow:var(--s1) var(--s1) 0 var(--warm-dark)}

/* inline SVG star (no glyphs anywhere) */
.star{width:1em;height:1em;display:inline-block;vertical-align:-.12em;flex:none}
.arrow{width:var(--icon-arrow-w);height:var(--icon-arrow-h);flex:none}

/* ============ NAV (near-black) ============ */
.nav{position:sticky;top:0;z-index:100;background:var(--near-black);color:var(--on-color);
  border-bottom:var(--bw) solid var(--char)}
.nav__in{display:flex;align-items:center;gap:var(--s5);
  min-height:var(--nav-h);max-width:var(--wrap);margin:0 auto;padding-inline:var(--pad-x)}
.brand{display:flex;align-items:baseline;gap:var(--s3);margin-right:auto}
.brand__mark{font-family:var(--f-display);font-weight:700;text-transform:uppercase;
  letter-spacing:var(--ls-head);font-size:var(--fs-brand);color:var(--on-color);line-height:1}
.brand__est{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-wide);color:var(--on-color-2)}
.nav__links{display:flex;align-items:center;gap:var(--s6)}
.nav__links a{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-nav);font-size:var(--fs-label);color:var(--on-color-2);
  padding-block:var(--s1h);border-bottom:var(--bw) solid transparent}
.nav__links a:hover{color:var(--on-color);border-bottom-color:var(--orange)}
.nav__cta{display:flex;align-items:center;gap:var(--s4)}
.nav__phone{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-label);
  letter-spacing:var(--ls-tight);color:var(--on-color)}
.nav__phone:hover{border-bottom:var(--bw) solid var(--orange)}
.nav__toggle{display:none;width:var(--tap);height:var(--tap);
  border:var(--bw) solid var(--dark-line-hi);padding:0;align-items:center;
  justify-content:center;flex-direction:column;gap:var(--s1h)}
.nav__toggle span{display:block;width:var(--s4h);height:var(--bw);background:var(--on-color)}

@media (max-width:920px){
  .nav__links{position:absolute;left:0;right:0;top:100%;flex-direction:column;
    align-items:stretch;gap:0;background:var(--near-black);
    border-bottom:var(--bw) solid var(--char);padding:var(--s3) var(--pad-x) var(--s5);
    display:none}
  .nav__links.open{display:flex}
  .nav__links a{padding-block:var(--s4);border-bottom:var(--bw-hair) solid var(--dark-line-soft)}
  .nav__phone{display:none}
  .nav__toggle{display:flex}
}
@media (max-width:480px){
  .nav__cta .btn{padding:var(--s3) var(--s3h);font-size:var(--fs-eyebrow)}
  .brand__est{display:none}
}

/* ============ HERO ============ */
.hero{position:relative;background:var(--near-black);color:var(--on-color);overflow:hidden}
.hero__bg{position:absolute;inset:0;z-index:0}
.hero__bg img{width:100%;height:100%;object-fit:cover}
.hero__scrim{position:absolute;inset:0;z-index:1;background:var(--scrim)}
.hero__in{position:relative;z-index:2;max-width:var(--wrap);margin:0 auto;
  padding:var(--pad-hero-top) var(--pad-x) var(--pad-hero-bot);
  display:grid;grid-template-columns:1.15fr .85fr;gap:var(--gap-wide);align-items:center}
/* D4 PLACEMENT 2 — the hero content panel is now the GUNMETAL ground.
   Still a solid-reading panel behind the headline => AA over any photo pixel.
   The 6px orange border-left keeps orange as the star and buffers the cool
   panel from the warm photograph on its leading edge. */
.hero__panel{background:var(--scrim-panel);border-left:var(--bw-rule-lg) solid var(--orange);
  padding:var(--pad-panel)}
.hero__eyebrow{letter-spacing:var(--ls-hero);margin-bottom:var(--s5)}
.hero h1{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h1);
  line-height:.98;text-transform:uppercase;letter-spacing:var(--ls-tight);color:var(--on-color);
  text-wrap:balance;margin-bottom:var(--s5)}
.hero__sub{font-size:var(--fs-body-lg);color:var(--on-color-2);max-width:46ch;margin-bottom:var(--s6)}
.hero__actions{display:flex;flex-wrap:wrap;gap:var(--s4);margin-bottom:var(--s7)}
/* D4: on the gunmetal panel the neutral --dark-line rule measures 1.17:1 and
   reads as a grey smudge; --gun-line (2.03:1) keeps the panel palette coherent */
.hero__specs{display:flex;flex-wrap:wrap;gap:var(--s3) var(--s5);
  padding-top:var(--s5);border-top:var(--bw) solid var(--gun-line)}
.hero__spec{display:inline-flex;align-items:center;gap:var(--s2);
  font-family:var(--f-mono);font-weight:500;font-size:var(--fs-label);
  letter-spacing:var(--ls-cap);color:var(--on-color-2)}
.hero__spec .star{color:var(--on-color)}
.hero__spec b{color:var(--on-color);font-weight:500}

/* FIG caption plate */
.figcap{position:absolute;left:var(--pad-x);bottom:var(--s4);z-index:2;
  background:var(--scrim-solid);border:var(--bw) solid var(--char);
  border-left:var(--bw-rule) solid var(--orange);padding:var(--s2) var(--s3h);
  max-width:calc(100% - var(--s9))}
.figcap span{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-btn);text-transform:uppercase;color:var(--on-color-2)}
.figcap b{color:var(--on-color);font-weight:500}

/* ============ FORM CARD (white ground, NEUTRAL at rest) ============ */
.formcard{background:var(--paper);color:var(--ink);border:var(--bw) solid var(--ink);
  box-shadow:var(--sh-orange);padding:var(--pad-card)}
.formcard__head{border-bottom:var(--bw) solid var(--ink);
  padding-bottom:var(--s4);margin-bottom:var(--s5)}
.formcard__kicker{color:var(--orange-deep);display:block;margin-bottom:var(--s2)}
.formcard h2{font-family:var(--f-display);font-weight:600;font-size:var(--fs-card-h);
  text-transform:uppercase;line-height:1.05;letter-spacing:var(--ls-head)}
.formcard__note{font-size:var(--fs-small);color:var(--muted);margin-top:var(--s2)}
.field{margin-bottom:var(--s4)}
.field label{display:block;font-family:var(--f-mono);font-weight:500;
  text-transform:uppercase;letter-spacing:var(--ls-btn);font-size:var(--fs-eyebrow);
  color:var(--ink);margin-bottom:var(--s1h)}
.field .req{color:var(--orange-deep)}
.field input,.field select,.field textarea{
  width:100%;font-family:var(--f-body);font-size:var(--fs-body);color:var(--ink);
  background:var(--paper);border:var(--bw) solid var(--ink);
  padding:var(--field-py) var(--field-px);line-height:1.3;
  transition:border-color .12s ease, box-shadow .12s ease}
.field input::placeholder,.field textarea::placeholder{color:var(--muted)}
/* appearance:none removes the native arrow, so draw one (U-42): two --ink gradient
   triangles make a 12x6 chevron that follows the token; the text keeps an 8px gap
   from it and ends in an ellipsis instead of running under it */
.field select{appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--ink) 50%),
    linear-gradient(135deg,var(--ink) 50%,transparent 50%);
  background-position:right calc(var(--field-px) + 6px) center,right var(--field-px) center;
  background-size:6px 6px;background-repeat:no-repeat;
  padding-right:calc(var(--field-px) + 20px);text-overflow:ellipsis}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--orange)}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{
  outline:var(--bw-rule-md) solid transparent;border-color:var(--orange);box-shadow:var(--ring-focus)}
/* FP-01: transparent, not none. It paints nothing in normal colours, but forced-colours modes
   drop the box-shadow and repaint this outline as a system-colour ring, so focus stays visible. */
.field input:disabled,.field select:disabled{
  background:var(--bone);border-color:var(--line);color:var(--muted);cursor:not-allowed}
/* error state — defined in CSS, NEVER applied at load (JS adds .field--error on invalid submit) */
.field--error input,.field--error select,.field--error textarea{
  border-color:var(--orange-deep);box-shadow:var(--ring-error)}
.field__err{display:none;align-items:center;gap:var(--s1h);margin-top:var(--s1h);
  font-family:var(--f-mono);font-weight:500;font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-tight);color:var(--orange-deep);text-transform:uppercase}
.field--error .field__err{display:flex}
.field__err svg{width:var(--icon-sm);height:var(--icon-sm);flex:none}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4)}
.formcard .btn--primary{width:100%;justify-content:center;padding:var(--s4);font-size:var(--fs-label)}
.formcard .btn--primary:disabled{background:var(--muted);border-color:var(--muted);
  box-shadow:none;transform:none;cursor:default}
.formcard__fine{font-size:var(--fs-eyebrow);color:var(--muted);margin-top:var(--s4);
  font-family:var(--f-mono);letter-spacing:var(--ls-tight);text-transform:uppercase;line-height:1.5}

@media (max-width:900px){
  .hero__in{grid-template-columns:1fr;gap:var(--s7)}
  .figcap{display:none}
}
@media (max-width:440px){.form-row{grid-template-columns:1fr}}

/* ============ SPEC PLATE (reusable) ============ */
.plate{position:relative;border:var(--bw) solid var(--ink);background:var(--paper);
  padding:var(--plate-top) var(--s5) var(--s5)}
.plate__tab{position:absolute;top:calc(var(--bw) * -1);left:calc(var(--bw) * -1);
  background:var(--orange);color:var(--on-color);
  font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-wide);font-size:var(--fs-micro);padding:var(--s1h) var(--s3);line-height:1}
.plate__title{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h4);letter-spacing:var(--ls-head);line-height:1.05;margin-bottom:var(--s4)}
.plate__rows{display:flex;flex-direction:column}
/* U-14: two auto grid tracks, not a nowrap flex row with an unshrinkable dt. Each column
   sizes between its min- and max-content and the two share any shortfall, so a long label
   wraps instead of pushing the value past the card edge, and short values stay on one line. */
.prow{display:grid;grid-template-columns:auto auto;justify-content:space-between;gap:var(--s5);
  padding:var(--s2h) 0;border-top:var(--bw-hair) solid var(--line)}
.prow:first-child{border-top:0}
.prow dt{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-btn);font-size:var(--fs-eyebrow);color:var(--muted)}
.prow dd{font-family:var(--f-body);font-weight:500;font-size:var(--fs-small);
  color:var(--ink);text-align:right}
/* dark variant */
.plate--dark{background:transparent;border-color:var(--warm-line)}
.plate--dark .plate__title{color:var(--on-color)}
.plate--dark .prow{border-top-color:var(--hair-on-dark)}
.plate--dark .prow dt{color:var(--on-color-2)}
.plate--dark .prow dd{color:var(--on-color)}

/* ============ SPEC STRIP (full orange) ============ */
.strip{padding-block:var(--strip-pad)}
.strip__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--bw);
  background:var(--warm-dark);border:var(--bw) solid var(--warm-dark)}
.stat{background:var(--orange);padding:var(--s5h) var(--s5);display:flex;
  flex-direction:column;gap:var(--s2)}
.stat__num{font-family:var(--f-display);font-weight:700;font-size:var(--fs-stat);
  line-height:.9;color:var(--on-color);display:flex;align-items:center;gap:var(--s2)}
.stat__num .star{color:var(--on-color);width:.7em;height:.7em}
.stat__label{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-mono);font-size:var(--fs-eyebrow);color:var(--on-color)}
@media (max-width:720px){.strip__grid{grid-template-columns:1fr 1fr}}

/* ============ SERVICES (full WARM BITUMEN — the large craft ground) ============ */
.svc-head{display:grid;grid-template-columns:1.25fr .75fr;gap:var(--gap-wide);
  align-items:end;margin-bottom:var(--s8)}
.svc-head .section-head{margin-bottom:0}
.svc-index{border-top:var(--bw) solid var(--warm-line)}
.irow{display:flex;justify-content:space-between;gap:var(--s4);
  padding:var(--s3) 0;border-bottom:var(--bw-hair) solid var(--warm-line)}
.irow dt{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-btn);font-size:var(--fs-eyebrow);color:var(--on-color-2)}
.irow dd{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-cap);font-size:var(--fs-eyebrow);color:var(--on-color);text-align:right}

/* --- 1. FEATURED service (wide photo + spec plate) --- */
.feat{display:grid;grid-template-columns:1.25fr .95fr;gap:var(--gap-wide);
  align-items:stretch;margin-bottom:var(--gap-grid)}
.feat__media{position:relative;border:var(--bw) solid var(--warm-line);
  min-height:var(--feat-h);background:var(--warm-2)}
.feat__media img{width:100%;height:100%;object-fit:cover;position:absolute;inset:0}
.feat__flag{position:absolute;left:calc(var(--bw) * -1);top:var(--s4h);z-index:2;
  background:var(--orange);color:var(--on-color);
  font-family:var(--f-mono);font-weight:500;text-transform:uppercase;letter-spacing:var(--ls-mono);
  font-size:var(--fs-label);padding:var(--s2) var(--s4);box-shadow:var(--sh-black)}
.feat__aside{display:flex;flex-direction:column;justify-content:center;gap:var(--s5)}
.feat__aside h3{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h2);line-height:1.02;letter-spacing:var(--ls-display);
  color:var(--on-color);margin-top:var(--s3)}
.feat__aside p{color:var(--on-color-2);font-size:var(--fs-body);max-width:44ch}

/* --- 2. TWO medium cards --- */
.svc-pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap-grid);
  margin-bottom:var(--s7)}
.svc{background:var(--warm-2);border:var(--bw) solid var(--warm-line);
  display:flex;flex-direction:column;
  transition:transform .14s ease, box-shadow .14s ease}
.svc:hover,.svc:focus-within{transform:var(--lift);box-shadow:var(--sh-orange-lg)}
.svc__img{aspect-ratio:16/9;overflow:hidden;border-bottom:var(--bw) solid var(--warm-line)}
.svc__img img{width:100%;height:100%;object-fit:cover;transition:transform .3s ease}
.svc:hover .svc__img img{transform:scale(1.04)}
.svc__body{padding:var(--s5);display:flex;flex-direction:column;gap:var(--s2);flex:1}
.svc__idx{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-wide);color:var(--on-color-2)}
.svc__name{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h4);letter-spacing:var(--ls-head);line-height:1.05;color:var(--on-color)}
.svc__name a{display:inline-flex;align-items:center;gap:var(--s3)}
.svc__name svg{width:var(--icon-arrow-w);height:var(--icon-arrow-h);flex:none;opacity:.6}
.svc:hover .svc__name svg{opacity:1}
.svc__spec{font-family:var(--f-body);font-size:var(--fs-small);color:var(--on-color-2)}

/* --- 3. THREE compact hairline rows --- */
.svc-rows{border-top:var(--bw) solid var(--warm-line);margin-bottom:var(--s7)}
.srow{border-bottom:var(--bw-hair) solid var(--warm-line)}
.srow a{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1.5fr) auto;
  gap:var(--s5);align-items:center;padding:var(--s5) var(--s4);
  border-left:var(--bw-rule) solid transparent;
  transition:border-color .14s ease, background-color .14s ease}
.srow a:hover{border-left-color:var(--orange);background:var(--warm-2)}
.srow__code{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-wide);font-size:var(--fs-eyebrow);color:var(--on-color-2)}
.srow__name{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h4);letter-spacing:var(--ls-head);line-height:1.05;color:var(--on-color)}
.srow__spec{font-size:var(--fs-small);color:var(--on-color-2)}
.srow svg{width:var(--icon-arrow-w);height:var(--icon-arrow-h);flex:none;color:var(--on-color)}

/* --- ONE section-level CTA (replaces the six repeated card CTAs) --- */
.svc-cta{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:var(--s5);border:var(--bw) solid var(--warm-line);padding:var(--s6) var(--s5)}
.svc-cta__txt{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h3);line-height:1.05;letter-spacing:var(--ls-head);
  color:var(--on-color);max-width:24ch}
.svc-cta__act{display:flex;flex-wrap:wrap;gap:var(--s4)}
@media (max-width:900px){
  .svc-head{grid-template-columns:1fr;align-items:start}
  .feat{grid-template-columns:1fr}
  .svc-pair{grid-template-columns:1fr}
  .srow a{grid-template-columns:auto minmax(0,1fr);gap:var(--s3) var(--s4)}
  .srow__spec{grid-column:1 / -1}
  .srow svg{display:none}
}

/* ============ DIFFERENTIATORS (near-black) — oversized moment 2 ============ */
.diff__top{display:grid;grid-template-columns:1.1fr .9fr;gap:var(--gap-wide);
  align-items:center;margin-bottom:var(--s9)}
.diff h2.mega{font-family:var(--f-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-mega);line-height:.94;letter-spacing:var(--ls-tight);
  color:var(--on-color);margin-bottom:var(--s6)}
.diff__lines{display:flex;flex-direction:column;gap:var(--s3)}
.diff__line{font-family:var(--f-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-diff);line-height:1;letter-spacing:var(--ls-tight);color:var(--on-color-2)}
.diff__line em{font-style:normal;color:var(--orange)}
.diff__media{border:var(--bw) solid var(--char);border-left:var(--bw-rule) solid var(--orange)}
.diff__media img{width:100%;aspect-ratio:4/5;object-fit:cover}
.diff__body{max-width:52ch;color:var(--on-color-2);font-size:var(--fs-body-lg)}
.diff__body strong{color:var(--on-color);font-weight:600}

.certs{margin-top:var(--s8)}
.certs__head{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;
  padding-bottom:var(--s4);margin-bottom:var(--s6);border-bottom:var(--bw) solid var(--char)}
.certs__head h3{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h3);letter-spacing:var(--ls-head);color:var(--on-color)}
.cert-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s4)}
.cert{position:relative;border:var(--bw) solid var(--warm-line);
  padding:var(--cert-top) var(--s4) var(--s4);background:var(--warm-dark)}
.cert__tab{position:absolute;top:calc(var(--bw) * -1);left:calc(var(--bw) * -1);
  background:var(--orange);color:var(--on-color);
  font-family:var(--f-mono);font-weight:500;letter-spacing:var(--ls-mono);
  font-size:var(--fs-nano);padding:var(--s1) var(--s2h);line-height:1}
.cert__name{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-body);line-height:1.08;letter-spacing:var(--ls-head);
  color:var(--on-color);margin-bottom:var(--s2)}
.cert__desc{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-cap);font-size:var(--fs-micro);color:var(--on-color-2);line-height:1.5}
@media (max-width:900px){.diff__top{grid-template-columns:1fr}
  .diff__media{max-width:var(--media-max)}.cert-grid{grid-template-columns:1fr 1fr}}
@media (max-width:460px){.cert-grid{grid-template-columns:1fr}}

/* ============ PROJECTS (cream MOSAIC — mixed tile sizes) ============ */
.proj-mosaic{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--gap-grid)}
.proj{border:var(--bw) solid var(--ink);background:var(--paper)}
.proj__img{overflow:hidden;border-bottom:var(--bw) solid var(--ink);background:var(--stone)}
.proj__img img{width:100%;height:100%;object-fit:cover}
.proj__cap{padding:var(--s3) var(--s4);display:flex;align-items:center;gap:var(--s3)}
.proj__no{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-btn);color:var(--on-color);background:var(--orange);
  padding:var(--s1) var(--s1h);line-height:1;flex:none}
.proj__meta{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-cap);font-size:var(--fs-eyebrow);color:var(--muted);line-height:1.4}
/* tile A — one large feature */
.proj--feature{grid-column:span 7}
.proj--feature .proj__img{aspect-ratio:3/2}
.proj--feature .proj__cap{padding:var(--s4) var(--s5);border-top:var(--bw-rule) solid var(--orange)}
/* tile B — three compact thumb rows, stacked */
.proj-stack{grid-column:span 5;display:grid;gap:var(--gap-grid);align-content:space-between}
.proj--row{display:grid;grid-template-columns:var(--thumb) minmax(0,1fr);align-items:center}
.proj--row .proj__img{aspect-ratio:1;border-bottom:0;border-right:var(--bw) solid var(--ink);height:100%}
/* tiles C/D — two unequal mediums */
.proj--wide{grid-column:span 5}
.proj--wide .proj__img{aspect-ratio:16/9}
.proj--mid{grid-column:span 4}
.proj--mid .proj__img{aspect-ratio:4/3}
/* tile E — solid ORANGE promise plate (pushes color into the light band) */
.proj-note{grid-column:span 3;background:var(--orange);color:var(--on-color);
  border:var(--bw) solid var(--warm-dark);padding:var(--s5);
  display:flex;flex-direction:column;gap:var(--s3)}
.proj-note .eyebrow{color:var(--on-color)}
.proj-note__big{font-family:var(--f-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-h3);line-height:1;letter-spacing:var(--ls-tight);color:var(--on-color)}
.proj-note p{font-size:var(--fs-small);color:var(--on-color)}
.proj-note__link{margin-top:auto;font-family:var(--f-mono);font-weight:500;
  text-transform:uppercase;letter-spacing:var(--ls-btn);font-size:var(--fs-eyebrow);
  color:var(--on-color);display:inline-flex;align-items:center;gap:var(--s2);
  border-bottom:var(--bw) solid var(--on-color);padding-bottom:var(--s1);align-self:flex-start}
.proj-note__link:hover{border-bottom-color:var(--near-black)}
@media (max-width:900px){
  .proj--feature,.proj-stack,.proj--wide,.proj--mid,.proj-note{grid-column:1 / -1}
}
@media (max-width:400px){
  .proj--row{grid-template-columns:1fr}
  .proj--row .proj__img{aspect-ratio:16/9;border-right:0;border-bottom:var(--bw) solid var(--ink)}
}

/* ============ PROCESS (GUNMETAL — the technical ground) ============ */
.proc-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0}
.step{position:relative;padding:var(--s6) var(--s5) var(--s5);
  border-top:var(--bw-rule-md) solid var(--orange)}
.step + .step{border-left:var(--bw-hair) solid var(--gun-line)}
.step__code{display:inline-block;font-family:var(--f-mono);font-weight:500;
  text-transform:uppercase;letter-spacing:var(--ls-wide);font-size:var(--fs-eyebrow);
  color:var(--on-color);background:var(--orange);padding:var(--s1) var(--s2);
  line-height:1.3;margin-bottom:var(--s3)}
.step__num{font-family:var(--f-display);font-weight:700;font-size:var(--fs-step);
  line-height:.85;color:var(--on-color);margin-bottom:var(--s4)}
.step__title{font-family:var(--f-display);font-weight:600;text-transform:uppercase;
  font-size:var(--fs-h4);letter-spacing:var(--ls-head);color:var(--on-color);margin-bottom:var(--s2)}
.step__desc{font-size:var(--fs-small);color:var(--on-color-2);max-width:26ch}
@media (max-width:820px){.proc-grid{grid-template-columns:1fr 1fr}
  .step + .step{border-left:0}.step:nth-child(even){border-left:var(--bw-hair) solid var(--gun-line)}}
@media (max-width:460px){.proc-grid{grid-template-columns:1fr}
  .step:nth-child(even){border-left:0}}

/* ============ TESTIMONIALS (bone) ============ */
.rev-top{display:grid;grid-template-columns:.85fr 1.15fr;gap:var(--gap-wide);
  align-items:end;margin-bottom:var(--s7)}
.rating{position:relative;border:var(--bw) solid var(--ink);background:var(--orange);
  color:var(--on-color);padding:var(--s7) var(--s5) var(--s5);box-shadow:var(--sh-ink-sm)}
.rating__tab{position:absolute;top:calc(var(--bw) * -1);left:calc(var(--bw) * -1);
  background:var(--near-black);color:var(--on-color);
  font-family:var(--f-mono);font-weight:500;letter-spacing:var(--ls-mono);
  font-size:var(--fs-nano);padding:var(--s1) var(--s2h);line-height:1}
.rating__score{display:flex;align-items:baseline;gap:var(--s3)}
.rating__num{font-family:var(--f-display);font-weight:700;font-size:var(--fs-rating);
  line-height:.85;color:var(--on-color)}
.rating__of{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-label);color:var(--on-color)}
.rating__stars{color:var(--on-color);width:var(--stars-w);height:var(--stars-h);margin:var(--s3) 0}
.rating__count{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-nav);font-size:var(--fs-eyebrow);color:var(--on-color)}
.rev-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap-grid)}
.rev{background:var(--paper);border:var(--bw) solid var(--ink);
  border-top:var(--bw-rule) solid var(--orange);
  padding:var(--s5);display:flex;flex-direction:column;gap:var(--s4)}
.rev__stars{color:var(--orange-deep);width:var(--stars-w-sm);height:var(--stars-h-sm)}
.rev__quote{font-size:var(--fs-body);color:var(--ink);line-height:1.5}
.rev__who{margin-top:auto;font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-cap);font-size:var(--fs-eyebrow);color:var(--muted)}
.rev__who b{color:var(--ink);font-weight:500}
@media (max-width:900px){.rev-top{grid-template-columns:1fr}.rev-grid{grid-template-columns:1fr}}

/* ============ FINAL CTA (full orange) ============ */
.final{text-align:center}
.final .eyebrow{color:var(--on-color);letter-spacing:var(--ls-hero);display:block;margin-bottom:var(--s4)}
.final h2{font-family:var(--f-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-cta);line-height:1;letter-spacing:var(--ls-tight);
  color:var(--on-color);max-width:18ch;margin:0 auto var(--s5)}
.final p{color:var(--on-color);font-size:var(--fs-body-lg);max-width:50ch;margin:0 auto var(--s7)}
.final__actions{display:flex;flex-wrap:wrap;gap:var(--s4);justify-content:center}
.final__bar{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--bw);
  background:var(--warm-dark);border:var(--bw) solid var(--warm-dark);
  margin-top:var(--s8);text-align:left}
.fcell{background:var(--near-black);padding:var(--s5);display:flex;flex-direction:column;gap:var(--s1)}
.fcell__k{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-wide);font-size:var(--fs-eyebrow);color:var(--on-color-2)}
.fcell__v{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-label);
  letter-spacing:var(--ls-cap);color:var(--on-color)}
a.fcell__v:hover{border-bottom:var(--bw) solid var(--orange)}
.final__fine{margin-top:var(--s6);font-family:var(--f-mono);font-weight:500;
  text-transform:uppercase;letter-spacing:var(--ls-cap);font-size:var(--fs-eyebrow);
  color:var(--on-color);line-height:1.6}
@media (max-width:640px){.final__bar{grid-template-columns:1fr}}

/* ============ FOOTER (near-black) ============ */
.foot{background:var(--near-black);color:var(--on-color-2);padding-block:var(--s8) var(--s6)}
.foot__grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1.1fr;gap:var(--s7)}
.foot__logo{height:var(--logo-h);width:auto;margin-bottom:var(--s4)}
.foot__brandline{font-size:var(--fs-small);color:var(--on-color-2);max-width:34ch;line-height:1.6}
.foot h4{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-mono);font-size:var(--fs-eyebrow);color:var(--on-color);
  border-top:var(--bw-rule) solid var(--orange);padding-top:var(--s3);margin-bottom:var(--s4)}
.foot ul li{margin-bottom:var(--s3)}
.foot a:hover{color:var(--on-color)}
.foot__nap{font-style:normal;font-size:var(--fs-small);line-height:1.8;color:var(--on-color-2)}
.foot__nap b{color:var(--on-color);font-weight:500}
.foot__bar{max-width:var(--wrap);margin:var(--s8) auto 0;
  padding:var(--s5) var(--pad-x) 0;border-top:var(--bw-hair) solid var(--char);
  display:flex;flex-wrap:wrap;gap:var(--s3) var(--s5);
  justify-content:space-between;align-items:center}
.foot__bar span{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-nav);font-size:var(--fs-eyebrow);color:var(--on-color-2)}
/* on orange, muted white (.82) drops to 3.65:1 — this sits on orange so it stays pure white */
.foot__bar span.foot__lic{background:var(--orange);color:var(--on-color);padding:var(--s1h) var(--s3)}
@media (max-width:860px){.foot__grid{grid-template-columns:1fr 1fr}}
@media (max-width:520px){.foot__grid{grid-template-columns:1fr}}

/* ============ FIXED CORNER LABEL ============ */
.corner-label{position:fixed;right:0;bottom:0;z-index:300;background:var(--orange);
  color:var(--on-color);font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-mono);font-size:var(--fs-nano);padding:var(--s1h) var(--s3);
  pointer-events:none;box-shadow:var(--sh-corner)}

/* reduced motion */
@media (prefers-reduced-motion:reduce){
  *{transition:none!important}
  .svc:hover .svc__img img{transform:none}
}

/* OWNER RULE 2026-07-23: NO black/near-black anywhere inside orange sections.
   Structure comes from WHITE dividers + DEEP-ORANGE fills only. Tokens only. */
.band-orange .strip__grid{background:var(--on-color);border-color:var(--on-color)}
.band-orange .final__bar{background:var(--on-color);border-color:var(--on-color)}
.band-orange .fcell{background:var(--orange-deep)}
.band-orange .btn--ink{background:var(--on-color);color:var(--orange-deep);border-color:var(--on-color);box-shadow:var(--s1) var(--s1) 0 var(--orange-deep)}
.band-orange .btn--ink:hover{background:var(--bone);color:var(--orange-deep);border-color:var(--bone);box-shadow:var(--s1) var(--s1) 0 var(--orange-deep)}
.band-orange .btn--ghost:hover{background:var(--on-color);color:var(--orange-deep);border-color:var(--on-color)}

/* OWNER RULE 2026-07-23: NO blue on the hero — it was blocking the photo.
   Solid steel slab -> light neutral veil so the roof photo reads through. */
:root{--hero-veil:rgba(14,14,14,.38);--hero-veil-line:rgba(255,255,255,.22)}
.hero__panel{background:var(--hero-veil);border-color:var(--hero-veil-line)}

/* Real DeHart logo asset in the nav (replaces the text wordmark). */
.brand__logo{height:34px;width:auto;display:block;flex:none}
@media (max-width:640px){.brand__logo{height:28px}}

/* OWNER 2026-07-23: nav bar ORANGE + primary CTA BLUE.
   Nav is now an orange surface, so the no-black rule applies to it:
   black shadow -> deep orange, dark toggle border -> white,
   .82 white links -> pure white (.82 = 3.65:1, fails AA on orange). */
.nav{background:var(--orange);border-bottom-color:var(--on-color)}
.nav a{color:var(--on-color)}
.nav__toggle{border-color:var(--on-color)}
.nav a:focus-visible,.nav button:focus-visible{outline-color:var(--on-color)}

/* Logo bigger + centered in the nav bar: links left | logo centre | phone right */
.nav__in{display:grid;grid-template-columns:1fr auto 1fr;align-items:center}
.nav__links{grid-column:1;grid-row:1;justify-self:start}
.brand{grid-column:2;grid-row:1;justify-self:center;margin-right:0}
.nav__cta{grid-column:3;grid-row:1;justify-self:end}
.brand__logo{height:48px}
@media (max-width:640px){.brand__logo{height:36px}}

/* Two-tier header: deep-orange utility strip + main orange bar.
   Logo given real breathing room; links trimmed to 3; phone is a weighted call block. */
:root{--rule-on-color:rgba(255,255,255,.22)}
.nav__util{background:var(--orange-deep);border-bottom:1px solid var(--rule-on-color)}
.nav__util-in{max-width:var(--wrap);margin:0 auto;padding:var(--s2) var(--s6);display:flex;gap:var(--s6);align-items:center;justify-content:center;flex-wrap:wrap}
.util__item{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--on-color);white-space:nowrap}
.nav__in{padding-top:var(--s5);padding-bottom:var(--s5);column-gap:var(--s7)}
.brand{padding:0 var(--s5)}
.brand__logo{height:56px}
.nav__phone{display:flex;align-items:center;justify-content:flex-end;line-height:1;text-decoration:none}
.nav__phone-v{font-family:var(--f-display);font-weight:600;font-size:var(--fs-h4);letter-spacing:var(--ls-display);color:var(--on-color)}
@media (max-width:920px){.nav__util-in{gap:var(--s4)}.util__item:nth-child(2){display:none}}
@media (max-width:640px){
.brand__logo{height:40px}
.nav__in{padding-top:var(--s4);padding-bottom:var(--s4);column-gap:var(--s4)}
.nav__util-in{padding:var(--s1) var(--s4);gap:var(--s3)}
.util__item{font-size:var(--fs-nano)}
.util__item:nth-child(3){display:none}
.nav__phone-v{font-size:var(--fs-body)}
/* mobile: promote toggle + phone to grid items so it reads toggle | logo | phone */
.nav__cta{display:contents}
.nav__toggle{grid-column:1;grid-row:1;justify-self:start}
.nav__phone{grid-column:3;grid-row:1;justify-self:end}
}


/* R1 · SITE SIGN — Maximum section colour-blocking — every band its own saturated ground */

/* ============================================================
   R1 — SITE SIGN.  Maximum section colour-blocking.
   Every band is its own saturated ground, edge to edge, like
   highway construction signage. This is the direct answer to
   "lack of colour accents or section colouring".
   ============================================================ */
:root{
  --warm:#14375e; --warm-2:#1b4778; --warm-line:#2f6ba6;   /* cobalt */
  --gun:#7a2f16;  --gun-2:#8f3a1d;  --gun-line:#b0522f;    /* burnt clay */
  /* #81 (2026-10-13): the tokens say what renders. --cream IS the light-gray cream band (#f3f4f5, = --cream-bright since the
     2026-07-28 bright pass). --bone stays the warm tint (page ground behind the bands, disabled fields, a hover); the
     .band-bone BAND paints --paper, so it is white, not bone. */
  --cream:#f3f4f5;                                          /* cream band */
  --bone:#f2e9d8;                                           /* warm tint (not a band colour) */
  --bw-rule-lg:6px;
}
/* every ground different, and none of them timid (#81: what each band renders after the bright pass) */
.band-bone{background:var(--paper);color:var(--ink)}
.band-cream{background:var(--cream);color:var(--ink)}
.band-warm{background:var(--warm);color:var(--on-color)}
.band-gun{background:var(--gun);color:var(--on-color)}
.band-dark{background:var(--near-black);color:var(--on-color)}
/* thick orange rule between every band so the blocking reads as deliberate */
.band + .band{border-top:6px solid var(--orange)}
.plate,.proj,.rev{border-width:3px}

/* ==== BEGIN R1 NAV DROPDOWN (_scripts/add-r1-nav-dropdown.py) ==== */
/* Services dropdown. Shared nav chrome, so it belongs in the system sheet —
   it was inline on the homepage only, which left it unstyled everywhere else. */
.nav__dd{position:relative;display:inline-block}
.nav__dd-trigger{display:inline-flex;align-items:center;gap:6px}
.nav__dd-caret{font-size:.7em;opacity:.75;transition:transform .15s}
.nav__dd:hover .nav__dd-caret,
.nav__dd:focus-within .nav__dd-caret{transform:rotate(180deg)}
.nav__dd-menu{position:absolute;top:calc(100% + 12px);left:-14px;min-width:266px;
  background:var(--near-black);border:var(--bw) solid var(--ink);
  box-shadow:var(--sh-black);padding:6px 0;margin:0;list-style:none;
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .14s ease,transform .14s ease,visibility .14s;z-index:500}
.nav__dd:hover .nav__dd-menu,
.nav__dd:focus-within .nav__dd-menu{opacity:1;visibility:visible;transform:none}
.nav__dd-menu a{display:block;padding:10px 18px;font-family:var(--f-mono);
  font-weight:500;font-size:var(--fs-micro);letter-spacing:var(--ls-mono);
  text-transform:uppercase;color:var(--on-color-2);text-decoration:none;
  white-space:nowrap}
.nav__dd-menu a:hover,
.nav__dd-menu a:focus{background:var(--orange);color:var(--on-color)}
/* AMBER, not brand orange: #cf4214 on --near-black is 4.08:1 (AA FAIL),
   #e47410 is 6.26:1. */
.nav__dd-all a{border-top:var(--bw-hair) solid var(--hair-on-dark);color:#e47410}
@media (max-width:900px){
  .nav__dd,.nav__dd-menu{display:block;position:static}
  .nav__dd-menu{opacity:1;visibility:visible;transform:none;border:none;
    box-shadow:none;min-width:0;padding:0 0 0 var(--s4)}
  .nav__dd-caret{display:none}
}
/* ==== END R1 NAV DROPDOWN ==== */

/* ==== BEGIN R1 SCROLL MARGIN (_scripts/add-r1-scroll-margin.py) ==== */
/* Anchor targets clear the sticky nav (141px desktop / 103px mobile, measured). */
[id]{scroll-margin-top:165px}
@media (max-width:900px){
  [id]{scroll-margin-top:124px}
}
/* ==== END R1 SCROLL MARGIN ==== */

/* ==== BEGIN R1 BRIGHT PASS (_scripts/restyle-r1-bright.py) ==== */
/* Grounds go white-dominant; saturated bands stay as bursts. Near-white gray
   keeps the band rhythm readable where two light bands meet. */
.band-bone{background:var(--paper)}
.band-cream{background:#f3f4f5}

/* Service-page hero: giant near-black condensed caps on white.
   section.* out-ranks the pages' inline .r1-head rules — no HTML edits. */
.band.r1-head{background:var(--paper);color:var(--ink)}
section.r1-head .display{color:var(--near-black);
  font-size:clamp(2.6rem,1.3rem + 6.5vw,5rem)}
section.r1-head .lead{color:var(--muted)}
section.r1-head .r1-crumb a{color:var(--muted)}
section.r1-head .r1-crumb span[aria-current]{color:var(--ink)}

/* The hero keeps its band-dark CLASS but renders white — its descendant
   ghost-button rules would otherwise paint white on white (1.00:1). */
.band-dark.r1-head .btn--ghost{color:var(--ink);border-color:var(--ink)}
.band-dark.r1-head .btn--ghost:hover{background:var(--ink);color:var(--on-color)}

/* Chunky square buttons: 3px borders, visible outline even on filled fills.
   Fill colors, white button text, and hover motion unchanged. */
.btn{border-width:3px}
.btn--primary,.btn--primary:hover{border-color:var(--near-black)}
.btn--ink{border-color:var(--on-color)}
/* ==== END R1 BRIGHT PASS ==== */

/* ---- 2026-07-31 improvements: financing chip + review links ---- */
.fin-chip{font-family:var(--f-mono);font-size:var(--fs-micro);
  letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--muted);
  margin:var(--s3) 0 0}
.fin-chip a{color:var(--orange-h);text-decoration-color:var(--orange)}
.rev-all{margin-top:var(--s5)}
.rev-all a{color:inherit;text-decoration-color:var(--orange)}

/* ============ PLACEMENT PASS · 2026-07-31 (post-launch critique) ============ */
/* header phone never wraps mid-number */
.nav__phone,.nav a[href^="tel:"]{white-space:nowrap}
/* stat strip: no empty tracks when a page ships 3 stats; no viewport overflow */
.strip__grid{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}
.stat{min-width:0}
.stat__num{overflow-wrap:break-word;font-size:min(var(--fs-stat),11vw)}
@media (max-width:720px){.strip__grid{grid-template-columns:1fr 1fr}}
/* form fine print (incl. the TCPA consent): 12px floor so legal text stays readable */
.formcard__fine{font-size:var(--fs-eyebrow);letter-spacing:.03em;line-height:1.55;margin-top:var(--s3)}
.formcard__fine+.formcard__fine{margin-top:var(--s2)}
.formcard .btn{min-width:0;white-space:normal}
/* 'not all roofers' band: top-align against the tall portrait, halve the gap
   to the closing paragraph */
.diff__top{align-items:start;margin-bottom:var(--s7)}
/* fig caption pins to the shared content edge, not the section corner */
.figcap{left:max(var(--pad-x),calc((100% - var(--wrap))/2 + var(--pad-x)))}
/* project mosaic: stack rows share height evenly (no 77px voids); bottom row
   is three EQUAL tiles instead of 5/4/3 ragged widths */
.proj-stack{align-content:stretch;grid-auto-rows:1fr}
.proj--wide,.proj--mid,.proj-note{grid-column:span 4}
/* no one-word orphans in review attributions; .nw = nowrap token helper */
.rev__who a{white-space:nowrap}
.nw{white-space:nowrap}
/* hero eyebrow chip stays one line on phones */
@media (max-width:480px){.hero__eyebrow{letter-spacing:.12em;font-size:.68rem}}

/* ============ LAPTOP BAND PASS · 2026-08-01 (921-1439px) ============ */
/* header: brand can never be crushed out; links compact instead.
   U-02: the band keeps the base '1fr auto 1fr' grid (L719). A 1fr track's auto minimum holds the nowrap
   link row inside column 1. The old 'minmax(0,1fr) max-content minmax(0,1fr)' tracks let the row run under
   .brand, which paints later in the DOM and took the clicks on 1-2 links. .nav__links{font-size:.8em} is
   gone too: every link sets its own size (.nav__links a, L277), so it changed nothing. */
@media (min-width:921px) and (max-width:1199.98px){
  .nav__in{column-gap:var(--s4)}
  .nav__links{gap:var(--s3)}
  .nav__links a{white-space:nowrap}
  .brand{padding-inline:var(--s3)}
  .brand__logo{height:44px;width:auto;max-width:none}
  :root{--fs-h1:clamp(2.5rem,1rem + 4vw,4.5rem)}
}
/* U-02: up to 999.98px the full row (links + logo + phone: 874px EN / 895px ES at full size) is wider than
   the content box (847px at 921), so the phone ran into the right gutter (ES 10px past the viewport at 921).
   Top-level links drop one type step (--fs-label to --fs-eyebrow) and the column gaps close to var(--s2).
   Dropdown items (.nav__dd-menu a) are not matched. */
@media (min-width:921px) and (max-width:999.98px){
  .nav__in{column-gap:var(--s2)}
  .nav__links>a,.nav__links .nav__dd-trigger{font-size:var(--fs-eyebrow)}
}
/* stat numerals scale with the band instead of pinning at the 1440 cap
   (the 11vw guard failed: flex min-width:auto — fixed here properly) */
@media (min-width:921px) and (max-width:1439.98px){
  .stat__num{font-size:min(var(--fs-stat),3vw);min-width:0}
  .stat__num>*{min-width:0}
}
@media (min-width:921px) and (max-width:1139.98px){
  .stat__label{font-size:calc(var(--fs-eyebrow)*.85);letter-spacing:.05em}
}

/* ============ TABLET/MOBILE PASS · 2026-08-01 (post 3-viewport audit) ============ */
:root{--tap:44px}
/* stat strips: tablet band wraps instead of clipping; mobile odd-count last
   stat spans the row (no empty white cell); numerals scale to band */
@media (min-width:721px) and (max-width:920.98px){
  .strip__grid{grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}
  .stat{min-width:0}
  .stat__num{font-size:min(var(--fs-stat),3vw);min-width:0}
  .stat__num>*{min-width:0}
}
@media (max-width:720px){
  .strip__grid>.stat:last-child:nth-child(odd){grid-column:1 / -1}
  .stat__num{font-size:min(var(--fs-stat),8vw)}
}
/* REGRESSION FIX: the placement-pass span-4 mosaic rule out-cascaded the
   900px stack — re-assert full-width tiles on small screens */
@media (max-width:900px){
  .proj--wide,.proj--mid,.proj-note{grid-column:1 / -1}
}
/* tap-target floor without visual change */
.nav__phone{min-height:var(--tap);display:inline-flex;align-items:center}
footer a{display:inline-block;padding-block:.65rem;margin-block:-.65rem}
.rev-all a{display:inline-block;padding-block:.65rem;margin-block:-.65rem}
/* columned lists never split an item; narrow cards fall to one column */
.plate__links>li,.plate ul>li{break-inside:avoid}
.plate__links a{overflow-wrap:break-word}
/* glossary never-skinned components: A-Z chips + byline row (.r1-note--byline: class hook, 2026-09-23 audit U-48) */
[aria-label="Jump to letter"]{display:flex;flex-wrap:wrap;gap:var(--s2);margin-block:var(--s5)}
[aria-label="Jump to letter"] a{display:inline-flex;align-items:center;justify-content:center;
  min-width:var(--tap);min-height:var(--tap);border:var(--bw) solid var(--ink);
  font-family:var(--f-mono);font-weight:500;text-decoration:none}
.r1-note--byline{display:flex;flex-wrap:wrap;align-items:center;
  column-gap:var(--s3);row-gap:var(--s1)}
/* form card fills the tablet band */
@media (min-width:721px) and (max-width:920.98px){
  .formcard{max-width:none}
}
/* odd-count 2-col step grids: last step spans the row */
@media (min-width:461px) and (max-width:920.98px){
  .proc-grid>.step:last-child:nth-child(odd){grid-column:1 / -1;border-left:0}
}
/* homepage tablet composition: full-bleed diff photo (through 900); steps 2x2 through 920 */
@media (min-width:721px) and (max-width:900px){
  .diff__media,.diff__media img{width:100%;max-width:none}
}
@media (min-width:461px) and (max-width:920.98px){
  .proc-grid{grid-template-columns:1fr 1fr}
  .step+.step{border-left:0}
  .step:nth-child(even){border-left:var(--bw-hair) solid var(--gun-line)}
}
/* nav drawer: dim the page behind; submenu rows reach tap height.
   U-58: the scrim is a body-level element (.nav-scrim, created by the nav script) stacked just under the sticky
   header (z-index 100). As a ::before of the drawer it painted inside the header's stacking context: it dimmed
   the orange bar and a tap on it hit #navlinks. */
.nav-scrim{display:none}
@media (max-width:920px){
  .nav-open .nav-scrim{display:block;position:fixed;inset:0;z-index:99;background:var(--scrim)}
  .nav__dd-menu a{padding-block:var(--s3);min-height:var(--tap)}
}

/* long text-stats scale down so they never clip (numerals keep full size) */
.stat__num--long{font-size:clamp(1.25rem,1.9vw + .6rem,2.1rem);line-height:1.05}

/* ---- AUDIT FIX PASS 2026-08-03 — shared primitives for the page rebuilds ---- */
/* corner badge opposite .plate__tab (Most Popular / $400 Value) */
.fin-pop{position:absolute;top:calc(var(--bw) * -1);right:calc(var(--bw) * -1);
  background:var(--near-black);color:var(--on-color);font-family:var(--f-mono);
  font-weight:500;text-transform:uppercase;letter-spacing:var(--ls-wide);
  font-size:var(--fs-micro);padding:var(--s1h) var(--s3);line-height:1}
/* fixed column counts for known card counts — auto-fit orphans a 3rd/4th card
   at laptop widths. Triple compound out-specifies the placement-pass auto-fit. */
@media (min-width:921px){
  .r1-prose .r1-plates.r1-plates--3up,.r1-plates.r1-plates--3up{grid-template-columns:repeat(3,1fr)}
  .r1-prose .r1-plates.r1-plates--2up,.r1-plates.r1-plates--2up{grid-template-columns:repeat(2,1fr)}
}
/* display numeral inside a plate (heroes $500 stat) */
.hd-stat{font-family:var(--f-display);font-weight:700;line-height:.85;
  font-size:clamp(3.75rem,7vw,6rem);color:var(--orange-deep);margin:0 0 var(--s3)}
/* Gallery cards: the per-card <a> carries an inline `height:100%`, so inside the
   flex-column card it stretched and shoved the label/title down by 70-90px, and
   mixed source aspect ratios put every card's label at a different height.
   Pin one ratio and stop the anchor stretching — rows align again.
   `!important` is required: the height lives in a style attribute. */
.r1-gallery .lg-card > a{height:auto !important}
.r1-gallery .lg-card img{aspect-ratio:4/3;object-fit:cover;height:auto}
/* Contact page: map + hours as a real two-column row (the map was a bare div
   rendering as a ~320px thumbnail with the band's right half empty). */
.ct-maprow{display:grid;grid-template-columns:1.35fr 1fr;gap:var(--s5);align-items:stretch}
.ct-map{border:var(--bw) solid var(--ink);background:var(--paper);min-height:340px}
.ct-map iframe{display:block;width:100%;height:100%;min-height:340px;border:0}
@media (max-width:900px){.ct-maprow{grid-template-columns:1fr}}
/* thank-you page: check mark + button row */
.ty-check{color:var(--orange);margin-bottom:var(--s3)}
.ty-check svg{display:block}
.ty-actions{display:flex;flex-wrap:wrap;gap:var(--s3)}
/* blog listing: post-count badge beside a category heading (was same-size
   heading text, reading as one garbled title: "HOW-TO GUIDES 10 POSTS") */
.cat-count{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--muted);
  vertical-align:middle;margin-left:var(--s3)}
/* The CTA trailing line is a footnote, not a second headline. At --fs-eyebrow
   in full-strength white it competed with the h2 and read as two more lines of
   shouting under the buttons. Drop to micro + 82% white so it settles into the
   footnote role the copy is actually playing. */
/* #15 (2026-10-11): `.final p` (0,1,1) out-ranked the bare .final__fine (0,1,0), so the footnote rendered
   as 19px all-caps. Scoped to the band so it wins; set as readable body type, not shouted mono caps. */
.final .final__fine{font-family:var(--f-body);font-weight:400;text-transform:none;letter-spacing:normal;
  font-size:var(--fs-small);line-height:1.5;color:var(--on-color);max-width:62ch;margin:var(--s6) auto 0}
/* Spanish project captions run longer than the English originals ("Condado de
   Stanislaus" vs "Stanislaus County") and clipped their caption bar. Let the
   caption wrap instead of truncating. */
.proj__cap{flex-wrap:wrap;row-gap:var(--s1h)}
.proj__meta{min-width:0}
/* A stat whose value is a long word still clipped at the 768 band; give the
   long variant one more step down there. */
@media (max-width:820px){.stat__num--long{font-size:clamp(1.05rem,2.6vw,1.6rem)}}
/* Wide tables scroll horizontally (the table element is itself the scroller via
   the placement block), but on a phone the extra columns simply vanished past
   the right edge with no cue that anything was there. Classic local/scroll
   background-attachment trick: the edge shadows appear ONLY while there is
   still content to scroll toward, and disappear at each end. Pure CSS, no JS,
   and it costs nothing on tables that already fit. */
.r1-prose table{
  background-image:
    linear-gradient(to right, var(--ground, var(--paper)) 30%, transparent),
    linear-gradient(to left,  var(--ground, var(--paper)) 30%, transparent),
    radial-gradient(farthest-side at 0 50%, rgba(10,10,10,.28), rgba(10,10,10,0)),
    radial-gradient(farthest-side at 100% 50%, rgba(10,10,10,.28), rgba(10,10,10,0));
  background-repeat:no-repeat;
  background-size:36px 100%,36px 100%,14px 100%,14px 100%;
  background-position:left center,right center,left center,right center;
  background-attachment:local,local,scroll,scroll;
}

/* ============ RE-AUDIT FIX PASS · 2026-08-03 ============ */
/* STAT ROW BASELINE (48 findings / 37 pages).
   .stat is a flex column, so each label sits directly under its own numeral.
   Long text values ("4.2 ★ · 51", "Workmanship") render at a reduced size via
   .stat__num--long, which made their labels float ~15px above the labels in
   the sibling cells and broke the row's baseline grid. Pushing the label to
   the bottom of the (equal-height) cell aligns every label regardless of how
   tall its numeral is. */
.stat__label{margin-top:auto}
.stat__num{align-items:flex-end}

/* NAV WRAP (40 findings / 40 pages).
   "SERVICE AREAS" wrapped to two lines on interior pages, leaving that nav
   slot vertically ragged. nowrap was only set inside the 921-1199 laptop band;
   the wrap happens above it too. Apply across the whole desktop nav. */
@media (min-width:921px){.nav__links a{white-space:nowrap}}

/* Role line under a leadership card name (About). */
.team-role{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--muted);
  margin-top:calc(var(--s2) * -1)}
/* Two-column list for short link lists. `max-width:none` is required: the
   74ch cap on .r1-prose children otherwise pins it to ~755px and the second
   column never gets room. */
@media (min-width:900px){
  .r1-prose ul.cols2{max-width:none;columns:2;column-gap:var(--s7)}
  .cols2 > li{break-inside:avoid}
}

/* ── NAV OVERFLOW CONTAINMENT (2026-08-06) ───────────────────────────────
   MOBILE — this fixes a LIVE BUG that predates the six services added below.
   .nav__links is position:absolute inside a position:sticky header, so page
   scroll does not move it (verified: scrollTo(0,400) left the panel at the
   same viewport offset). At 375x812 the last four links — Financing @856,
   Reviews @910, About @964, Espanol @1018 — sat below the fold with
   max-height:none / overflow:visible and were simply unreachable.
   Cap the panel to the space beneath the header and let it scroll.

   DESKTOP — .nav__dd-menu had max-height:none / overflow:visible. At 1280x720
   the 12-item menu measured 492px from a top of 110px (~41px/item); 18 items
   projects to 738px, bottom at 848px against a 720px viewport. Two columns
   keeps it to ~9 rows. `columns` works here because .nav__dd-menu is a plain
   block <ul> — it would have been silently ignored on a flex container. */
@media (max-width:920px){
  /* U-57(b): the panel hangs from the sticky header's padding edge (top:100%; .nav is its containing block), so a
     percentage max-height resolves against the header's real height at every breakpoint and the panel ends at the
     viewport bottom (101px matched only the <=640 header; at 641-920 the panel ran 37.6px past the viewport) */
  .nav__links.open{max-height:calc(100dvh - 100%);overflow-y:auto;
    -webkit-overflow-scrolling:touch;overscroll-behavior:contain}
}
@media (min-width:921px){
  .nav__dd-menu{columns:2;column-gap:0;min-width:544px;
    max-height:calc(100vh - 140px);overflow-y:auto}
  .nav__dd-menu li{break-inside:avoid}
  .nav__dd-menu li.nav__dd-all{column-span:all}
}

/* ==== AUDIT FIX PASS 2026-09-23 ============================================
   Site audit fixes, appended LAST so they win ties against every earlier pass.
   One sub-section per fix cluster; each rule names its finding id. ==== */

/* ---- A1-nav ---- */
/* ---- A1 NAV: mobile drawer, Services disclosure + dropdown, toggle (U-57, MO-03, MO-02, U-03, FP-04) ----
   Companion to the shared nav script (<script id="r1-nav">, _scripts/inject-r1-nav.py). The scrim (U-58) and the
   drawer max-height (U-57b) are fixed in place: TABLET/MOBILE PASS and NAV OVERFLOW CONTAINMENT. */
/* MO-02: the drawer's Services disclosure button (created by the nav script) exists only in the <=920 drawer */
.nav__dd-btn{display:none}
@media (max-width:920px){
  /* U-57(a)/MO-03(a): the drawer spans the header. L720 .nav__links{justify-self:start} also aligned the absolutely
     positioned panel, which shrink-wrapped to 188-311px on the side opposite the toggle */
  .nav__links{justify-self:stretch}
  /* MO-03(b): a drawer item scrolled into view by Tab keeps its focus ring (3px + 2px offset) inside the scrollport */
  .nav__links.open{scroll-padding-block:var(--s2)}
  /* U-57(c)/MO-03(c): the submenu is in-flow through 920, not 900 (the frozen dropdown block and the removed homepage
     copies switched at 900, so at 901-920 a hidden absolute menu left 467px of empty scroll and 101px of sideways
     scroll). The .nav__links prefix outranks the homepage inline .nav__dd rules (0,1,0), which load after this sheet */
  .nav__links .nav__dd,.nav__links .nav__dd-menu{display:block;position:static}
  /* U-57(c): same 920 switch for the frozen block's 900px drawer-list look (visible, flat, indented) */
  .nav__links .nav__dd-menu{opacity:1;visibility:visible;transform:none;border:none;
    box-shadow:none;min-width:0;padding:0 0 0 var(--s4)}
  /* U-57(c): the desktop caret is not used in the drawer (the disclosure button carries its own) */
  .nav__links .nav__dd-caret{display:none}
  /* MO-03(d): the Services row divider spans the row (inline-flex shrank it to the label); no-JS fallback row */
  .nav__links .nav__dd-trigger{display:flex}
  /* MO-02: Services is a collapsed disclosure (button, aria-expanded) so the six primary links and the language link
     sit in the first view. Same type and row rule as the drawer links (L296) */
  .nav__links .nav__dd-btn{display:flex;align-items:center;justify-content:space-between;
    gap:var(--s3);width:100%;margin:0;padding:var(--s4) 0;background:none;border:0;
    border-bottom:var(--bw-hair) solid var(--dark-line-soft);font:inherit;font-family:var(--f-mono);
    font-weight:500;font-size:var(--fs-label);letter-spacing:var(--ls-nav);text-transform:uppercase;
    text-align:left;color:var(--on-color);cursor:pointer}
  /* MO-02: the button replaces the hub link in the drawer; the hub stays one tap away as the submenu's "View All" row */
  .nav__links .nav__dd--btn > .nav__dd-trigger{display:none}
  /* MO-02: collapsed by default (the script resets aria-expanded to false each time the drawer opens) */
  .nav__dd-btn[aria-expanded="false"] ~ .nav__dd-menu{display:none}
  /* MO-02: the button's caret turns with its state (reduced motion: the global transition:none pass applies) */
  .nav__dd-btn-caret{transition:transform .15s}
  /* MO-02: expanded state of the caret */
  .nav__dd-btn[aria-expanded="true"] .nav__dd-btn-caret{transform:rotate(180deg)}
}
@media (min-width:921px){
  /* U-03: hover bridge over the 12px gap between the trigger and the menu (top:calc(100% + 12px) in the frozen
     dropdown block). On the parent: a ::before on the menu is clipped by the menu's own overflow:auto */
  .nav__dd:hover::after{content:"";position:absolute;left:0;right:0;top:100%;height:14px}
  /* U-03: Escape (nav script) dismisses the open dropdown until the pointer or keyboard focus comes back to it, or
     both have left it. (0,4,0) outranks .nav__dd:hover/:focus-within .nav__dd-menu (0,3,0) here and in the homepage
     inline copy */
  .nav__links .nav__dd.is-dismissed .nav__dd-menu{opacity:0;visibility:hidden;transition:none}
  /* U-03: the dismissed menu's caret points down again */
  .nav__links .nav__dd.is-dismissed .nav__dd-caret{transform:none}
}
/* FP-04: forced colours repaint the background-drawn toggle bars (L288) as Canvas on a Canvas header; paint them
   CanvasText */
@media (forced-colors:active){.nav__toggle span{background:CanvasText}}

/* ---- A2-header ---- */
/* U-26: object-fit:contain keeps the header logo's aspect ratio. Below ~362px the auto brand track is
   narrower than the logo, so img{max-width:100%} (L165) clamps its width while height:40px (L739) holds,
   which squashed the PNG (43% at 320, 84% at 280). */
.brand__logo{object-fit:contain}
/* U-26: at 360px and below, the .brand side padding (24px, L733) and the header column gap (16px, L740) drop
   to var(--s2). The logo then keeps its natural 97.7x40 size down to 320 and is still 64px wide at 280.
   Toggle, logo and phone still fit, and the phone keeps its right edge (302px at 320). */
@media (max-width:360px){.brand{padding-inline:var(--s2)}.nav__in{column-gap:var(--s2)}}
/* U-31: the 17-item EN Services menu is columns:2 under max-height:calc(100vh - 140px) (L1077-1078). When
   that cap binds (viewports 512px tall or less), multicol pushes 'Roofing Warranty' sideways into a third
   column outside the 540px box. Up to 700px tall the menu is a 2-column grid instead, so the overflow scrolls
   vertically and nothing leaves the box. grid-auto-flow:column with 9 rows (ceil(17/2)) keeps the
   column-major order multicol gave; if the menu grows past 18 items, raise the row count to ceil(items/2).
   'View All' spans the row below. Taller viewports keep multicol, so their rendering is unchanged.
   :has(>li:nth-child(10)) leaves the 4-item ES menu on columns. 1fr (auto minimum), not minmax(0,1fr): the
   homepage menu is shrink-to-fit and stays as wide as its items. */
@media (min-width:921px) and (max-height:700px){
  .nav__dd-menu:has(>li:nth-child(10)){display:grid;grid-auto-flow:column;
    grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(9,auto)}
  .nav__dd-menu:has(>li:nth-child(10))>.nav__dd-all{grid-column:1 / -1;grid-row:10}
}

/* ---- A3-sticky ---- */
/* ---- A3 STICKY + SCROLL: U-04 (+ FP-07, FAQ-06) ---- */
/* U-04: --hdr-h = the sticky header's rendered height, on the header's own breakpoints (measured on all 92 URLs, EN + ES,
   every width 320-1600): 140.59px at 641-920 and >=1200 (56px logo), 128.59px at 921-1199.98 (44px logo, LAPTOP BAND
   PASS), 103px at <=640 (40px logo, two-tier <=640 block). If the header's height changes, change these numbers with it. */
:root{--hdr-h:141px}
@media (min-width:921px) and (max-width:1199.98px){:root{--hdr-h:129px}}
@media (max-width:640px){:root{--hdr-h:103px}}
/* U-04 (Phase 3 review css-01): the padding below is only safe together with the :has() rule that turns it off while
   focus is in the header. Browsers without :has() (Firefox <121 incl. ESR 115, Safari <15.4, Chromium <105) keep HEAD
   behaviour exactly: no padding, the frozen [id] margins. --hdr-h stays outside (the A1 drawer uses it). */
@supports selector(:has(*)){
/* U-04/FP-07/FAQ-06 (2.4.11): scroll-padding tells the viewport its top band is covered by the sticky .nav, so Shift+Tab,
   focus() on an invalid field, #fragment jumps and PageDown stop below the bar. + --s5 (24px) is the clearance: a text
   field's caret is what Chromium scrolls into view, 14px (2px border + 12px padding) below the field's top, and its 3px
   ring sits above that; display headings (line-height 1.04) draw their text box up to 10px above the heading's box. */
html{scroll-padding-top:calc(var(--hdr-h) + var(--s5))}
/* U-04: no padding while focus is inside the header. A stuck header item can never leave the padded band, so Chromium
   scrolled the page 180-500px each time focus reached the logo, phone, a drawer item or a dropdown item. */
:root:has(.nav :focus){scroll-padding-top:0}
/* U-04: neutralise the frozen R1 SCROLL MARGIN block ([id]{scroll-margin-top:165px}, 124px at <=900px). It would add to
   the padding, and it switches at 900px, not at the header's 920px. */
:root [id]{scroll-margin-top:0} /* (0,2,0): holds wherever the frozen block sits (A3 review) */
/* U-04: a same-page link in the header (#reviews on /) scrolls while it still has focus (the HTML spec scrolls to the
   fragment before moving focus), when the padding above is off; the target's own margin keeps it below the bar. */
:root:has(.nav :focus) [id]:not(.nav *){scroll-margin-top:calc(var(--hdr-h) + var(--s5))}
}

/* ---- B-forms ---- */
/* U-42: a select still showing its value="" placeholder option read as already answered (--ink, same as
   entered text); mute it like input placeholders (.field input::placeholder is --muted) until a choice is made */
.field select:has(option[value=""]:checked){color:var(--muted)}
/* U-42: the muted colour must not leak into the open option list: every choice stays --ink */
.field select option{color:var(--ink)}
/* U-42: /instant-quote/ estimator — the frozen page block's `#estimator input,#estimator select{background:…;
   padding:…}` (1,0,1) loads after this sheet and resets the chevron and its room; restate them at (1,1,1) */
#estimator .field select{
  background-image:linear-gradient(45deg,transparent 50%,var(--ink) 50%),
    linear-gradient(135deg,var(--ink) 50%,transparent 50%);
  background-position:right calc(var(--s3) + 6px) center,right var(--s3) center;
  background-size:6px 6px;background-repeat:no-repeat;
  padding-right:calc(var(--s3) + 20px)}
/* U-42: forced-colours mode drops gradient backgrounds, which would leave the select arrowless again;
   hand the arrow back to the platform there */
@media (forced-colors:active){.field select{appearance:auto}}
/* U-12: fine-print links (consent Privacy/Terms, financing) outside .r1-prose had only the base
   a{color:inherit;text-decoration:none} reset — same colour as the text, no underline (1.4.1, F73).
   Same treatment as .r1-prose a:not([class]); inside .r1-prose that (0,2,1) rule still wins with equal values */
.formcard__fine a{color:var(--orange-h);text-decoration:underline;text-underline-offset:2px;
  text-decoration-thickness:1px}
.formcard__fine a:hover{color:var(--ink);background:var(--stone)}

/* ---- C1-focus ---- */
/* ---- C1 focus + contrast ---- */
/* U-07: HQ service-area tile. The frozen service block colours the 'HQ' ::after --orange on the
   near-black tile (4.08:1) and never changes it when :hover paints the tile orange (1:1), and
   #r1-placement's flex-wrap shrink-wraps the tile so the label's margin-left:auto collapses
   ("TURLOCKHQ"). Put the orange on a chip ground instead: white label on --orange = 4.73:1 at
   rest and on hover; gap keeps it reading "TURLOCK HQ". (0,3,x) beats the inline (0,2,x). */
.r1-prose .r1-city.r1-city--hq{gap:var(--s2)}
.r1-prose .r1-city.r1-city--hq::after{color:var(--on-color);background:var(--orange);padding:0 var(--s1)}
/* U-08: a phone-width comparison table is itself the scroller (#r1-placement display:block +
   overflow-x:auto), so Chrome makes it a tab stop, but its outside ring is clipped by the
   overflow of its wrapper (.r1-scroll or an inline overflow div). Draw the ring inside the box. */
.r1-prose table:focus-visible{outline-offset:calc(var(--bw-rule-md) * -1)}
/* U-09: L181-185 turn the ring white on cobalt/clay/hero grounds, and that also reached links and
   buttons sitting on the white cards inside those bands (white ring on paper, 1.00:1). Cards keep
   the light-ground ring: --orange on --paper = 4.73:1. (0,4,1) beats (0,2,1). */
:is(.band-warm,.band-warm-2,.band-gun,.band-gun-2,.hero__panel)
  :is(.rev,.r1-note,.formcard,.plate:not(.plate--dark)) :is(a,button):focus-visible{outline-color:var(--orange)}
/* FP-02: the frozen /instant-quote/ block's `#estimator input,#estimator select{border:...ink}`
   (1,0,1) beats the .field focus border-color (0,2,1), leaving only the 28% halo (1.50:1).
   (1,1,1) restores the orange focus border (3.93:1 change against ink). */
#estimator :is(input,select):focus-visible{border-color:var(--orange)}
/* FP-03: forced-colours modes repaint the .nav/.foot grounds as Canvas but leave the white-on-
   transparent logo raster alone, so on a light Canvas the logo vanishes (header + footer, every
   page). Give the IMAGES their own brand backplate; the .brand link itself stays forced, so its
   system focus ring still draws (opting the link out would repaint that ring white). */
@media (forced-colors:active){
  .brand__logo{forced-color-adjust:none;background:var(--orange)}
  .foot__logo{forced-color-adjust:none;background:var(--near-black)}
}
/* FP-05: homepage rating graphic. The unfilled-star outline layer is white at opacity .55 on the
   orange panel (2.40:1). Paint it with the secondary on-colour token at full layer opacity
   (>=3:1, see evidence). CSS opacity overrides the SVG presentation attribute; the rating value,
   the clip width and the text are unchanged. */
.rating__stars > g[opacity]{opacity:1;color:var(--on-color-2)}

/* ---- C2-reflow ---- */
/* U-11 · .btn is white-space:nowrap (BUTTONS block), so on phone widths a long CTA label
   was wider than its column: the button (and on /es/ the whole hero grid track) ran past
   the viewport and html/body/.hero overflow clipping cut it off. Let the label wrap inside
   a button capped at its column (a wrapped label is centred, as a button label should be);
   tel: buttons keep the phone number on one line. */
@media (max-width:440px){
  .btn:not([href^="tel:"]){white-space:normal;max-width:100%;text-align:center}
  .btn{line-height:1.25} /* orchestrator (C2 review; Phase 3 css-02): wrapped 2-line labels need more than .btn
     line-height:1, and tel: buttons take the same value so a CTA pair stays one height */
}
/* U-15 + GAP-WL-05 · the only 8-cell stat strip keeps 8 columns from 921px (strip auto-fit
   minmax(0,1fr)); at 921-1099 its 104-125px cells are narrower than '50,000' / 'In-House',
   which ran up to the white divider. Four columns (two rows) in that band; from 1100 the
   single row fits (with 'In-House' on the --long scale). */
/* #87 (2026-10-11): from 1100 the single row of 8 broke 'In-House' / 'Flat-Rate' mid-tile; two rows of four at every desktop width */
@media (min-width:921px){
  .strip__grid:has(>.stat:nth-child(7)){grid-template-columns:repeat(4,1fr)}
}
/* U-15 · 2-column phone strips leave a ~91px content box at 320, narrower than a
   14-character mono label ('Certifications', 121px), which spilled out of its cell.
   Step the label down exactly as the 921-1139 laptop band already does. */
@media (max-width:380px){
  .stat__label{font-size:calc(var(--fs-eyebrow)*.85);letter-spacing:.05em}
}
/* U-20 + WS-05 + GAP-WL-03 · a step title that is one unbreakable run (a verification URL)
   was wider than its column and ran off-screen or over the next step's title. Break it
   rather than overflow. */
.step__title{overflow-wrap:anywhere}
/* ...except where the #r1-placement laptop band sets 5+ step grids to 5 columns
   (921-1139.98, its '.proc-grid:has(>.step:nth-child(5))' rule): those ~170px tracks leave
   ordinary long words ('PROFESSIONAL', 'WALKTHROUGH') a few px into the step padding, and
   breaking them mid-word would be worse than that invisible spill. */
@media (min-width:921px) and (max-width:1139.98px){
  .proc-grid:has(>.step:nth-child(5)) .step__title{overflow-wrap:normal}
}
/* U-59 · a <caption> is laid out at the full table-grid width, so inside a horizontally
   scrolling table it was cut at the phone viewport edge. Keep it within the visible width
   and in view while the table scrolls. */
.r1-prose table caption{position:sticky;left:0;max-width:calc(100vw - 2 * var(--pad-x))}
/* U-68 · content after a CTA row sat directly under the buttons' hard offset shadow
   (/es/ FAQ aside: the shadow fused with the review line). */
.svc-cta__act + *{margin-top:var(--s4)}
/* U-69 · the service block's reading cap (.r1-prose > *{max-width:74ch}) pinned the contact
   map + hours row to 74ch; it is a band-width component (see .ct-maprow). */
.r1-prose > .ct-maprow{max-width:none}
/* U-70 · the 14px-tall 'Leer en español' link sat 2.6px above the hero CTA, so its 24px
   target circle overlapped the button (2.5.8). Space the action row off the cert line. */
.r1-certline + .final__actions{margin-top:var(--s4)}

/* ---- C3-components ---- */
/* --- C3 components / type (U-13 WS-03 U-45 U-49 U-50 U-51 U-52 U-55 GAP-WL-01 GAP-WL-02 G-10) --- */
/* U-45 · the frozen company/location blocks set font-family:var(--f-head), a token the system never
   defined: invalid at computed-value time, so card titles and city prices fell back to body Plex. */
:root{--f-head:var(--f-display)}
/* U-13 · on /es/ the promise note sits INSIDE .proj-stack; the span-4 mosaic rule (L872) made it span 4
   implicit stack tracks, so the two thumb rows auto-placed side by side. One stack track. */
.proj-stack > .proj-note{grid-column:1 / -1}
/* U-13 + WS-03 · the thumb is aspect-ratio 1 (16/9 at <=400) with height:100% of a 1fr row, so a row
   taller than --thumb widened the photo past its track, under the chip and over the caption. */
.proj--row .proj__img{max-width:100%}
/* U-13 · with the note inside the stack (/es/), 1fr rows made each thumb row as tall as the note
   (portrait crops, empty caption boxes); size those rows to their content. */
.proj-stack:has(> .proj-note){grid-auto-rows:auto}
/* U-13 · where the stack column is the taller one (/es/ 901-1125) the grid stretches the feature tile;
   a column flex box lets its photo take that height instead of a void under the caption. */
.proj--feature{display:flex;flex-direction:column}
/* U-13 · the feature photo grows (object-fit:cover crops it); no-op where the feature is the taller item */
.proj--feature .proj__img{flex:1 0 auto}
/* U-49 · the cream-band ground exists only as a literal inside the frozen BRIGHT PASS block
   (.band-cream{background:#f3f4f5}, L821) and no token held it: name it so the cover can use it. */
:root{--cream-bright:#f3f4f5}
/* U-49 · the scroll-cue covers read --ground (L1019-1020, default --paper): cream bands */
.band-cream{--ground:var(--cream-bright)}
/* U-49 · white cards inside a cream band are a paper ground again */
.plate,.r1-note,.formcard{--ground:var(--paper)}
/* U-50 · Oswald ships no italic face, so an <em> in a display heading was a synthesised slant. */
h1 em,.display em{font-style:normal}
/* U-51 · native file button (UA grey chrome, Arial) -> ghost button in the field-label type */
.field input[type=file]::file-selector-button{appearance:none;margin-right:var(--s3);
  padding:var(--s2) var(--s3);border:var(--bw) solid var(--ink);background:transparent;
  color:var(--ink);font-family:var(--f-mono);font-weight:500;font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-btn);text-transform:uppercase;cursor:pointer}
/* U-51 · file button hover mirrors .btn--ghost:hover */
.field input[type=file]:hover::file-selector-button{background:var(--ink);color:var(--on-color)}
/* U-51 · UA <hr>: inset grey border, margin:auto centred it off the text edge at >=1024 */
.r1-prose hr{border:0;border-top:var(--bw-hair) solid var(--line);margin-inline:0}
/* U-51 · code/kbd/samp fell back to the UA monospace (Courier) */
code,kbd,samp{font-family:var(--f-mono);font-size:.9em}
/* U-52 · builder-stripped 'Quick answer'/'Bottom line' callouts are bare <aside> children of the prose:
   give them the .r1-note card. Child combinator only: bare asides nested in aside.r1-note stay unboxed. */
.r1-prose > aside:not([class]){max-width:none;background:var(--paper);
  border:var(--bw) solid var(--ink);border-left:var(--bw-rule-lg) solid var(--orange);
  padding:var(--s4) var(--s5);font-size:var(--fs-small)}
/* U-52 · the callout's leading <span> label reads as the .r1-note caption label */
.r1-prose > aside:not([class]) > span:first-child{display:block;margin-bottom:var(--s1h);
  font-family:var(--f-mono);font-weight:500;font-size:var(--fs-micro);
  letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--muted)}
/* U-52 · classless <dl> in prose rendered UA dt/dd stacks: the .prow ledger (mono label left, value
   right, hairline between rows). Covers bare dt/dd pairs and div-wrapped rows. 80ch = the frozen
   block's measure for p/ul/ol, so label and value never drift a full card-width apart. */
.r1-prose dl:not([class]){display:grid;grid-template-columns:minmax(0,1fr) auto;max-width:80ch}
/* U-52 · ledger row padding + hairline */
.r1-prose dl:not([class]) > :is(dt,dd,div){padding:var(--s2h) 0;
  border-top:var(--bw-hair) solid var(--line)}
/* U-52 · no hairline above the first row */
.r1-prose dl:not([class]) > :is(dt:first-child,dt:first-child + dd,div:first-child){border-top:0}
/* U-52 · value column gutter */
.r1-prose dl:not([class]) > dd{padding-left:var(--s5)}
/* U-52 · div-wrapped rows (es/contact) span the ledger and lay out like .prow */
.r1-prose dl:not([class]) > div{grid-column:1 / -1;display:flex;flex-wrap:wrap;
  justify-content:space-between;column-gap:var(--s5)}
/* U-52 · label = .prow dt */
.r1-prose dl:not([class]) dt{font-family:var(--f-mono);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-btn);font-size:var(--fs-eyebrow);color:var(--muted)}
/* U-52 · value = .prow dd */
.r1-prose dl:not([class]) dd{font-family:var(--f-body);font-weight:500;font-size:var(--fs-small);
  color:var(--ink);text-align:right}
/* U-55 · Oswald 700 accented capitals reach 1.075em above the baseline (N-tilde 1.048em), so the
   .98 / 1.02 leading drove the accents into the line above on the Spanish pages. */
:lang(es) .hero h1,:lang(es) .display{line-height:1.15}
/* GAP-WL-01 · >=921 the laptop pin gives 5 tracks to 5+ steps and 1fr 1fr to fewer (to 1139), the frozen
   auto-fit gives 5 from 1140: 6 steps rendered 5+1, 3 steps 2+1. Count-aware tracks; the first step of
   each wrapped row sits on the grid edge, so it carries no column rule. 7+ steps: the existing 5 tracks,
   pinned here so the 5n+1 row starts are exact. */
@media (min-width:921px){
  /* GAP-WL-01 · 3 steps (was 2+1 to 1139) and 6 steps (was 5+1) -> 3 tracks; (0,4,0) beats the BLOCK (0,3,0) */
  .proc-grid:has(>.step:nth-child(3):last-child),
  .proc-grid:has(>.step:nth-child(6):last-child){grid-template-columns:repeat(3,1fr)}
  /* GAP-WL-01 · step 4 of a 3+3 grid starts row 2 on the grid edge: no column rule */
  .proc-grid:has(>.step:nth-child(6):last-child)>.step:nth-child(4){border-left:0}
  /* GAP-WL-01 · 7+ steps keep the 5 tracks they already get (laptop pin / frozen auto-fit), pinned */
  .proc-grid:has(>.step:nth-child(7)){grid-template-columns:repeat(5,1fr)}
  /* GAP-WL-01 · steps 6, 11, … start a wrapped row on the grid edge: no column rule */
  .proc-grid:has(>.step:nth-child(7))>.step:nth-child(5n+1){border-left:0}
}
/* GAP-WL-01 · 921-1139: 4 steps render 2x2 (laptop pin 1fr 1fr), so step 3 starts a row on the grid
   edge and must not carry the column rule. */
@media (min-width:921px) and (max-width:1139.98px){
  /* GAP-WL-01 · 4-step 2x2: step 3 sits on the grid edge */
  .proc-grid:has(>.step:nth-child(4):last-child)>.step:nth-child(3){border-left:0}
}
/* G-10 · the estimator result (.plate__price) had no system rule and read as body text. Same property
   set as the frozen city rule, which loads later and still decides the 19 city plates. */
.plate__price{font-family:var(--f-display);font-weight:700;font-size:var(--fs-h3);
  color:var(--ink);margin:var(--s2) 0}

/* ---- D2-home-es-estimator ---- */
/* U-19 FAQ-02 FAQ-03 — .r1-faq (native <details> FAQ) promoted from the frozen per-page service block into the system sheet so /es/ (no service block) can use it; values byte-identical to the 90 frozen copies, which load later and still win ties, so those pages render unchanged */
.r1-faq{border-top:var(--bw) solid var(--ink)}
/* U-19 — item rule: hairline separator between questions */
.r1-faq__item{border-bottom:var(--bw-hair) solid var(--line)}
/* U-19 — question row: full-width summary, 44px tap height, ink on the band ground (replaces the legacy .faq-q button) */
.r1-faq__q{list-style:none;cursor:pointer;display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s4);padding:var(--s4h) 0;font-family:var(--f-display);font-weight:600;font-size:var(--fs-h4);text-transform:uppercase;letter-spacing:var(--ls-head);color:var(--ink);min-height:var(--tap)}
/* U-19 — hide the WebKit disclosure triangle (the +/– glyph below is the indicator) */
.r1-faq__q::-webkit-details-marker{display:none}
/* U-19 (1.4.11) — open/closed indicator in brand orange instead of the legacy #ccc-on-#fafaf9 chip */
.r1-faq__q::after{content:'+';flex:none;font-family:var(--f-mono);font-size:var(--fs-h4);line-height:1;color:var(--orange)}
/* U-19 — open state glyph */
.r1-faq__item[open] .r1-faq__q::after{content:'\2013'}
/* U-19 — open state question colour (AA dark orange) */
.r1-faq__item[open] .r1-faq__q{color:var(--orange-h)}
/* U-19 — hover colour (legacy hover used the undefined --orange-text) */
.r1-faq__q:hover{color:var(--orange-h)}
/* U-19 (2.4.7) — visible keyboard focus on the question */
.r1-faq__q:focus-visible{outline:var(--bw) solid var(--orange);outline-offset:2px}
/* U-19 (1.4.3) FAQ-02 (1.4.10) — answer text in --muted (AA on the bone ground); no max-height, so a narrowed viewport reflows instead of clipping; closed <details> content is not rendered, so its links leave the tab order */
.r1-faq__a{padding:0 0 var(--s5);font-size:var(--fs-body);line-height:1.65;color:var(--muted);max-width:68ch}
/* FAQ-03 (1.4.1) — answer links carry an underline, not colour alone */
.r1-faq__a a{color:var(--orange-h);text-decoration:underline;text-underline-offset:2px}

/* ---- Z2-phase3 ---- */
/* review markup-01 — the FAQ open/closed glyph is decoration: CSS alt text ('' after the slash) keeps '+' / '–' out of the
   accessible name ('…worth it? plus, collapsed'). (0,2,1)/(0,4,1) beat the frozen per-page copies (0,1,1)/(0,3,1) that load
   after this sheet. Browsers without alt-text support drop the declaration and keep the glyph from the rules above. */
.r1-faq .r1-faq__q::after{content:'+' / ''}
.r1-faq .r1-faq__item[open] .r1-faq__q::after{content:'\2013' / ''}
/* C3 review — paint the bright cream band from the token C3 introduced, so --cream-bright is the one source of truth
   (the frozen BRIGHT PASS block still carries the literal; same computed #f3f4f5). / and /es/ use .band-cream too since
   2026-10-02, when their inline direction-override block was removed. */
.band-cream{background:var(--cream-bright)}
/* U-27 — the footer tap-target floor (footer a{padding-block:.65rem;margin-block:-.65rem}) gives the phone and email links in
   the address block 46px hit boxes on a 25.2px line pitch, so they overlap and the later mailto link wins: taps on the lower
   third of the phone number opened email. Each NAP link keeps its own line box (~25px, >= the 24px 2.5.8 minimum); no visual
   change. (0,1,1) beats footer a (0,0,2); other footer links keep the padded floor. */
.foot__nap a{padding-block:0;margin-block:0}

/* ==== DESIGN PASS 2026-09-28 ==============================================
   Batch-2 design calls, appended LAST so they win ties against every earlier pass.
   One sub-section per design call. ==== */

/* ---- design/sticky-landscape ---- */
/* ---- B2 sticky-landscape (WS-02) ---- */
/* WS-02: on a short landscape viewport (landscape phones, and any desktop window or zoom level that leaves 500px of
   height or less) the two-tier sticky header was 140.6px (103px at <=640, 128.6px at 921-1199), 30-39% of the screen
   height, on all 92 pages. It stays sticky and compacts to one 62px row: the 44px menu toggle and the 44px phone link
   set the row height, with 8px of padding above and below. Portrait viewports and viewports taller than 500px are
   unchanged. */
@media (max-height:500px) and (orientation:landscape){
  /* WS-02: the utility strip (licence line, service area, hours) is hidden in this state only. The footer of every
     page carries the same licence line (checked on all 92 pages) */
  .nav__util{display:none}
  /* WS-02: the bar drops the --nav-h (68px) floor and the --s5/--s4 padding. The toggle and phone keep 44px tap boxes */
  .nav__in{min-height:0;padding-block:var(--s2)}
  /* WS-02: logo at the <=640 portrait size (40px; was 56px at 641-920 and >=1200, 44px at 921-1199) so it fits
     inside the 44px row. (0,2,0): the short-viewport state outranks the (0,1,0) .brand__logo height of every width
     band (LAPTOP BAND PASS and any band added later), wherever that band sits in the sheet */
  .nav .brand__logo{height:40px}
  /* WS-02: --hdr-h = the compact header's measured height (44px row + 2 x 8px padding + 2px border), keeping the A3
     scroll-padding and scroll-margin (U-04) in step with the bar. Change it if the bar's height changes. The A3 block
     lists the other three header states */
  :root{--hdr-h:62px}
}
@media (min-width:921px) and (max-height:500px) and (orientation:landscape){
  /* WS-02: the desktop Services dropdown (932x430 and 926x428 phones, short desktop windows) was capped at
     calc(100vh - 140px) (NAV OVERFLOW CONTAINMENT), which assumes the two-tier header. Under the compact bar the cap
     follows --hdr-h: the menu hangs 12px below the trigger and ends 11px above the viewport bottom (measured at
     932x430 and 1024x480; the whole EN menu fits from 1280x500). The <=920 drawer is not matched: calc(100dvh - 100%)
     already caps it against the header's real height (U-57b) */
  .nav__dd-menu{max-height:calc(100vh - var(--hdr-h) - var(--s2))}
}

/* ---- design/nav-panels ---- */
/* ---- U-23 nav-panels: 'View All Services' row of the desktop Services dropdown and the mobile drawer ----
   U-23 (item) / specificity override (mechanism): the frozen R1 NAV DROPDOWN block paints this row with the literal
   amber #e47410 at (0,1,1), and the EN + ES homepage inline copy repeats it after this sheet as var(--amber,#e47410),
   also (0,1,1). The near-black panels stay. No orange token reaches 4.5:1 as text on --near-black: --orange 4.08,
   --orange-h 3.25, --orange-deep 2.83, --gun-line 3.76. The row text therefore takes the system's dark-ground text
   token, --on-color (19.30:1, one step brighter than the --on-color-2 items above it). The orange moves to the row's
   top rule, which is non-text (4.08:1 against the panel, over the 3:1 non-text floor); its width is unchanged, so
   nothing moves. At (0,2,1) this rule outranks both copies. Hover and focus keep the frozen --orange fill with the
   same --on-color text (4.73:1), so neither state changes. */
.nav__links .nav__dd-all a{color:var(--on-color);border-top-color:var(--orange)}

/* ---- design/type-emphasis ---- */
/* TE-a (item: display-heading <em>, light grounds) / mechanism: U-50 set h1 em,.display em{font-style:normal} and nothing
   else, so the emphasised words render identical to the rest of the heading. This gives them the system's own emphasis
   treatment, the .diff__line em precedent: upright, in --orange. Every h1/.display outside the photo hero sits on --paper
   (90 pages, measured), where #cf4214 is 4.73:1 (AA-large needs 3:1; it also clears 4.5:1). The em is styled directly,
   so it beats the colour it used to inherit from section.r1-head .display. Specificity (0,0,2)/(0,1,1); no inline block
   sets em colour, so there is nothing to out-rank. */
h1 em,.display em{color:var(--orange)}

/* TE-a (item: display-heading <em>, on-dark variant) / mechanism: the photo hero's ground is the photograph under a
   0.38 near-black veil. Measured behind /es/ "desde 1975" (text hidden, every pixel), --orange scores 2.42-2.69:1 at the
   5th percentile and 1.77:1 at worst, at every width from 320 to 1440. That fails AA-large, so the words stay --on-color
   (8.4:1 at worst). The emphasis is an --orange stripe drawn behind the lower part of the letters. It is an underline with
   a negative offset: underlines paint under the glyphs, so the letters stay solid white, and white on --orange is 4.73:1.
   The stripe runs from 0.14em above the baseline to 0.02em below it. It stays clear of the accented capitals on the next
   line, which reach 0.075em below this baseline at the ES line-height of 1.15 (U-55); a stripe hung under the baseline
   touched the O-acute of "COTIZACION" at 320 and 1440. skip-ink:none keeps the stripe unbroken where it crosses the glyphs.
   (0,1,2) beats the light rule above. */
.hero :is(h1,.display) em{color:var(--on-color);text-decoration:underline var(--orange);
  text-decoration-thickness:.16em;text-underline-offset:-.14em;text-decoration-skip-ink:none}

/* U-32 (item: faux-bold mono) / mechanism: IBM Plex Mono loads 400 and 500 only. A <strong> inside the mono captions asks
   for more. It gets `bolder` (700) from the UA sheet, or 600 from the frozen `.r1-prose strong`. Both fall on the 500 face,
   so Chrome smears it into a synthetic bold. That hits 142 elements on 75 pages: the form fine print ("not"/"no"),
   the review bylines and the /projects/ figcaptions. This extends the system's existing `.rev__who b{color:var(--ink);
   font-weight:500}` to <strong> and to the other two mono contexts. The emphasis becomes the loaded 500 face plus --ink
   against the --muted caption. No new font request. (0,1,2) beats the frozen `.r1-prose strong` (0,1,1), which loads after
   the sheet. Every one of these contexts sits in a white card (.formcard/.rev/.plate), where --ink is the text rule. */
:is(.formcard__fine,.rev__who,.r1-prose figcaption) :is(strong,b){font-weight:500;color:var(--ink)}

/* ---- design/header-breakpoints ---- */
/* ---- B2 header-breakpoints (A2 follow-up) ----
   HB-01 (item): a large browser DEFAULT font size (Settings > Font size 20px / 24px; not zoom) pushed the header's
   phone link off-screen. Every header size is rem (links --fs-label, phone --fs-h4, utility strip --fs-micro), but
   the header breakpoints are px (920/921 drawer, 1199.98/1200 laptop band) and the header box is capped at the px
   --wrap. Measured on HEAD: at 20px the desktop row overflowed at 921-1100 (phone up to 111px past the viewport); at
   24px at 921-1440 (phone up to 273px off; logo also off and phone covered at 921-1024); from 1200 up, at any
   default of 17px or more, the row squeezed the logo to 0px wide.
   Mechanism: those breakpoints restated in em, which media queries resolve against the browser's default font size
   (57.5em = 920px, 75em = 1200px at 16px), plus a header box that is never narrower than its own row. Each em range
   starts where its px twin stops, so at a 16px default both ranges are empty and the header renders as before. At
   20px / 24px the tablet header (drawer) runs to 1150 / 1380px and the laptop band to 1500 / 1800px. Keep the copies
   in step with their px twins (named per rule) if those change. */

/* HB-01 laptop band, em twin of LAPTOP BAND PASS (921-1199.98): the tighter header row (gaps, 44px logo) that fits
   the desktop row, carried to 75em. Empty at 16px (74.99875em = 1199.98px). */
@media (min-width:1200px) and (max-width:74.99875em){
  /* HB-01: column gap, as LAPTOP BAND PASS .nav__in */
  .nav__in{column-gap:var(--s4)}
  /* HB-01: link gap, as LAPTOP BAND PASS .nav__links */
  .nav__links{gap:var(--s3)}
  /* HB-01: brand padding, as LAPTOP BAND PASS .brand */
  .brand{padding-inline:var(--s3)}
  /* HB-01: 44px logo that cannot be squeezed, as LAPTOP BAND PASS .brand__logo */
  .brand__logo{height:44px;width:auto;max-width:none}
}

/* HB-01 tablet header, em twin of every <=920px header rule: menu drawer + toggle, carried to 57.5em. Placed after
   the laptop band above so it wins every tie with it (at 24px the two overlap at 1200-1380). Empty at 16px
   (57.5em = 920px). */
@media (min-width:921px) and (max-width:57.5em){
  /* HB-01: drawer panel, as NAV <=920 .nav__links (L294). Its .nav__phone{display:none} is NOT copied: the later
     tap-target floor (.nav__phone{display:inline-flex}) overrides it at <=920, so the tablet header shows the phone */
  .nav__links{position:absolute;left:0;right:0;top:100%;flex-direction:column;
    align-items:stretch;gap:0;background:var(--near-black);
    border-bottom:var(--bw) solid var(--char);padding:var(--s3) var(--pad-x) var(--s5);
    display:none}
  /* HB-01: open state, as NAV <=920 */
  .nav__links.open{display:flex}
  /* HB-01: drawer rows, as NAV <=920 */
  .nav__links a{padding-block:var(--s4);border-bottom:var(--bw-hair) solid var(--dark-line-soft)}
  /* HB-01: the menu toggle, as NAV <=920 */
  .nav__toggle{display:flex}
  /* HB-01: two-item utility strip, as the two-tier header <=920 (L754) */
  .nav__util-in{gap:var(--s4)}
  /* HB-01: as the two-tier header <=920 (L754) */
  .util__item:nth-child(2){display:none}
  /* HB-01: page scrim behind the open drawer, as TABLET/MOBILE PASS <=920 (U-58) */
  .nav-open .nav-scrim{display:block;position:fixed;inset:0;z-index:99;background:var(--scrim)}
  /* HB-01: submenu rows reach tap height, as TABLET/MOBILE PASS <=920 */
  .nav__dd-menu a{padding-block:var(--s3);min-height:var(--tap)}
  /* HB-01: the drawer ends at the viewport bottom and scrolls, as NAV OVERFLOW CONTAINMENT <=920 (U-57b) */
  .nav__links.open{max-height:calc(100dvh - 100%);overflow-y:auto;
    -webkit-overflow-scrolling:touch;overscroll-behavior:contain}
  /* HB-01: full-width drawer, as A1-nav <=920 (U-57a) */
  .nav__links{justify-self:stretch}
  /* HB-01: focus ring stays inside the scrollport, as A1-nav <=920 (MO-03b) */
  .nav__links.open{scroll-padding-block:var(--s2)}
  /* HB-01: in-flow submenu, as A1-nav <=920 (U-57c) */
  .nav__links .nav__dd,.nav__links .nav__dd-menu{display:block;position:static}
  /* HB-01: flat drawer-list submenu, as A1-nav <=920 (U-57c) */
  .nav__links .nav__dd-menu{opacity:1;visibility:visible;transform:none;border:none;
    box-shadow:none;min-width:0;padding:0 0 0 var(--s4)}
  /* HB-01: no desktop caret in the drawer, as A1-nav <=920 (U-57c) */
  .nav__links .nav__dd-caret{display:none}
  /* HB-01: full-width no-JS Services row, as A1-nav <=920 (MO-03d) */
  .nav__links .nav__dd-trigger{display:flex}
  /* HB-01: Services disclosure button, as A1-nav <=920 (MO-02) */
  .nav__links .nav__dd-btn{display:flex;align-items:center;justify-content:space-between;
    gap:var(--s3);width:100%;margin:0;padding:var(--s4) 0;background:none;border:0;
    border-bottom:var(--bw-hair) solid var(--dark-line-soft);font:inherit;font-family:var(--f-mono);
    font-weight:500;font-size:var(--fs-label);letter-spacing:var(--ls-nav);text-transform:uppercase;
    text-align:left;color:var(--on-color);cursor:pointer}
  /* HB-01: the button replaces the hub link, as A1-nav <=920 (MO-02) */
  .nav__links .nav__dd--btn > .nav__dd-trigger{display:none}
  /* HB-01: collapsed by default, as A1-nav <=920 (MO-02) */
  .nav__dd-btn[aria-expanded="false"] ~ .nav__dd-menu{display:none}
  /* HB-01: caret turn, as A1-nav <=920 (MO-02) */
  .nav__dd-btn-caret{transition:transform .15s}
  /* HB-01: expanded caret, as A1-nav <=920 (MO-02) */
  .nav__dd-btn[aria-expanded="true"] .nav__dd-btn-caret{transform:rotate(180deg)}
  /* HB-01: undo the U-02 one-step-smaller desktop links (921-999.98), which would otherwise shrink drawer text */
  .nav__links>a,.nav__links .nav__dd-trigger{font-size:var(--fs-label)}
  /* HB-01: undo the >=921 two-column, height-capped dropdown (NAV OVERFLOW CONTAINMENT); the drawer scrolls instead */
  .nav__links .nav__dd-menu{columns:auto;max-height:none;overflow-y:visible}
  /* HB-01: undo the >=921 hover bridge (U-03); under a static submenu it hung a 14px hover strip below the drawer */
  .nav__links .nav__dd:hover::after{content:none}
  /* HB-01: undo the >=921 short-viewport grid (U-31, max-height:700px). :has(>li) only ties that rule's (0,2,1) so
     this later rule wins; the collapsed state (0,3,0) still hides the menu. Own rule: dropped with U-31 where :has()
     is unsupported */
  .nav__links .nav__dd-menu:has(>li){display:block}
}

/* HB-01 header box, the px --wrap half of the cause: from 921 up the header box is never narrower than its own row.
   min-content widens it (still centred) only when the row outgrows the 1200px wrap, which never happens at 16px; the
   logo loses img{max-width:100%} so it counts in that width instead of being squeezed to 0px (as the laptop band
   already does at 921-1199). */
@media (min-width:921px){
  /* HB-01: header box at least as wide as its row */
  .nav__in{min-width:min-content}
  /* HB-01: logo keeps its natural width inside the header row */
  .brand__logo{max-width:none}
}

/* ==== DESIGN REVIEW FIX PASS 2026-10-11 (seo/brands/dehart/design-review-2026-10-06/REVIEW-FINAL.md #1-23) ==== */
/* #1 · hero action pair (Estimate + Call) sits left under the left-aligned lead; the shared .final__actions centres */
section.r1-head .final__actions{justify-content:flex-start;margin-top:var(--s5)}
@media (max-width:480px){section.r1-head .final__actions .btn{flex:1 1 100%;justify-content:center}}
/* #1 · phones: a long H1 + lead pushed the pair below the first screen on 55 of 87 pages (measured at 390x664,
   iPhone Safari's visible area). On phones the pair follows the headline; the lead reads after it. Desktop unchanged. */
@media (max-width:640px){
  section.r1-head > .wrap{display:flex;flex-direction:column;align-items:flex-start}
  section.r1-head > .wrap > *{order:2}
  section.r1-head > .wrap > :is(nav,.eyebrow,h1){order:0}
  section.r1-head > .wrap > .final__actions{order:1;align-self:stretch;margin-top:var(--s4);margin-bottom:var(--s5)}
}
/* #12 · button row after a pricing band (light grounds): left-aligned, full band width, spaced off the price cards */
.r1-prose > .final__actions.r1-cta-row{max-width:none;justify-content:flex-start;margin-top:var(--s6)}
/* #5 · "Get a free estimate" closes the phone/tablet menu drawer; desktop keeps its header as is */
.nav__links > a.nav__est{display:none}
@media (max-width:920px){
  .nav__links > a.nav__est{display:flex;justify-content:center;margin-top:var(--s5);padding:var(--s4);
    border:3px solid var(--on-color);background:var(--orange);color:var(--on-color)}
}
/* #6 · short laptop screens (768-820px tall): the old trim set .hero padding, but the padding lives on .hero__in.
   Compact the header too, and keep --hdr-h (anchor offsets) equal to its rendered height (measured 109px at
   921-1440 wide). Starts above 500px tall so the WS-02 landscape-phone header (62px) keeps its own values. */
@media (min-width:921px) and (min-height:500.02px) and (max-height:820px){
  :root{--hdr-h:109px}
  .hero__in{padding-top:var(--s5);padding-bottom:var(--s6)}
  .hero__eyebrow{margin-bottom:var(--s3)}
  .hero h1{font-size:clamp(2.5rem,7.2vh,4.5rem);margin-bottom:var(--s4)}
  .hero__sub{margin-bottom:var(--s5)}
  .hero__actions{margin-bottom:var(--s5)}
  .nav__in{padding-top:var(--s3);padding-bottom:var(--s3)}
  .brand__logo{height:48px}
}
/* #6 · phones: the 5-line hero H1 pushed both buttons to the bottom edge of an iPhone's ~660px visible area */
@media (max-width:430px){
  .hero__in{padding-top:var(--s5)}
  .hero h1{font-size:clamp(2.125rem,9.4vw,2.5rem)}
  .hero__eyebrow{margin-bottom:var(--s4)}
  .hero__sub{margin-bottom:var(--s5)}
}
/* #19 · one-column hero (<=900px): the photo stretched behind panel + form (~1,760px) and cover-cropped to sky and
   hedge. Stop it at the headline panel and aim at the roof; the form card below sits on the dark ground. */
.hero__bg picture{display:block;width:100%;height:100%}
@media (max-width:900px){
  .hero__bg{bottom:auto;height:var(--hero-photo-h,640px)}
  .hero__bg img{object-position:50% 62%}
}
/* #10 · Name + Phone share a row on the long lead forms (the shared .form-row stacks them under 441px) */
.formcard .form-row .field{margin-bottom:0}
.formcard .form-row{margin-bottom:var(--s4)}
/* #9 · /contact/: optional fields fold under "Add details" so the form starts with the 4 that matter */
.formcard__more{margin-bottom:var(--s4);border-top:var(--bw-hair) solid var(--stone);padding-top:var(--s3)}
.formcard__more > summary{cursor:pointer;min-height:var(--tap);display:flex;align-items:center;
  font-family:var(--f-mono);font-weight:500;font-size:var(--fs-label);letter-spacing:var(--ls-cap);
  text-transform:uppercase;color:var(--ink)}
.formcard__more > summary:hover{color:var(--orange-h)}
.formcard__more[open] > summary{margin-bottom:var(--s3)}
/* #13 #21 · /contact/: one office plate; the call is a full button, not a 19px text link */
.plate .btn.plate__call{display:flex;justify-content:center;width:100%;margin:var(--s4) 0}
/* #17 · /financing/: each plan card ends in its own button (preselects the plan in the form) */
.plate .btn.plate__cta{display:flex;justify-content:center;width:100%;margin-top:var(--s5)}
/* #20 · roof-repair's photo capped so it no longer fills a desktop screen */
.r1-prose figure.r1-fig--capped img{max-height:min(56vh,480px)}
/* #8 · the referral question stays but is optional: say so in the label, quieter than a required mark */
.field label .opt{font-weight:400;color:var(--muted);text-transform:none;letter-spacing:normal}
/* ==== END DESIGN REVIEW FIX PASS 2026-10-11 ==== */

/* ==== LOW-TIER PASS 2026-10-11 (REVIEW-FINAL.md #24-92, batch 1) ==== */
/* #24 · form fine print (incl. the TCPA consent, wording unchanged) reads as body text, not shouted mono caps */
.formcard__fine{font-family:var(--f-body);text-transform:none;letter-spacing:normal}
.r1-prose .r1-hint,.r1-prose .fin-chip,.fin-chip{font-family:var(--f-body);text-transform:none;letter-spacing:normal;font-size:var(--fs-eyebrow)}
/* #26 · the whole homepage service card is the link (it already lifts like one) */
.svc{position:relative}
.svc__name a::after{content:"";position:absolute;inset:0}
/* #27 · sidebar row: the second lead form takes two tracks instead of a ~200px column */
@media (min-width:1200px){.r1-aside__in > .formcard{grid-column:span 2}}
/* #28 · no dead gap when the sidebar opens its band */
.band > .wrap > .r1-aside:first-child{margin-top:0}
/* #32 · secondary links get the same invisible tap floor as the footer (no visual change) */
.r1-crumb a,.r1-certline a,.plate li:not([class]) > a,.r1-note li > a{display:inline-block;padding-block:.65rem;margin-block:-.65rem}
/* #33 · footer phone/email: underlined, own 40px rows, no overlapping hit boxes (license chip untouched) */
.foot__nap a{display:inline-block;padding-block:var(--s2);text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--orange)}
/* #39 · narrow phones: tighter cell padding so 12px labels fit; the .85 step-down only below 341px */
@media (max-width:380px){.stat{padding:var(--s5) var(--s4)}}
@media (min-width:341px) and (max-width:380px){.stat__label{font-size:var(--fs-eyebrow);letter-spacing:var(--ls-mono)}}
/* #40 · two-line card, form and FAQ titles: display leading, not 1.65 body leading */
.r1-prose h3,.r1-faq__q,.formcard :is(h2,h3){line-height:1.1;text-wrap:balance}
/* #41 · no orphaned last word in display headings and eyebrows */
.display,.section-head h2,.eyebrow,.final h2{text-wrap:balance}
/* #42 · breadcrumb: the "/" ends a line instead of starting the next (beats the inline (0,1,3) rule) */
nav .r1-crumb li + li::before{content:none}
nav .r1-crumb li:not(:last-child)::after{content:"/";color:var(--orange);flex:none}
/* #43 · homepage: specs as a 2x2 grid on desktop; balanced project captions */
@media (min-width:901px){.hero__specs{display:grid;grid-template-columns:auto auto;justify-content:start;column-gap:var(--s6)}}
.proj__meta{text-wrap:balance}
/* #44 · one thick rule per section break (the band border), not two stacked */
.band + .band .r1-prose > h2:first-child{border-top:0;padding-top:0}
/* #54 · process steps on phones: full-width text, no second number label */
@media (max-width:560px){.step__desc{max-width:none} .step__code{display:none}}
/* #57 · homepage hero panel keeps its orange rule (the owner's 07-23 veil stays) */
.hero__panel{border-left-color:var(--orange)}
/* #60 · plates without a tab don't keep the tab's empty top band */
.plate:not(:has(> .plate__tab)){padding-top:var(--s5)}
/* #62 · "Most Popular" badge sits on the card corner */
.plate .fin-pop{margin:0}
/* #63 · payment-factor rows: label over value on phones; label column + left-aligned value on wide plates */
@media (max-width:560px){.prow{grid-template-columns:1fr;gap:var(--s1)} .prow dd{text-align:left}}
@media (min-width:561px){.r1-prose > .plate .prow{grid-template-columns:14rem 1fr;justify-content:start} .r1-prose > .plate .prow dd{text-align:left}}
/* #64 · the financing chip is a full-width line, not capped at 74ch */
.r1-prose > .fin-chip{max-width:none}
/* #65 · guide callout: its first label reads as the caption label; the second, redundant one is hidden */
.r1-prose > aside.r1-note > span:first-child{display:block;margin-bottom:var(--s1h);font-family:var(--f-mono);font-weight:500;
  font-size:var(--fs-micro);letter-spacing:var(--ls-mono);text-transform:uppercase;color:var(--muted)}
.r1-prose > aside.r1-note > span:first-child + span{display:none}
/* #67 · gallery cards: label as a mono eyebrow, the last line as a mono spec line */
.r1-gallery .lg-card > span{font-family:var(--f-mono);font-weight:500;font-size:var(--fs-eyebrow);text-transform:uppercase;
  letter-spacing:var(--ls-mono);color:var(--gun)}
.r1-gallery .lg-card > p:last-child{font-family:var(--f-mono);font-size:var(--fs-eyebrow);letter-spacing:var(--ls-cap);color:var(--muted)}
/* #69 · the menu button sits left at every menu width (it switched sides at 641-920) */
@media (max-width:920px){.nav__cta{display:contents} .nav__toggle{grid-column:1;grid-row:1;justify-self:start}
  .nav__phone{grid-column:3;grid-row:1;justify-self:end}}
/* #71 · 5-step grids: descriptions start on one line */
@media (min-width:921px){.proc-grid:has(> .step:nth-child(5)) .step__title{min-height:2lh}}
/* #72 · Turlock neighborhoods are a list, not button-like chips */
.r1-prose .r1-hood,.r1-hood{border:0;background:none;padding:0 0 0 var(--s4);position:relative}
.r1-hood::before{content:"";position:absolute;left:0;top:.45em;width:6px;height:6px;background:var(--orange)}
.r1-hoods{display:grid;grid-template-columns:1fr 1fr;gap:var(--s2) var(--s5)}
/* #73 · About "by the numbers": the description stays beside the number */
@media (max-width:900px){.srow__row .srow__spec{grid-column:2 / -1}}
/* #76 · leadership cards: tighter role line and title leading */
.r1-prose .plate .team-role{margin-top:var(--s1)}
.plate h3{line-height:1.1}
/* #77 · photos after a paragraph or callout keep the prose gap (the inline figure{margin:0} removed it) */
.r1-prose > :is(p,aside,ul,ol) + figure,.r1-prose > figure + :is(p,aside){margin-top:var(--s5)}
/* #78 · links in a page-head lead look like links (#b53811 on white = 5.9:1) */
section.r1-head .lead a{color:var(--orange-h);text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px}
/* #79 · one form-title size on every page */
.r1-prose .formcard :is(h2,h3){font-size:var(--fs-card-h)}
/* #83 · header links and phone show a white underline on hover (orange vanished on the orange bar) */
@media (min-width:921px){.nav__links > a:hover,.nav__dd-trigger:hover,.nav__phone:hover{text-decoration:underline;
  text-decoration-color:var(--on-color);text-decoration-thickness:2px;text-underline-offset:6px;border-bottom-color:transparent}}
/* #84 · field hover = heavier ink edge, so it never looks like the orange focus/error state */
.field:not(.field--error) :is(input,select,textarea):not(:focus-visible):not([aria-invalid="true"]):hover{border-color:var(--ink);box-shadow:inset 0 0 0 1px var(--ink)}
/* ==== END LOW-TIER PASS 2026-10-11 ==== */

/* ==== BATCH 2 PASS 2026-10-12 — design review REVIEW-FINAL.md #29 #38 #45 #46 #53 #68 #70 #75 #80 #85 (rework batch).
   #47 #52 #56 live in the per-page placement block (_scripts/restyle-r1-placement.py); #46 #80 and the #45 base values
   are token edits in :root. ==== */

/* #45 · band padding: three steps on the 8px grid (48/64/96; large bands 64/88/120; strips 32/40/56; service sections
   40/48/64) instead of five fluid formulas. The service-section value is emitted inline by the frozen builders as
   .band.r1-sec{clamp(38px,4.2vw,68px)} (0,2,0); section.band.r1-sec (0,2,1) points it at the token. */
@media (min-width:641px){:root{--band-pad:64px;--band-pad-lg:88px;--strip-pad:40px;--sec-pad:48px}}
@media (min-width:1100px){:root{--band-pad:96px;--band-pad-lg:120px;--strip-pad:56px;--sec-pad:64px}}
section.band.r1-sec{padding-block:var(--sec-pad)}
/* #45 · a row list or card grid that ends a band adds no margin under the band's own padding (bottom-heavy bands) */
.wrap > .svc-rows:last-child,.r1-prose > :last-child,.wrap > :last-child{margin-bottom:0}

/* #38 · content-bearing micro text reads at 12px, not 10-11px: captions, cert and trust lines, breadcrumbs, roles,
   card links, city and neighborhood links, the Services menu, card tabs, the financing chip and callout labels.
   :root raises each to at least (0,2,0) so the later per-page inline rules (0,1,0)-(0,1,1) cannot undo it. Decorative
   counters and the hero eyebrow on phones (sized to keep the hero buttons on the first screen) are not changed. */
:root :is(figure > figcaption,.figcap span,.cert__desc,.util__item,.plate__tab,.lg-card__more,.r1-city,.r1-hood,
  .r1-certline,.r1-certline a,.r1-crumb,.r1-crumb a,.r1-crumb li,.r1-crumb span,.team-role,.fin-pop,.fin-chip,
  .r1-byline span,.r1-toc__label,.proj__no,.brand__est,.nav__dd-menu a){font-size:var(--fs-eyebrow)}
:root .r1-note > :is(strong,span):first-child{font-size:var(--fs-eyebrow)}

/* #29 · homepage project mosaic, desktop: the three stacked projects get a wide photo track instead of a 104px
   portrait thumb, captions stack (number above the line), and the two bottom photos share one 4:3 crop. The photo is
   absolutely placed so its intrinsic height never sizes the row (rows share the feature's height on /, and size to a
   9rem floor where the stack also holds the promise note, /es/). */
@media (min-width:901px){
  .proj--row{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);align-items:stretch;min-height:9rem}
  .proj--row .proj__img{position:relative;aspect-ratio:auto;height:auto;max-width:none}
  .proj--row .proj__img img{position:absolute;inset:0}
  .proj--row .proj__cap{flex-direction:column;align-items:flex-start;justify-content:center}
  .proj--wide .proj__img{aspect-ratio:4/3}
}
/* #68 · homepage mosaic on phones: projects 02-06 are compact rows (photo left, caption right, number above),
   about half the old ~3 screens of full-width cards. Overrides the <=400px 16:9 stacked card. */
@media (max-width:640px){
  .proj--row,.proj--wide,.proj--mid{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,3fr);align-items:stretch}
  .proj--row .proj__img,.proj--wide .proj__img,.proj--mid .proj__img{aspect-ratio:4/3;height:auto;
    border-bottom:0;border-right:var(--bw) solid var(--ink)}
  .proj--row .proj__cap,.proj--wide .proj__cap,.proj--mid .proj__cap{flex-direction:column;align-items:flex-start;
    justify-content:center;padding:var(--s3)}
}
/* #68 · the gallery's way on to the full projects page */
.proj-more{margin-top:var(--s5)}
.proj-more a{display:inline-flex;align-items:center;gap:var(--s2);min-height:var(--tap);font-family:var(--f-mono);
  font-weight:500;font-size:var(--fs-eyebrow);letter-spacing:var(--ls-btn);text-transform:uppercase;color:var(--ink);
  text-decoration:none;border-bottom:var(--bw) solid var(--orange)}
.proj-more a:hover{color:var(--orange-h)}

/* #53 · lists inside full-width cards were held to the 80ch reading measure and ran as one long column: they span the
   card and flow into two columns where each column gets at least 15rem. The Services index in a card goes 2-up from
   9rem; city chips are a uniform 2-up grid on small phones. */
.r1-prose .plate ul:not([class]),.plate > ul:not([class]){max-width:none;columns:15rem 2;column-gap:var(--s6)}
.plate ul:not([class]) > li,.plate .plate__links > li{break-inside:avoid}
.plate .plate__links{columns:9rem 2;column-gap:var(--s5)}
@media (max-width:460px){
  :root .r1-cities{display:grid;grid-template-columns:1fr 1fr}
  :root .r1-cities > .r1-city{justify-content:center;text-align:center}
}

/* #70 · static row lists (no links; /financing/ differentiators and 24 more pages): at 921px+ the code, name and spec
   sit in a tight 3rem / 18rem / rest grid instead of three wide fractions */
@media (min-width:921px){.svc-rows .srow > .srow__row{grid-template-columns:3rem 18rem minmax(0,1fr)}}

/* #75 · /about/ 1975 timeline: the existing step component, three stops, the page's own wording */
.r1-timeline ol.proc-grid{list-style:none;margin:0;padding:0}
/* the light version of the step: it sits on the story's cream band, not the gunmetal process band */
:root .r1-timeline .step__num{margin-bottom:var(--s3);color:var(--orange-deep)}
:root .r1-timeline .step__title{color:var(--ink)}
:root .r1-timeline .step__desc{max-width:32ch;color:var(--muted)}
.r1-timeline .step{padding-inline:var(--s5) var(--s4)}
.r1-timeline .step:first-child{padding-left:0}
@media (min-width:641px){:root .r1-timeline .proc-grid{grid-template-columns:repeat(3,1fr)}
  :root .r1-timeline .step + .step{border-left:var(--bw-hair) solid var(--line)}}
@media (max-width:640px){:root .r1-timeline .proc-grid{grid-template-columns:1fr}
  :root .r1-timeline .step{border-left:0;padding:var(--s4) 0}}

/* #85 · desktop Services menu opens from its own button (click / Enter / Space), not on keyboard focus, so Tab moves
   from "Services" to the button and on to the next header link instead of through all 18 menu links. Hover still
   opens it. The button (nav script, _scripts/inject-r1-nav.py) follows the Services link and shows only its caret here;
   its text stays for screen readers. Without the script there is no button and the old focus-within opening stays.
   The 57.5625em floor keeps these rules out of the large-default-font drawer layout (HB-01, 921px to 57.5em). */
@media (min-width:921px) and (min-width:57.5625em){
  .nav__links .nav__dd--btn{display:inline-flex;align-items:center}
  .nav__links .nav__dd--btn > .nav__dd-trigger .nav__dd-caret{display:none}
  .nav__links .nav__dd--btn > .nav__dd-btn{display:inline-flex;align-items:center;justify-content:center;
    width:28px;min-height:var(--tap);margin:0 0 0 2px;padding:0;background:none;border:0;color:var(--on-color);
    font:inherit;font-size:var(--fs-eyebrow);cursor:pointer}
  .nav__dd--btn .nav__dd-btn-txt{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
    clip-path:inset(50%);white-space:nowrap}
  .nav__links .nav__dd--btn:not(:hover) > .nav__dd-btn[aria-expanded="false"] ~ .nav__dd-menu{opacity:0;
    visibility:hidden;transform:translateY(-6px)}
  .nav__links .nav__dd--btn > .nav__dd-btn[aria-expanded="true"] ~ .nav__dd-menu{opacity:1;visibility:visible;
    transform:none}
  .nav__dd--btn > .nav__dd-btn[aria-expanded="true"] .nav__dd-btn-caret{transform:rotate(180deg)}
  .nav__dd--btn > .nav__dd-btn .nav__dd-btn-caret{transition:transform .15s}
}
/* ==== END BATCH 2 PASS 2026-10-12 ==== */

/* ==== OWNER CALLS 2026-10-13 — design review #55 ==== */
/* #55 · desktop header: logo left | links | phone right (the logo now comes first in the markup). The centred logo sat
   ~290px right of the page centre because the seven links are much wider than the phone. Phones and tablets keep the
   menu button left, logo centre, phone right (explicit columns at <=920). The 57.5625em floor leaves the
   large-default-font drawer layout (HB-01) alone. */
@media (min-width:921px) and (min-width:57.5625em){
  .nav__in{grid-template-columns:auto minmax(0,1fr) auto}
  .brand{grid-column:1;justify-self:start;padding-inline:0 var(--s4)}
  .nav__links{grid-column:2;justify-self:center}
  .nav__cta{grid-column:3;justify-self:end}
}
/* #30 · photos in running text stop at the text's own width (80ch, the paragraph measure) instead of the full 1,120px
   column, where 800px job photos were upscaled ~1.4x and soft, and one photo took a whole laptop screen. Left-aligned
   with the text. The frozen builders set .r1-prose > figure{max-width:none} inline (0,1,1); :root makes this (0,3,1).
   Classed figures (review cards, the capped repair photo, captions overlays) keep their own sizing. Phones: no change. */
:root .r1-prose > figure:not([class]){max-width:80ch}
/* #91 · /es/ on phones: the Spanish intro under the H1 is ~6 lines, so the buttons follow the H1 directly (the #1 order
   the service-page heads already use); the intro and the specs come after them. English homepage unchanged. */
@media (max-width:640px){
  :lang(es) .hero__panel{display:flex;flex-direction:column;align-items:flex-start}
  :lang(es) .hero__panel > *{order:2}
  :lang(es) .hero__panel > :is(.hero__eyebrow,h1){order:0}
  :lang(es) .hero__panel > .hero__actions{order:1}
}
/* ==== END OWNER CALLS 2026-10-13 ==== */
