/* Self-hosted Barlow + Barlow Condensed (latin subset) — replaces the Google
   Fonts <link>. Removes the only third-party origin on the site, so the CSP
   needs no fonts.googleapis.com / fonts.gstatic.com entries and visitor IPs
   never reach Google. Regenerate from the Google CSS if weights change. */

@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/barlow-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../assets/fonts/barlow-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Barlow';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../assets/fonts/barlow-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../assets/fonts/barlow-condensed-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../assets/fonts/barlow-condensed-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../assets/fonts/barlow-condensed-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   LEAD-GEN CONTRACTOR TEMPLATE — design system
   Architectural-editorial direction, built on the Torres system.
   No build step. Vanilla. Reskin per client via the CLIENT THEME block.
   Component reference: DESIGN.md
   ============================================================ */

/* ============================================================
   CLIENT THEME  —  the ONLY block you reskin per client.

   THREE accent roles, not two. The accent has to be a button
   FILL under white text *and* accent TEXT on the dark panel, and
   no single colour clears 4.5:1 for both — the feasible luminance
   band is empty (needs >=.2209 for the panel, <=.1833 for white
   text on it). Earlier builds in this family shipped one token and
   silently failed the button case. So:
     --acc    fill only   — white text sits on it
     --acc-2  text on LIGHT backgrounds
     --acc-d  text/icons on DARK surfaces (panel, panel-2, ink)
   Ratios below are measured by _raw/pal.py, not estimated.
   Recompute if you reskin; do not copy a number forward.

   BR2 sent three pieces of artwork and no photographs: a white-on-black
   "BR2 PROPERTY SOLUTIONS" wordmark, the same lockup over an American flag
   with their Bobcat excavator and track loader cut into it, and a third copy
   of the machines on a grey studio gradient. So this palette IS sampled —
   from the flag — which is the one case in this line where the patriotic
   read is the client's own choice rather than ours.

   How red and blue coexist without a fourth accent role:

   Navy is the SURFACE family, not an accent. Making blue --acc-2 would
   collide head-on with a role red already owns, and a site with two text
   accents has none. So navy became --panel / --panel-2 and the ink and
   ground tokens are tinted into it, which leaves red as the only saturated
   text colour on the site and gives the flag artwork a ground it belongs on.

   --blue is the flag's blue and is NON-TEXT ONLY: rules, stripe fills, the
   stat bar. It measures 4.36:1 on --panel and 3.57:1 on --panel-2 — over the
   3:1 non-text bar, under the 4.5:1 text bar. It is in the token set because
   the hero art puts real flag blue on screen and an interface with zero blue
   in it reads as a mismatch. Do NOT promote it to text.

   --gold is the review-star colour and nothing else. This build publishes a
   rating — 5.0 from 4 Google reviews — but the band the stars sit in is
   .on-dark, where --gold measures 3.08:1 on --panel-2. The existing
   `.on-dark .quote-tile .q-stars` rule swaps them to --acc-d (7.13:1). Do NOT
   repurpose --gold as a second accent; --blue already fills that slot, and
   only for non-text.
   ============================================================ */
:root {
  --acc:     #b4271f;   /* flag red — fills/buttons ONLY (white text on it: 6.46:1). Sampled from the client's own logo artwork. */
  --acc-2:   #8e1c16;   /* deep red — accent text on light (8.43:1 on --bg, 7.77:1 on --bg-2, 9.02:1 on white) */
  --acc-d:   #ff9a91;   /* pale red — accent text on dark (8.69:1 on --panel, 7.13:1 on --panel-2, 7.94:1 on the page-hero red stop) */
  --blue:    #4f80bd;   /* flag blue — NON-TEXT ONLY (4.36:1 on --panel, 3.57:1 on --panel-2, 4.07:1 on white). Rules and stripe fills. Never type. */
  --gold:    #9a6a0f;   /* review stars on a LIGHT ground only. NON-TEXT ONLY; on the dark band .on-dark swaps them to --acc-d. */
  --panel:   #101828;   /* navy near-black (17.75:1 vs white) */
  --panel-2: #1b2942;   /* elevated navy surface (14.56:1 vs white) */
  --r:       3px;       /* squared off harder than the parent — this is equipment, not a boutique */
}

/* Fonts: default Barlow Condensed (display) + Barlow (body).
   To swap, change the two --font vars below AND the <link> in each page's
   <head>. Alt pairings in DESIGN.md (Bricolage / Fraunces / Archivo). */
:root {
  --font-display: "Barlow Condensed", "Barlow", system-ui, sans-serif;
  --font-body:    "Barlow", system-ui, -apple-system, sans-serif;
  --display-upper: uppercase;   /* set to `none` for editorial (non-caps) headings */
  --display-tracking: .005em;
}

/* ---------- derived tokens ----------
   The ink and ground tokens are navy-tinted to match --panel. A neutral or
   warm-grey body copy under a navy header reads as two different blacks, and
   this build has more dark surface than any of its siblings.
   The page ground is a cool paper rather than the parent's warm one, so red
   is the only saturated thing on a light section and the flag blue in the
   hero artwork does not look like it wandered in from another site.
   Measured on --bg: --ink 16.94:1, --ink-2 9.45:1, --ink-3 5.67:1 (the
   small-meta token, so it has to clear 4.5:1 on its own).
   On --bg-2: --ink 15.61:1, --ink-2 8.71:1, --ink-3 5.22:1. */
:root {
  --bg:      #f7f7f8;
  --bg-2:    #eceef2;
  --ink:     #0f1622;
  --ink-2:   #3a4250;
  --ink-3:   #596374;
  --line:    rgba(15,22,34,.12);
  --line-2:  rgba(15,22,34,.07);
  --line-d:  rgba(255,255,255,.14);
  --white:   #ffffff;

  --wrap: 1200px;
  --gutter: clamp(1.25rem, 4vw, 3rem);

  --shadow-sm: 0 1px 2px rgba(15,22,34,.06), 0 2px 8px rgba(15,22,34,.05);
  --shadow-md: 0 8px 30px rgba(15,22,34,.10);
  --shadow-lg: 0 24px 60px rgba(15,22,34,.16);

  --ease: cubic-bezier(.2,.7,.2,1);
  --r-lg: calc(var(--r) + 4px);
  --acc-soft: color-mix(in srgb, var(--acc) 14%, transparent);
  --acc-glow: color-mix(in srgb, var(--acc) 34%, transparent);
}

/* ---------- reset ---------- */
*,*::before,*::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-body);
  font-size: 1.0625rem;              /* 17px */
  line-height: 1.65;
  color: var(--ink-2);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img { max-width: 100%; display: block; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
ul { list-style: none; }
[hidden] { display: none !important; }   /* beats class display rules (e.g. .form) on toggle */
:focus-visible { outline: 3px solid var(--acc); outline-offset: 3px; border-radius: 2px; }
[id] { scroll-margin-top: 92px; }

/* ---------- type ---------- */
h1,h2,h3,h4,.display {
  font-family: var(--font-display);
  color: var(--ink);
  line-height: 1.03;
  font-weight: 700;
  letter-spacing: var(--display-tracking);
  text-transform: var(--display-upper);
}
h1 { font-size: clamp(2.5rem, 5.2vw, 4.4rem); }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.9rem); }
h3 { font-size: clamp(1.35rem, 2.4vw, 1.75rem); letter-spacing: .01em; }
h4 { font-size: 1.2rem; letter-spacing: .02em; }
p  { max-width: 66ch; }
strong { color: var(--ink); font-weight: 600; }
.lead { font-size: clamp(1.12rem, 1.7vw, 1.35rem); line-height: 1.6; color: var(--ink-2); max-width: 60ch; }

/* eyebrow — accent bar + uppercase label; optional leading number */
.eyebrow {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--font-display);
  font-weight: 600; font-size: .95rem;
  text-transform: uppercase; letter-spacing: .22em;
  color: var(--ink-2); margin-bottom: 1.1rem;
}
.eyebrow::before { content: ""; width: 34px; height: 3px; background: var(--acc); display: inline-block; flex: none; }
.eyebrow .num { color: var(--acc-2); font-variant-numeric: tabular-nums; letter-spacing: .1em; }
/* --acc-2 is tuned for contrast on the light bg; on a dark hero or panel it goes
   nearly invisible. In dark contexts the num takes the eyebrow's own colour. */
.eyebrow.light .num, .on-dark .eyebrow .num { color: inherit; }
.eyebrow.center { justify-content: center; }
.on-dark .eyebrow, .eyebrow.light { color: var(--acc-d); }
/* .text-white is a 0,1,0 utility and .eyebrow.light is 0,2,0, so the utility NEVER won and
   the hero eyebrow rendered in --acc-d rather than white. On a flat dark panel that is
   harmless; over the hero PHOTOGRAPH it measured 4.49:1 desktop and 2.80:1 mobile against
   a 4.5 floor (15px/600 is small text) - an AA failure, measured with _raw/scrim2.py.
   This rule makes the utility actually apply. Inherited from the parent build and still
   live in its siblings; see LAUNCH.md. */
