/* template1 — photo-forward contractor layout. Colours come from the client record. */
/* The palette. Every surface, border and shadow on the page comes from here, so
   a client can be switched to dark by redefining this block alone — the engine
   injects the dark values into the page head, the same way it injects a brand
   colour. Anything sitting ON the brand-coloured bands stays #fff in both
   themes: those bands are the client's colour, not a theme surface. */
:root {
  --primary: #0d528a; --accent: #f36b21;
  --ink: #0c1d36; --muted: #526174;
  --bg: #fff; --surface: #fff; --surface-2: #eef1f4; --surface-3: #dbe5ed;
  --soft: #f4f7fa; --line: #e5eaf0; --line-strong: #d9e2ea;
  /* The brand colour used as text. On a light page that is the brand itself;
     on a dark one it is lightened, because a navy link on near-black is
     unreadable. Buttons keep --primary as a fill with white on top. */
  --brand-text: var(--primary);
  /* Service and review cards stay white in both themes — on a charcoal page a
     white card is the contrast, so its text and borders can't follow the page. */
  --card-bg: #fff; --card-ink: #0c1d36; --card-muted: #526174;
  --card-line: #e5eaf0; --card-media: #eef1f4;
  /* The quote card sits a step above the banded sections, so a service page
     reads as three greys: page, band, then the form. Light mode has nowhere
     lighter to go than the band it already used, so it stays put there. */
  --card-raised: var(--soft);
  /* The quote card's own colour, so it stands out as the thing to act on
     rather than blending into the grey rhythm around it — blue on a light
     page, and swapped for the accent colour on dark so it still pops against
     the surrounding charcoal rather than reading as "more navy". */
  --quote-bg: var(--primary);
  --band-edge: transparent;
  --shadow: 7, 39, 70;
  --map-line: 124, 134, 146;
}
:root { --content-width: 1240px; --side-space: clamp(40px, 5vw, 92px); --content-edge: max(var(--side-space), calc((100vw - var(--content-width)) / 2)); }
/* One paragraph scale for the whole site — three sizes, no more. Every bit of
   running text uses one of these so nothing drifts a pixel or two off. */