.eyebrow.text-white, .eyebrow.text-white .num { color: #fff; }

/* ---------- layout ---------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
.wrap-narrow { max-width: 820px; }
.section { padding-block: clamp(3.25rem, 6vw, 5.5rem); }
.section.tight { padding-block: clamp(3rem, 5vw, 4.5rem); }
.section.alt { background: var(--bg-2); }
.section.dark { background: var(--panel); color: rgba(255,255,255,.82); }
.section.dark h1,.section.dark h2,.section.dark h3,.section.dark h4 { color: #fff; }
.section-head { max-width: 640px; margin-bottom: clamp(2.25rem, 4vw, 3.25rem); }
.section-head.center { margin-inline: auto; text-align: center; }
.section-head .lead { margin-top: .5rem; }
.divider { height: 1px; background: var(--line); border: 0; }

.grid { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); }
.cols-2 { grid-template-columns: repeat(2, 1fr); }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
/* .split centres its two columns, which is right when one of them is a
   photograph — a tall figure beside short copy wants the copy centred on it.
   It is wrong when BOTH columns are text: the two eyebrows and the two h2s
   land at different heights and read as a mistake. The text/text splits on
   the service and city pages use .split.top. */
.split.top { align-items: start; }

/* ---------- buttons ---------- */
.btn {
  --bw: 0;
  display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
  font-family: var(--font-display);
  font-weight: 700; font-size: 1.05rem;
  text-transform: uppercase; letter-spacing: .06em;
  padding: .92rem 1.6rem;
  border: var(--bw) solid transparent; border-radius: var(--r);
  transition: transform .18s var(--ease), background .18s var(--ease), color .18s var(--ease), box-shadow .18s var(--ease), border-color .18s var(--ease);
  white-space: nowrap; line-height: 1;
}
.btn svg { width: 1.05em; height: 1.05em; flex: none; }
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn-primary { background: var(--acc); color: #fff; box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--acc-2); box-shadow: var(--shadow-md); }
.btn-dark { background: var(--ink); color: #fff; }
/* Hover lift off --ink (#0f1622). Literal, not a token, and RESKIN-SENSITIVE: every
   build in this line has inherited a stale value here. White on this is 13.96:1. */
.btn-dark:hover { background: #1e2c46; }
.btn-outline { --bw: 2px; background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-outline:hover { background: var(--ink); color: #fff; }
.btn-ghost-light { --bw: 2px; background: transparent; color: #fff; border-color: rgba(255,255,255,.4); }
.btn-ghost-light:hover { background: #fff; color: var(--ink); border-color: #fff; }
.btn-lg { padding: 1.1rem 2rem; font-size: 1.18rem; }
.btn-block { width: 100%; }
.btn-row { display: flex; flex-wrap: wrap; gap: .9rem; }

/* ---------- header / nav ---------- */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid transparent;
  transition: box-shadow .25s var(--ease), border-color .25s var(--ease), background .25s var(--ease);
}
.site-header.scrolled { box-shadow: var(--shadow-sm); border-color: var(--line-2); }
.nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; height: 76px; }
/* ---------- brand lockup ----------
   TYPE, NOT AN IMAGE. This client has no logo artwork of any kind, so the wordmark is
   set in the site's own display face. That is also why it is not an inline SVG with
   <text>: an SVG loaded through <img src> is an isolated document and cannot reach the
   page's self-hosted @font-face, so it would silently fall back to a system serif.
   Rendering the lockup as real HTML text uses the already-loaded Barlow Condensed, adds
   no request, scales cleanly and stays selectable and readable to screen readers.
   BR2 DID SUPPLY A LOGO, so none of the above applies to this build: .brand-logo is the
   shipped header path and carries the client's own keyed wordmark
   (assets/img/wordmark-ink.webp). .brand-mark / .brand-name below are the DORMANT
   text-lockup fallback, kept intact for the next client in this line who turns up with
   no artwork at all — which is most of them. Do not delete them, and do not "tidy" the
   reasoning above: it is why the fallback is text rather than a generated SVG. */
.brand { display: flex; align-items: center; gap: .6rem; flex-shrink: 0; }
.brand-logo { height: 44px; width: auto; max-width: 170px; object-fit: contain; object-position: left center; }
.brand-mark {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: var(--r);
  background: var(--panel); color: var(--acc-d);
  display: grid; place-items: center;
}
.brand-mark svg { width: 23px; height: 23px; }
.brand-name {
  font-family: var(--font-display); font-weight: 700; font-size: 1.28rem;
  text-transform: uppercase; letter-spacing: .015em; color: var(--ink); line-height: 1.05; white-space: nowrap;
}
/* BR2 is written BR<sup>2</sup> everywhere, including inside headings, leads and
   the footer paragraph. Unstyled <sup> shifts the line box and opens a ragged
   gap above the line it sits in; these two rules keep it optical. */
/* One selector, every context BR<sup>2</sup> appears in — headings, leads, list items,
   figcaptions, the brand lockup and the footer link. Missing `li` here left the
   superscript oversized and riding high in the about-page link list. */
sup { font-size: .62em; line-height: 0; vertical-align: baseline; top: -.42em; position: relative; }
.brand-name span { display: block; font-size: .56rem; letter-spacing: .17em; color: var(--ink-3); font-weight: 600; margin-top: .18rem; }
/* Footer sits on --panel, so the ink-coloured lockup has to invert there. */
.site-footer .brand-mark { background: rgba(255,255,255,.09); color: var(--acc-d); }
.site-footer .brand-name { color: #fff; }
.site-footer .brand-name span { color: rgba(255,255,255,.62); }
@media (max-width: 400px) {
  .brand-name { font-size: 1.12rem; }
  .brand-name span { letter-spacing: .12em; }
}

.nav-links { display: flex; align-items: center; flex-wrap: nowrap; gap: .1rem; }
.nav-links > a, .nav-item > a {
  font-family: var(--font-display); font-weight: 600;
  text-transform: uppercase; letter-spacing: .03em; font-size: .98rem;
  color: var(--ink); padding: .5rem .55rem; border-radius: 4px; position: relative;
  white-space: nowrap; transition: color .15s var(--ease); display: inline-flex; align-items: center; gap: .3rem;
}
.nav-links > a::after, .nav-item > a::after {
  content: ""; position: absolute; left: .7rem; right: .7rem; bottom: .28rem; height: 2px;
  background: var(--acc); transform: scaleX(0); transform-origin: left; transition: transform .2s var(--ease);
}
.nav-links > a:hover::after, .nav-item > a:hover::after,
.nav-links a[aria-current="page"]::after, .nav-item > a[aria-current="page"]::after { transform: scaleX(1); }

/* services dropdown */
.nav-item { position: relative; }
.nav-item > a svg { width: .8em; height: .8em; transition: transform .2s var(--ease); }
.nav-item:hover > a svg, .nav-item:focus-within > a svg { transform: rotate(180deg); }
.sub-menu {
  position: absolute; top: 100%; left: 0; min-width: 250px; padding: .5rem 0 .6rem;
  background: var(--white); border: 1px solid var(--line);
  border-radius: 0 0 var(--r-lg) var(--r-lg); box-shadow: var(--shadow-md);
  display: none; z-index: 70;
}
.nav-item:hover .sub-menu, .nav-item:focus-within .sub-menu { display: block; }
.sub-menu a {
  display: block; padding: .55rem 1.2rem; color: var(--ink); font-family: var(--font-display);
  font-weight: 600; text-transform: uppercase; letter-spacing: .04em; font-size: .95rem;
  transition: background .15s, color .15s;
}
.sub-menu a:hover { background: var(--acc-soft); color: var(--acc-2); }
.sub-label { display: block; padding: .5rem 1.2rem .2rem; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .16em; color: var(--ink-3); }

/* Service Area dropdown — FOURTEEN towns, so two columns instead of a menu taller than
   the viewport. Sits on top of the generic .sub-menu. Matches wittyrealty, which needed
   the same thing for nine. */
.sub-menu-cols { display: none; grid-template-columns: 1fr 1fr; min-width: 360px; }
.nav-item:hover .sub-menu-cols,
.nav-item:focus-within .sub-menu-cols { display: grid; }
.sub-menu-cols .sub-label,
.sub-menu-cols a:last-child { grid-column: 1 / -1; }
/* "Full Service Area" is the last child — it spans both columns and is ruled off as a
   catch-all rather than reading like a fifteenth town. */
.sub-menu-cols a:last-child { border-top: 1px solid var(--line); margin-top: .35rem; padding-top: .65rem; }

/* A dropdown hanging off a nav item near the right edge would run off-screen. With five
   tabs, Service Area is second-from-last, so this is load-bearing — do not delete it
   when the tab list changes without re-checking where the last dropdown sits. */
.nav-links > .nav-item:nth-last-child(-n+2) .sub-menu { left: auto; right: 0; border-radius: 0 0 var(--r-lg) var(--r-lg); }

.nav-cta { display: flex; align-items: center; gap: .7rem; flex-shrink: 0; }
.nav-phone { display: none; align-items: center; gap: .4rem; font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; color: var(--ink); letter-spacing: .02em; white-space: nowrap; }
@media (min-width: 1480px) { .nav-phone { display: inline-flex; } }
.nav-phone svg { width: 1.05em; height: 1.05em; color: var(--acc-2); }

.nav-toggle { display: none; width: 44px; height: 44px; background: var(--ink); border-radius: var(--r); position: relative; border: 0; }
.nav-toggle span { position: absolute; left: 11px; right: 11px; height: 2.5px; background: #fff; border-radius: 2px; transition: transform .3s var(--ease), opacity .2s var(--ease); }
.nav-toggle span:nth-child(1) { top: 15px; }
.nav-toggle span:nth-child(2) { top: 21px; }
.nav-toggle span:nth-child(3) { top: 27px; }
body.menu-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
body.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* mobile menu */
.mobile-menu {
  position: fixed; inset: 76px 0 0 0; z-index: 55;
  background: var(--panel); color: #fff;
  transform: translateX(100%); transition: transform .35s var(--ease);
  display: flex; flex-direction: column; padding: 2rem var(--gutter) 3rem; overflow-y: auto;
}
body.menu-open { overflow: hidden; }
body.menu-open .mobile-menu { transform: translateX(0); }
.mobile-menu a.m-link { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; font-size: 1.55rem; color: #fff; padding: .6rem 0; border-bottom: 1px solid var(--line-d); }
.mobile-menu a.m-link[aria-current="page"] { color: var(--acc-d); }
.mobile-menu a.m-sub { display: block; font-family: var(--font-display); text-transform: uppercase; letter-spacing: .04em; font-size: 1.05rem; color: rgba(255,255,255,.7); padding: .45rem 0 .45rem 1rem; }
/* Group heading inside the mobile menu — the "What we do" label above the service
   anchors. Present in the wittyrealty build and missing from this one until the nav
   gained dropdowns. witty styles it with --acc-light, a token this build does not
   have; --acc-d is the correct text-on-dark accent here (8.69:1 on --panel). */
.mobile-menu a.m-group {
  display: block; margin-top: .9rem; padding: .3rem 0 .3rem 1rem;
  font-family: var(--font-display); font-weight: 700; font-size: .82rem;
  text-transform: uppercase; letter-spacing: .14em; color: var(--acc-d);
}
.mobile-menu .m-actions { margin-top: 1.8rem; display: grid; gap: .9rem; }
.mobile-menu .m-phone { font-family: var(--font-display); font-weight: 700; font-size: 1.5rem; color: var(--acc-d); display: flex; align-items: center; gap: .6rem; }
@media (min-width: 1181px) { .mobile-menu { display: none; } }

/* ---------- hero (full-bleed) ---------- */
.hero { position: relative; color: #fff; isolation: isolate; }
.hero-media { position: absolute; inset: 0; z-index: -2; background: var(--panel); }
.hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: center; }

/* ============================================================
   HERO SCRIM — INHERITED, AND UNUSED IN THIS BUILD.

   These two rules wash a photographic hero so copy stays legible over it. This
   client's hero carries no photograph (see .hero-flat below), so .hero-flat
   switches them off and no scrim measurement applies here.

   They are kept, not deleted, because gallery.html and the interior page heroes
   use .page-hero, and because the moment Bryer sends camera files the fastest
   path to a photographic hero is to drop .hero-flat and put an <img> back in
   .hero-media. IF THAT HAPPENS: the wash is only valid for the image it was
   measured against, so measure it — do not assume these stops still pass.
   ============================================================ */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--panel) 78%, transparent) 0%,
      color-mix(in srgb, var(--panel) 50%, transparent) 55%,
      color-mix(in srgb, var(--panel) 8%, transparent) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--panel) 12%, transparent) 0%,
      color-mix(in srgb, var(--panel) 46%, transparent) 100%);
}
@media (max-width: 640px) {
  .hero::before {
    background:
      linear-gradient(90deg,
        color-mix(in srgb, var(--panel) 68%, transparent) 0%,
        color-mix(in srgb, var(--panel) 54%, transparent) 100%),
      linear-gradient(180deg,
        color-mix(in srgb, var(--panel) 20%, transparent) 0%,
        color-mix(in srgb, var(--panel) 58%, transparent) 100%);
  }
}
/* ============================================================
   .hero-flat — NO PHOTOGRAPH UNDER THE HERO.

   This client has two real job photographs, both portrait-orientation, and
   neither gives copy a large, evenly-dark, detail-free area to sit on. Cropping
   either one to a 1600x900 hero would invent a frame the photographer never
   took. So the hero ground is flat --panel and white on it measures 17.75:1 —
   there is no scrim, and therefore nothing to measure. That is why this build
   ships no scrim script: run _raw/pal.py, not a scrim tool.

   SPECIFICITY, NOT SOURCE ORDER: .hero::before is defined ~40 lines above but
   the hero VARIANTS in this file are scattered, and this line has already
   shipped a .hero-plain::before that lost on source order and silently did
   nothing. .hero.hero-flat::before is 0,2,0 and wins wherever it sits.
   ============================================================ */
.hero-flat { background: var(--panel); }
.hero.hero-flat::before { display: none; }
.hero-inner { padding-block: clamp(4rem, 9vw, 6.25rem); max-width: 1000px; }
.hero h1 { color: #fff; margin-bottom: 1.35rem; text-shadow: 0 2px 24px rgba(0,0,0,.35); }
.hero h1 .acc { color: var(--acc-d); }
.hero .lead { color: rgba(255,255,255,.9); margin-bottom: 2rem; }
.hero .btn-row { margin-bottom: 2rem; }
.hero-note { display: flex; align-items: center; gap: .6rem; font-size: .98rem; color: rgba(255,255,255,.85); }
.hero-note svg { color: var(--acc-d); width: 1.15em; height: 1.15em; flex: none; }

/* hero with inline quote form (home) — asymmetric split */
.hero-split .hero-inner { max-width: none; display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.hero-split .hero-copy { max-width: 40rem; }
@media (max-width: 1024px) { .hero-split .hero-inner { grid-template-columns: 1fr; } }

/* ---------- quote form card ---------- */
.quote-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: clamp(1.5rem, 3vw, 2rem); box-shadow: var(--shadow-lg); color: var(--ink-2); }
.quote-card h2, .quote-card h3 { font-size: clamp(1.5rem, 2.6vw, 1.9rem); margin-bottom: .3rem; }
.quote-card > p { color: var(--ink-3); font-size: .95rem; margin-bottom: 1.1rem; }
.form { display: grid; gap: 1rem; }
.field { display: grid; gap: .4rem; }
.field.row2 { grid-template-columns: 1fr 1fr; gap: 1rem; }
.field label { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; font-size: .9rem; color: var(--ink); }
.field input, .field select, .field textarea {
  font: inherit; color: var(--ink); background: var(--white);
  border: 1.5px solid var(--line); border-radius: var(--r); padding: .8rem .9rem; width: 100%;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.field textarea { resize: vertical; min-height: 120px; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--acc); outline: none; box-shadow: 0 0 0 3px var(--acc-soft); }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form .btn { margin-top: .3rem; }
.form-note { font-size: .85rem; color: var(--ink-3); }
/* SMS consent box. A2P 10DLC wants express written consent captured separately from
   the form submission itself, which is why this is a real checkbox and NOT a line of
   fine print: ticking it is the consent, and the .consent note below says so. The
   wording is matched to the SMS section of terms.html and privacy-policy.html — if one
   changes, all three change. */
.field-check {
  display: grid; grid-template-columns: auto 1fr; gap: .7rem;
  align-items: start; padding: .9rem 1rem;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--r);
}
.field-check input[type="checkbox"] {
  width: 20px; height: 20px; margin-top: .1rem; flex: none;
  accent-color: var(--acc); cursor: pointer;
}
.field-check span { font-size: .82rem; line-height: 1.5; color: var(--ink-2); cursor: pointer; }
.field-check:focus-within { border-color: var(--acc); box-shadow: 0 0 0 3px var(--acc-soft); }
/* Missed-tick state. :user-invalid, NOT :invalid — a required unchecked box is
   :invalid from the moment the page loads, so :invalid would paint every form red
   before the visitor has touched anything. :user-invalid only applies after they
   interact or try to submit, which is the moment the warning is useful.
   Wrapped in :has() so the whole box reacts, not just the input. Degrades to the
   browser's own validation bubble where either selector is unsupported. */
.field-check:has(input:user-invalid) { border-color: var(--acc); background: var(--acc-soft); }
.field-check b { color: var(--acc-2); }

.consent { font-size: .78rem; color: var(--ink-3); line-height: 1.5; }
.consent a { color: var(--acc-2); text-decoration: underline; }
.form-error { color: #9f1d1d; background: #fff1f1; border: 1px solid #e8b4b4; padding: .7rem .8rem; margin-top: .8rem; font-weight: 600; }
.form button[aria-busy="true"] { cursor: wait; opacity: .7; }
.form-success { text-align: center; padding: 1.5rem .5rem; }
.form-success svg { color: var(--acc-2); margin: 0 auto 1rem; width: 46px; height: 46px; }
.form-success h3 { margin-bottom: .5rem; }
.form-success a { color: var(--acc-2); font-weight: 700; }

/* ---------- page hero (interior) ---------- */
.page-hero { position: relative; color: #fff; isolation: isolate; }
.page-hero .hero-media { position: absolute; inset: 0; z-index: -2; }
.page-hero .hero-media img { width: 100%; height: 100%; object-fit: cover; }
.page-hero::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(180deg, rgba(15,13,10,.62), rgba(15,13,10,.82)); }
/* Interior page heroes. The third stop is a hardcoded literal on purpose - it is the
   warm end of the navy-to-bronze sweep and has no token, because no other surface uses
   it. It MUST be re-picked whenever the theme is reskinned: the parent build left a
   green #2e3b34 here, which survived the palette swap and put a green cast on every
   interior page of a navy-and-bronze site. White text on this stop measures 12.88:1
   and --acc-d on it 7.24:1. */
.page-hero.plain { background: linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 65%, #3a1512 100%); }
.page-hero.plain::before { display: none; }
.page-hero-inner { padding-block: clamp(3.5rem, 8vw, 6rem); max-width: 820px; }
.page-hero h1 { color: #fff; margin-bottom: .9rem; }
.page-hero p { color: rgba(255,255,255,.9); max-width: 60ch; }
.crumbs { font-family: var(--font-display); text-transform: uppercase; letter-spacing: .14em; font-size: .9rem; color: rgba(255,255,255,.75); margin-bottom: 1rem; }
/* TEMPLATE BUG, fixed here: this was var(--acc), which is the FILL-ONLY accent role.
   On the .page-hero.plain ground it measures 2.57:1 — the breadcrumb vanished on hover.
   --acc-d is the text-on-dark role and measures 7.72:1 on --panel, 6.61:1 on the warm
   stop. Every sibling build still carries the --acc version. */
.crumbs a:hover { color: var(--acc-d); }
.crumbs span { color: rgba(255,255,255,.45); margin: 0 .3rem; }

/* ---------- trust strip ---------- */
.trust { background: var(--ink); color: #fff; border-top: 3px solid var(--acc); }
.trust-grid { display: grid; grid-template-columns: repeat(4,1fr); }
.trust-item { display: flex; align-items: center; gap: .9rem; padding: 1.4rem var(--gutter); }
.trust-item + .trust-item { border-left: 1px solid var(--line-d); }
.trust-item svg { width: 30px; height: 30px; color: var(--acc-d); flex: none; }
.trust-item .t-top { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; font-size: 1.18rem; line-height: 1; color: #fff; }
.trust-item .t-sub { font-size: .86rem; color: rgba(255,255,255,.62); }
.stars { color: var(--gold); letter-spacing: 1px; }

/* ---------- cards ---------- */
/* position:relative is LOAD-BEARING — .svc-tag ("Most requested" / "Winter") is an
   absolutely-positioned child that lives inside .svc-body. Without a positioned
   ancestor it anchors to the document instead and both badges pile up in the page's
   top-left corner, overlapping the header. INHERITED BUG: .card is unpositioned in
   the sjohnson parent and therefore in every sibling build that shows a svc-tag. */
.card { position: relative; background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-sm); transition: transform .25s var(--ease), box-shadow .25s var(--ease); display: flex; flex-direction: column; }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }

/* .xlink — a whole card that is one link. Used for the service↔service and
   service↔city cross-link grids on the service and city pages. It only needs
   the padding .svc-body would have given it and a visible focus ring, because
   .card already carries the border, shadow and hover lift and `a { color:
   inherit }` keeps the type looking like type. */
.card.xlink { padding: 1.4rem 1.5rem 1.5rem; }
.card.xlink h3 { margin-bottom: .5rem; }
.card.xlink p { color: var(--ink-2); font-size: 1rem; margin-bottom: 1rem; }
.card.xlink .svc-link { margin-top: auto; }
.card.xlink:focus-visible { outline: 3px solid var(--acc-2); outline-offset: 2px; }
.svc-card .svc-photo { aspect-ratio: 16/11; overflow: hidden; position: relative; }
.svc-card .svc-photo img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.svc-card:hover .svc-photo img { transform: scale(1.06); }
.svc-photo.no-photo {
  background:
    linear-gradient(140deg, var(--panel) 0%, var(--panel-2) 100%);
  display: grid; place-items: center;
}
.svc-photo.no-photo svg { width: 34%; height: auto; max-width: 120px; color: var(--acc-d); opacity: .9; }
.svc-card:hover .svc-photo.no-photo svg { transform: none; }

/* ============================================================
   PHOTO PLACEHOLDERS — .ph
   This client has NO photographs at all: no website, no Facebook page, and an
   unclaimed Google Business Profile with zero images. Putting stock photography
   on a contractor's site implies it is their work, so every image slot on this
   build renders as an obviously-labelled placeholder instead, and gallery.html
   says so in plain text on the page.
   .ph is deliberately the SAME treatment as the hero art, so the empty slots
   read as a design decision rather than as broken images.
   DELETE this block and swap in <img> the day real photographs arrive.
   ============================================================ */
.ph {
  background:
    radial-gradient(110% 80% at 78% 12%, color-mix(in srgb, var(--acc) 20%, transparent) 0%, transparent 60%),
    linear-gradient(150deg, var(--panel-2) 0%, var(--panel) 100%);
  display: grid; place-content: center; justify-items: center; gap: .55rem;
  text-align: center; padding: 1.25rem; position: relative; overflow: hidden;
  width: 100%; height: 100%;
}
.ph::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(118deg, rgba(255,255,255,.03) 0 1px, transparent 1px 34px);
}
.ph svg { width: 40px; height: 40px; color: var(--acc-d); opacity: .95; position: relative; }
.ph b {
  position: relative; font-family: var(--font-display); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; font-size: 1.02rem;
  line-height: 1.15; color: #fff;
}
.ph i {
  position: relative; font-style: normal; font-size: .76rem; letter-spacing: .07em;
  text-transform: uppercase; color: rgba(255,255,255,.58);
}
/* The .ph fills its slot absolutely so it inherits the slot's aspect-ratio. Each of
   these parents MUST be positioned or the placeholder escapes to the viewport and
   covers the whole page — .figure had no position of its own, which is exactly what
   happened the first time this rendered. */
.gallery-item, .car-item, .figure { position: relative; }
.gallery-item .ph, .car-item .ph, .figure .ph { position: absolute; inset: 0; }
.figure.tall .ph b { font-size: 1.15rem; }

.svc-tag { position: absolute; top: .9rem; left: .9rem; background: var(--acc); color: #fff; font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; font-size: .82rem; padding: .28rem .7rem; border-radius: 4px; }
/* .svc-tag is absolute against .card, which is right when it sits over a
   photograph and WRONG on a text-only card, where it lands straight on top of the
   <h3>. Land Clearing has carried a tag on a photoless card since this build
   started and the title was half-hidden behind it; adding Drainage made it two.
   The builders add .svc-tag-flow when a card has no photo — .svc-body is a flex
   column, so static + align-self is all it needs to sit above the title. */
.svc-tag-flow { position: static; align-self: flex-start; margin-bottom: .75rem; }
.svc-body { padding: 1.4rem 1.5rem 1.6rem; flex: 1; display: flex; flex-direction: column; }
.svc-body h3 { margin-bottom: .5rem; }
/* The card heading is a link to the service's own page. `a { color: inherit }`
   already makes it look like the heading, so all it needs is a hover affordance. */
.svc-body h3 a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* TEMPLATE BUG, fixed here. This was `flex: 1` on EVERY paragraph. .svc-body is a
   flex column, so in a grid row whose height is set by a taller sibling card, all
   the slack got divided between the paragraphs — punching a ragged gap between each
   one and the next. It only shows when cards in a row differ in height, which needs
   either multi-paragraph bodies or a photo on some cards and not others. This build
   has both: only two of six service cards carry a photograph, because only two real
   job photographs exist.
   Slack now collects in one place — above the link, via .svc-link's margin-top —
   so bodies stay tight at the top and every card's link still lines up at the
   bottom of its row. */
.svc-body p { color: var(--ink-2); font-size: 1rem; margin-bottom: 1rem; }
.svc-link { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink); display: inline-flex; align-items: center; gap: .45rem; align-self: flex-start; margin-top: auto; padding-top: .35rem; }
.svc-link svg { width: 1em; height: 1em; color: var(--acc-2); transition: transform .2s var(--ease); }
.svc-card:hover .svc-link svg, .svc-link:hover svg { transform: translateX(4px); }

/* feature / why-choose */
.feature { padding: 1.6rem 0; }
.feature .f-num { font-family: var(--font-display); font-weight: 700; font-size: 2.6rem; color: var(--acc); line-height: 1; }
.feature h3 { margin: .7rem 0 .5rem; }
.feature p { color: var(--ink-2); font-size: 1rem; }
.feature.on-line { border-top: 1px solid var(--line); padding-top: 1.6rem; }
.on-dark .feature.on-line { border-top-color: var(--line-d); }
.on-dark .feature p { color: rgba(255,255,255,.72); }
.icon-badge { width: 56px; height: 56px; border-radius: var(--r); background: var(--acc-soft); color: var(--acc-2); display: grid; place-items: center; margin-bottom: 1rem; }
.on-dark .icon-badge { background: rgba(255,255,255,.1); color: var(--acc-d); }
.icon-badge svg { width: 28px; height: 28px; }

/* stat row */
.stats { display: grid; grid-template-columns: repeat(4,1fr); gap: clamp(1rem,3vw,2rem); }
.stat .s-num { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.6rem,5vw,3.6rem); color: var(--ink); line-height: 1; }
.on-dark .stat .s-num { color: #fff; }
.stat .s-num .u { color: var(--acc); }
.stat .s-label { text-transform: uppercase; letter-spacing: .1em; font-size: .86rem; font-family: var(--font-display); font-weight: 600; color: var(--ink-3); margin-top: .35rem; }
.on-dark .stat .s-label { color: rgba(255,255,255,.6); }

/* process / steps */
.steps { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); grid-template-columns: repeat(4, 1fr); counter-reset: step; }
.step { position: relative; padding-top: 1.4rem; border-top: 3px solid var(--line); }
.step .step-n { font-family: var(--font-display); font-weight: 700; font-size: 1rem; letter-spacing: .1em; color: var(--acc-2); }
.step h3 { margin: .5rem 0 .4rem; }
.step p { color: var(--ink-2); font-size: 1rem; }
.step.active { border-top-color: var(--acc); }

/* ---------- testimonials ---------- */
/* Rating summary line. Sits directly above the two quote tiles and is the only
   place a bare number appears, so the denominator is part of the component and
   not optional copy: "5.0" alone reads as a hundred reviews, and this company
   has two. The link out to the listing is deliberate — a rating you can check
   in one click is worth more than a bigger one you cannot. */
.rating-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: .3rem .75rem; }
.rating-line .q-stars { color: var(--gold); font-size: 1.35rem; letter-spacing: 2px; line-height: 1; }
.on-dark .rating-line .q-stars { color: var(--acc-d); }
.rating-line strong { font-family: var(--font-display); font-size: clamp(2rem, 4vw, 2.6rem); line-height: 1; color: var(--ink); }
.on-dark .rating-line strong { color: #fff; }
.rating-line .rating-meta { font-size: .98rem; color: var(--ink-2); }
.on-dark .rating-line .rating-meta { color: rgba(255,255,255,.78); }
.rating-line .rating-meta b { font-weight: 600; color: inherit; }

.quote-tile { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 1.75rem; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: 1rem; }
.on-dark .quote-tile { background: var(--panel-2); border-color: var(--line-d); }
/* Stars are gold, not green — the one conventional colour on the page. They are
   decorative glyphs with an aria-label and a numeric rating in text beside them,
   so --gold's 3.28:1 is measured against the 3:1 non-text bar, not 4.5:1. */
.quote-tile .q-stars { color: var(--gold); font-size: 1.1rem; letter-spacing: 1px; }
.quote-tile blockquote { font-size: 1.08rem; color: var(--ink); line-height: 1.55; }
.on-dark .quote-tile blockquote { color: #fff; }
.quote-tile .q-who { display: flex; align-items: center; gap: .75rem; margin-top: auto; }
.q-avatar { width: 44px; height: 44px; border-radius: 50%; background: var(--acc); color: #fff; display: grid; place-items: center; font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; flex: none; }
/* display:block is load-bearing — the attribution markup uses <span>s, so without it the
   reviewer name and the "Google review · 3 months ago" line run together as one sentence. */
.q-name { display: block; font-weight: 600; color: var(--ink); font-family: var(--font-display); text-transform: uppercase; letter-spacing: .03em; font-size: 1.05rem; line-height: 1.1; }
.on-dark .q-name { color: #fff; }
.q-meta { display: block; font-size: .85rem; color: var(--ink-3); margin-top: .15rem; }
.on-dark .q-meta { color: rgba(255,255,255,.55); }

/* ---------- CTA band ---------- */
.cta-band { background: var(--panel); color: #fff; border-radius: var(--r-lg); padding: clamp(2.5rem,6vw,4.5rem); position: relative; overflow: hidden; text-align: center; }
.cta-band h2 { color: #fff; margin-bottom: 1rem; position: relative; }
.cta-band p { color: rgba(255,255,255,.8); margin: 0 auto 2rem; position: relative; }
.cta-band .btn-row { justify-content: center; position: relative; }
.cta-phone { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.7rem,3.4vw,2.4rem); color: var(--acc-d); display: inline-block; position: relative; }

/* ---------- gallery ---------- */
.gallery-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: clamp(.75rem,1.5vw,1.1rem); }
.gallery-item { position: relative; overflow: hidden; border-radius: var(--r); aspect-ratio: 4/3; margin: 0; }
.gallery-item.wide { grid-column: span 2; aspect-ratio: 8/5; }
.gallery-item img, .gallery-item video { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.gallery-item:hover img { transform: scale(1.05); }
.gallery-item figcaption {
  position: absolute; inset: auto 0 0 0; padding: 1.6rem 1.1rem .9rem;
  background: linear-gradient(0deg, rgba(15,13,10,.85), transparent);
  color: #fff; font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; font-size: 1.02rem;
  transform: translateY(6px); opacity: .92; transition: transform .3s var(--ease);
}
.gallery-item:hover figcaption { transform: translateY(0); opacity: 1; }
.g-tag { color: var(--acc); font-size: .78rem; letter-spacing: .14em; display: block; }

/* infinite carousel */
.carousel { position: relative; }
.car-track { display: flex; gap: clamp(.75rem,1.5vw,1.1rem); overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; padding-block: .25rem; }
.car-track::-webkit-scrollbar { display: none; }
.car-item { flex: 0 0 clamp(280px, 32vw, 440px); margin: 0; position: relative; overflow: hidden; border-radius: var(--r); aspect-ratio: 4/3; }
.car-item img { width: 100%; height: 100%; object-fit: cover; }
.car-item figcaption { position: absolute; inset: auto 0 0 0; padding: 1.6rem 1.1rem .9rem; background: linear-gradient(0deg, rgba(15,13,10,.85), transparent); color: #fff; font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: .05em; font-size: 1.02rem; }
.car-btn { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; width: 44px; height: 44px; border-radius: 50%; border: 0; cursor: pointer; background: rgba(15,13,10,.72); color: #fff; display: grid; place-items: center; transition: background .18s var(--ease); }
.car-btn:hover { background: rgba(15,13,10,.92); }
.car-btn svg { width: 22px; height: 22px; }
.car-btn.prev { left: .6rem; } .car-btn.next { right: .6rem; }
/* gallery-page carousel: bigger frames than the home teaser, plus the hover
   zoom the old collage grid had */
.carousel-lg .car-item { flex: 0 0 clamp(300px, 46vw, 620px); aspect-ratio: 3/2; }
.carousel-lg .car-item img { transition: transform .5s var(--ease); }
.carousel-lg .car-item:hover img { transform: scale(1.04); }

/* ---------- figures / image blocks ---------- */
.figure { border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-md); }
.figure img { width: 100%; }
.figure.tall { aspect-ratio: 4/5; }
.figure.tall img { height: 100%; object-fit: cover; }


/* checklist */
.checks { display: grid; gap: .85rem; margin-top: 1.5rem; }
.checks.checks-2col { grid-template-columns: 1fr 1fr; gap: 1rem 2rem; }
@media (max-width: 620px) { .checks.checks-2col { grid-template-columns: 1fr; } }
/* TEMPLATE BUG, fixed here. This was `display: flex` with a gap, which works only
   while the <li> contains the icon plus ONE run of plain text. The moment an item
   contains inline markup — a <strong>, a link, an <em> — every one of those becomes
   a SEPARATE FLEX ITEM in its own column, and the sentence renders as three or four
   ragged columns with gaps punched between them. That is exactly what the honest-
   accounting list on why-us.html and the "where to find us" list on about.html do.
   Every sibling build hid this by only ever putting bare text in a .checks li.

   Fixed as normal inline flow with the icon absolutely positioned in the gutter, so
   arbitrary inline markup inside an item just works. Plain-text items render
   identically to before. */
.checks li { position: relative; padding-left: 1.95rem; color: var(--ink); }
.checks li svg { position: absolute; left: 0; top: .2em; width: 22px; height: 22px; color: var(--acc-2); }
.on-dark .checks li, .on-dark .checks li strong { color: #fff; }
.on-dark .checks li svg { color: var(--acc-d); }

/* pill list (service areas / tags) */
.pill-list { display: flex; flex-wrap: wrap; gap: .6rem; }
/* Clicking a town in the Service Area dropdown highlights that pill, so the link lands
   on something instead of just scrolling to a wall of identical chips. */
.pill-list li:target { background: var(--acc); color: #fff; border-color: var(--acc); box-shadow: 0 0 0 4px var(--acc-soft); }
.pill-list li { scroll-margin-top: 7rem; }
/* The service-area page is split per county so the nav dropdown has real anchors to
   point at (#jefferson-county / #st-louis-county). scroll-margin-top keeps the heading
   clear of the sticky header when one of those anchors is followed. */
.area-group { margin-top: 1.6rem; }
.area-group h3 { font-size: 1.2rem; scroll-margin-top: 6rem; }
.pill-list li, .pill { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; font-size: 1rem; padding: .5rem 1rem; border: 1px solid var(--line); border-radius: 40px; background: var(--white); color: var(--ink); }
.pill-list li.primary, a.pill.primary { background: var(--ink); color: #fff; border-color: var(--ink); }
a.pill { transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease); }
a.pill:hover { background: var(--acc); color: #fff; border-color: var(--acc); }

/* ---------- FAQ ---------- */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary { list-style: none; cursor: pointer; padding: 1.35rem 0; display: flex; align-items: center; justify-content: space-between; gap: 1rem; font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .02em; font-size: 1.22rem; color: var(--ink); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary .ico { width: 30px; height: 30px; border-radius: 50%; background: var(--bg-2); display: grid; place-items: center; flex: none; transition: background .2s; position: relative; }
.faq summary .ico::before, .faq summary .ico::after { content: ""; position: absolute; background: var(--ink); border-radius: 2px; }
.faq summary .ico::before { width: 13px; height: 2.5px; }
.faq summary .ico::after { width: 2.5px; height: 13px; transition: transform .25s var(--ease); }
.faq details[open] summary .ico { background: var(--acc); }
.faq details[open] summary .ico::before, .faq details[open] summary .ico::after { background: #fff; }
.faq details[open] summary .ico::after { transform: scaleY(0); }
.faq .faq-body { padding: 0 0 1.4rem; color: var(--ink-2); max-width: 72ch; }

/* ---------- contact ---------- */
.contact-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: clamp(1.5rem,3vw,2.2rem); box-shadow: var(--shadow-sm); }
.contact-line { display: flex; gap: 1rem; padding: 1.15rem 0; border-bottom: 1px solid var(--line); align-items: flex-start; }
.contact-line:last-child { border-bottom: 0; }
.contact-line .ci { width: 46px; height: 46px; border-radius: var(--r); background: var(--acc-soft); color: var(--acc-2); display: grid; place-items: center; flex: none; }
.contact-line .ci svg { width: 22px; height: 22px; }
.contact-line .cl-label { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; font-size: .82rem; color: var(--ink-3); }
/* min-width:0 is load-bearing: the value cell is a flex child, and a long unbroken
   token (an email address) otherwise sets the card's min-content width and pushes
   the whole contact panel past the viewport on a phone. `overflow-wrap: anywhere`
   then lets that token actually break. Found on this build because the placeholder
   email is 42 characters; the same bug is latent in every sibling with a shorter
   one. Do not remove either declaration. */
.contact-line > div:not(.ci) { min-width: 0; }
.contact-line .cl-val { font-size: 1.1rem; color: var(--ink); font-weight: 500; overflow-wrap: anywhere; }
.contact-line a.cl-val:hover { color: var(--acc-2); }
.map-embed { border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line); background: var(--bg-2); box-shadow: var(--shadow-sm); min-height: 320px; }
.map-embed iframe { display: block; width: 100%; height: 100%; min-height: 340px; border: 0; }

/* booking calendar embed slot */
.calendar-embed { border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow-sm); background: var(--white); }
.calendar-embed iframe { display: block; width: 100%; min-height: 680px; border: 0; }

/* ---------- financing ---------- */
.fin-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 1.75rem; box-shadow: var(--shadow-sm); }
.fin-card h3 { margin-bottom: .5rem; }
.fin-card .fin-big { font-family: var(--font-display); font-weight: 700; font-size: 2.4rem; color: var(--acc-2); line-height: 1; margin: .4rem 0 .8rem; }

/* ---------- article / blog ---------- */
.post-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-sm); transition: transform .25s var(--ease), box-shadow .25s var(--ease); display: flex; flex-direction: column; }
.post-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.post-card .post-photo { aspect-ratio: 16/9; overflow: hidden; }
.post-card .post-photo img { width: 100%; height: 100%; object-fit: cover; }
.post-body { padding: 1.4rem 1.5rem 1.6rem; display: flex; flex-direction: column; gap: .6rem; flex: 1; }
.post-cat { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: .1em; font-size: .78rem; color: var(--acc-2); }
.post-card h3 { font-size: 1.35rem; }
.post-card p { color: var(--ink-2); font-size: 1rem; flex: 1; }
.post-meta { font-size: .85rem; color: var(--ink-3); }

.article { max-width: 720px; margin-inline: auto; }
.article > * + * { margin-top: 1.35rem; }
.article h2 { font-size: clamp(1.7rem, 3vw, 2.3rem); margin-top: 2.5rem; }
.article h3 { margin-top: 2rem; }
.article p, .article li { color: var(--ink-2); font-size: 1.12rem; line-height: 1.7; max-width: none; }
.article ul, .article ol { padding-left: 1.3rem; display: grid; gap: .6rem; }
.article ul { list-style: disc; } .article ol { list-style: decimal; }
.article a { color: var(--acc-2); text-decoration: underline; }
.article blockquote { border-left: 3px solid var(--acc); padding: .3rem 0 .3rem 1.3rem; font-size: 1.25rem; color: var(--ink); font-style: italic; }
.article img, .article .figure { border-radius: var(--r-lg); margin-block: 2rem; }

/* ---------- legal prose ---------- */
.legal { max-width: 780px; margin-inline: auto; }
.legal > * + * { margin-top: 1.1rem; }
.legal h2 { font-size: clamp(1.5rem, 2.6vw, 2rem); margin-top: 2.5rem; }
.legal h3 { margin-top: 1.6rem; font-size: 1.25rem; }
.legal p, .legal li { color: var(--ink-2); max-width: none; }
.legal ul { list-style: disc; padding-left: 1.3rem; display: grid; gap: .5rem; }
.legal a { color: var(--acc-2); text-decoration: underline; }
.legal .updated { color: var(--ink-3); font-size: .95rem; }


/* ---------- footer ---------- */
.site-footer { background: var(--panel); color: rgba(255,255,255,.68); padding-top: clamp(3rem,6vw,4.5rem); border-top: 3px solid var(--acc-d); }
/* FIVE columns since the Where We Work column was added: brand, Explore,
   What We Do, Where We Work, Get in Touch. The 900px breakpoint below drops
   it to two and the 600px one to a single column, so the extra column only
   exists at a width that can carry it. */
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr 1.2fr; gap: clamp(1.5rem,3vw,2.4rem); padding-bottom: 3rem; }
/* The footer wordmark. The client's original is white type on solid black, so its
   LUMINANCE IS ITS ALPHA — keyed once, the ink colour is then a free choice, which is
   how one source file (_raw/src/logo-wordmark.png) serves three surfaces:
     wordmark.webp       white  -> here, on --panel
     wordmark-ink.webp   --ink  -> the light header (.brand-logo)
     favicon.png         white on --panel, the BR2 mark alone
   All three are cropped identically and all three DROP the phone number baked into the
   original, because the header and footer publish it as real linkable text instead —
   two copies of a phone number drift. */
.footer-wordmark { width: 280px; max-width: 100%; height: auto; }
/* Facebook is the ONLY social account this client has. No Instagram, no TikTok, no BBB,
   and the Yelp listing is one they cancelled — so there is no icon row, just this link.
   The label is wrapped in a <span> deliberately: this is an inline-flex row with a gap,
   and a bare <sup> inside it becomes a THIRD FLEX ITEM that collects the gap on both
   sides, rendering "BR  2  on Facebook". The span keeps the superscript inline. */
.footer-social { display: inline-flex; align-items: center; gap: .55rem; margin-top: 1.15rem; color: #fff; font-family: var(--font-display); text-transform: uppercase; letter-spacing: .05em; font-size: .95rem; transition: color .15s; }
.footer-social svg { width: 1.2em; height: 1.2em; color: var(--acc-d); flex: none; transition: color .15s; }
.footer-social:hover, .footer-social:hover svg { color: var(--acc-d); }
.site-footer p { color: rgba(255,255,255,.62); font-size: .98rem; margin-top: 1rem; max-width: 34ch; }
/* Footer column headings. These were <h4> under a page whose previous heading is an
   <h2> — a two-level skip on every page, and on 404.html a skip straight from the
   <h1>. They are siblings of the page's own sections, so they are <h2> now and
   .footer-h holds the size that h4 used to give them. */
.footer-col .footer-h { color: #fff; font-size: 1.05rem; letter-spacing: .06em; margin-bottom: 1.1rem; }

/* .col-label — the four column labels on services.html ("Water", "Ground", ...).
   They were <h4> purely for their size, which skipped a level under the section's
   <h2>. They are <h3> now for structure, and this rule keeps the 1.2rem they have
   always rendered at; a bare <h3> is 1.75rem and made them as loud as a service
   card title. Same trade as .footer-h directly above. */
.col-label { font-size: 1.2rem; }
.footer-col ul { display: grid; gap: .6rem; }
.footer-col a { color: rgba(255,255,255,.68); font-size: 1rem; transition: color .15s; }
.footer-col a:hover { color: var(--acc-d); }
.footer-contact li { display: flex; gap: .65rem; align-items: flex-start; margin-bottom: .85rem; color: rgba(255,255,255,.78); font-size: 1rem; }
.footer-contact svg { width: 19px; height: 19px; color: var(--acc-d); flex: none; margin-top: 3px; }
.footer-review { display: inline-flex; align-items: center; gap: .5rem; margin-top: .4rem; color: #fff !important; font-family: var(--font-display); text-transform: uppercase; letter-spacing: .04em; font-size: .95rem; }
.footer-review svg { color: var(--acc-d); width: 1.1em; height: 1.1em; }
.footer-bottom { border-top: 1px solid var(--line-d); padding: 1.5rem 0; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: center; font-size: .86rem; color: rgba(255,255,255,.5); }
.footer-bottom a { color: rgba(255,255,255,.6); }
.footer-bottom a:hover { color: var(--acc-d); }
.footer-legal { display: flex; gap: 1.1rem; flex-wrap: wrap; align-items: center; }
/* These are real links now (Privacy Policy / Terms), not the plain spans the parent
   had. .footer-bottom a already sets the colour; the underline is what marks them as
   links against the locality span sitting beside them. */
.footer-legal a { text-decoration: underline; text-underline-offset: 2px; }

/* ---------- sticky mobile call/text/quote bar ---------- */
.sticky-bar { display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 58; background: var(--panel); border-top: 2px solid var(--acc-d); padding: .6rem .7rem calc(.6rem + env(safe-area-inset-bottom)); gap: .5rem; }
.sticky-bar .btn { flex: 1; padding: .8rem .4rem; font-size: .98rem; }

/* skip link */
.skip { position: absolute; left: -999px; top: 0; z-index: 100; background: var(--ink); color: #fff; padding: .8rem 1.2rem; border-radius: 0 0 var(--r) 0; }
.skip:focus { left: 0; }

/* ---------- chat widget ---------- */
.chat-widget { position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 70; display: flex; flex-direction: column; align-items: flex-end; gap: .8rem; }
.chat-bubble {
  width: 58px; height: 58px; border-radius: 50%; border: 0;
  background: var(--acc); color: #fff; display: grid; place-items: center;
  box-shadow: var(--shadow-md);
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.chat-bubble:hover { transform: translateY(-2px); background: var(--acc-2); }
.chat-bubble svg { width: 26px; height: 26px; }
.chat-bubble.open .ico-chat { transform: scale(.9); }
.chat-card {
  width: min(340px, calc(100vw - 2.2rem));
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.chat-head { background: var(--panel); color: #fff; padding: 1rem 1.1rem; display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; border-bottom: 3px solid var(--acc-d); }
.chat-head strong { color: #fff; font-family: var(--font-display); text-transform: uppercase; letter-spacing: .04em; font-size: 1.15rem; display: block; line-height: 1.1; }
.chat-head span { font-size: .82rem; color: rgba(255,255,255,.65); }
.chat-close { background: none; border: 0; color: rgba(255,255,255,.7); padding: .2rem; flex: none; }
.chat-close:hover { color: #fff; }
.chat-close svg { width: 18px; height: 18px; }
.chat-body { padding: 1rem 1.1rem 1.1rem; }
.chat-body .form { gap: .7rem; }
.chat-body .field input, .chat-body .field textarea { padding: .6rem .7rem; font-size: .95rem; }
.chat-body .consent { font-size: .7rem; }
.chat-alt { margin-top: .7rem; font-size: .82rem; color: var(--ink-3); text-align: center; }
.chat-alt a { color: var(--acc-2); font-weight: 600; }

/* ---------- signature details (restrained: hierarchy, not decoration) ---------- */
/* ghost section numerals — subtle outlined numeral behind the section label (aids hierarchy) */
.section-head { position: relative; }
.section-head .eyebrow .num {
  position: absolute; top: -.55em; left: -.06em; z-index: -1;
  font-size: clamp(4.5rem, 9vw, 7.5rem); font-weight: 700; line-height: 1;
  color: transparent; -webkit-text-stroke: 1.5px var(--gold);
  opacity: .18; letter-spacing: 0; pointer-events: none;
}
.section-head.center .eyebrow .num { left: 50%; transform: translateX(-50%); }
/* Hero border only — the duplicate ::before override that lived here was a scrim
   for a photograph this client does not have. Removed; see the HERO ART note above. */
.hero { border-bottom: 2px solid var(--acc); }
/* gallery tag chips: straight, solid accent label */
.g-tag { display: inline-block; background: var(--acc); color: #fff; padding: .18rem .55rem; border-radius: 3px; margin: 0 .5rem .3rem 0; font-weight: 700; }

@media (max-width: 760px) {
  .chat-widget { bottom: calc(64px + .9rem + env(safe-area-inset-bottom)); right: .9rem; }
  .chat-bubble { width: 52px; height: 52px; }
}

/* ---------- reveal ---------- */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
.reveal.d1 { transition-delay: .08s; } .reveal.d2 { transition-delay: .16s; }
.reveal.d3 { transition-delay: .24s; } .reveal.d4 { transition-delay: .32s; }

/* ---------- utilities ---------- */
.text-center { text-align: center; }
.mt-1{margin-top:.5rem}.mt-2{margin-top:1rem}.mt-3{margin-top:1.5rem}.mt-4{margin-top:2rem}
.mb-0{margin-bottom:0}.maxw-none{max-width:none}
.accent-bar { width: 60px; height: 4px; background: var(--acc); border: 0; margin: 1.25rem 0; }
.accent-bar.center { margin-inline: auto; }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 980px) {
  .cols-4 { grid-template-columns: repeat(2,1fr); }
  .cols-3 { grid-template-columns: repeat(2,1fr); }
  .steps { grid-template-columns: repeat(2,1fr); }
  .trust-grid { grid-template-columns: repeat(2,1fr); }
  .trust-item:nth-child(3) { border-left: 0; }
  .trust-item { border-top: 1px solid var(--line-d); }
  .trust-item:nth-child(-n+2) { border-top: 0; }
  .stats { grid-template-columns: repeat(2,1fr); gap: 2rem 1.5rem; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 1180px) {
  .nav-links, .nav-phone { display: none; }
  .nav-toggle { display: block; }
  .nav-cta .btn { display: none; }
}
@media (max-width: 760px) {
  /* No mobile scrim override: there is no hero photograph. See HERO ART above. */
  .split { grid-template-columns: 1fr; gap: 2rem; }
  .split.reverse .split-media { order: -1; }
  .cols-3, .cols-4, .cols-2, .steps { grid-template-columns: 1fr; }
  .gallery-grid { grid-template-columns: 1fr 1fr; }
  .gallery-item.wide { grid-column: span 2; }
  .field.row2 { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .footer-bottom { flex-direction: column; text-align: center; }
  .sticky-bar { display: flex; }
  body { padding-bottom: 64px; }   /* room for sticky bar */
  h1 { font-size: clamp(2.05rem, 8.5vw, 2.9rem); }
}
@media (max-width: 460px) {
  .gallery-grid { grid-template-columns: 1fr; }
  .gallery-item.wide { grid-column: auto; }
  .stats { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .reveal { opacity: 1; transform: none; }
  .hero-media img, .svc-card:hover .svc-photo img, .gallery-item:hover img, .carousel-lg .car-item:hover img { transform: none !important; }
}

/* ---------- utilities replacing inline style attributes ----------
   These exist so the CSP can drop style-src 'unsafe-inline'. Don't
   reintroduce style="" attributes: they will be blocked. */
.img-cover { width: 100%; height: 100%; object-fit: cover; }
.bg-white { background: #fff; }
.wrap-wide { max-width: 1360px; }
.jc-center { justify-content: center; }
.pi-0 { padding-inline: 0; }

/* --- offer callout: the 10% repeat-customer discount ---------------------
   Matches the wittyrealty build, which is the reference layout for this line. The
   discount was previously only a clause in the hero note, the CTA bands and one FAQ
   row; witty gives it a section of its own and it converts better with one.
   --acc-2 for the numeral, not --acc: this sits on --white, where --acc is a FILL
   colour and would measure 4.02:1 as display type. --acc-2 is the text-on-light role
   (9.02:1 on white). */
.offer {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: clamp(1rem, 3vw, 2.25rem);
  background: var(--white); border: 1px solid var(--line);
  border-left: 5px solid var(--acc); border-radius: var(--r-lg);
  padding: clamp(1.5rem, 3vw, 2.25rem); box-shadow: var(--shadow-md);
}
.offer-num {
  font-family: var(--font-display); font-weight: 700; line-height: .9;
  font-size: clamp(3.2rem, 7vw, 4.6rem); color: var(--acc-2);
}
.offer h3 { margin-bottom: .35rem; }
.offer p { color: var(--ink-2); margin: 0; }
@media (max-width: 860px) {
  .offer { grid-template-columns: 1fr; text-align: left; }
  .offer .btn { justify-self: start; }
}
.center-vh { min-height: 66vh; display: grid; place-items: center; }
.m-auto-top { margin: 1rem auto 0; }
.form-note-tight { margin: .3rem 0 1.1rem; }
.link-strong { color: var(--acc-2); font-weight: 600; }

/* Inline links inside body prose. The global reset is `a { color: inherit;
   text-decoration: none }`, which is right for nav, cards and buttons — every one
   of those carries its own affordance. It is wrong inside a paragraph, where an
   unstyled link is indistinguishable from the text around it. This was inherited
   from the template and shipped that way on the sibling builds; the "See them on
   Google" link on the reviews page is the case that exposed it.
   --acc-2 measures 6.82:1 on --bg and 7.43:1 on white, so colour is not the sole
   signal but it is a legible one, and the underline carries the rest. */
.lead a, .faq-body a, .form-note a, .contact-card p a, .feature p a {
  color: var(--acc-2);
  text-decoration: underline;
  text-underline-offset: .18em;
}
.on-dark .lead a, .on-dark .feature p a { color: var(--acc-d); }
.text-white { color: #fff; }
.ai-start { align-items: start; }

/* ---------- accent on dark surfaces ----------
   These elements render on both light and dark sections, so the base rule keeps
   the fill accent and only the .on-dark case swaps to the light-on-dark token.
   Without this the accent lands at 3.24:1 on --panel and fails AA. */
.on-dark .feature .f-num { color: var(--acc-d); }
.on-dark .stat .s-num .u { color: var(--acc-d); }
.on-dark .stars,
.on-dark .quote-tile .q-stars { color: var(--acc-d); }
.on-dark .svc-link { color: var(--acc-d); }
.on-dark .section-head .eyebrow .num { -webkit-text-stroke-color: var(--acc-d); }

/* ============================================================
   CLIENT COMPONENTS — BR2 Property Solutions only.

   There are none. The parent build inherited and then deleted a three-choice
   option band (profile diagrams, pattern tiles, a 42-swatch colour list) from
   ITS parent; that deletion holds here too. An excavating contractor publishes
   no option catalogue, and building one would be inventing an offering.

   .sw-note survived and is used four times: under the review quotes on
   index.html, under the gallery grid, under the honest-accounting list on
   why-us.html and under the payment line on about.html.

   Everything else this build needed already existed in the shared system.
   The only NEW rules in this file are .footer-wordmark, .footer-social,
   .hero-flat and the <sup> rule — all commented where they sit.
   ============================================================ */
.sw-note { font-size: .87rem; color: var(--ink-3); margin-top: 1.1rem; max-width: 62ch; }
.on-dark .sw-note { color: rgba(255,255,255,.7); }
.sw-note a { color: var(--acc-2); text-decoration: underline; }
.on-dark .sw-note a { color: var(--acc-d); }

/* ============================================================
   TEMPLATE BUG, fixed here — LINKS THAT DO NOT LOOK LIKE LINKS.

   The base reset is `a { color: inherit; text-decoration: none; }`, and the
   shared system then re-styles links only in the contexts it happened to use:
   .lead, .faq-body and .sw-note all got a rule. TWO CONTEXTS NEVER DID, and
   both of them carry real outbound links in this build:

     .checks a  — every item in the honest-accounting list on why-us.html and
                  the whole "where to find us" list on about.html. Measured
                  before the fix: #0f1622 on light, #ffffff on --on-dark, no
                  underline either way. Identical to the body text around it.
     .cl-val a  — the "go and read them there" link under Rating in the
                  contact-page card. Measured before the fix: #0f1622, no
                  underline.

   The parent build hid this by having no links in either place. Underline as
   well as colour, because on the dark panel --acc-d against white body text is
   a hue shift a colour-blind visitor may not resolve.
   ============================================================ */
.checks a, .cl-val a { color: var(--acc-2); text-decoration: underline; }
.on-dark .checks a, .on-dark .cl-val a { color: var(--acc-d); }

/* ---------- gallery grid: uniform rows ----------
   TEMPLATE BUG, fixed here. `.gallery-item` is aspect-ratio 4/3 and
   `.gallery-item.wide` is 8/5, so in a 3-column grid a wide tile is far taller
   than the single tile beside it, the row height follows the tall one, and the
   short tile leaves a hole underneath. Every sibling build hid this by having
   no photographs — the placeholders were all the same size.

   Fixing it by making the rows a fixed height and dropping the per-item ratio:
   a wide tile is then 2 columns x 1 row and the eight tiles pack into four full
   rows with no gaps. The tile order on gallery.html is
   [wide, 1, 1, wide, wide, 1, 1, wide] for exactly that reason — reordering the
   figures without keeping that pattern brings the holes back. */
.gallery-grid { grid-auto-rows: clamp(190px, 18vw, 300px); }
.gallery-grid .gallery-item,
.gallery-grid .gallery-item.wide { aspect-ratio: auto; height: 100%; }
@media (max-width: 760px) {
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid .gallery-item.wide { grid-column: span 2; }
}

/* ---------- .gallery-grid.pairs — a FOUR-tile gallery ----------
   The 3-column packing above only works for eight tiles in the
   [wide,1,1,wide,wide,1,1,wide] pattern. This client has two job photographs
   (one of them shot twice) plus one equipment photo — four tiles, which pack a
   3-column grid as 1+1 / 2 / 2 and leave the entire right-hand third of the
   page empty. So gallery.html uses two columns and no wide tiles.

   Rows are taller here than in the 8-tile grid because two of the four source
   photographs are portrait (1800x2400) and a 600x300 letterbox crops them to
   nothing. At 2 columns this lands near 3:2, which both orientations survive.
   When Bryer sends more photographs: drop .pairs, restore the wide/narrow
   pattern above, and check the packing before shipping. */
.gallery-grid.pairs { grid-template-columns: repeat(2, 1fr); grid-auto-rows: clamp(240px, 26vw, 400px); }
.gallery-grid.pairs .gallery-item.wide { grid-column: span 1; }
@media (max-width: 560px) {
  .gallery-grid.pairs { grid-template-columns: 1fr; }
}