:root {
  /* The floors are what a phone gets. Body stays at 16px there: smaller is hard
     to read on a job site, and under 16px iOS zooms the page on a form tap. */
  --text-lede: clamp(17px, 1.45vw, 20px);   /* the sentence under a heading */
  --text-body: clamp(16px, 1.3vw, 18px);    /* ordinary prose */
  --text-small: clamp(14px, 1.15vw, 16px);  /* captions, card copy, fine print */
}
* { box-sizing: border-box; }
body { margin: 0; font: var(--text-body)/1.6 Arial, Helvetica, sans-serif; color: var(--ink); background: var(--bg); }
a { color: var(--brand-text); }
main { max-width: calc(var(--content-width) + 2 * var(--side-space)); margin: 0 auto; padding: 0 var(--side-space); }
h1, h2, h3 { line-height: 1.12; letter-spacing: -.035em; font-weight: 750; }
h1 { font-size: clamp(52px, 4.3vw, 86px); } h2 { font-size: clamp(36px, 2.5vw, 48px); } h3 { font-size: clamp(19px, 1.75vw, 25px); }
.placeholder { background: linear-gradient(135deg, var(--surface-2), var(--surface-3)); color: var(--muted); display: flex; align-items: center; justify-content: center; min-height: 230px; font-size: var(--text-small); }
/* All action buttons share the hero's scale; labels determine their width. */
.button, .contact-form button { display: inline-flex; align-items: center; justify-content: center; gap: 13px; max-width: 100%; background: var(--button-bg, var(--primary)); color: var(--button-ink, #fff); padding: clamp(12px, 1.3vw, 15px) clamp(15px, 1.7vw, 20px); border: 1px solid transparent; border-radius: 6px; text-decoration: none; font-family: inherit; font-size: var(--text-small); line-height: 1.6; font-weight: 700; text-align: center; cursor: pointer; box-shadow: 0 2px 4px rgba(var(--shadow), .12); }
.button, .contact-form button { transition: background-color .18s ease, box-shadow .18s ease, color .18s ease; }
.button:hover, .contact-form button:hover { filter: brightness(1.15); box-shadow: 0 4px 14px rgba(var(--shadow),.24); }
.hero-actions .button, .site-header .button, .site-header .phone { display: inline-flex; align-items: center; justify-content: center; gap: 13px; }
.action-icon { flex: 0 0 auto; }
.site-header .phone { border-radius: 6px; padding: 11px; transition: background-color .18s ease, color .18s ease; }
.site-header .phone:hover { background: var(--soft); color: var(--brand-text); }
a:focus-visible, .contact-form button:focus-visible { outline: 5px solid var(--primary); outline-offset: 6px; }
section[id] { scroll-margin-top: 71px; }
@media (prefers-reduced-motion: reduce) { .button, .contact-form button, .site-header .phone { transition: none; } }

/* The header lines up with the page content: --content-edge is the same value
   the hero copy and the section grids start at, so the brand sits directly
   above the headline and the quote button above the content's right edge. */
.site-header { max-width: none; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 14px; padding: 18px var(--content-edge); border-bottom: 1px solid var(--line); }
.site-header .brand { flex: 0 1 auto; min-width: 0; font-weight: 800; color: var(--brand-text); text-decoration: none; font-size: 20px; letter-spacing: -.04em; margin-right: 18px; }
.site-header .brand img { height: 40px; width: auto; max-width: 100%; object-fit: contain; display: block; }
.site-header nav { display: flex; flex: 1 1 auto; gap: 15px; margin-left: auto; min-width: 0; }
.site-header nav a, .nav-item-label { text-decoration: none; color: var(--ink); font-size: 16px; font-weight: 600; white-space: nowrap; }
.site-header nav a[aria-current] { color: var(--brand-text); }
.site-header .phone { flex: 0 0 auto; font-size: 14px; font-weight: 700; text-decoration: none; color: var(--ink); white-space: nowrap; }
.site-header .header-cta { flex: 0 0 auto; white-space: nowrap; margin-left: auto; }
/* Services submenu. Opens on hover and on :focus-within, so it is reachable by
   keyboard without script; the parent is still a plain link to /services. */
.site-header .nav-item { position: relative; display: flex; align-items: center; }
.site-header .nav-item > a, .site-header .nav-item > .nav-item-label { display: inline-flex; align-items: center; gap: 5px; cursor: default; }
.nav-caret { flex: 0 0 auto; transition: transform .18s ease; }
.site-header .nav-item:hover .nav-caret, .site-header .nav-item:focus-within .nav-caret { transform: rotate(180deg); }
.subnav { position: absolute; top: 100%; left: -12px; z-index: 30; display: none; flex-direction: column; min-width: 232px; padding: 8px; background: var(--surface); border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 14px 34px rgba(var(--shadow),.16); }
.site-header .nav-item:hover .subnav, .site-header .nav-item:focus-within .subnav { display: flex; }
.site-header .subnav a { padding: 9px 12px; border-radius: 6px; font-size: 15px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.site-header .subnav a:hover { background: var(--soft); color: var(--brand-text); }
.nav-toggle-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.nav-toggle-btn { display: none; }
.nav-toggle-input:focus-visible + .nav-toggle-btn { outline: 3px solid var(--primary); outline-offset: 3px; }
/* The links stay visible as long as they fit. The quote button drops first,
   and only once even the links can't fit do they collapse into a menu button.
   Measured, not guessed, and re-measured every time the row changes. With six
   nav items (no Home — the brand is the home link) the full row needs ~1260px,
   and the row without the quote button ~985px; the longer of the two demo
   brands sets the second number. Adding or renaming a nav item, changing the
   gutter, or a longer brand all move these — re-measure, don't assume. A header
   that no longer fits wraps to two or three rows silently, with no error. */
@media (max-width: 1260px) {
  .site-header .header-cta { display: none; }
  .site-header nav { gap: 13px; }
  .site-header nav a { font-size: 15px; }
  .site-header .phone { margin-left: auto; }
}
@media (max-width: 985px) {
  .site-header .phone { order: 2; margin-left: auto; }
  .nav-toggle-btn { display: flex; align-items: center; justify-content: center; order: 4; width: 44px; height: 44px; flex-shrink: 0; margin-left: 10px; color: var(--ink); cursor: pointer; }
  .site-header nav {
    display: none; flex-basis: 100%; order: 5; margin-left: 0;
    flex-direction: column; align-items: flex-start; gap: 2px;
    overflow-x: visible; padding-top: 14px; margin-top: 12px; border-top: 1px solid var(--line);
  }
  .site-header nav a { width: 100%; padding: 12px 0; font-size: 16px; white-space: normal; }
  #nav-toggle:checked ~ nav { display: flex; }
  /* No hover on a touch screen, so the submenu is simply always open here. */
  .site-header .nav-item { display: block; width: 100%; }
  .site-header .nav-caret { display: none; }
  .subnav { position: static; display: flex; min-width: 0; padding: 0 0 8px 16px; background: none; border: 0; box-shadow: none; }
  .site-header .subnav a { padding: 9px 0; font-size: 15px; color: var(--muted); white-space: normal; }
}

.hero { display: grid; gap: 0; padding: 0; margin: 0 calc(50% - 50vw); }
/* The gap to the image scales with the window, so a narrow window does not
   steal the width the headline needs. */
.hero-copy { display: flex; flex-direction: column; justify-content: center; padding: clamp(46px, 5.6vw, 80px) max(24px, calc((100vw - 1192px) / 2)); padding-right: clamp(40px, 5vw, 80px); }
.hero .hero-media { min-height: clamp(360px, 48vw, 690px); border-radius: 0; overflow: hidden; }
.hero .hero-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero .hero-media.video { position: relative; aspect-ratio: 16 / 9; min-height: 0; }
.hero .hero-media.video iframe { position: absolute; inset: 0; }
.hero .eyebrow { color: var(--accent); font-size: var(--text-small); font-weight: 800; margin: 0 0 15px; }
.hero h1 { max-width: 760px; margin: 0; }
.hero .sub { max-width: 760px; font-size: var(--text-lede); color: var(--muted); }
.hero .rating { color: var(--brand-text); font-size: var(--text-small); font-weight: 700; }
.hero .hero-actions { display: flex; flex-wrap: wrap; gap: clamp(9px, 1vw, 12px); }
.hero .button-outline { background: transparent; border: 1px solid var(--brand-text); color: var(--brand-text); }
.hero .button-outline:hover { background: var(--primary); color: #fff; }
.hero .hero-media { position: relative; }
.hero .hero-media-badge { position: absolute; right: 24px; bottom: 24px; left: auto; margin: 0; display: flex; align-items: center; gap: 11px; max-width: min(300px, calc(100% - 48px)); background: var(--surface); color: var(--ink); font-size: 14px; line-height: 1.35; padding: 12px 16px; border-radius: 5px; box-shadow: 0 5px 14px rgba(0,0,0,.14); }
.hero-media-badge svg { flex: 0 0 26px; }
.hero-badge-label, .hero-badge-area { display: block; }
.hero-badge-label { color: var(--muted); font-size: 12px; margin-bottom: 2px; }
.hero-badge-area { font-weight: 600; }
@media (min-width: 800px) { .hero { grid-template-columns: 1fr 1fr; } }
/* In the two-column hero the copy only gets half the width, so the headline
   scales down rather than sitting at the full-width floor and over-wrapping. */
@media (min-width: 800px) and (max-width: 1250px) {
  .hero h1 { font-size: clamp(34px, 4.2vw, 52px); }
}

.full-bleed { width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); }

.trust-bar { background: var(--soft); border: 0; border-block: 1px solid var(--band-edge); padding: clamp(26px, 3vw, 41px) 0; }
/* A deeper grey than the default — for when this sits right under a
   page-head of the same base grey, so the two stay visibly distinct. */
.trust-bar-dark { background: var(--surface-3); }
.trust-bar .grid { max-width: 1240px; margin: 0 auto; padding: 0 35px; grid-template-columns: repeat(4, 1fr); gap: 17px 23px; }
/* Once the cells narrow, the icon sits over centred text so all four stay in one
   even row instead of clustering left and leaving a gap down the middle. */
@media (max-width: 1050px) {
  .trust-item { flex-direction: column; gap: 9px; justify-content: flex-start; text-align: center; padding: 0 6px; }
  .trust-item:first-child { padding-left: 6px; }
  .trust-bar .grid { column-gap: 0; }
  .trust-item + .trust-item { border-left: 1px solid var(--line-strong); }
}
@media (max-width: 700px) {
  .trust-bar .grid { grid-template-columns: repeat(2, 1fr); row-gap: 30px; }
  .trust-item:nth-child(odd) { border-left: 0; }
}
/* Roomy again once stacked, so the sizes go back up. */
@media (max-width: 700px) {
  .trust-item { font-size: 17px; }
  .trust-item svg { width: 40px; height: 40px; }
  .trust-item strong { font-size: 17px; }
  .trust-item small { font-size: 15px; }
}
@media (max-width: 430px) {
  .trust-bar .grid { grid-template-columns: 1fr; }
  .trust-item + .trust-item { border-left: 0; }
}
/* Sizes scale with the viewport so the bar grows and shrinks with the window. */
.trust-item { display: flex; align-items: center; gap: clamp(10px, 1.3vw, 18px); color: var(--ink); font-size: clamp(14px, 1.3vw, 18px); padding: 0 clamp(8px, 1.8vw, 25px); }
.trust-item:first-child { padding-left: 0; }
.trust-item svg { color: var(--ink); flex-shrink: 0; width: clamp(30px, 3.4vw, 48px); height: clamp(30px, 3.4vw, 48px); }
.trust-item span { display: grid; gap: 1px; }.trust-item strong { font-size: clamp(14px, 1.3vw, 18px); }.trust-item small { color: var(--muted); font-size: clamp(12px, 1.15vw, 16px); }

/* Vertical rhythm scales with the window too, so a narrow screen is not mostly
   whitespace and a wide one keeps the sections apart. */
section { padding: clamp(54px, 6.4vw, 90px) 0; border: 0; }
main > h1 { margin: clamp(34px, 4vw, 56px) 0 0; }
main > h1 + section { padding-top: 40px; }
.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(225px, 1fr)); }
/* Deliberate column steps: the cards stretch to fill the row, then drop to
   fewer columns at set widths rather than reflowing on content width. */
.services .grid { grid-template-columns: repeat(4, 1fr); gap: clamp(12px, 1.4vw, 18px); }
/* In the widest band the column count follows the number of services, so a
   contractor with three, five or six never gets a last row with one card
   stranded against the left edge. Scoped to this band so the steps below win. */
@media (min-width: 1001px) {
  .services .grid:has(> :nth-child(3):last-child),
  .services .grid:has(> :nth-child(5):last-child),
  .services .grid:has(> :nth-child(6):last-child),
  .services .grid:has(> :nth-child(9):last-child) { grid-template-columns: repeat(3, 1fr); }
  .services .grid:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 1000px) {
  .services .grid { grid-template-columns: repeat(2, 1fr); }
  /* An odd number leaves a hole in the last row, so that card takes the row. */
  .services .grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  /* Cards are wider at fewer columns, so the image takes a shallower ratio and
     the type starts from a larger base for the width it now has. */
  .services .card-media { aspect-ratio: 5 / 2; }
  .services .card-body { padding: clamp(13px, 1.7vw, 17px) clamp(14px, 1.8vw, 18px) clamp(15px, 1.9vw, 19px); }
  .services .card h3 { font-size: clamp(16px, 2vw, 20px); }
  .services .card p, .services .card-link { font-size: clamp(14px, 1.7vw, 16px); }
  .services .card-link { margin-top: 8px; }
}
@media (max-width: 560px) {
  .services .grid { grid-template-columns: 1fr; }
  .services .card-media { aspect-ratio: 16 / 9; }
}
.services { position: relative; }.services > h2 { margin: 0 0 clamp(24px, 3vw, 40px); }.services > p { position: absolute; right: 0; top: clamp(58px, 6.6vw, 98px); margin: 0; font-size: var(--text-small); font-weight: 700; }
/* The service-areas hub: with only a couple of cards the page is short
   enough that the quote band would sit above the fold. Extra room below the
   cards keeps it a deliberate scroll away instead. */
.area-cards { padding-bottom: clamp(140px, 18vw, 240px); }
.card { display: block; border: 1px solid var(--card-line); border-radius: 6px; overflow: hidden; text-decoration: none; color: inherit; transition: transform .15s, box-shadow .15s; background: var(--card-bg); }
.card:hover { box-shadow: 0 10px 24px rgba(var(--shadow), .1); transform: translateY(-2px); }
.card-media { aspect-ratio: 4 / 3; overflow: hidden; }
.card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-media-fallback { display: flex; align-items: center; justify-content: center; background: var(--card-media); color: var(--card-muted); }
.card-body { padding: clamp(13px, 1.45vw, 18px) clamp(13px, 1.45vw, 18px) clamp(15px, 1.7vw, 21px); }
.card h3 { margin: 0 0 8px; color: var(--card-ink); font-size: clamp(16px, 1.6vw, 20px); }
.card p { margin: 0; color: var(--card-muted); font-size: var(--text-small); }
.card-link { display: inline-block; margin-top: clamp(8px, 1vw, 12px); color: var(--primary); font-size: var(--text-small); font-weight: 500; }

/* Flat brand colour, matching the closing band. */
.about { background: var(--primary); color: #fff; padding: clamp(54px, 6vw, 84px) 0 0; }
.about-inner { display: grid; gap: clamp(36px, 5.6vw, 78px); max-width: 1240px; margin: 0 auto; padding: 0 35px clamp(46px, 5.6vw, 78px); }
.about-media { min-height: clamp(300px, 37vw, 518px); }.about-media img { width: 100%; height: 100%; min-height: clamp(300px, 37vw, 518px); object-fit: cover; border-radius: 6px; display: block; }
.about-media-placeholder { position: relative; overflow: hidden; border: 1px solid rgba(255,255,255,.28); border-radius: 6px; background: linear-gradient(145deg, rgba(255,255,255,.16), rgba(5,31,57,.15)); }
.about-media-placeholder::before, .about-media-placeholder::after { content: ''; position: absolute; border: 1px solid rgba(255,255,255,.22); transform: rotate(-33deg); }
.about-media-placeholder::before { width: 518px; height: 518px; right: -223px; top: -130px; border-radius: 50%; }.about-media-placeholder::after { width: 274px; height: 274px; left: -104px; bottom: -98px; border-radius: 50%; }
.about-copy { max-width: 700px; }.about-copy h2 { margin: 0 0 clamp(16px, 2vw, 26px); }/* Two or three sentences of prose, not a one-line lede, so it takes the body
   size — the lede size is for the single sentence under a heading. */
.about-copy p { opacity: .92; font-size: var(--text-body); max-width: 60ch; }
.about .meta { opacity: .78; font-size: var(--text-small); font-weight: 700; }
.about .button { background: #fff; color: var(--primary); }
@media (min-width: 800px) { .about-inner { grid-template-columns: 1fr 1fr; align-items: center; } }
/* Four even cells that scale with the window, stepping to 2 then 1. */
.about-stats { display: grid; grid-template-columns: repeat(4, 1fr); max-width: 1240px; margin: 0 auto; border-top: 1px solid rgba(255,255,255,.25); padding: 29px 0; }
.about-stat { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 9px clamp(8px, 1.6vw, 29px); text-align: center; }
.about-stat + .about-stat { border-left: 1px solid rgba(255,255,255,.2); }
.about-stat strong { font-size: clamp(26px, 2.3vw, 46px); line-height: 1; letter-spacing: -.04em; font-weight: 700; }
.about-stat .stat-label { font-size: clamp(13px, 1.15vw, 16px); line-height: 1.15; color: rgba(255,255,255,.85); }
.about-stat .stat-icon { display: flex; align-items: center; justify-content: center; height: clamp(24px, 2.4vw, 33px); margin-top: 0; color: #fff; }
@media (max-width: 700px) {
  .about-stats { grid-template-columns: repeat(2, 1fr); row-gap: 26px; }
  .about-stat:nth-child(odd) { border-left: 0; }
  /* Roomier again once there are only two per row. */
  .about-stat strong { font-size: 34px; }
  .about-stat .stat-label { font-size: 15px; }
  .about-stat .stat-icon { height: 30px; }
}
@media (max-width: 430px) {
  .about-stats { grid-template-columns: 1fr; }
  .about-stat + .about-stat { border-left: 0; }
}
.stat-icon svg { width: clamp(22px, 2.2vw, 30px); height: clamp(22px, 2.2vw, 30px); }
.stat-stars { font-size: clamp(16px, 1.6vw, 22px); letter-spacing: 2px; line-height: 1; }
@media (max-width: 700px) {
  .stat-icon svg { width: 28px; height: 28px; }
  .stat-stars { font-size: 20px; }
}

.gallery, .reviews { max-width: 1240px; margin: auto; position: relative; }.gallery h2, .reviews h2 { margin: 0 0 clamp(24px, 3vw, 40px); }.gallery > p { position: absolute; right: 0; top: clamp(58px, 6.6vw, 98px); margin: 0; font-size: var(--text-small); font-weight: 700; }
/* Three across, shrinking with the window, then straight to one. Both of these
   sections hold three items, so a two-column step would always strand the third
   on a half-empty row — 3 or 1 is always a full row. */
.gallery .grid, .reviews .grid { grid-template-columns: repeat(3, 1fr); gap: clamp(13px, 1.5vw, 18px); }
.reviews-heading { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px 24px; margin-bottom: clamp(24px, 3vw, 40px); }
.reviews .reviews-heading h2 { margin: 0; font-size: clamp(26px, 2.2vw, 34px); }
.reviews-heading > a { flex-shrink: 0; font-size: var(--text-small); font-weight: 700; }
.not-found { max-width: 800px; margin: auto; text-align: center; }
.not-found-code { margin: 0; color: var(--brand-text); font-size: clamp(64px, 12vw, 128px); font-weight: 800; line-height: 1; letter-spacing: -.05em; }
.not-found-business { color: var(--muted); font-size: var(--text-small); font-weight: 700; }
.not-found h1 { font-size: clamp(32px, 5vw, 52px); margin: 24px 0 16px; }
.not-found .lede { font-size: var(--text-lede); color: var(--muted); }
.not-found-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 32px; }
@media (max-width: 560px) { .not-found-actions .button { width: 100%; } }
@media (max-width: 800px) { .gallery .grid, .reviews .grid { grid-template-columns: 1fr; gap: 16px; } }
.gallery-item img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 6px; display: block; }
.gallery-item p { margin: 12px 0 0; font-size: var(--text-body); color: var(--muted); }
.project-placeholder { aspect-ratio: 16 / 9; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(10px, 1.4vw, 17px); border: 1px solid var(--line); border-radius: 6px; background: linear-gradient(135deg, var(--surface-2), var(--surface-3)); color: var(--muted); font-size: var(--text-small); }
.project-placeholder svg { width: clamp(30px, 3.4vw, 46px); height: clamp(30px, 3.4vw, 46px); }
/* These two sit on the same white, so the gap is the only thing separating
   them. Collapsed to nothing it read as one long section. */
.gallery + .reviews { padding-top: clamp(20px, 2.6vw, 38px); }

/* Its own band: full-bleed on the same soft grey as the trust bar, set apart
   from the reviews above it. */
.service-area { background: var(--soft); border-block: 1px solid var(--band-edge); width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); margin-top: clamp(40px, 5vw, 72px); padding: clamp(64px, 7.6vw, 104px) var(--side-space); }
.service-area-inner { display: grid; grid-template-columns: minmax(0, .9fr) minmax(300px, 1.1fr); gap: clamp(36px, 5.6vw, 78px); align-items: center; max-width: var(--content-width); margin: 0 auto; text-align: left; }
.service-area-copy { max-width: 460px; }.service-area-copy .lede { color: var(--muted); font-size: var(--text-body); margin: 0 0 clamp(24px, 2.8vw, 35px); }.service-area-copy .button { background: transparent; color: var(--brand-text); border: 1px solid var(--line-strong); border-color: color-mix(in srgb, var(--primary) 38%, var(--surface)); box-shadow: none; transition: background-color .18s ease, color .18s ease, border-color .18s ease; }.service-area-copy .button:hover { background: var(--primary); color: #fff; border-color: var(--primary); filter: none; }
.service-area-map { position: relative; min-height: clamp(260px, 30vw, 374px); overflow: hidden; border-radius: 6px; border: 1px solid var(--line); background-color: var(--surface); background-image: linear-gradient(28deg, transparent 47%, rgba(var(--map-line),.14) 48%, rgba(var(--map-line),.14) 51%, transparent 52%), linear-gradient(-42deg, transparent 46%, rgba(var(--map-line),.12) 47%, rgba(var(--map-line),.12) 50%, transparent 51%), linear-gradient(90deg, rgba(var(--map-line),.12) 1px, transparent 1px), linear-gradient(rgba(var(--map-line),.12) 1px, transparent 1px); background-size: auto, auto, 52px 52px, 52px 52px; }
.service-area-map::before { content: ''; position: absolute; inset: 55px 24%; border-radius: 50%; background: rgba(110,190,255,.22); background: color-mix(in srgb, var(--primary) 20%, var(--surface)); }.map-pin { position: absolute; width: 22px; height: 22px; border-radius: 50% 50% 50% 0; background: var(--primary); transform: rotate(-45deg); box-shadow: 0 2px 7px rgba(var(--shadow),.2); }.map-pin::after { content: ''; position: absolute; width: 32%; height: 32%; border-radius: 50%; background: #fff; top: 32%; left: 32%; }.pin-one { left: 47%; top: 43%; }.pin-two { left: 67%; top: 29%; }.pin-three { left: 60%; top: 67%; }
.service-area :is(h1, h2) { margin: clamp(10px, 1.4vw, 17px) 0 clamp(18px, 2.3vw, 29px); }
/* On /service-areas this heading is the h1, but it sits in the same half-width
   column, so it keeps the section scale rather than the page-title one. */
.service-area-copy h1 { font-size: clamp(36px, 2.5vw, 48px); }
@media (max-width: 700px) { .service-area-copy h1 { font-size: clamp(27px, 7.4vw, 36px); } }
/* This heading and the About one only get half the row, so between the two-column
   step and full width they scale below the shared h2 floor instead of over-wrapping. */
@media (min-width: 801px) and (max-width: 1150px) {
  .service-area :is(h1, h2), .about-copy h2 { font-size: clamp(26px, 3.3vw, 40px); }
}
.map-pin { width: clamp(16px, 1.8vw, 22px); height: clamp(16px, 1.8vw, 22px); }

/* Tighter underneath: the attribution line already sits on its own, so the
   full padding below it left the card looking bottom-heavy. */
.review { margin: 0; background: var(--card-bg); color: var(--card-ink); border: 1px solid var(--card-line); border-radius: 6px; padding: clamp(20px, 2.6vw, 35px); padding-bottom: clamp(14px, 1.7vw, 22px); font-size: var(--text-body); box-shadow: 0 5px 15px rgba(var(--shadow),.04); }
.review .stars { color: var(--accent); margin: 0 0 12px; letter-spacing: 3px; font-size: clamp(15px, 1.4vw, 19px); }
.review cite { color: var(--card-muted); font-style: normal; font-size: var(--text-small); }
.review.sample { border-style: dashed; }

/* Flat, not a gradient: the darkened end was dulling the brand colour across
   most of the band. */
.cta { background: var(--primary); color: #fff; border-radius: 0; padding: clamp(40px, 4.4vw, 62px) clamp(20px, 2.4vw, 29px); margin: 0 calc(50% - 50vw); }
/* The closing band asks for the lead here, so copy and form sit side by side:
   the reason to act stays in view while the fields are being filled in. */
.cta-inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(28px, 3.8vw, 56px); align-items: center; max-width: var(--content-width); margin: 0 auto; text-align: left; }
.cta h2 { margin: 0 0 10px; }
.cta .lede { color: rgba(255,255,255,.82); font-size: var(--text-lede); margin: 0; }
.cta-actions { display: flex; flex-wrap: wrap; gap: clamp(12px, 1.7vw, 21px); }

/* The reasons to fill the form in, and the way out for people who would rather
   call. Only the closing band has these — the popup and /contact don't. */
.cta-points { list-style: none; display: flex; flex-wrap: wrap; gap: 8px 20px; margin: clamp(14px, 1.8vw, 20px) 0 0; padding: 0 0 clamp(16px, 2vw, 22px); font-size: var(--text-small); font-weight: 700; border-bottom: 1px solid rgba(255,255,255,.22); }
.cta-points li { display: flex; align-items: center; gap: 9px; }
.cta-points li::before { content: ''; flex: 0 0 auto; width: 19px; height: 19px; border-radius: 50%; background: rgba(255,255,255,.18) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 13 4 4 10-10'/%3E%3C/svg%3E") center / 11px 11px no-repeat; }
.cta-talk { margin: clamp(16px, 2vw, 22px) 0 12px; color: #fff; font-size: clamp(20px, 1.9vw, 26px); font-weight: 750; letter-spacing: -.03em; }
/* Same outline button as the hero's call action — .cta a.button-outline already
   gives it the white-on-navy treatment for this band. */
.cta-phone { margin: 0; }

/* A white card, so the fields keep the same contrast they have on /contact
   instead of needing a second set of colours for navy. */
.cta-form { background: var(--surface); border-radius: 8px; padding: clamp(18px, 2vw, 24px); box-shadow: 0 12px 34px rgba(var(--shadow), .22); }
.form-title { margin: 0 0 clamp(12px, 1.4vw, 16px); font-size: clamp(20px, 1.9vw, 26px); color: var(--ink); }
.cta-form .contact-form label { color: var(--muted); }
.cta-form .contact-form input, .cta-form .contact-form textarea { border: 1px solid var(--card-line); background: var(--card-bg); }
.cta-form .contact-form button { width: 100%; }
.contact-form button { width: 100%; }
/* Stacks below the two-column hero breakpoint, form first is wrong — the
   reason to fill it in has to come before the fields. */
@media (max-width: 860px) {
  .cta-inner { grid-template-columns: 1fr; gap: clamp(26px, 4vw, 40px); }
  .cta-copy { text-align: center; }
  .cta-actions, .cta-points { justify-content: center; }
}
.cta-actions .button { display: inline-flex; align-items: center; justify-content: center; gap: 13px; }
.cta a.button:not(.button-outline) { background: #fff; color: var(--primary); }
.cta a.button:not(.button-outline):hover { background: var(--soft); filter: none; }
.cta a.button-outline { background: transparent; border: 1px solid rgba(255,255,255,.5); color: #fff; }
.cta a.button-outline:hover { background: #fff; color: var(--primary); }

/* Named areas so the order can differ from the markup. Stacked, the contact
   details stay together — phone, email, address, map — and the form follows.
   Putting the form second read faster but split the phone from the two lines
   that belong with it. */
.contact { display: grid; gap: clamp(20px, 2.6vw, 30px); grid-template-areas: "phone" "details" "form"; }
.contact-phone { grid-area: phone; }
.contact > .quote-card { grid-area: form; }
.contact-details { grid-area: details; }
@media (min-width: 800px) {
  /* Same 322px rail as the service pages rather than half the page, so the form
     reads the same on both. The details take the rest. */
  /* A third empty row takes the slack. With only two rows, a form taller than
     the details stretched both of them and pushed the phone away from the
     email — the gap came from the grid, not from any margin. */
  .contact { grid-template-columns: minmax(0, 1fr) 322px; grid-template-areas: "phone form" "details form" ". form"; grid-template-rows: auto auto 1fr; align-items: start; column-gap: clamp(28px, 3.6vw, 46px); row-gap: clamp(10px, 1.1vw, 14px); }
  .contact-details { max-width: 560px; }
}
.contact-form label { display: block; margin-bottom: clamp(14px, 1.7vw, 21px); font-weight: 500; font-size: var(--text-body); color: var(--muted); }
/* The field name is carried by the placeholder visually and by this span for
   screen readers — a placeholder alone vanishes as soon as someone types. */
.field-label { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.contact-form .field { margin-bottom: 8px; }
.contact-form select { display: block; width: 100%; padding: clamp(10px, 1.1vw, 12px) 14px; padding-right: 44px; border: 1px solid var(--card-line); border-radius: 6px; font-family: inherit; font-size: var(--text-body); color: var(--card-ink); background-color: var(--card-bg); appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230c1d36' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 18px 18px; }
.contact-form select:invalid, .contact-form select option[value=""] { color: var(--card-muted); }
.contact-form input::placeholder, .contact-form textarea::placeholder { color: var(--card-muted); opacity: 1; }
.contact-form input:focus-visible, .contact-form textarea:focus-visible, .contact-form select:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; border-color: var(--primary); }
/* "or call us now" — a rule either side of the words. */
.form-or { display: flex; align-items: center; gap: 14px; margin: 14px 0; color: var(--muted); font-size: var(--text-small); font-weight: 700; }
.form-or::before, .form-or::after { content: ''; flex: 1 1 auto; height: 1px; background: var(--line); }
.contact-form .button-quiet { width: 100%; background: var(--surface); color: var(--ink); border: 1px solid var(--line); box-shadow: none; font-size: var(--text-body); }
.contact-form .button-quiet:hover { background: var(--soft); border-color: var(--line-strong); filter: none; box-shadow: none; }
/* The quote popup. Native <dialog>, so Escape and the focus trap come from the
   browser rather than from script we'd have to maintain. */
.lead-dialog { width: min(440px, calc(100vw - 28px)); max-height: calc(100dvh - 28px); padding: 0; border: 0; border-radius: 10px; background: var(--surface); color: var(--ink); box-shadow: 0 24px 60px rgba(var(--shadow), .34); overflow: auto; }
.lead-dialog::backdrop { background: rgba(var(--shadow), .62); }
.lead-dialog-inner { position: relative; padding: clamp(20px, 3vw, 28px); }
.lead-dialog-title { margin: 0 44px 14px 0; font-size: clamp(22px, 2.4vw, 28px); }
.lead-close-form { margin: 0; }
.lead-close { position: absolute; top: 10px; right: 10px; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--muted); font-size: 28px; line-height: 1; cursor: pointer; }
.lead-close:hover { background: var(--soft); color: var(--ink); }
.lead-close:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }
/* The popup already says what it is in its own heading. */
.lead-dialog .form-reassure { margin-bottom: 0; }
@media (max-width: 560px) { .lead-dialog { width: calc(100vw - 20px); } }

.form-reassure { margin: 10px 0 0; text-align: center; color: var(--muted); font-size: var(--text-small); }
/* Fields set their own size rather than inheriting the label's: the compact
   form uses a smaller label, and an input under 16px makes iOS zoom the page
   when it is tapped. --text-body floors at 16px on phones. */
/* Fields stay a fixed light surface in both themes, the same family as the
   white service/review cards — a form field on a dark page reads as a real
   input either way, rather than a slightly-different dark box. */
.contact-form input, .contact-form textarea { display: block; width: 100%; padding: clamp(11px, 1.2vw, 15px); border: 0.5px solid var(--card-line); border-radius: 6px; font-family: inherit; font-size: var(--text-body); margin-top: 6px; background: var(--card-bg); color: var(--card-ink); }
.contact-form .field input, .contact-form .field textarea, .contact-form .field select { margin-top: 0; padding: clamp(10px, 1.1vw, 12px) 14px; }
.contact-form .field textarea { display: block; resize: vertical; }
/* Tighter padding takes the fields to 40px, which is under the size a thumb
   hits reliably. On phones they keep a 44px floor — gloved hands, job site. */
@media (max-width: 560px) {
  .contact-form .field input, .contact-form .field select { min-height: 44px; }
}
.contact-form .consent { font-size: var(--text-small); margin: 4px 0 12px; line-height: 1.4; }
.contact-form .consent { color: var(--muted); }
.contact-form.compact label { margin-bottom: clamp(11px, 1.3vw, 15px); font-size: var(--text-small); }
.contact-form.compact .consent { margin: 2px 0 10px; }
/* The card forms sit in a fixed-width box, so they run tighter than the one on
   /contact. The 44px phone floor still applies on top of this. */
.contact-form.compact .field { margin-bottom: 6px; }
.contact-form.compact .field input, .contact-form.compact .field select, .contact-form.compact .field textarea { padding: clamp(9px, 1vw, 11px) 13px; }
.contact-form.compact .form-reassure { margin-top: 8px; }
.map { min-height: clamp(220px, 24vw, 288px); }
/* Phone, email and address are one block: same size, weight and letter-spacing.
   The phone was set much larger and the address in muted grey, so the three
   lines read as three different things. */
.contact-details p, .contact-phone { margin: 0 0 clamp(10px, 1.1vw, 14px); font-size: var(--text-body); font-weight: 700; letter-spacing: 0; line-height: 1.4; color: var(--ink); }
.contact-details p:last-of-type { margin-bottom: 0; }
.contact-details a, .contact-phone a { text-decoration: none; font-weight: inherit; font-size: inherit; letter-spacing: inherit; }
.contact-details a:hover, .contact-phone a:hover { text-decoration: underline; }
.contact-phone { margin-bottom: 0; }
/* Icon then text on one baseline. The marks sit in a fixed-width column so the
   phone, email and address all start at the same left edge. */
.contact-phone a, .contact-line { display: flex; align-items: flex-start; gap: clamp(9px, 1vw, 12px); }
.contact-ico { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; width: 22px; color: var(--brand-text); }
.contact-ico svg { width: 19px; height: 19px; }
/* One icon size too, now the three lines match. */
.contact-ico { height: 1.4em; }
.contact-details .map { margin-top: clamp(18px, 2.2vw, 28px); border-radius: 6px; overflow: hidden; }
.contact-details .map iframe { display: block; width: 100%; height: 280px; border: 0; }
/* The area page's email is contact-details' last line (no hours below it any
   more), so restore the space that :last-of-type otherwise zeroes out, and
   give the map — a section of its own here, not nested inside contact-details
   like on /contact — its own top gap plus a larger size. */
.area-contact .contact-details p:last-of-type { margin-bottom: clamp(10px, 1.1vw, 14px); }
.area-contact + .map { margin: clamp(28px, 3.4vw, 42px) 0 clamp(40px, 5vw, 64px); border-radius: 6px; overflow: hidden; max-width: 460px; aspect-ratio: 1 / 1; }
.area-contact + .map iframe { display: block; width: 100%; height: 100%; border: 0; }
.contact-emergency { margin: 0 0 clamp(10px, 1.1vw, 14px); padding: 14px 18px; border-radius: 6px; background: var(--primary); }
.contact-emergency a { display: flex; align-items: flex-start; gap: clamp(9px, 1vw, 12px); color: #fff; text-decoration: none; font-weight: 700; }
.contact-emergency .contact-ico { color: #fff; }
.contact-emergency small { display: block; font-size: var(--text-small); font-weight: 400; text-transform: uppercase; letter-spacing: .05em; opacity: .85; }
.contact-details p.contact-license { margin-top: clamp(43px, 5.2vw, 63px); }
.contact-hours { margin-top: clamp(38px, 4.6vw, 54px); }
.contact-hours .contact-line strong { font-size: var(--text-body); }
.hours-list { display: grid; gap: 6px; margin: 10px 0 0; font-size: var(--text-small); }
.hours-list div { display: flex; justify-content: space-between; gap: 16px; padding: 4px 0; border-bottom: 1px solid var(--line); }
.hours-list dt { color: var(--ink); font-weight: 500; }
.hours-list dd { margin: 0; color: var(--muted); }
.contact-social { display: flex; flex-wrap: wrap; gap: 16px; margin-top: clamp(38px, 4.6vw, 54px); font-size: var(--text-small); }
.contact-social a { color: var(--primary); font-weight: 700; }

.service .lede { font-size: var(--text-lede); color: var(--muted); }
.service img, .post img { width: 100%; display: block; margin: clamp(16px, 1.9vw, 23px) 0; border-radius: 12px; }
/* Prose gets a reading measure instead of running the full content width.
   Scoped to the body copy: when this matched every descendant of .service it
   also capped the gallery track and the FAQ list, which are layout, not prose. */
.service-body p, .service-body ul, .service-body ol, .service-body h2, .service-body h3,
.post p, .post ul, .post ol, .post h2, .post h3 { max-width: 68ch; }
.post h1 { max-width: 20ch; }
.post-back { margin: 0 0 clamp(16px, 2vw, 22px); }
.post-back a { color: var(--muted); font-size: var(--text-small); font-weight: 700; text-decoration: none; }
.post-back a:hover { color: var(--primary); text-decoration: underline; }
/* Page titles are not the hero, so on a phone they come down to a size that
   does not eat the first screen. The hero headline keeps its own scale. */
@media (max-width: 700px) {
  main > h1, .service h1, .post h1 { font-size: clamp(32px, 8.6vw, 46px); }
  /* Section headings come off the 36px floor too, or a long service area or
     business name turns into six lines of heading on a phone. */
  h2 { font-size: clamp(27px, 7.4vw, 36px); }
}
.posts { list-style: none; padding: 0; margin: clamp(24px, 3vw, 40px) 0 0; display: grid; gap: clamp(14px, 1.6vw, 20px); max-width: 68ch; }
.posts li { padding-bottom: clamp(14px, 1.6vw, 20px); border-bottom: 1px solid var(--line); font-size: var(--text-body); color: var(--muted); }
.posts a { font-weight: 700; text-decoration: none; }
.posts a:hover { text-decoration: underline; }

/* Dark full-bleed band, same layout as template3: brand+description,
   quick links, contact, in a 3-column grid over a copyright line. */
.site-footer { background: var(--footer-bg, var(--ink)); color: rgba(255,255,255,.68); padding: clamp(40px, 5vw, 54px) 0 clamp(20px, 2.6vw, 28px); font-size: var(--text-small); }
.footer-grid { display: grid; grid-template-columns: 1.4fr .7fr .9fr; gap: clamp(30px, 4vw, 54px); max-width: calc(var(--content-width) + 2 * var(--side-space)); margin: 0 auto; padding: 0 var(--side-space) clamp(24px, 2.8vw, 35px); border-bottom: 1px solid rgba(255,255,255,.15); }
.footer-brand { display: block; margin-bottom: 10px; color: #fff; font-weight: 800; font-size: clamp(18px, 1.8vw, 23px); letter-spacing: -.03em; }
.footer-about p { margin: 0 0 16px; max-width: 34ch; }
.footer-nav, .footer-contact { display: grid; align-content: start; gap: 7px; }
.footer-nav strong, .footer-contact strong { margin-bottom: 4px; color: #fff; }
.footer-nav a, .footer-contact a, .footer-contact span { color: rgba(255,255,255,.68); text-decoration: none; font-size: var(--text-small); }
.footer-nav a:hover, .footer-contact a:hover { color: #fff; }
.social { display: flex; gap: clamp(10px, 1.2vw, 15px); }
.social-icon { display: flex; align-items: center; justify-content: center; width: clamp(36px, 3.6vw, 46px); height: clamp(36px, 3.6vw, 46px); border-radius: 50%; background: rgba(255,255,255,.12); color: #fff; transition: background-color .18s ease; }
.social-icon svg { width: clamp(16px, 1.6vw, 20px); height: clamp(16px, 1.6vw, 20px); }
.social-icon:hover { background: var(--primary); }
.footer-bottom { max-width: calc(var(--content-width) + 2 * var(--side-space)); margin: 0 auto; padding: clamp(18px, 2vw, 24px) var(--side-space) 0; }
.footer-bottom p { margin: 0; }
.footer-bottom a { color: rgba(255,255,255,.68); }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } .footer-about { grid-column: 1 / -1; } }
@media (max-width: 700px) { .footer-grid { grid-template-columns: 1fr; } }

/* Shared content edges keep generous side space throughout the layout. */
.trust-bar .grid, .about-inner {
  max-width: calc(var(--content-width) + 2 * var(--side-space));
  padding-left: var(--side-space);
  padding-right: var(--side-space);
}
.hero-copy { padding-left: var(--content-edge); }
.about-stats { max-width: var(--content-width); width: calc(100% - 2 * var(--side-space)); }
.cta { padding-left: var(--side-space); padding-right: var(--side-space); }
@media (max-width: 799px) { .hero-copy { padding-right: var(--side-space); } }
/* On mobile the media fills the whole hero behind a readable white scrim. */
@media (max-width: 799px) {
  .hero { position: relative; isolation: isolate; }
  .hero .hero-media { position: absolute; inset: 0; min-height: 0; z-index: 0; pointer-events: none; }
  .hero .hero-media.video { aspect-ratio: auto; }
  .hero .hero-media.placeholder { color: transparent; }
  .hero .hero-media-badge { display: none; }
  .hero .hero-copy { position: relative; z-index: 1; background: color-mix(in srgb, var(--bg) 88%, transparent); }
}
/* On a phone the headline comes down so the quote button is on the first screen
   without scrolling — that button is the whole point of the page. */
@media (max-width: 700px) { .hero h1 { font-size: clamp(38px, 10.5vw, 52px); } }
@media (max-width: 700px) { .trust-item { padding: 11px 0; }.services > h2, .gallery h2 { margin-bottom: 20px; }.services > p, .gallery > p { position: static; margin: 0 0 30px; }.about-stats { padding: 0 35px 10px; }.service-area-inner { grid-template-columns: 1fr; gap: 40px; }.service-area-map { min-height: 316px; } }

/* Keep tablet columns readable and let long client text shrink within grids. */
.grid > *, .hero > *, .about-inner > *, .contact > *, .service-area-inner > * { min-width: 0; }
main, .site-header .brand, .site-footer { overflow-wrap: anywhere; }
@media (max-width: 800px) {
  .service-area-inner { grid-template-columns: 1fr; gap: 40px; }
  .service-area-copy { max-width: none; }
}
/* Phone gutters leave more room for copy without losing the inset layout. */
@media (max-width: 560px) {
  :root { --side-space: 24px; }
  /* Brand, phone and menu share one row. Laid out end to end they need ~470px
     at 375px wide, so the brand takes what is left and wraps to a second line
     rather than pushing the phone off the row — the number has to stay visible,
     it is the fastest way a customer on a job site gets in touch. */
  .site-header { padding-left: var(--side-space); padding-right: var(--side-space); gap: 8px 10px; }
  /* flex-basis 0, not auto: with auto the brand demands its full text width and
     the flex container wraps the row before the brand ever shrinks. */
  .site-header .brand { flex: 1 1 0; min-width: 0; order: 1; margin-right: 0; font-size: 17px; line-height: 1.2; }
  .site-header .phone { flex: 0 0 auto; order: 2; margin-left: auto; gap: 7px; padding: 8px 4px; font-size: 13px; }
  .site-header .phone svg { width: 19px; height: 19px; }
  .nav-toggle-btn { order: 3; margin-left: 2px; }
  .site-header nav { order: 5; }
  .hero-actions .button, .cta-actions .button, .cta-phone .button { width: 100%; }
  .trust-bar .grid, .about-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .trust-item { padding: 8px; gap: 10px; }
  .trust-item:nth-child(even) { border-left: 1px solid var(--line-strong); }
  .trust-item strong { font-size: var(--text-small); }
  .trust-item small { font-size: var(--text-small); }
  .about-stats { padding: 24px 0; row-gap: 20px; }
  .about-stat:nth-child(even) { border-left: 1px solid rgba(255,255,255,.2); }
  .about-stat .stat-label { font-size: var(--text-small); }
  .footer-nav a, .footer-contact a { display: inline-flex; align-items: center; min-height: 44px; }
  .social-icon { width: 44px; height: 44px; }
}

/* ---- /services/<slug> ---- */
/* The heading is a full-bleed band like the home hero: tinted copy panel on the
   left, photo running to the right edge of the window. */
.page-head { display: grid; gap: 0; margin: 0 calc(50% - 50vw); background: var(--soft); border-bottom: 1px solid var(--band-edge); }
@media (min-width: 800px) { .page-head { grid-template-columns: 1fr 1fr; } }
.page-head-copy { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: clamp(40px, 5vw, 76px) clamp(28px, 4vw, 64px) clamp(40px, 5vw, 76px) var(--content-edge); }
.page-head h1 { margin: 0 0 clamp(14px, 1.7vw, 20px); font-size: clamp(32px, 3.6vw, 52px); }
.page-head .lede { margin: 0; font-size: var(--text-lede); color: var(--muted); max-width: 46ch; }
/* A fixed square, not stretched to match the heading text beside it --
   otherwise a page with a longer h1/lede gets a taller image than one with
   a short heading, even though both use object-fit: cover. */
.page-head-media { align-self: start; aspect-ratio: 1 / 1; min-height: 0; overflow: hidden; }
.page-head-media img, .page-head-media .placeholder { width: 100%; height: 100%; object-fit: cover; border-radius: 0; display: block; }
@media (max-width: 799px) { .page-head-copy { padding-right: var(--side-space); } }
.page-head-copy .eyebrow { color: var(--accent); font-size: var(--text-small); font-weight: 800; margin: 0 0 10px; }
.page-head-rating { margin: clamp(10px, 1.2vw, 14px) 0 0; color: var(--brand-text); font-size: var(--text-small); font-weight: 700; }
.page-head-actions { display: flex; flex-wrap: wrap; gap: clamp(9px, 1vw, 12px); margin-top: clamp(20px, 2.4vw, 28px); }

/* Location page local-relevance copy and the neighbourhood chips beside it —
   the record's own specifics, never generated by the template. */
.area-intro { max-width: 72ch; margin: 0 0 clamp(28px, 3.4vw, 42px); padding: 0; }
.area-intro p { font-size: var(--text-body); line-height: 1.6; color: var(--ink); margin: 0 0 14px; }
.area-intro p.lede { font-size: var(--text-lede); font-weight: 600; }
.area-intro p:last-child { margin-bottom: 0; }
.area-contact { max-width: 460px; padding: clamp(28px, 3.4vw, 42px) 0 0; }
.area-contact h2 { margin: 0 0 clamp(16px, 2vw, 22px); font-size: clamp(26px, 2.2vw, 34px); }

/* Quick-scan services list — small linked tiles, not the full card grid. */
.service-links { padding: clamp(20px, 2.4vw, 30px) 0 0; }
.service-links h2 { margin: 0 0 clamp(16px, 2vw, 22px); font-size: clamp(26px, 2.2vw, 34px); }
.service-links-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px 12px; list-style: none; margin: 0; padding: 0; }
.service-links-list a { display: block; padding: 10px 14px; background: var(--soft); border: 1px solid var(--line); border-radius: 6px; color: var(--ink); font-size: var(--text-small); font-weight: 600; text-decoration: none; }
.service-links-list a:hover { border-color: var(--line-strong); color: var(--primary); }

.service-layout { display: grid; grid-template-columns: minmax(0, 1fr) 322px; gap: clamp(28px, 3.4vw, 56px); align-items: start; padding: clamp(28px, 3.2vw, 44px) 0 clamp(40px, 5vw, 64px); }
/* Area page only (trust bar sits right above it there): the intro paragraph
   should start level with the quote form beside it, not with extra air above it. */
.trust-bar + .service-layout { padding-top: clamp(37px, 3.9vw, 43px); }
.service-body { margin-top: 0; padding-top: 0; border-top: 0; }
.service-body > :first-child { margin-top: 0; }

/* FAQs — <details> so it opens with no script. */
.faqs { padding: clamp(28px, 3.4vw, 42px) 0 0; }
/* On the homepage .faqs is a standalone top-level section (not nested inside
   a service's article), so it needs the full section rhythm on both edges —
   the tight nested version above is for the service-page case only. */
main > .faqs { padding: clamp(54px, 6.4vw, 90px) 0; }
.faqs h2 { margin: 0 0 clamp(16px, 2vw, 22px); font-size: clamp(26px, 2.2vw, 34px); }
.faq { border: 1px solid var(--line); border-radius: 8px; background: var(--soft); margin-bottom: 10px; }
.faq summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: clamp(13px, 1.4vw, 17px) clamp(14px, 1.6vw, 20px); font-size: var(--text-body); font-weight: 600; cursor: pointer; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; flex: 0 0 auto; font-size: 24px; font-weight: 400; line-height: 1; color: var(--muted); }
.faq[open] summary::after { content: '–'; }
.faq summary:focus-visible { outline: 3px solid var(--primary); outline-offset: -3px; border-radius: 8px; }
.faq-answer { padding: 0 clamp(14px, 1.6vw, 20px) clamp(14px, 1.6vw, 18px); }
.faq-answer p { margin: 0; color: var(--muted); font-size: var(--text-body); max-width: 68ch; }

/* The rail follows you down the page while the content scrolls past. */
/* Form and proof points read as one panel, not two stacked boxes: no gap
   between them, corners rounded only on the outside, one shared edge. */
.service-aside { position: sticky; top: 20px; display: grid; gap: 0; }
/* Shared by the service rail and /contact. Standalone it is a closed card; in
   the rail the proof points join underneath, so it drops its bottom edge.
   It carries the brand colour itself — blue on light, the accent on dark —
   rather than another shade of grey, so the one form on the page that asks
   for a name and number is unmistakably the thing to act on. */
.quote-card { background: var(--quote-bg); color: var(--quote-ink, #fff); border-radius: 10px; padding: clamp(18px, 2vw, 24px); }
.service-aside .quote-card { border-radius: 10px 10px 0 0; }
.quote-card .form-title { color: var(--quote-ink, #fff); }
.quote-card .contact-form button { width: 100%; }
.quote-card .contact-form button { background: var(--quote-btn-bg, var(--button-bg, var(--primary))); color: var(--quote-btn-ink, var(--button-ink, #fff)); }
.quote-card .form-reassure, .quote-card .consent, .quote-card .form-or span { color: var(--quote-ink, inherit); opacity: .85; }
/* The submit inverts to white, same pattern as a button on the closing band:
   text takes the card's own colour, so it stays legible whichever brand
   colour is behind it. */
.quote-card .contact-form button[type="submit"] { background: var(--quote-btn-bg, #fff); color: var(--quote-btn-ink, var(--quote-bg)); }
.quote-card .contact-form button[type="submit"]:hover { background: var(--quote-btn-bg, var(--soft)); filter: none; }
.quote-card .contact-form .consent, .quote-card .form-reassure { color: rgba(255, 255, 255, .82); }
.quote-card .form-or { color: rgba(255, 255, 255, .82); }
.quote-card .form-or::before, .quote-card .form-or::after { background: rgba(255, 255, 255, .35); }
.quote-card .contact-form .button-quiet { background: transparent; color: var(--quote-ink, #fff); border-color: var(--quote-ink, rgba(255, 255, 255, .5)); }
.quote-card .contact-form .button-quiet:hover { background: var(--quote-ink, #fff); color: var(--quote-bg); border-color: var(--quote-ink, #fff); }
/* A touch deeper than the form above it, mixed from the client's own primary
   so it still reads as their colour rather than a fixed blue. */
.aside-points { list-style: none; display: grid; gap: clamp(14px, 1.6vw, 18px); margin: 0; padding: clamp(18px, 2vw, 22px) clamp(18px, 2vw, 24px); background: var(--soft); background: color-mix(in srgb, var(--primary) 10%, var(--card-raised)); border: 1px solid var(--line); border-radius: 0 0 10px 10px; }
.aside-points li { display: flex; align-items: flex-start; gap: 12px; }
.aside-points svg { flex: 0 0 auto; color: var(--brand-text); }
.aside-points span { display: grid; gap: 2px; min-width: 0; }
.aside-points strong { font-size: var(--text-small); }
.aside-points small { color: var(--muted); font-size: var(--text-small); }

@media (max-width: 980px) {
  /* The rail becomes a block under the copy; sticky would trap it mid-page. */
  .service-layout { grid-template-columns: 1fr; }
  /* Capped width once stacked, so it needs centring — left-aligned it sat with
     up to 460px of dead space beside it on a tablet. */
  .service-aside { position: static; max-width: 420px; margin-inline: auto; }
}


/* Service photo gallery — four to a page, arrows page through the rest.
   The track is a real scroller with snap points, so it swipes on touch and
   still works if the arrow script never runs.

   Flex, not grid: grid-auto-columns with a percentage inside an overflow
   container sizes against the scroll width, not the visible width, so the row
   shrank to fit its own content and stopped short of the right edge. A flex
   basis resolves against the scrollport, which is what we want. */
.svc-gallery { padding: clamp(28px, 3.4vw, 42px) 0 0; --gal-gap: clamp(12px, 1.4vw, 18px); --gal-cols: 3; }
.svc-gallery > h2 { margin: 0 0 clamp(14px, 1.8vw, 20px); font-size: clamp(26px, 2.2vw, 34px); }
.svc-gallery-body { position: relative; }
.svc-gallery-track { list-style: none; display: flex; gap: var(--gal-gap); margin: 0; padding: 0 0 4px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-x: contain; }
.svc-gallery-track::-webkit-scrollbar { display: none; }
.svc-gallery-track > li { flex: 0 0 calc((100% - (var(--gal-cols) - 1) * var(--gal-gap)) / var(--gal-cols)); min-width: 0; scroll-snap-align: start; }
.svc-gallery-track img, .svc-gallery-track .project-placeholder { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 8px; display: block; }
.svc-gallery-track p { margin: 8px 0 0; color: var(--muted); font-size: var(--text-small); }

/* Arrows sit on the outer edges of the photo row, centred on the images —
   the offset lifts them clear of the caption line underneath. */
.svc-gallery-body:not([data-gal-ready]) .gal-btn { display: none; }
.gal-btn { position: absolute; top: 50%; transform: translateY(calc(-50% - 15px)); z-index: 1; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0; border: 1px solid var(--line); border-radius: 50%; background: var(--surface); color: var(--ink); cursor: pointer; box-shadow: 0 2px 10px rgba(var(--shadow),.16); transition: background-color .18s ease, border-color .18s ease, opacity .18s ease; }
.gal-prev { left: 8px; }
.gal-next { right: 8px; }
.gal-btn:hover:not(:disabled) { background: var(--soft); border-color: var(--line-strong); }
.gal-btn:disabled { opacity: 0; pointer-events: none; }
.gal-btn:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

/* Fewer per page as the column narrows, so the photos never get tiny. */
@media (max-width: 760px) { .svc-gallery { --gal-cols: 2; } }
@media (max-width: 460px) { .svc-gallery { --gal-cols: 1.25; } }

/* ---- /about ---- */
/* Small facts under the lede in the head band: one row, hairline dividers
   between. The copy column is only half the page, so the type and gaps scale
   down with it to keep all four on one line. */
.head-facts { list-style: none; display: flex; align-items: stretch; margin: clamp(18px, 2.2vw, 26px) 0 0; padding: 0; }
/* Cells stretch to the tallest so the dividers between them are all the same
   length even when a label runs to two lines in a narrow column. */
.head-facts li { display: grid; grid-template-rows: auto auto 1fr; justify-items: center; gap: 2px; flex: 1 1 0; min-width: 0; padding: 0 clamp(6px, .9vw, 14px); text-align: center; }
.head-facts li + li { border-left: 1px solid var(--line-strong); }
.head-facts .fact-icon { display: flex; color: var(--ink); }
.head-facts .fact-icon svg { width: clamp(16px, 1.5vw, 20px); height: clamp(16px, 1.5vw, 20px); }
.head-facts strong { font-size: clamp(17px, 1.6vw, 23px); line-height: 1.1; letter-spacing: -.03em; }
.head-facts .fact-label { color: var(--muted); font-size: clamp(11px, .95vw, 13px); line-height: 1.2; }

.about-layout { display: grid; grid-template-columns: minmax(0, 1fr) 322px; gap: clamp(28px, 3.4vw, 56px); align-items: start; padding: clamp(34px, 4vw, 56px) 0 clamp(20px, 2.4vw, 32px); }
.about-story h2 { margin: 0 0 clamp(14px, 1.8vw, 20px); font-size: clamp(26px, 2.4vw, 38px); }
.about-story p { margin: 0 0 1em; max-width: 68ch; font-size: var(--text-lede); color: var(--muted); }
.about-side { display: grid; gap: 14px; }
.info-card { padding: clamp(18px, 2vw, 24px); border: 1px solid var(--line); border-radius: 10px; background: var(--soft); }
.info-card h3 { margin: 0 0 clamp(12px, 1.4vw, 16px); font-size: clamp(17px, 1.5vw, 20px); }
.info-list { margin: 0; display: grid; gap: 12px; }
.info-list > div { display: grid; gap: 2px; }
.info-list dt { color: var(--muted); font-size: var(--text-small); font-weight: 700; }
.info-list dd { margin: 0; font-size: var(--text-small); font-weight: 600; }
.info-phone { margin: 0 0 clamp(14px, 1.6vw, 18px); }
.info-phone .button { width: 100%; }
.info-list dd a { font-weight: 600; word-break: break-word; }
.info-note { margin: clamp(12px, 1.4vw, 16px) 0 0; font-size: var(--text-small); }
.info-note a { font-weight: 700; text-decoration: none; }
.info-note a:hover { text-decoration: underline; }
@media (max-width: 980px) {
  /* Stacked, the two cards sit side by side across the full width. Centred at a
     capped width they floated in the middle under left-aligned prose, which is
     what made the rail look misplaced rather than stacked. */
  .about-layout { grid-template-columns: 1fr; }
  .about-side { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; max-width: none; margin-inline: 0; width: 100%; }
}
@media (max-width: 620px) { .about-side { grid-template-columns: 1fr; } }

/* ---- /projects ---- */
/* The full set, so it steps 3 -> 2 -> 1 rather than the home page's 3 -> 1:
   with seven or more on screen at once, two across is a real stage. */
.projects-all { padding: clamp(34px, 4vw, 52px) 0 clamp(40px, 5vw, 64px); }
.projects-all .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(14px, 1.7vw, 22px); }
@media (max-width: 980px) { .projects-all .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .projects-all .grid { grid-template-columns: 1fr; } }
.projects-all .gallery-item p { margin: 10px 0 0; }

/* Trust badges on /about: logos only, no heading and no captions. A soft
   full-bleed strip, marks sized to a common height because certification logos
   arrive in wildly different shapes. */
.badges { background: var(--soft); border-block: 1px solid var(--band-edge); width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); padding: clamp(26px, 3.2vw, 42px) var(--side-space); }
.badge-row { list-style: none; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: clamp(22px, 3.4vw, 54px); max-width: var(--content-width); margin: 0 auto; padding: 0; }
.badge-row li { flex: 0 0 auto; }
.badge-row a { display: block; transition: opacity .18s ease; }
.badge-row a:hover { opacity: .75; }
.badge-row a:focus-visible { outline: 3px solid var(--primary); outline-offset: 4px; border-radius: 4px; }
.badge-row img { display: block; height: clamp(44px, 4.6vw, 62px); width: auto; max-width: 170px; object-fit: contain; }
.badge-fallback { display: flex; align-items: center; justify-content: center; height: clamp(44px, 4.6vw, 62px); width: clamp(72px, 7.6vw, 100px); border: 1px dashed var(--line-strong); border-radius: 8px; background: var(--surface); color: var(--muted); }

/* ---- /reviews ---- */
/* No photo on this one, so the band is a single column across the width. */
.page-head-plain { grid-template-columns: 1fr; }
@media (min-width: 800px) { .page-head-plain { grid-template-columns: 1fr; } }
.page-head-plain .page-head-copy { padding-right: var(--content-edge); }
.page-head-plain .lede { max-width: none; }
.head-stars { color: var(--accent); letter-spacing: 2px; }

/* Four across, arrows page through the rest. Same scroller as the service
   gallery — real scrolling with snap points, so it swipes on touch too. */
.reviews-all { padding: clamp(34px, 4vw, 52px) 0 clamp(24px, 3vw, 40px); --rev-gap: clamp(14px, 1.7vw, 20px); --rev-cols: 4; }
.reviews-rail { position: relative; }
.reviews-track { display: flex; align-items: stretch; gap: var(--rev-gap); margin: 0; padding: 0 0 4px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-x: contain; }
.reviews-track::-webkit-scrollbar { display: none; }
.reviews-track > .review { flex: 0 0 calc((100% - (var(--rev-cols) - 1) * var(--rev-gap)) / var(--rev-cols)); min-width: 0; scroll-snap-align: start; display: flex; flex-direction: column; }
/* The attribution sits on the bottom edge so it lines up across the row
   however long the review above it runs. */
.reviews-track > .review .review-by, .reviews-fixed > .review .review-by { margin-top: auto; }
.reviews-rail .gal-btn { top: 50%; transform: translateY(-50%); }
@media (max-width: 1100px) { .reviews-all { --rev-cols: 3; } }
@media (max-width: 820px) { .reviews-all { --rev-cols: 2; } }
@media (max-width: 560px) { .reviews-all { --rev-cols: 1.15; } }

/* Where a review came from. */
.review-by { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: auto; padding-top: clamp(11px, 1.3vw, 15px); }
.review-by cite { display: inline; margin: 0; padding: 0; font-style: normal; color: var(--card-muted); font-size: var(--text-small); }
.src { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 999px; font-size: clamp(11px, 1vw, 12px); font-weight: 700; line-height: 1.7; }
.src-google { background: var(--card-media); color: var(--card-ink); }
.src-google svg { flex: 0 0 auto; }
.src-sample { background: transparent; border: 1px dashed var(--card-line); color: var(--card-muted); }

/* The headline score in the reviews page band. */
.score { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin: 0; font-size: var(--text-lede); color: var(--muted); }
.score strong { color: var(--ink); font-size: clamp(20px, 1.9vw, 26px); letter-spacing: -.03em; }
.score-stars { color: var(--accent); letter-spacing: 2px; }
.google-link { display: inline-block; margin-top: clamp(12px, 1.5vw, 18px); font-size: var(--text-small); font-weight: 700; text-decoration: none; }
.google-link:hover { text-decoration: underline; }

/* One or two reviews fill the row rather than leaving holes in a three-wide grid. */
.reviews[data-count="1"] .grid { grid-template-columns: minmax(0, 1fr); }
.reviews[data-count="2"] .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.reviews-fixed { display: grid; grid-template-columns: repeat(var(--rev-cols), minmax(0, 1fr)); gap: var(--rev-gap); align-items: stretch; }
.reviews-all[data-count="1"] { --rev-cols: 1; }
.reviews-all[data-count="2"] { --rev-cols: 2; }
.reviews-all[data-count="3"] { --rev-cols: 3; }
.reviews-fixed > .review { display: flex; flex-direction: column; }
@media (max-width: 820px) { .reviews-fixed { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .reviews-fixed { grid-template-columns: 1fr; } }
/* Page-head photos are always square, so a tall or very wide photo never blows the header up */
.page-head-media { min-height: 0; align-self: center; }
.page-head-media img, .page-head-media .placeholder { height: auto; min-height: 0; aspect-ratio: 1 / 1; }

/* Real service-area map: pins and labels are styled by /assets/service-map.js */
.service-area-map-live { min-height: clamp(280px, 30vw, 374px); }

/* Header photos are a short landscape crop so the text starts higher on the page */
.page-head-media { aspect-ratio: 16 / 10; align-self: center; }
.page-head-media img, .page-head-media .placeholder { height: 100%; aspect-ratio: 16 / 10; }
