/* ==========================================================================
   Self-hosted fonts (latin subset, variable weight; SIL OFL 1.1, see
   assets/fonts/README). Served from this site so no visitor's IP address is
   sent to Google. Paths are relative to this file: css/ -> ../assets/fonts/.
   ========================================================================== */
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/outfit-latin-var.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: "Inter";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../assets/fonts/inter-latin-var.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: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url("../assets/fonts/jetbrains-mono-latin-var.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;
}

/* ==========================================================================
   Avon Pest Control — Design Tokens
   ========================================================================== */

:root {
  /* Palette (client brand colors) */
  --ink: #12151d;
  --ink-soft: #1b212b;
  --off: #e8e8e7;
  --off-dim: #d7d8d3;
  --off-card: #f1f0ec;
  --sage: #5a9572;
  --sage-light: #82ad93;
  /* Lighter step for small text on the mid-green credentials band, keeps AA */
  --sage-pale: #9cc2ab;
  --sage-dark: #436e56;
  /* Darker step for small text on the light-grey surfaces, keeps AA at 4.5:1 */
  --sage-deep: #3d6450;
  --mid-green: #3d5d50;
  --deep-forest: #2e453f;
  --text-dim: #47504b;
  --line: rgba(18, 21, 29, 0.13);
  --line-on-dark: rgba(232, 232, 231, 0.14);

  /* Type */
  --display: "Outfit", "Segoe UI", system-ui, sans-serif;
  --sans: "Inter", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  /* Rhythm */
  --gutter: clamp(1.5rem, 4vw, 4rem);
  --radius: 4px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-snap: cubic-bezier(0.22, 1, 0.36, 1);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  overflow-x: hidden;
}

body {
  background: var(--off);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }

a { color: inherit; text-decoration: none; }

ul { list-style: none; }

button { font: inherit; border: none; background: none; cursor: pointer; }

.container {
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1.14;
}

/* Leading is size-specific, like tracking: tight on display sizes, looser as
   headings get small. A single 1.14 was right for h1 and cramped for h4. */
h1 { line-height: 1.06; }
h2 { line-height: 1.12; }
h3 { line-height: 1.25; }
h4 { line-height: 1.35; }

/* Optical sizing: tracking is a function of size, not a fixed constant.
   Large display type (>2.5rem) reads loose at the -0.02em baseline above —
   tighten it. Applied here, and to .process-step h4 at its own rule below
   (the later declaration in the cascade — editing that one directly rather
   than fighting it from up here). */
h1, .cta-band h2, .cta-copy h2, .cred-grid h2 {
  letter-spacing: -0.03em;
}

/* text-wrap: balance/pretty removed site-wide — Safari's balance implementation
   was forcing short 2-3 word headings (e.g. "Rats & Mice" in a narrow index-row
   column) onto one word per line, stacking vertically. Chrome rendered every
   width fine; the bug only showed in Safari, which is why it wasn't caught by
   this build's normal screenshot verification. Removing both rather than
   patching the one row that got reported — the same narrow-column risk exists
   on every short h3/h4 across the site (service cards, team names, news
   titles), and there's no way to verify a narrower fix without a Safari
   render target. */

.on-dark { color: var(--off); }

/* Outfit has no true italic — emphasis is carried by colour, not slant. */
em, .accent {
  font-style: normal;
  font-weight: inherit;
  color: var(--sage);
}


/* Accessible focus state
   ========================================================================== */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--sage);
  outline-offset: 3px;
}

/* Buttons
   ========================================================================== */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.8rem 1.4rem;
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.005em;
  border-radius: var(--radius);
  transition: color 0.35s var(--ease), background-color 0.35s var(--ease), border-color 0.35s var(--ease), transform 0.12s ease-out;
  white-space: nowrap;
}

.btn-primary {
  background: var(--sage);
  color: var(--ink);
}

.btn-primary:hover { background: var(--sage-light); }

/* Apple's fluid-interface rule: feedback belongs on press, not just hover —
   hover doesn't exist on touch, and even with a mouse, a button that only
   reacts on :hover feels dead when you actually click it. */
.btn:active { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .btn:active { transform: none; }
}

/* On dark surfaces the primary reads as a deep-green solid, per brand */
.cta-section .btn-primary,
.hero .btn-primary { background: var(--sage); color: var(--ink); }

.btn-ghost {
  border: 1px solid var(--line-on-dark);
  color: var(--off);
}

.btn-ghost:hover {
  border-color: var(--sage-light);
  color: var(--sage-light);
}

.btn-outline-dark {
  border: 1px solid var(--line);
  color: var(--ink);
}

.btn-outline-dark:hover {
  border-color: var(--mid-green);
  background: var(--mid-green);
  color: var(--off);
}

.btn-arrow { transition: transform 0.35s var(--ease-snap); }
.btn:hover .btn-arrow { transform: translateX(4px); }

/* ==========================================================================
   Header
   ========================================================================== */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  padding: 1.15rem 0;
  /* Padding is deliberately NOT animated: it triggers layout on every frame of
     the transition, on a fixed element, on every scroll. The condense reads
     through a transform on the logo instead, which stays on the compositor. */
  transition: background-color 0.4s var(--ease), box-shadow 0.4s var(--ease);
}

.site-header .logo { transition: transform 0.4s var(--ease); transform-origin: left center; }
.site-header.scrolled .logo { transform: scale(0.88); }

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

/* A flat translucent bar reads as a dark rectangle, not glass. A bright hairline
   at the very top edge — where light would catch a real material — is what
   sells the translucency; the shadow below just separates it from content. */
.site-header.scrolled {
  background: rgba(18, 21, 29, 0.78);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  /* Bright top edge = light catching a real material. No bottom divider: the
     soft edge below does that job without drawing a line. */
  box-shadow: inset 0 1px 0 rgba(232, 232, 231, 0.1);
}

/* Scroll edge effect rather than a 1px rule — content fades under the chrome. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 26px;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(180deg, rgba(18,21,29,0.55), rgba(18,21,29,0));
  transition: opacity 0.4s var(--ease);
}
.site-header.scrolled::after { opacity: 1; }

@media (prefers-reduced-transparency: reduce) {
  .site-header.scrolled { background: var(--ink); backdrop-filter: none; }
}

.logo {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4rem;
  color: var(--off);
}

/* Client-supplied mark, cropped to just the "Avon" wordmark (transparent PNG,
   1492x486). The full lockup file also included a "Pest Control" subtitle,
   but at any legible header height that line rendered at 9-11px — an
   illegible grey smear, not a caption. Cropping the mark down and setting
   "Pest Control" as real site type below it fixes that and, as a side
   effect, keeps the caption in the same typeface as everything else on the
   page instead of a second, uncontrolled font baked into a raster image.
   The mid-grey (#a5a5a5) and both greens were checked against the ink
   background before shipping as-is — 7.4:1 contrast, no recolour needed. */
.logo-mark-img {
  height: 32px;
  width: auto;
  display: block;
}

.logo small {
  display: block;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sage-light);
}

.footer-brand .logo-mark-img { height: 30px; }
.footer-brand .logo small { font-size: 0.62rem; }

.main-nav {
  display: flex;
  align-items: center;
  gap: 2.2rem;
}

.main-nav a {
  position: relative;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--off);
  padding-bottom: 4px;
  white-space: nowrap;
}

.main-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 1px;
  background: var(--sage-light);
  transition: width 0.35s var(--ease-snap);
}

.main-nav a:hover::after { width: 100%; }

/* Blog dropdown — the only nav item with children. The trigger itself is a
   real link to /blog/, so it still goes somewhere with no JS and no hover.
   Opens on hover/focus-within for a mouse or keyboard, and js/main.js adds
   an `.is-open` class for a tap on touch devices, where hover never fires.

   .nav-dropdown-menu is the hit-tested box (position: absolute, top: 100%,
   NO margin) — it sits flush against the trigger so there is no dead strip
   between them. The visible "gap" is padding-top on that same box instead:
   padding is still part of the element for hover/pointer-events purposes,
   margin is not, so the pointer never crosses a patch of the page that
   isn't the dropdown while moving from the trigger down into the panel.
   The actual painted panel (background/blur/border) lives one level in, on
   .nav-dropdown-panel, so it still visually starts below that gap. */
.nav-dropdown { position: relative; }

.nav-dropdown-chevron {
  margin-left: 0.35rem;
  transition: transform 0.25s var(--ease);
}
.nav-dropdown:hover .nav-dropdown-chevron,
.nav-dropdown:focus-within .nav-dropdown-chevron,
.nav-dropdown.is-open .nav-dropdown-chevron {
  transform: rotate(180deg);
}

.nav-dropdown-menu {
  position: absolute;
  top: 100%;
  left: 50%;
  min-width: 190px;
  padding-top: 0.9rem;
  transform: translateX(-50%);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s var(--ease), visibility 0.2s;
  z-index: 60;
}

.nav-dropdown:hover .nav-dropdown-menu,
.nav-dropdown:focus-within .nav-dropdown-menu,
.nav-dropdown.is-open .nav-dropdown-menu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.nav-dropdown-panel {
  min-width: 190px;
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  background: rgba(18, 21, 29, 0.94);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid var(--line-on-dark);
  transform: translateY(4px);
  transition: transform 0.2s var(--ease);
}

.nav-dropdown:hover .nav-dropdown-panel,
.nav-dropdown:focus-within .nav-dropdown-panel,
.nav-dropdown.is-open .nav-dropdown-panel {
  transform: translateY(0);
}

.nav-dropdown-menu a {
  padding: 0.6rem 0.85rem;
  font-size: 0.85rem;
  white-space: nowrap;
  transition: background-color 0.2s, color 0.2s;
}
.nav-dropdown-menu a::after { display: none; }
.nav-dropdown-menu a:hover { background: rgba(232, 232, 231, 0.07); color: var(--sage-light); }

@media (prefers-reduced-motion: reduce) {
  .nav-dropdown-menu { transition: opacity 0.15s linear, visibility 0.15s; }
  .nav-dropdown-panel { transition: none; transform: none; }
  .nav-dropdown-chevron { transition: none; }
}

.header-cta { display: flex; align-items: center; gap: 1.4rem; flex-shrink: 0; }

.header-phone {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--off);
  white-space: nowrap;
  transition: color 0.3s;
}

.header-phone-icon {
  width: 17px;
  height: 17px;
  color: var(--sage-light);
  flex-shrink: 0;
}

.header-phone .number { font-family: var(--display); font-weight: 600; font-size: 1.05rem; }

.header-phone:hover, .header-phone:hover .header-phone-icon { color: var(--sage-light); }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 0 9px;
  margin-right: -9px;
  box-sizing: border-box;
}
.nav-toggle span { height: 1px; background: var(--off); width: 100%; }

/* ==========================================================================
   Welcome dialog — rebrand notice, shown once per visitor (see main.js)
   ========================================================================== */

/* Centering set explicitly rather than relying on the UA stylesheet's default
   dialog centering — that default didn't survive the rest of this rule
   (landed pinned to the top-left corner instead), and explicit is safer
   across browsers than debating why. */
.welcome-dialog {
  position: fixed;
  inset: 0;
  margin: auto;
  max-width: 480px;
  width: calc(100% - 2.5rem);
  max-height: calc(100% - 2.5rem);
  border: none;
  border-top: 3px solid var(--sage);
  padding: 0;
  background: var(--off);
  color: var(--ink);
}

.welcome-dialog::backdrop {
  background: rgba(18, 21, 29, 0.7);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

@media (prefers-reduced-transparency: reduce) {
  .welcome-dialog::backdrop { background: rgba(18, 21, 29, 0.92); backdrop-filter: none; }
}

.welcome-dialog-inner { position: relative; padding: 2.6rem; }

.welcome-close {
  position: absolute;
  top: 1.2rem;
  right: 1.2rem;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  border-radius: var(--radius);
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease);
}
.welcome-close:hover { color: var(--ink); background: var(--off-card); }
.welcome-close svg { width: 16px; height: 16px; }

.welcome-dialog h2 {
  /* Clear of the close button at narrow widths. */
  padding-right: 2.2rem;
  font-size: 1.55rem;
  letter-spacing: -0.025em;
  max-width: 20ch;
  margin-bottom: 1rem;
}

.welcome-dialog p { color: var(--text-dim); font-size: 0.96rem; max-width: 46ch; }

.welcome-dialog-actions {
  display: flex;
  align-items: center;
  margin-top: 1.8rem;
}

@keyframes welcome-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.welcome-dialog[open] { animation: welcome-in 0.4s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .welcome-dialog[open] { animation: none; }
}

@media (max-width: 520px) {
  .welcome-dialog-inner { padding: 2.2rem 1.6rem; }
  .welcome-dialog-actions { flex-direction: column; align-items: flex-start; gap: 1rem; }
  .welcome-dialog-actions .btn { width: 100%; justify-content: center; }
}

/* ==========================================================================
   Hero
   ========================================================================== */

/* A raw min-height:100vh was tried here before and reverted — it left a dead
   band above the fold on tall viewports and pushed the strip beneath it right
   onto the fold line, cutting it in half. clamp() keeps the same bottom-anchored
   layout genuinely larger and more dramatic on ordinary screens, without that
   failure mode reappearing on a tall monitor: the max bound stops it running away. */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  background: var(--ink);
  overflow: hidden;
  padding-top: 11rem;
  min-height: clamp(36rem, 88vh, 50rem);
}

.hero-texture {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.5;
}

/* The photo variant replaces the abstract line texture — real image detail
   makes the decorative lines redundant clutter rather than added richness. */
.hero--photo .hero-texture { display: none; }

/* background-image is set inline per-page (via style=""), so it can point at
   whatever photo that page actually has rather than a shared CSS default. */
.hero-photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-size: cover;
  background-position: center 58%;
}

/* Scrim: strong at top (nav legibility over sky/roofline) and bottom (lede +
   actions sit here), lighter through the middle so the vehicle itself still
   reads as a photograph rather than being crushed under a flat tint. */
/* Two layers: a vertical scrim for nav/footer legibility, plus a horizontal
   one that pushes the busiest part of the photo — the hex livery pattern and
   the van's own printed tagline — down under the lede text specifically,
   so our copy doesn't compete with the image's own typography. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(
      90deg,
      rgba(18, 21, 29, 0.35) 0%,
      rgba(18, 21, 29, 0.35) 38%,
      rgba(18, 21, 29, 0.68) 55%,
      rgba(18, 21, 29, 0.72) 100%
    ),
    linear-gradient(
      180deg,
      rgba(18, 21, 29, 0.88) 0%,
      rgba(18, 21, 29, 0.55) 24%,
      rgba(18, 21, 29, 0.5) 55%,
      rgba(18, 21, 29, 0.78) 80%,
      rgba(18, 21, 29, 0.95) 100%
    );
}

.hero-glow {
  position: absolute;
  top: -20%;
  right: -10%;
  width: 60vw;
  height: 60vw;
  background: radial-gradient(circle, rgba(90, 149, 114, 0.16) 0%, rgba(90, 149, 114, 0) 70%);
  z-index: 1;
}

/* A second, lower, slower glow gives the hero real depth instead of one flat
   radial — two light sources read as atmosphere, one reads as a sticker. */
.hero-glow-2 {
  top: auto;
  right: auto;
  left: -8%;
  bottom: -25%;
  width: 50vw;
  height: 50vw;
  background: radial-gradient(circle, rgba(90, 149, 114, 0.1) 0%, rgba(90, 149, 114, 0) 70%);
}

@keyframes hero-breathe {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.8; transform: scale(1.04); }
}

html.js .hero-glow {
  animation: hero-breathe 14s ease-in-out infinite;
}

html.js .hero-glow-2 {
  animation: hero-breathe 18s ease-in-out infinite reverse;
}

@media (prefers-reduced-motion: reduce) {
  html.js .hero-glow, html.js .hero-glow-2 { animation: none; }
}

.hero-inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 3rem;
  align-items: end;
  padding-bottom: 7rem;
}


/* Display type gets scale AND tighter tracking as it grows — a fixed
   letter-spacing value is wrong at every size except the one it was tuned
   for. This is the loudest element on the page; it was under-using the
   6rem ceiling by nearly a third. */
.hero h1 {
  /* 5.6rem only ever suited a four-word headline; any real sentence wrapped to
     five lines and swallowed the viewport. Sized for two or three lines. */
  font-size: clamp(2.1rem, 4vw, 3.5rem);
  color: var(--off);
  font-weight: 600;
  letter-spacing: -0.032em;
}

.hero h1 em { color: var(--sage-light); }

.hero-lede {
  max-width: 30ch;
  color: rgba(232, 232, 231, 0.72);
  font-size: 1.05rem;
  padding-bottom: 0.6rem;
}

.hero-lede-wrap {
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
  align-items: flex-start;
  border-left: 1px solid var(--line-on-dark);
  padding-left: 2rem;
}

.hero-actions {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

/* Full-bleed homepage hero, after the reel the owner supplied on 16 Oct
   2026: the photograph fills the frame, the copy sits centred over it, and
   "Request a quotation" opens a step-by-step enquiry card in place. */
.hero--full {
  justify-content: center;
  min-height: clamp(40rem, 100vh, 62rem);
  padding: 8rem 0 5rem;
  text-align: center;
}

/* The shared parallax rule oversizes hero photos by 28% so they can travel,
   but nothing drives that travel, so here it was only zoom. Fit the frame. */
.hero--full .hero-photo {
  top: 0;
  height: 100%;
  transform: none;
  background-position: 70% center;
}

/* Darker through the middle, where the copy and the card sit, and at the top
   for the header. The photo's own pale sky needs more than the usual scrim. */
.hero--full .hero-scrim {
  background:
    radial-gradient(ellipse 60% 55% at 50% 50%, rgba(18, 21, 29, 0.55), rgba(18, 21, 29, 0) 100%),
    linear-gradient(180deg, rgba(18, 21, 29, 0.9) 0%, rgba(18, 21, 29, 0.55) 22%, rgba(18, 21, 29, 0.5) 30%, rgba(18, 21, 29, 0.5) 70%, rgba(18, 21, 29, 0.85) 100%);
}

.hero-center {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The reference sets the line light and lets one word carry the weight. */
.hero--full h1 {
  font-size: clamp(2.7rem, 7vw, 6rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.045em;
  color: var(--off);
  text-shadow: 0 2px 30px rgba(18, 21, 29, 0.35);
}
.hero--full h1 strong { font-weight: 700; }

.hero--full .hero-lede {
  max-width: 38rem;
  margin-top: 1.6rem;
  padding: 0;
  font-size: 1.05rem;
  line-height: 1.6;
  color: rgba(232, 232, 231, 0.85);
}

.hero--full .hero-actions {
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  margin-top: 2.4rem;
}
.hero--full .hero-actions[hidden] { display: none; }

.btn-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.95rem 1.6rem;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-weight: 600;
  font-size: 0.98rem;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.15s ease;
}
.btn-pill:active { transform: scale(0.97); }
.btn-pill:disabled { opacity: 0.6; cursor: default; }
.btn-pill--brand { background: var(--sage); color: var(--ink); }
.btn-pill--brand:hover { background: var(--sage-light); }

.hero-or { color: rgba(232, 232, 231, 0.8); font-size: 0.98rem; }
.hero-or[hidden] { display: none; }
.hero-or a {
  color: var(--off);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: rgba(232, 232, 231, 0.4);
  text-underline-offset: 3px;
}
.hero-or a:hover { text-decoration-color: var(--sage-light); }

/* ---- The funnel card ---------------------------------------------------- */

.funnel {
  width: min(100%, 38rem);
  margin-top: 2.2rem;
  text-align: left;
  background: #fbfbfa;
  color: var(--ink);
  border-radius: 1.4rem;
  padding: 0.7rem;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.55);
  animation: funnel-in 0.35s var(--ease, ease) both;
}
.funnel[hidden] { display: none; }
@keyframes funnel-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

.funnel-pc {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  background: #fff;
  border: 1px solid rgba(18, 21, 29, 0.1);
  border-radius: 1rem;
  padding: 0.45rem 0.45rem 0.45rem 1rem;
}
.funnel-pc[hidden] { display: none; }
.funnel-pc:focus-within { border-color: var(--sage-dark); }
.funnel-pin { width: 1.2rem; height: 1.2rem; color: var(--sage-dark); flex: none; }
.funnel-pc-field { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.funnel-pc-field label { font-size: 0.72rem; color: rgba(18, 21, 29, 0.55); }
.funnel-pc-field input {
  border: 0;
  padding: 0.1rem 0;
  background: transparent;
  font: inherit;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink);
  text-transform: uppercase;
  min-width: 0;
}
.funnel-pc-field input::placeholder { text-transform: none; font-weight: 400; color: rgba(18, 21, 29, 0.4); }
.funnel-pc-field input:focus { outline: none; }
.funnel-pc .btn-pill { padding: 0.8rem 1.2rem; }
.funnel-pc .btn-pill.is-change { background: var(--ink); color: var(--off); }

.funnel-error { font-size: 0.86rem; color: #a33a2a; padding: 0.5rem 0.9rem 0; }
.funnel-error:empty { display: none; }
.funnel-error a { color: inherit; font-weight: 600; }

.funnel-progress {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
  padding: 0.9rem 0.6rem 0.2rem;
}
.funnel-progress span {
  height: 3px;
  border-radius: 3px;
  background: rgba(18, 21, 29, 0.1);
  transition: background 0.3s ease;
}
.funnel-progress span.is-done { background: var(--sage-dark); }

.funnel-body { padding: 0.6rem 0.6rem 0.5rem; }
.funnel-body:empty { display: none; }

.funnel-checking { display: flex; align-items: center; gap: 0.7rem; color: rgba(18, 21, 29, 0.7); padding: 0.4rem 0; }
.funnel-spinner {
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
  border: 2px solid rgba(18, 21, 29, 0.15);
  border-top-color: var(--sage-dark);
  animation: funnel-spin 0.7s linear infinite;
}
@keyframes funnel-spin { to { transform: rotate(360deg); } }

.funnel-status {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  padding: 0.8rem 0.9rem;
  border-radius: 0.8rem;
  font-size: 0.92rem;
  margin-bottom: 1rem;
}
.funnel-status svg { flex: none; width: 1.4rem; height: 1.4rem; padding: 0.2rem; border-radius: 50%; color: #fff; }
.funnel-status.is-yes { background: rgba(90, 149, 114, 0.12); }
.funnel-status.is-yes svg { background: var(--sage-dark); }
.funnel-status.is-maybe { background: rgba(18, 21, 29, 0.05); }
.funnel-status.is-maybe svg { background: var(--ink); }

.funnel-q {
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 400;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0 0 0.9rem;
}
.funnel-q strong { font-weight: 700; }
.funnel-sub { font-size: 0.88rem; color: rgba(18, 21, 29, 0.6); margin: -0.5rem 0 1rem; }

.funnel-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.55rem; }
.funnel-tile {
  padding: 0.95rem 0.8rem;
  border-radius: 0.8rem;
  border: 1px solid rgba(18, 21, 29, 0.12);
  background: #fff;
  font: inherit;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.funnel-tile:hover { border-color: var(--sage-dark); background: rgba(90, 149, 114, 0.08); }
.funnel-tile:active { transform: scale(0.97); }

.funnel-slots { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.55rem; margin-bottom: 0.7rem; }
.funnel-slot {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.75rem 0.4rem;
  border-radius: 0.8rem;
  border: 1px solid rgba(18, 21, 29, 0.12);
  background: #fff;
  cursor: pointer;
}
.funnel-slot input { position: absolute; opacity: 0; pointer-events: none; }
.funnel-slot-day { font-weight: 600; font-size: 0.92rem; }
.funnel-slot-hours { font-size: 0.78rem; color: rgba(18, 21, 29, 0.6); }
.funnel-slot:has(input:checked) { border-color: var(--sage-dark); background: rgba(90, 149, 114, 0.1); }
.funnel-slot:has(input:focus-visible) { outline: 2px solid var(--sage-dark); outline-offset: 2px; }

.funnel-fields { display: grid; grid-template-columns: 1fr 1fr auto; gap: 0.55rem; }
.funnel-fields input {
  min-width: 0;
  padding: 0.85rem 0.9rem;
  border-radius: 0.8rem;
  border: 1px solid rgba(18, 21, 29, 0.12);
  background: #fff;
  font: inherit;
  font-size: 0.98rem;
  color: var(--ink);
}
.funnel-fields input:focus { outline: none; border-color: var(--sage-dark); }
.funnel-trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

.funnel-back {
  margin-top: 0.8rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.85rem;
  color: rgba(18, 21, 29, 0.55);
  cursor: pointer;
  padding: 0.2rem 0;
}
.funnel-back:hover { color: var(--ink); }

.funnel-done { display: flex; gap: 0.9rem; align-items: flex-start; padding: 0.4rem 0.2rem; }
.funnel-done .funnel-q { margin-bottom: 0.6rem; }
.funnel-done .funnel-sub { margin: 0; }
.funnel-done-tick {
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--sage-dark);
  color: #fff;
}
.funnel-done-tick svg { width: 1.3rem; height: 1.3rem; }

@media (max-width: 760px) {
  .hero--full { padding: 7rem 0 3.5rem; min-height: 100svh; }
  .hero--full .hero-photo { background-position: 78% center; }
  .hero--full .hero-actions { flex-direction: column; gap: 1rem; }
  .funnel-pc { flex-wrap: wrap; padding: 0.7rem; }
  .funnel-pc .btn-pill { width: 100%; }
  .funnel-tiles { grid-template-columns: repeat(2, 1fr); }
  .funnel-fields { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .funnel { animation: none; }
  .funnel-spinner { animation-duration: 2s; }
  .btn-pill:active, .funnel-tile:active { transform: none; }
}

.hero-bar {
  position: relative;
  z-index: 2;
  border-top: 1px solid var(--line-on-dark);
  margin-top: 3.5rem;
}

.hero-bar .container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.hero-stat {
  padding: 1.6rem var(--gutter);
  border-right: 1px solid var(--line-on-dark);
}
.hero-stat:last-child { border-right: none; }

.hero-stat .num {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.9rem;
  letter-spacing: -0.02em;
  color: var(--sage-light);
  display: flex;
  align-items: baseline;
  gap: 0.15rem;
}

.hero-stat .cap {
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 500;
  color: rgba(232, 232, 231, 0.65);
  margin-top: 0.45rem;
}

/* ==========================================================================
   Marquee strip
   ========================================================================== */

.marquee {
  background: var(--sage);
  color: var(--ink);
}

.marquee-track {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.marquee-track span {
  display: inline-flex;
  align-items: center;
  padding: 0.75rem 2.5rem;
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 600;
  border-right: 1px solid rgba(18, 21, 29, 0.25);
}
.marquee-track span:last-child { border-right: none; }

/* ==========================================================================
   Section scaffolding
   ========================================================================== */

section { position: relative; }

.section-pad { padding: 7.5rem 0; }
.section-pad-sm { padding: 5rem 0; }

/* space-between stranded a short heading at one edge and its lede at the other,
   with a void between. A grid keeps them related regardless of heading length. */
.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: end;
  gap: 2rem 3rem;
  margin-bottom: 3.5rem;
}

.section-head h2 { font-size: clamp(1.9rem, 3.2vw, 2.6rem); max-width: 18ch; }
.section-head p { max-width: 38ch; color: var(--text-dim); font-size: 1rem; padding-bottom: 0.4rem; }

/* Reveal.
   The STATIC style is the visible one — always, in every environment. Motion is
   layered on as a keyframe animation with no fill-mode, so the element renders
   fully visible before it runs and returns to that same visible style after.
   Critically, CSS transitions/animations are paused in background tabs and skipped
   by many headless renderers; because visibility is never the animation's job,
   a reveal that never fires is invisible as a bug, not as blank content. */
.reveal { opacity: 1; transform: none; }

@keyframes reveal-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Rows in a list settle from the side; blocks rise. One identical entrance on
   every element reads as a tic rather than as intent. */
@keyframes reveal-slide {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: translateX(0); }
}

html.js .reveal.animate-in {
  animation: reveal-in 0.6s var(--ease);
}

html.js .index-row.animate-in {
  animation: reveal-slide 0.5s var(--ease);
}

/* Quiet entrance for dense text blocks — no movement, just presence. */
html.js .prose.animate-in,
html.js .checklist.animate-in {
  animation: reveal-fade 0.55s var(--ease);
}

@keyframes reveal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js .reveal.animate-in { animation: none; }
}

/* ==========================================================================
   Intro / philosophy
   ========================================================================== */

.intro {
  background: var(--off);
}

.intro-grid {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 5rem;
  align-items: start;
}

/* Centred against the story rather than pinned. It used to be sticky, which
   gave the card ~300px of travel: it lagged behind the page, slid under the
   header, and on the way out ran over the badge row. A card that short has
   nothing to gain from following the reader, so it simply sits level with the
   middle of the text beside it. */
.intro-figure {
  align-self: center;
  /* Its own stacking context, which `sticky` used to provide by accident:
     without one, the offset frame's z-index:-1 drops behind the section's
     background and disappears. */
  position: relative;
  z-index: 0;
}

.intro-figure .frame {
  border: 1px solid var(--line);
  padding: 2.4rem;
  position: relative;
  background: var(--off-card);
}

.intro-figure .frame::after {
  content: "";
  position: absolute;
  top: 14px; left: 14px; right: -14px; bottom: -14px;
  border: 1px solid var(--sage);
  z-index: -1;
}

.intro-quote {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.4rem, 2.2vw, 1.9rem);
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--ink);
  max-width: 22ch;
}

.intro-cite {
  margin-top: 1.6rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--sage-dark);
}

.intro-body p {
  font-size: 1.02rem;
  color: var(--text-dim);
  max-width: 62ch;
  margin-bottom: 1.4rem;
}

.intro-body p:first-of-type {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1.22;
  max-width: 21ch;
  margin-bottom: 1.8rem;
}

.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem 2.2rem;
  margin-top: 2.6rem;
  padding-top: 2.4rem;
  border-top: 1px solid var(--line);
}

.badge {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.005em;
  color: var(--ink);
}

.badge svg { width: 22px; height: 22px; stroke: var(--mid-green); flex-shrink: 0; }

/* ==========================================================================
   Comparison — the argument, told as a two-column contrast
   ========================================================================== */

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.compare-section {
  background: var(--ink);
  color: var(--off);
}

.compare-section .section-head h2 { color: var(--off); }
.compare-section .section-head p { color: rgba(232, 232, 231, 0.62); }

/* The shared .checklist component is tuned for light backgrounds; this section
   is the one dark-ground place it appears, so contrast is restored here rather
   than forking a second component. */
.compare-section .checklist li { color: rgba(232, 232, 231, 0.82); }
.compare-section .checklist svg { color: var(--sage-light); }
.compare-section .checklist strong { color: var(--off); }

/* ==========================================================================
   Services — editorial index, not a card grid
   ========================================================================== */

.services { background: var(--off-dim); }

.service-list {
  border-top: 1px solid var(--line);
}



.service-num {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  color: var(--sage-deep);
}

.service-icon { width: 46px; height: 46px; }
.service-icon path,
.service-icon circle,
.service-icon ellipse,
.service-icon line {
  stroke: var(--mid-green);
  transition: stroke 0.4s var(--ease);
}



.service-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--sage-deep);
  white-space: nowrap;
}

.service-link .arrow {
  display: inline-block;
  transition: transform 0.35s var(--ease-snap);
}

/* ==========================================================================
   Credentials / stats band
   ========================================================================== */

.credentials {
  background: var(--deep-forest);
  color: var(--off);
  position: relative;
  overflow: hidden;
}

.credentials::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 20% 20%, rgba(90, 149, 114, 0.14), transparent 55%);
}

.cred-grid {
  position: relative;
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 4rem;
  align-items: center;
}

.cred-grid h2 { color: var(--off); max-width: 15ch; margin-bottom: 2.4rem; }

.cred-list { display: flex; flex-direction: column; gap: 0; }

.cred-item {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 1.6rem;
  padding: 1.8rem 0;
  border-top: 1px solid var(--line-on-dark);
  align-items: baseline;
}
.cred-list .cred-item:last-child { border-bottom: 1px solid var(--line-on-dark); }

.cred-item .n {
  font-family: var(--display);
  font-weight: 600;
  font-size: 2.2rem;
  letter-spacing: -0.02em;
  color: var(--sage-light);
}
.cred-item .n sup { font-family: var(--mono); font-size: 0.8rem; top: -1.1em; letter-spacing: 0.04em; color: var(--sage-pale); }

.cred-item h4 { color: var(--off); font-size: 1.02rem; font-weight: 600; font-family: var(--display); letter-spacing: -0.01em; margin-bottom: 0.3rem; }
.cred-item p { color: rgba(232, 232, 231, 0.62); font-size: 0.9rem; max-width: 44ch; }

.cred-panel {
  border: 1px solid var(--line-on-dark);
  padding: 3rem;
  background: rgba(232, 232, 231, 0.03);
}

.cred-panel h3 {
  color: var(--off);
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.28;
  margin-bottom: 1.6rem;
}

.cert-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
}

.cert-chip {
  border: 1px solid var(--line-on-dark);
  border-radius: var(--radius);
  padding: 0.5rem 0.9rem;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 500;
  color: rgba(232, 232, 231, 0.85);
}

/* ==========================================================================
   Process
   ========================================================================== */

.process { background: var(--off); }

.process-list {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2.5rem;
}

.process-step { position: relative; padding-top: 2.4rem; }

.process-step::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 1px;
  background: var(--line);
}

.process-step::after {
  content: "";
  position: absolute;
  top: -1px; left: 0;
  width: 34px;
  height: 3px;
  background: var(--sage);
}

.process-step .step-num {
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--sage-dark);
  margin-bottom: 1.1rem;
  display: block;
}

/* Small heading: -0.02em reads cramped below ~1.15rem — relax toward zero. */
.process-step h4 { font-size: 1.12rem; margin-bottom: 0.7rem; font-weight: 600; letter-spacing: -0.008em; }
.process-step p { color: var(--text-dim); font-size: 0.92rem; }

/* ==========================================================================
   Testimonial
   ========================================================================== */

/* The one fully "drenched" moment on the page (impeccable's own term for it):
   every other section is ink, off-white, or deep-forest — sage otherwise only
   ever appears as an accent. A real testimonial, in a customer's own words,
   is the right place to spend a genuine color commitment: it's a warm, human
   beat, and the jolt from ink into full sage is what makes someone's thumb
   stop scrolling. */
.testimonial {
  background: var(--sage);
  color: var(--ink);
  padding: 9rem 0;
  position: relative;
  overflow: hidden;
}

.testimonial::before {
  content: "\201C";
  position: absolute;
  top: -4rem;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--display);
  font-size: 24rem;
  color: rgba(18, 21, 29, 0.07);
  line-height: 1;
  pointer-events: none;
}

.testimonial-inner {
  position: relative;
  max-width: 780px;
  margin-inline: auto;
  text-align: center;
}

.testimonial blockquote {
  font-family: var(--display);
  font-size: clamp(1.4rem, 2.7vw, 2.1rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.28;
  color: var(--ink);
}

.testimonial-attr {
  margin-top: 2.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.testimonial-attr .avatar {
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(18, 21, 29, 0.35);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--ink);
}

.testimonial-attr .who { text-align: left; }
.testimonial-attr .who strong { display: block; font-size: 0.92rem; font-weight: 600; color: var(--ink); }
/* Solid ink, not an alpha-blend: on this mid-tone sage bg an alpha-blend
   lightens the composite past 4.5:1. Hierarchy here comes from the mono
   font, smaller size and tracking against the name's larger sans/bold —
   it doesn't need color to also carry the difference, so full ink is safe. */
.testimonial-attr .who span { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.06em; color: var(--ink); }

.testimonial-dots {
  display: flex;
  justify-content: center;
  gap: 0;
  margin-top: 3rem;
}
/* The button is a 24px target; the dot it shows is drawn inside it. */
.testimonial-dots button {
  --dot: 6px;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  background: none;
}
.testimonial-dots button::before {
  content: "";
  width: var(--dot); height: var(--dot); border-radius: 50%;
  background: rgba(18, 21, 29, 0.25);
  transition: background 0.3s;
}
.testimonial-dots button.active::before { background: var(--ink); }

/* ==========================================================================
   Coverage
   ========================================================================== */

.coverage { background: var(--off-dim); }

.coverage-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4rem;
  align-items: center;
}

.coverage-copy h2 { margin-bottom: 1.5rem; }

.coverage-copy p {
  color: var(--text-dim);
  font-size: 1.05rem;
  max-width: 46ch;
  margin-bottom: 2rem;
}

.town-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1rem;
}

.town-list span {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  position: relative;
  padding-left: 1rem;
}
.town-list span::before {
  content: "";
  position: absolute;
  left: 0; top: 0.5em;
  width: 5px; height: 5px;
  background: var(--sage);
}



/* ==========================================================================
   CTA / Contact
   ========================================================================== */

.cta-section { background: var(--ink); color: var(--off); position: relative; overflow: hidden; }

.cta-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5rem;
}

.cta-copy h2 {
  color: var(--off);
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  max-width: 14ch;
  margin-bottom: 1.6rem;
}

.cta-copy p {
  color: rgba(232, 232, 231, 0.65);
  font-size: 1.05rem;
  max-width: 42ch;
  margin-bottom: 2.4rem;
}

.response-times {
  display: flex;
  gap: 2.5rem;
  margin-top: 3rem;
  padding-top: 2.4rem;
  border-top: 1px solid var(--line-on-dark);
}

.response-times div span {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
  color: var(--sage-light);
}
.response-times div small {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 500;
  color: rgba(232, 232, 231, 0.65);
}

.quote-form {
  background: var(--off);
  padding: 3rem;
  border-top: 3px solid var(--sage);
}

.quote-form h3 {
  color: var(--ink);
  font-size: 1.4rem;
  margin-bottom: 0.4rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.quote-form .sub {
  color: var(--text-dim);
  font-size: 0.88rem;
  margin-bottom: 2rem;
}

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin-bottom: 1.2rem; }
.form-field { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.2rem; }
.form-field label {
  font-family: var(--sans);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
}
.form-field input,
.form-field select,
.form-field textarea {
  border: none;
  border-bottom: 1px solid var(--line);
  background: transparent;
  padding: 0.6rem 0.1rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink);
  transition: border-color 0.3s;
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  border-color: var(--sage-dark);
}
/* A visible ring for keyboard users; a colour change on a 1px border is not enough. */
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible {
  outline: 2px solid var(--sage-dark);
  outline-offset: 2px;
}

.quote-form .btn-primary { width: 100%; justify-content: center; margin-top: 0.6rem; }

/* Spam trap: present for scripts, invisible and unreachable for people. */
.form-trap { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.form-note {
  margin-top: 1.2rem;
  font-size: 0.72rem;
  color: var(--text-dim);
  text-align: center;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  background: var(--ink);
  color: rgba(232, 232, 231, 0.6);
  border-top: 1px solid var(--line-on-dark);
  padding-top: 5rem;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.3fr 0.85fr 0.85fr 0.85fr;
  gap: 3rem;
  padding-bottom: 3.5rem;
}

.footer-brand .logo { margin-bottom: 1.4rem; }
.footer-brand p { max-width: 32ch; font-size: 0.88rem; line-height: 1.7; }

.footer-contact {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 1.4rem;
}
.footer-contact a { font-size: 0.88rem; transition: color 0.3s; }
.footer-contact a:hover { color: var(--off); }

.footer-col h5,
.footer-col .footer-title {
  letter-spacing: normal;
  line-height: 1.4;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--sage-light);
  margin-bottom: 1.4rem;
}

.footer-col ul { display: flex; flex-direction: column; gap: 0.85rem; }
.footer-col a { font-size: 0.88rem; transition: color 0.3s; }
.footer-col a:hover { color: var(--off); }

/* A compact, wrapped alternative to a nine-item vertical list — same links,
   same SEO value, a fraction of the height. Sits as its own strip so it
   doesn't compete with the four-column grid above it for width. */
.footer-pests {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 0.7rem;
  padding: 1.8rem 0;
  border-top: 1px solid var(--line-on-dark);
}
.footer-pests-label {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.82rem;
  color: rgba(232, 232, 231, 0.55);
  margin-right: 0.4rem;
}
.footer-pests-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.footer-pests-list a {
  font-size: 0.82rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--line-on-dark);
  border-radius: 999px;
  transition: border-color 0.3s, color 0.3s;
}
.footer-pests-list a:hover { border-color: var(--sage-light); color: var(--sage-light); }

.footer-bottom {
  border-top: 1px solid var(--line-on-dark);
  padding: 1.6rem 0;
  display: flex;
  justify-content: space-between;
  font-size: 0.76rem;
}

.footer-bottom .legal-links { display: flex; gap: 1.6rem; }

/* ==========================================================================
   Placeholder marker (dev-only helper, remove once real assets are in)
   ========================================================================== */

/* The preview banner is fixed so the fixed header can be offset beneath it.
   Previously the banner sat in flow at z-index 200 while the header was fixed
   at top:0 — so on mobile, where the banner wraps to two lines, it covered the
   menu button entirely and the nav could not be opened by tapping at all.
   --banner-h is measured in JS; it defaults to 0 so deleting the banner before
   launch needs no other change. */
.draft-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background: var(--sage-dark);
  color: var(--off);
  text-align: center;
  font: 400 11px/1.6 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 8px 16px;
  z-index: 200;
}

:root { --banner-h: 0px; }
body { padding-top: var(--banner-h); }
.site-header { top: var(--banner-h); }

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 1080px) {
  .intro-grid { grid-template-columns: 1fr; }
  .intro-figure { align-self: auto; }
  .cred-grid { grid-template-columns: 1fr; gap: 3rem; }
  .process-list { grid-template-columns: repeat(2, 1fr); }
  .cta-grid { grid-template-columns: 1fr; gap: 3rem; }
  .coverage-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .service-link { grid-column: 2 / -1; margin-top: 0.4rem; }
}

/* The nav, phone number and CTA button need more room than 760px gives them
   below roughly 1000px wide. The mobile sheet takes over a bit earlier
   instead of the desktop nav being squeezed. */
@media (max-width: 1140px) {
  .main-nav, .header-phone { display: none; }
  .nav-toggle { display: flex; }
}

@media (max-width: 760px) {
  /* Header CTA would push the menu button off-screen; the hero carries its own. */
  .site-header .header-cta > .btn { display: none; }
  .header-cta { gap: 0; }
  .hero-inner { grid-template-columns: 1fr; gap: 2.5rem; }
  .hero-lede-wrap { border-left: none; padding-left: 0; border-top: 1px solid var(--line-on-dark); padding-top: 1.4rem; }
  .hero-bar .container { grid-template-columns: repeat(2, 1fr); }
  .hero-stat:nth-child(2) { border-right: none; }
  /* .section-head is a grid, so the `flex-direction: column` that used to sit
     here did nothing at all — every section head has been holding two ~150px
     columns at phone width. Stacking it is what the rule always intended. */
  .section-head { grid-template-columns: 1fr; align-items: start; }
  .process-list { grid-template-columns: 1fr; }
  .service-icon { margin-bottom: 0.4rem; }
  .service-link { grid-column: auto; margin-top: 0.6rem; }
  .form-row { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; gap: 2.5rem; }
  .footer-bottom { flex-direction: column; gap: 0.8rem; text-align: center; }
}

/* ==========================================================================
   Inner pages — shared components
   ========================================================================== */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 300;
  background: var(--sage);
  color: var(--ink);
  padding: 0.8rem 1.2rem;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

.main-nav a.is-active { color: var(--sage-light); }
.main-nav a.is-active::after { width: 100%; }

/* Page hero
   ========================================================================== */

.page-hero {
  position: relative;
  background: var(--ink);
  color: var(--off);
  padding: 12rem 0 5rem;
  overflow: hidden;
}

.page-hero .container { position: relative; z-index: 2; }
.page-hero h1 {
  color: var(--off);
  font-size: clamp(2.1rem, 4.4vw, 3.6rem);
  letter-spacing: -0.022em;
  max-width: 20ch;
}
.page-hero .eyebrow { color: var(--sage-light); }
.page-lede {
  margin-top: 1.4rem;
  max-width: 58ch;
  font-size: 1.08rem;
  color: rgba(232, 232, 231, 0.72);
}

/* Plain sans, sentence case — deliberately NOT the site's mono/tracked/uppercase
   label voice. That treatment sits right above a heading everywhere it's used
   (page titles, section heads), and breadcrumbs occupy that exact slot too; in
   the small-caps style they read as a second eyebrow rather than as wayfinding. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: rgba(232, 232, 231, 0.55);
  margin-bottom: 1.8rem;
}
.crumbs a { color: rgba(232, 232, 231, 0.8); }
.crumbs a:hover { color: var(--sage-light); text-decoration: underline; }
.crumbs .sep { opacity: 0.4; }

/* Trust strip
   ========================================================================== */

.trust-strip { background: var(--deep-forest); color: var(--off); }
.trust-strip .container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.trust-item {
  padding: 1.5rem var(--gutter);
  border-right: 1px solid var(--line-on-dark);
}
.trust-item:first-child { padding-left: 0; }
.trust-item:last-child { border-right: none; }
.trust-item .n {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.35rem;
  letter-spacing: -0.02em;
  color: var(--sage-pale);
}
.trust-item .c {
  display: block;
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 500;
  color: rgba(232, 232, 231, 0.68);
  margin-top: 0.4rem;
}

/* Surfaces & layout helpers
   ========================================================================== */

.alt-surface { background: var(--off-dim); }

.split {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 4rem;
  align-items: start;
}

.prose p { color: var(--text-dim); max-width: 64ch; margin-bottom: 1.3rem; }
/* Pest guides use lists; the reset strips their markers, so restore them here. */
.prose ul { color: var(--text-dim); max-width: 64ch; margin: 0 0 1.5rem; padding-left: 1.2rem; list-style: disc; }
.prose li { margin-bottom: 0.55rem; padding-left: 0.2rem; }
.prose li::marker { color: var(--mid-green); }
.prose p + h3, .prose ul + h3 { margin-top: 2rem; }
.prose .lede-para {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.35rem, 2.3vw, 1.85rem);
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--ink);
  max-width: 24ch;
  margin-bottom: 1.6rem;
}
.prose.narrow { max-width: 62ch; }

.story-byline {
  font-family: var(--sans);
  font-style: italic;
  font-size: 0.9rem;
  color: var(--text-dim);
  max-width: 62ch;
  margin: 0 auto 2.4rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}

.article-byline {
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--text-dim);
  max-width: 62ch;
  margin: 0 auto 2.4rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.2rem;
}

.note-panel {
  background: var(--off-card);
  border-top: 3px solid var(--sage);
  padding: 2.2rem;
}
.alt-surface .note-panel { background: var(--off); }
.note-panel.tall { padding: 2.6rem; }
.note-panel h3 {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin-bottom: 1rem;
}
.note-panel p { color: var(--text-dim); font-size: 0.95rem; margin-bottom: 1rem; }
.note-cta { margin-bottom: 0 !important; }

.plain-list { display: flex; flex-direction: column; gap: 0.85rem; }
.plain-list li {
  font-size: 0.95rem;
  color: var(--text-dim);
  padding-left: 1.1rem;
  position: relative;
}
.plain-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 5px; height: 5px;
  background: var(--sage);
}

/* Checklist
   ========================================================================== */

.checklist h3 {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin-bottom: 1.3rem;
}
.checklist ul { display: flex; flex-direction: column; gap: 0.95rem; }
.checklist li {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  font-size: 0.97rem;
  color: var(--text-dim);
}
.checklist svg {
  width: 19px; height: 19px;
  flex-shrink: 0;
  margin-top: 0.28em;
  color: var(--sage-deep);
}

/* Index rows (services / sectors / specialist)
   ========================================================================== */

.index-list { border-top: 1px solid var(--line); }

.index-row {
  display: grid;
  grid-template-columns: minmax(175px, 250px) minmax(0, 1fr) auto;
  align-items: center;
  gap: 2rem;
  padding: 1.9rem 1.5rem;
  border-bottom: 1px solid var(--line);
  transition: background-color 0.4s var(--ease);
}
.index-row:hover { background: var(--off-card); }
.alt-surface .index-row:hover { background: var(--off); }
.index-row h3 { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; }
.index-row p { color: var(--text-dim); font-size: 0.94rem; max-width: 56ch; }
.index-row .service-link { color: var(--sage-deep); white-space: nowrap; }
.index-row:hover .service-link .arrow { transform: translateX(5px); }

/* Value cards
   ========================================================================== */

.value-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.value-card { background: var(--off-dim); padding: 2.4rem 2rem; }
.value-card h3 {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 0.7rem;
}
.value-card p { color: var(--text-dim); font-size: 0.94rem; }

/* Team
   ========================================================================== */

/* A compact preview of the team, used on About to link through to the full
   /team/ page — an overlapping avatar stack reads as "real, specific people"
   without repeating the full team-card treatment for a single link row. */
.team-preview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.5rem;
  padding: 1.8rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transition: background-color 0.4s var(--ease);
}
.alt-surface .team-preview:hover,
.team-preview:hover { background: var(--off-card); }

.team-preview-avatars { display: flex; }
.team-preview-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--off-card);
  border: 2px solid var(--off);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1rem;
  color: var(--sage-deep);
  margin-left: -14px;
}
.team-preview-avatar:first-child { margin-left: 0; }

.team-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 2.5rem;
}
.team-card h3 {
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 0.2rem;
}
.team-role {
  font-family: var(--sans);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--sage-deep);
  margin-bottom: 1rem;
}
.team-card p { color: var(--text-dim); font-size: 0.94rem; margin-bottom: 0.9rem; }
.team-personal { font-style: italic; color: var(--text-dim); opacity: 0.9; }

.team-portrait {
  aspect-ratio: 4 / 5;
  background: var(--off-card);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  margin-bottom: 1.3rem;
}
.team-portrait span {
  font-family: var(--display);
  font-weight: 600;
  font-size: 2.6rem;
  color: var(--sage);
}

/* Reviews
   ========================================================================== */

.review-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.5rem;
}
.review-card {
  background: var(--off-card);
  border-top: 3px solid var(--sage);
  padding: 2rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.alt-surface .review-card { background: var(--off); }
.review-card .stars { color: var(--sage-deep); letter-spacing: 0.18em; font-size: 0.9rem; }
.review-card blockquote {
  font-size: 0.98rem;
  color: var(--ink);
  line-height: 1.65;
  flex-grow: 1;
}
.review-card figcaption { display: flex; flex-direction: column; gap: 0.15rem; }
.review-card figcaption strong { font-size: 0.9rem; font-weight: 600; }
.review-card figcaption span {
  font-family: var(--sans);
  font-size: 0.82rem;
  color: var(--text-dim);
}

/* News
   ========================================================================== */

.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
}
.news-card {
  border-top: 1px solid var(--line);
  padding-top: 1.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.news-tag {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--sage-deep);
}
.news-card h3 { font-size: 1.2rem; font-weight: 600; letter-spacing: -0.02em; }
.news-card p { color: var(--text-dim); font-size: 0.94rem; flex-grow: 1; }
.news-card .service-link { color: var(--sage-deep); }
.news-card:hover .service-link .arrow { transform: translateX(5px); }

.section-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  margin-top: 2.6rem;
  padding-top: 2.2rem;
  border-top: 1px solid var(--line);
}

/* Coverage groups — replaced a flat alphabetical 3-column list of 32 towns
   (the most generic possible "areas we cover" pattern) with the same two
   dialling-area groups used on the home page, each showing its core towns
   as filled chips and everywhere else as outline chips. Structure the reader
   can actually use — which number to call — instead of a phone-book dump. */

/* Map + the two dialling-area cards side by side: the map answers "roughly
   where", the cards answer "which number do I actually call" — different
   jobs, kept next to each other rather than one replacing the other. */
/* The map was boxed at 300px beside an 800px chip panel — it read as a
   thumbnail rather than the point of the section. Given near-equal weight it
   becomes the thing you look at, and the chips become the reference beside it. */
.coverage-layout {
  display: grid;
  grid-template-columns: minmax(300px, 400px) minmax(0, 1fr);
  gap: 3rem;
  align-items: start;
  margin-bottom: 1.5rem;
}

/* The map keeps a defined field — a map with no edge floats — but takes the
   edge OR a shadow, never both. */
.coverage-map {
  background: var(--off-card);
  border: 1px solid var(--line);
  padding: 1.6rem 1.6rem 1.2rem;
  margin: 0;
}

.coverage-map-svg { width: 100%; height: auto; display: block; }

.coverage-map-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1.5rem;
  margin-top: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--text-dim);
}
.key-item { display: inline-flex; align-items: center; gap: 0.5rem; }
.key-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--sage-deep);
  opacity: 0.5;
}
.key-dot--core { opacity: 1; }

/* ==========================================================================
   Coverage regions — a gazetteer, not a control panel
   ==========================================================================
   These town names were previously bordered pills, half of them filled green.
   Nothing here is clickable except the phone number, so pills read as filter
   chips in a search UI and inverted the hierarchy: the loudest thing on the
   page was a list of places you cannot click, while the one genuinely
   actionable element — the local number — sat in 12px grey mono beside it.

   Now the structure carries the meaning instead of boxes: region, then the
   number at real size, then the towns as an ordinary two-column list. Core
   towns take ink and a little weight; the rest sit quieter. One hairline
   separates the two regions and there are no other borders. */

.coverage-regions { display: flex; flex-direction: column; }

.region + .region {
  margin-top: 2.8rem;
  padding-top: 2.8rem;
  border-top: 1px solid var(--line);
}

.region-name {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
}

/* The same symbol the map draws for a base — a ring around a solid core — so
   each region is tied to its own marker rather than a generic pin icon. */
.region-mark {
  flex: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1.5px solid var(--mid-green);
  position: relative;
}
.region-mark::after {
  content: "";
  position: absolute;
  inset: 3.5px;
  border-radius: 50%;
  background: var(--mid-green);
}

/* The only interactive thing in the section, so it is the only thing given an
   interactive affordance — and enough size to be an easy target on a phone. */
.region-phone {
  display: inline-block;
  margin-top: 0.85rem;
  padding: 0.3rem 0 0.45rem;
  font-family: var(--display);
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  border-bottom: 1px solid var(--sage);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.region-phone:hover,
.region-phone:focus-visible { color: var(--sage-deep); border-color: var(--sage-deep); }

.region-towns {
  columns: 2;
  column-gap: 2rem;
  margin-top: 1.5rem;
  font-size: 0.9rem;
  line-height: 1.9;
  color: var(--text-dim);
}
.region-towns li { break-inside: avoid; }
.region-towns .is-core { color: var(--ink); font-weight: 500; }

.note-panel.wide { max-width: 62ch; }

/* CTA band
   ========================================================================== */

.cta-band { background: var(--ink); color: var(--off); }
.cta-band-inner {
  display: grid;
  grid-template-columns: 1.3fr 0.7fr;
  gap: 3rem;
  align-items: center;
}
.cta-band h2 {
  color: var(--off);
  font-size: clamp(1.7rem, 2.9vw, 2.4rem);
  max-width: 18ch;
  margin-bottom: 1rem;
}
.cta-band p { color: rgba(232, 232, 231, 0.65); max-width: 46ch; }
.cta-band-actions { display: flex; flex-direction: column; gap: 0.9rem; align-items: flex-start; }
.cta-band-actions .btn { width: 100%; justify-content: center; }

/* Contact
   ========================================================================== */

.contact-section { background: var(--ink); color: var(--off); }
.contact-section .cta-copy h2 { color: var(--off); }

.contact-lines { display: flex; flex-direction: column; gap: 0; margin: 2rem 0; }
.contact-line {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 1.2rem 0;
  border-top: 1px solid var(--line-on-dark);
  transition: padding-left 0.35s var(--ease);
}
.contact-lines .contact-line:last-child { border-bottom: 1px solid var(--line-on-dark); }
.contact-line:hover { padding-left: 0.6rem; }
.cl-label {
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--sage-light);
}
.cl-number {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
  color: var(--off);
}
.cl-number.small { font-size: 1.05rem; }

/* Responsive
   ========================================================================== */

@media (max-width: 1080px) {
  .split { grid-template-columns: 1fr; gap: 2.5rem; }
  .trust-strip .container { grid-template-columns: repeat(2, 1fr); }
  .trust-item:nth-child(2) { border-right: none; }
  .trust-item:nth-child(3) { padding-left: 0; }
  .cta-band-inner { grid-template-columns: 1fr; }
  .index-row { grid-template-columns: minmax(170px, 220px) minmax(0, 1fr); }
  .index-row .service-link { grid-column: 1 / -1; margin-top: 0.4rem; }
  .coverage-layout { grid-template-columns: 1fr; }
  /* Was capped at 340px, which shrank every map label below legibility once
     the section stacked. The map is drawn in viewBox units, so its type scales
     with its width — it needs real width to stay readable, not a thumbnail. */
  .coverage-map { max-width: 560px; margin-inline: auto; }
}

@media (max-width: 760px) {
  .page-hero { padding: 9rem 0 3.5rem; }
  .trust-strip .container { grid-template-columns: 1fr; }
  .trust-item { border-right: none; border-bottom: 1px solid var(--line-on-dark); padding-left: 0; }
  .trust-item:last-child { border-bottom: none; }
  .index-row { grid-template-columns: 1fr; row-gap: 0.55rem; padding: 1.7rem 0; }
  .index-row .service-link { grid-column: auto; }
  .cta-band-actions { width: 100%; }

  /* Two columns are kept on the narrowest screens: one column would turn a
     nineteen-town region into a nineteen-line wall. Type and gutter come down
     instead so the longest name ("Bourton-on-the-Water") still fits a column. */
  .region-towns { column-gap: 1.2rem; font-size: 0.86rem; line-height: 1.8; }
  .region-phone { font-size: 1.45rem; }
}

/* ==========================================================================
   Dialling panel — replaced a decorative map that conveyed no information
   ========================================================================== */

.dial-panel {
  background: var(--off-card);
  border-top: 3px solid var(--sage);
  padding: 2.4rem;
}

.dial-panel h3 {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 0.7rem;
}

.dial-note {
  color: var(--text-dim);
  font-size: 0.92rem;
  margin-bottom: 1.8rem;
  max-width: 46ch;
}

/* Single flex column. The previous grid split the dialling code and the rest
   of the number into separate cells with the area list spanning between them —
   which at narrow widths stacked as "01926" / paragraph / "632 929", tearing
   the phone number in half. A number must never be breakable. */
.dial-row {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 1.3rem 0;
  border-top: 1px solid var(--line);
  transition: padding-left 0.35s var(--ease);
}
.dial-row:hover { padding-left: 0.5rem; }
.dial-row:hover { padding-left: 0.5rem; }



.dial-number {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.6rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  white-space: nowrap;
}

.dial-area {
  font-size: 0.86rem;
  color: var(--text-dim);
  max-width: 46ch;
}

.dial-foot {
  border-top: 1px solid var(--line);
  padding-top: 1.3rem;
  margin-top: 0.2rem;
  font-size: 0.86rem;
  color: var(--text-dim);
}

@media (max-width: 520px) {
  .dial-number { font-size: 1.4rem; }
}

/* ==========================================================================
   Page-hero variants — a service page, an editorial page and a hub page
   should not open with an identical block
   ========================================================================== */

/* Detail: tighter, denser, with a rule that ties the heading to the crumb trail */
.page-hero--detail {
  padding: 10.5rem 0 3.5rem;
  border-bottom: 1px solid var(--line-on-dark);
}
.page-hero--detail h1 {
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  max-width: 17ch;
}
.page-hero--detail .page-lede {
  font-size: 1rem;
  max-width: 52ch;
}
.page-hero--detail .eyebrow { margin-bottom: 0.9rem; }

/* Editorial: quieter, wider measure, the heading carries it alone */
.page-hero--editorial {
  padding: 13rem 0 6rem;
  background: var(--ink);
}
.page-hero--editorial .eyebrow { display: none; }
.page-hero--editorial h1 {
  font-size: clamp(2.4rem, 5vw, 4.2rem);
  max-width: 16ch;
}
.page-hero--editorial .page-lede {
  font-size: 1.15rem;
  max-width: 54ch;
  margin-top: 1.8rem;
}

@media (max-width: 760px) {
  .page-hero--detail { padding: 8.5rem 0 2.75rem; }
  .page-hero--editorial { padding: 9.5rem 0 3.5rem; }
}

/* Badge row runs the full width under the story, in its own container after
   .intro-grid (see _home_main.html for why it is not a grid child). */
.intro .badge-row {
  margin-top: 4.5rem;
}

.intro-quote-cta {
  margin-top: 1.6rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.intro-quote-cta .service-link { color: var(--sage-deep); }
.intro-quote-cta:hover .arrow { transform: translateX(5px); }


/* Index/service row titles sit in a deliberately narrow column. Two words either
   side of an ampersand ("Wasps & Bees") must break as a pair, never leaving the
   "&" stranded on its own line, which is what the reported screenshots showed. */
.index-row h3,

/* ==========================================================================
   Photographic inner-page heroes
   ========================================================================== */

.page-hero--photo { position: relative; overflow: hidden; }

/* The image is referenced once here rather than inlined per page, so swapping
   in real per-section photography later is a single change plus a class. */
.page-hero--photo .hero-photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: url("../assets/photography/hero-van.jpg");
  background-size: cover;
  transform: scale(1.02);
  /* Source frame is a near-black vehicle on an overcast day — it needs lifting
     before any scrim goes over it, or the result reads as grey noise. */
  filter: brightness(1.22) contrast(1.08) saturate(1.06);
}

/* Inner-page heroes are shorter than the home hero, so the scrim has to work
   harder — text sits directly over the busiest part of the frame here. */
.page-hero--photo .hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Two stacked gradients multiply, so the values here are deliberately lighter
     than they look: 0.5 over 0.62 leaves barely a third of the image showing,
     which read as noise rather than a photograph. The left stays dark enough to
     carry the headline; the right is allowed to open right up so the vehicle
     actually reads. */
  background:
    linear-gradient(90deg,
      rgba(18,21,29,0.92) 0%,
      rgba(18,21,29,0.78) 38%,
      rgba(18,21,29,0.26) 68%,
      rgba(18,21,29,0.04) 100%),
    linear-gradient(180deg,
      rgba(18,21,29,0.72) 0%,
      rgba(18,21,29,0.06) 42%,
      rgba(18,21,29,0.5) 100%);
}

.page-hero--photo .container { position: relative; z-index: 2; }
.page-hero--photo .hero-glow { z-index: 1; }

@media (max-width: 760px) {
  /* Portrait viewports crop the frame hard; lean the scrim darker so the
     headline never lands on a bright patch of sky or wet tarmac. */
  .page-hero--photo .hero-scrim {
    background: linear-gradient(180deg,
      rgba(18,21,29,0.92) 0%,
      rgba(18,21,29,0.78) 45%,
      rgba(18,21,29,0.9) 100%);
  }
}

/* ==========================================================================
   Per-pest callout — the one block unique to each service page
   ========================================================================== */

.pest-callout {
  border-top: 3px solid var(--sage);
  background: var(--off);
  padding: 2.2rem 2.4rem;
  max-width: 74ch;
}

.pest-callout h3 {
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 0.9rem;
}

.pest-callout p {
  color: var(--text-dim);
  font-size: 1rem;
  max-width: 66ch;
}

/* ==========================================================================
   Coverage map — technical treatment
   ========================================================================== */

/* One soft field with no boundary. Any drawn edge on a coverage map gets read
   as the limit of the service, so there deliberately isn't one — the tint
   simply thins out. Stops live here rather than on the <stop> elements
   because var() is only reliable inside a real CSS declaration. */
.map-field-a { stop-color: var(--sage); stop-opacity: 0.2; }
.map-field-b { stop-color: var(--sage); stop-opacity: 0.13; }
.map-field-c { stop-color: var(--sage); stop-opacity: 0; }

/* Every size in this map is in viewBox units, and the SVG renders at roughly
   0.58 of that — so the old 8-9px type arrived on screen at about 5px. These
   values are set so the smallest annotation lands near 8px rendered. */
/* Core towns are separated from the rest by a drawn edge rather than only by
   opacity: at this size a 6px dot at 0.95 and a 4px dot at 0.5 read as the
   same mark slightly faded. The halo makes a principal town a distinct symbol. */
.map-dot { fill: var(--sage-deep); opacity: 0.42; }
.map-dot--core {
  fill: var(--sage-deep);
  opacity: 1;
  stroke: var(--off-card);
  stroke-width: 2;
  paint-order: stroke;
}

.map-base circle { fill: var(--mid-green); }
.map-base-core { fill: var(--off-card); }

/* Map labels sit at 9px; mono at that size costs legibility for no gain, and
   the rest of the site no longer sets small text in mono either. */
.map-label {
  font-family: var(--sans);
  /* Keep in step with FS_CORE/FS_EXT in coverage_map.py — those size the
     collision boxes, this sizes what is actually drawn. */
  font-size: 23px;
  letter-spacing: 0.005em;
  fill: var(--ink);
  font-weight: 500;
}
.map-label--core { fill: var(--ink); font-weight: 600; }

.map-label--base {
  fill: var(--ink);
  font-weight: 700;
  /* Keep in step with FS_BASE in coverage_map.py. */
  font-size: 28px;
  letter-spacing: -0.005em;
}

/* Phone widths. This block has to sit after the map type above rather than up
   with the other mobile rules, because these selectors are the same
   specificity and the later declaration would otherwise win at every width.

   At ~330px wide the map cannot carry a town label at any size that is both
   legible and free of collisions, so it drops them and keeps what only a map
   can say: where the two bases are and how the towns spread around them.
   Every town is still named in full in the list directly beneath. */
@media (max-width: 760px) {
  .map-labels text { display: none; }
  .map-label--base { font-size: 34px; }
}

/* Map sits at equal weight beside the town lists rather than as a thumbnail;
   the footprint is portrait, so this matches the lists' height.

   This is deliberately scoped to widths above the stacking breakpoint. It, and
   an earlier `minmax(360px, 1fr)` variant, previously sat unscoped and later in
   the file than the `max-width: 1080px` rule that stacks the section — so they
   won it at every width and the section never actually stacked. Below 1080px
   the map was being squeezed into half a phone screen. */
@media (min-width: 1081px) {
  /* The list is the answer to "do you come to my town?", so it gets the room.
     An even split gave the map as much space as the list and, having that
     much space, it kept demanding content to justify it — counties, rings,
     then every town name duplicated from the list alongside. Narrower, it can
     do the one job a list cannot: show the patch is a single coherent area. */
  .coverage-layout { grid-template-columns: 0.68fr 1fr; gap: 3rem; }
  .coverage-map { padding: 1.4rem; }
}

/* Two town columns, not three. A third was briefly needed while the frame
   stretched west and the map rendered square; with the tighter frame the map
   is portrait again and three columns overshoot, leaving the map the taller
   half by some margin. Two keeps the halves within about sixty pixels. */

/* ==========================================================================
   Response — feedback on press, not only on hover
   ==========================================================================
   Hover does not exist on touch. With feedback living only in :hover, every
   one of these elements was inert to a phone user — and a phone is where a
   panicking homeowner actually is. Press states are the foundation the rest
   of the interaction sits on, so they go on everything tappable.

   Values are deliberately small and fast (~0.97 scale, ~110ms out): the point
   is to acknowledge the touch within a frame or two, not to perform. */

.index-row,
.dial-row,
.contact-line,
.news-card,
.review-card,
.region-phone,
.service-link,
.welcome-close,
.footer-col a,
.main-nav a,
.logo {
  -webkit-tap-highlight-color: transparent;
}

/* Rows and cards settle slightly INTO the page — they are surfaces, so they
   depress rather than shrink. */
.index-row:active,
.news-card:active,
.review-card:active {
  background-color: var(--off-dim);
  transition: background-color 0.11s ease-out;
}

.dial-row:active,
.contact-line:active {
  transform: scale(0.995);
  transition: transform 0.11s ease-out;
}

/* Small controls scale — they read as objects rather than surfaces. */
.service-link:active,
.welcome-close:active,
.nav-toggle:active {
  transform: scale(0.96);
  transition: transform 0.11s ease-out;
}

.main-nav a:active,
.footer-col a:active,
.header-phone:active {
  opacity: 0.62;
  transition: opacity 0.09s ease-out;
}

.logo:active { transform: scale(0.97); transition: transform 0.11s ease-out; }

/* §14 — the third accessibility signal, previously unhandled. Near-solid
   surfaces with defined borders, and no translucency to muddy edges. */
@media (prefers-contrast: more) {
  .site-header.scrolled {
    background: var(--ink);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: 0 0 0 1px var(--off);
  }
  .site-header::after { display: none; }
  .welcome-dialog { border: 2px solid var(--ink); }
  .welcome-dialog::backdrop { background: rgba(18, 21, 29, 0.96); backdrop-filter: none; }
  .region-phone { border-bottom-color: var(--ink); }
  .btn-ghost { border-color: var(--off); }
  .index-row { border-bottom-color: rgba(18, 21, 29, 0.45); }
  .hero-scrim,
  .page-hero--photo .hero-scrim { background: rgba(18, 21, 29, 0.92); }
}

@media (prefers-reduced-motion: reduce) {
  .index-row:active, .news-card:active, .review-card:active,
  .dial-row:active, .contact-line:active,
  .service-link:active, .welcome-close:active,
  .nav-toggle:active, .logo:active { transform: none; }
  .site-header .logo, .site-header.scrolled .logo { transition: none; transform: none; }
}

/* ==========================================================================
   Mobile nav — translucent sheet, anchored to its trigger
   ========================================================================== */

html.nav-locked { overflow: hidden; }

@media (max-width: 1140px) {
  .main-nav {
    display: flex;
    position: fixed;
    inset: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.75rem;
    z-index: 99;
    font-size: 1.35rem;

    /* A real material rather than a solid fill: the page stays faintly present
       behind it, which keeps the sheet reading as a layer over the site. */
    background: rgba(18, 21, 29, 0.86);
    backdrop-filter: blur(22px) saturate(140%);
    -webkit-backdrop-filter: blur(22px) saturate(140%);

    /* Anchored to the toggle in the top-right — it grows out of the control
       that summoned it, and collapses back into it. */
    transform-origin: calc(100% - 2.4rem) 2.6rem;
    opacity: 0;
    transform: scale(0.94);
    pointer-events: none;
    transition: opacity 0.28s var(--ease), transform 0.28s var(--ease);
  }

  .main-nav.is-open {
    opacity: 1;
    transform: scale(1);
    pointer-events: auto;
  }

  .main-nav a { font-size: 1.35rem; }

  /* The toggle sits above the sheet so it can dismiss it. */
  .nav-toggle { position: relative; z-index: 100; }

  /* The sheet is already one long tappable list — a hover panel makes no
     sense here, so Blog's children sit permanently open underneath it,
     just smaller, instead of behind a second tap. */
  .nav-dropdown { display: flex; flex-direction: column; align-items: center; gap: 0.9rem; }
  .nav-dropdown-chevron { display: none; }
  .nav-dropdown-menu {
    position: static;
    /* The desktop panel is centred with translateX(-50%); in the sheet it
       sits in the flow, so that shift would push it off-centre. */
    transform: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    padding-top: 0;
    min-width: 0;
  }
  .nav-dropdown-panel {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: none;
    padding: 0;
    min-width: 0;
    gap: 0.9rem;
    align-items: center;
    transform: none;
  }
  .nav-dropdown-menu a { font-size: 1.05rem; padding: 0; white-space: normal; opacity: 0.8; }
}

@media (max-width: 1140px) and (prefers-reduced-motion: reduce) {
  .main-nav { transition: opacity 0.2s linear; transform: none; }
  .main-nav.is-open { transform: none; }
}

@media (max-width: 1140px) and (prefers-reduced-transparency: reduce) {
  .main-nav { background: var(--ink); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ==========================================================================
   1. HERO PARALLAX
   ==========================================================================
   The photo is oversized and offset so it can travel without exposing an edge.
   JS writes --par (px) on scroll; the transform stays on the compositor. */

.hero-photo,
.page-hero--photo .hero-photo {
  top: -14%;
  height: 128%;
  will-change: transform;
  transform: translate3d(0, var(--par, 0px), 0) scale(1.02);
}

/* ==========================================================================
   2. MAP DRAW-IN
   ========================================================================== */

/* Static default: fully drawn. The animation only ever plays on top, so a
   failed observer or a paused tab leaves a complete, readable map. */
@keyframes map-pop {
  0%   { transform: scale(0); opacity: 0; }
  70%  { transform: scale(1.28); }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes map-fade { from { opacity: 0; } to { opacity: 1; } }

/* Dots scale from their own centre, so each lands on its town. */
html.js .coverage-map.is-drawing .map-dot,
html.js .coverage-map.is-drawing .map-base {
  transform-box: fill-box;
  transform-origin: center;
  animation: map-pop 0.5s var(--ease-snap) both;
  animation-delay: calc(700ms + var(--i, 0) * 26ms);
}
html.js .coverage-map.is-drawing .map-base { animation-delay: 1.35s; }

html.js .coverage-map.is-drawing .map-labels text,
html.js .coverage-map.is-drawing .map-field {
  animation: map-fade 0.7s var(--ease) both;
  animation-delay: calc(1.5s + var(--i, 0) * 22ms);
}

/* ==========================================================================
   3. SCROLL-LINKED SECTION TRANSITIONS
   ==========================================================================
   Different content arrives differently: headings rise and settle, rows come
   in from the side in sequence, panels lift toward the reader. */

@keyframes rise-in {
  from { opacity: 0; transform: translate3d(0, 34px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes lift-in {
  from { opacity: 0; transform: translate3d(0, 26px, 0) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

html.js .section-head.animate-in { animation: rise-in 0.72s var(--ease); }
html.js .note-panel.animate-in,
html.js .coverage-map.animate-in,
html.js .quote-form.animate-in,
html.js .pest-callout.animate-in { animation: lift-in 0.66s var(--ease); }

/* Children of a revealed list stagger rather than moving as one block. */
html.js .index-list.animate-in .index-row,
html.js .value-grid.animate-in .value-card,
html.js .review-grid.animate-in .review-card,
html.js .process-list.animate-in .process-step {
  animation: lift-in 0.6s var(--ease) both;
}
html.js .index-list.animate-in .index-row:nth-child(1),
html.js .value-grid.animate-in .value-card:nth-child(1),
html.js .review-grid.animate-in .review-card:nth-child(1),
html.js .process-list.animate-in .process-step:nth-child(1) { animation-delay: 0ms; }
html.js .index-list.animate-in .index-row:nth-child(2),
html.js .value-grid.animate-in .value-card:nth-child(2),
html.js .review-grid.animate-in .review-card:nth-child(2),
html.js .process-list.animate-in .process-step:nth-child(2) { animation-delay: 70ms; }
html.js .index-list.animate-in .index-row:nth-child(3),
html.js .value-grid.animate-in .value-card:nth-child(3),
html.js .review-grid.animate-in .review-card:nth-child(3),
html.js .process-list.animate-in .process-step:nth-child(3) { animation-delay: 140ms; }
html.js .index-list.animate-in .index-row:nth-child(4),
html.js .value-grid.animate-in .value-card:nth-child(4),
html.js .review-grid.animate-in .review-card:nth-child(4),
html.js .process-list.animate-in .process-step:nth-child(4) { animation-delay: 210ms; }
html.js .index-list.animate-in .index-row:nth-child(n+5),
html.js .review-grid.animate-in .review-card:nth-child(n+5) { animation-delay: 280ms; }

/* ==========================================================================
   4. DEPTH — layered surfaces and overlap
   ========================================================================== */

:root {
  --lift-1: 0 1px 2px rgba(18,21,29,.05), 0 4px 12px rgba(18,21,29,.06);
  --lift-2: 0 2px 4px rgba(18,21,29,.06), 0 12px 32px rgba(18,21,29,.10);
  --lift-3: 0 4px 8px rgba(18,21,29,.08), 0 28px 64px rgba(18,21,29,.16);
}

.review-card, .note-panel, .dial-panel, .value-card { box-shadow: var(--lift-1); }
/* The map takes its hairline border instead of a shadow — carrying both is the
   ghost-card look, and a drawn edge is the more cartographic of the two. */
.quote-form { box-shadow: var(--lift-2); }

.review-card, .quote-form, .note-panel, .dial-panel {
  transition: box-shadow 0.45s var(--ease), transform 0.45s var(--ease);
}
.review-card:hover { box-shadow: var(--lift-2); transform: translateY(-3px); }

/* Sections overlap rather than merely stacking — the drenched testimonial
   rides up over the section above it and casts onto it. */
.testimonial {
  position: relative;
  z-index: 2;
  margin-top: -3.5rem;
  border-radius: 26px 26px 0 0;
  box-shadow: 0 -18px 50px rgba(18,21,29,.18);
}
.cta-band {
  position: relative;
  z-index: 2;
  margin-top: -2.5rem;
  border-radius: 26px 26px 0 0;
}

@media (max-width: 760px) {
  .testimonial, .cta-band { margin-top: -1.75rem; border-radius: 18px 18px 0 0; }
}

/* ==========================================================================
   5. TYPOGRAPHIC MOMENTS
   ==========================================================================
   Two places where the type is allowed to be genuinely large: the opening
   statement of the story, and the customer's own words. */

.intro-body p:first-of-type {
  font-size: clamp(1.9rem, 4.4vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.035em;
  max-width: 15ch;
}

.testimonial blockquote {
  font-size: clamp(1.75rem, 4.2vw, 3.1rem);
  line-height: 1.14;
  letter-spacing: -0.032em;
}

@media (prefers-reduced-motion: reduce) {
  .hero-photo, .page-hero--photo .hero-photo { transform: none !important; }

  html.js .coverage-map.is-drawing .map-dot,
  html.js .coverage-map.is-drawing .map-base,
  html.js .coverage-map.is-drawing .map-labels text,
  html.js .coverage-map.is-drawing .map-field,
  html.js .section-head.animate-in,
  html.js .note-panel.animate-in,
  html.js .coverage-map.animate-in,
  html.js .quote-form.animate-in,
  html.js .pest-callout.animate-in,
  html.js .index-list.animate-in .index-row,
  html.js .value-grid.animate-in .value-card,
  html.js .review-grid.animate-in .review-card,
  html.js .process-list.animate-in .process-step { animation: none; }
  .review-card:hover { transform: none; }
}

/* ==========================================================================
   Testimonial carousel controls
   ========================================================================== */

.testimonial-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.8rem;
  margin-top: 2.6rem;
}

.t-btn {
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(18,21,29,.28);
  border-radius: 50%;
  color: var(--ink);
  transition: background-color .3s var(--ease), border-color .3s var(--ease), transform .11s ease-out;
}
.t-btn svg { width: 15px; height: 15px; }
.t-btn:hover { background: rgba(18,21,29,.09); border-color: rgba(18,21,29,.5); }
.t-btn:active { transform: scale(.94); }

.t-pause .ic-play { display: none; }
.t-pause.is-paused .ic-pause { display: none; }
.t-pause.is-paused .ic-play { display: block; }

.testimonial-dots { margin-top: 0; gap: 0; }
.testimonial-dots button { --dot: 8px; }

@media (prefers-reduced-motion: reduce) { .t-btn:active { transform: none; } }

/* ==========================================================================
   Form validation states
   ========================================================================== */

.field-error {
  font-size: 0.82rem;
  color: #9d3521;
  margin-top: 0.4rem;
  min-height: 0;
}
.form-field.has-error input,
.form-field.has-error select,
.form-field.has-error textarea { border-bottom-color: #9d3521; }

.form-status {
  margin-top: 1rem;
  font-size: 0.88rem;
  text-align: center;
}
.form-status.is-error { color: #9d3521; font-weight: 500; }
.form-status.is-success { color: var(--sage-deep); font-weight: 500; }

.quote-form button[type=submit][disabled] { opacity: .55; pointer-events: none; }

/* ==========================================================================
   Mobile call bar
   ========================================================================== */

.call-bar { display: none; }

@media (max-width: 760px) {
  .call-bar {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 95;
    gap: 1px;
    background: var(--line-on-dark);
    padding-bottom: env(safe-area-inset-bottom, 0);
    box-shadow: 0 -8px 28px rgba(18,21,29,.28);
  }
  .call-bar-primary,
  .call-bar-secondary {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 0.85rem 1rem;
    min-height: 56px;           /* comfortable thumb target */
  }
  .call-bar-primary {
    flex: 1 1 auto;
    background: var(--ink);
    color: var(--off);
  }
  .call-bar-primary svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--sage-light); }
  .call-bar-primary small {
    display: block;
    font-family: var(--sans);
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--sage-light);
  }
  .call-bar-primary strong {
    display: block;
    font-family: var(--display);
    font-size: 1.02rem;
    letter-spacing: -0.01em;
  }
  .call-bar-secondary {
    flex: 0 0 auto;
    background: var(--sage);
    color: var(--ink);
    font-weight: 600;
    font-size: 0.9rem;
  }
  .call-bar-primary:active, .call-bar-secondary:active { filter: brightness(0.92); }

  /* Content must not sit under the bar. */
  body { padding-bottom: 56px; }
  .site-footer { padding-bottom: 1rem; }

  /* The bar would sit on top of the nav sheet; hide it while that's open. */
  html.nav-locked .call-bar { display: none; }
}

/* ==========================================================================
   Areas Covered — the explorable coverage map
   ==========================================================================
   A live map, full-bleed, with the finder floating over it on wide screens.
   The stage and the overlay share one grid cell, so the section is always as
   tall as whichever is taller: a long answer grows the map with it rather than
   pushing the panel into an inner scrollbar. Nothing in this section scrolls
   on its own, and the map never takes the wheel (see js/coverage-map.js).

   Below 900px the panel stops floating: finder above the map, regions below
   it, both in ordinary flow. Without JavaScript, or where the map cannot run,
   the stage is removed and the region lists stand alone, fully expanded. */

.coverage-explorer {
  background: var(--off-dim);
  padding-top: 7.5rem;
}
.coverage-explorer .section-head { margin-bottom: 3rem; }

.cx { display: grid; position: relative; }
/* Several pieces here set their own display, which would otherwise beat the
   user-agent rule and leave a `hidden` list on screen. */
.cx [hidden] { display: none !important; }
.cx > .cx-stage,
.cx > .cx-overlay { grid-area: 1 / 1; }

.cx-stage {
  position: relative;
  z-index: 0; /* its own stacking context: the veil must never cover the panel */
  min-height: clamp(700px, 88vh, 900px);
  background: #e7e6e0;
  overflow: hidden;
}
/* Two classes deep on purpose: maplibre-gl.css sets `.maplibregl-map
   { position: relative }` on this same element and loads after us. */
.cx-stage .cx-map { position: absolute; inset: 0; }

/* The map dissolves into the section above and below instead of starting at
   a hard edge: part of what makes it read as a place rather than a widget. */
.cx-stage::before,
.cx-stage::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 110px;
  z-index: 1;
  pointer-events: none;
}
.cx-stage::before { top: 0; background: linear-gradient(var(--off-dim), rgba(215, 216, 211, 0)); }
.cx-stage::after { bottom: 0; background: linear-gradient(rgba(215, 216, 211, 0), var(--off-dim)); }

.cx-veil {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  background:
    radial-gradient(60% 55% at 58% 50%, rgba(90, 149, 114, 0.16), rgba(90, 149, 114, 0) 70%),
    #e7e6e0;
  transition: opacity 0.9s var(--ease), visibility 0.9s;
}
.cx-veil span {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.cx--map-ready .cx-veil { opacity: 0; visibility: hidden; }

/* --- Map controls -------------------------------------------------------- */

.cx-tools {
  position: absolute;
  z-index: 2;
  top: 2.2rem;
  right: max(var(--gutter), calc((100% - 1280px) / 2 + var(--gutter)));
  display: flex;
  flex-direction: column;
  background: rgba(243, 242, 238, 0.9);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(18, 21, 29, 0.1);
  border-radius: var(--radius);
  box-shadow: 0 18px 36px -22px rgba(18, 21, 29, 0.45);
  overflow: hidden;
  transition: opacity 0.6s var(--ease);
}
.cx--map-pending .cx-tools { opacity: 0; pointer-events: none; }
.cx-tool {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: var(--ink);
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.cx-tool + .cx-tool { border-top: 1px solid rgba(18, 21, 29, 0.08); }
.cx-tool svg { width: 18px; height: 18px; }
.cx-tool:hover { background: #fff; }
.cx-tool:focus-visible { outline-offset: -3px; }
.cx-tool--text {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
/* A toggle keeps its label; the pressed state is what changes. */
.cx-tool--text[aria-pressed="true"] { color: var(--off); background: var(--mid-green); }
.cx-tool--text[aria-pressed="true"]:hover { background: var(--deep-forest); }

/* MapLibre's own chrome, brought into the site's type and tone. */
.cx .maplibregl-ctrl-bottom-right { z-index: 2; }
.cx .maplibregl-ctrl-attrib {
  font-family: var(--sans);
  font-size: 0.68rem;
  background: rgba(243, 242, 238, 0.85);
}
.cx .maplibregl-ctrl-attrib a { color: var(--text-dim); }
.cx .maplibregl-cooperative-gesture-screen {
  z-index: 4;
  background: rgba(18, 21, 29, 0.55);
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 500;
  letter-spacing: -0.01em;
}

/* --- Markers ------------------------------------------------------------- */

/* Marker roots are positioned by MapLibre with an inline transform, so no
   animation is ever put on them directly — only on their children. */
.cx-base-marker {
  width: 30px;
  height: 30px;
  pointer-events: none;
  transition: opacity 0.4s var(--ease);
}
.cx-base-marker.is-dim { opacity: 0.2; }
.cx-base-pulse {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 1.5px solid var(--sage);
  animation: cx-pulse 3.2s var(--ease) infinite;
}

@keyframes cx-pulse {
  0% { transform: scale(0.55); opacity: 0.9; }
  70%, 100% { transform: scale(2.3); opacity: 0; }
}

.cx-pin { pointer-events: none; }
.cx-pin-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: cx-drop 0.6s var(--ease) both;
}
.cx-pin-card {
  background: var(--ink);
  color: var(--off);
  padding: 0.5rem 0.8rem 0.55rem;
  border-radius: var(--radius);
  text-align: center;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: 0 18px 30px -16px rgba(18, 21, 29, 0.7);
}
.cx-pin-card strong {
  display: block;
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}
.cx-pin-card span {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--sage-pale);
}
.cx-pin-stem { width: 1.5px; height: 30px; background: var(--ink); }
.cx-pin-dot {
  position: relative;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--sage);
  box-shadow: 0 0 0 3px #f6f5f1, 0 0 0 4.5px var(--ink);
}
.cx-pin-pulse {
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  border: 1.5px solid var(--sage);
  animation: cx-pulse 2.4s var(--ease) infinite;
}

@keyframes cx-drop {
  from { opacity: 0; transform: translateY(-14px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .cx-base-pulse, .cx-pin-pulse { animation: none; opacity: 0; }
  .cx-pin-inner, .cx-answer { animation: none; }
}

/* --- The floating panel -------------------------------------------------- */

.cx-overlay {
  position: relative;
  z-index: 2;
  pointer-events: none;
  padding-block: 2.2rem;
}
.cx-panel {
  pointer-events: auto;
  width: 410px;
  background: rgba(243, 242, 238, 0.88);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  backdrop-filter: blur(18px) saturate(1.25);
  border: 1px solid rgba(18, 21, 29, 0.09);
  border-radius: var(--radius);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.7) inset,
    0 44px 80px -44px rgba(18, 21, 29, 0.5),
    0 14px 28px -20px rgba(18, 21, 29, 0.25);
}

.cx-finder { padding: 1.7rem 1.7rem 1.5rem; border-bottom: 1px solid var(--line); }
.cx-finder-label {
  display: block;
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--ink);
  margin-bottom: 0.95rem;
}
.cx-search { position: relative; }
.cx-search svg {
  position: absolute;
  left: 0.95rem;
  top: 50%;
  width: 18px;
  height: 18px;
  transform: translateY(-50%);
  color: var(--text-dim);
  pointer-events: none;
}
.cx-search input {
  width: 100%;
  height: 3.2rem;
  padding: 0 1rem 0 2.8rem;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid rgba(18, 21, 29, 0.18);
  border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
  -webkit-appearance: none;
  appearance: none;
}
.cx-search input::placeholder { color: #7b827d; }
.cx-search input:focus-visible {
  outline: none;
  border-color: var(--sage-deep);
  box-shadow: 0 0 0 3px rgba(90, 149, 114, 0.28);
}

.cx-results {
  margin-top: 0.6rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.cx-results li + li { border-top: 1px solid var(--line); }
.cx-result {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  min-height: 46px;
  padding: 0.55rem 0.9rem;
  text-align: left;
  font-size: 0.95rem;
  color: var(--ink);
  transition: background-color 0.15s var(--ease);
}
.cx-result:hover,
.cx-result:focus-visible { background: var(--off-card); }
.cx-result:focus-visible { outline-offset: -3px; }
.cx-result-name { flex: 1; font-weight: 500; }
.cx-result-meta {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  white-space: nowrap;
}

.cx-answer {
  margin-top: 1rem;
  padding: 1.1rem 1.15rem 1.2rem;
  background: #fff;
  border: 1px solid var(--line);
  border-left: 3px solid var(--sage);
  border-radius: var(--radius);
  animation: cx-rise 0.45s var(--ease);
}
.cx-answer[data-kind="no"] { border-left-color: var(--text-dim); }
@keyframes cx-rise {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
.cx-answer-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0.75rem;
  align-items: start;
}
.cx-answer[data-kind="no"] .cx-answer-head { grid-template-columns: minmax(0, 1fr); }
.cx-answer-mark {
  width: 26px;
  height: 26px;
  margin-top: 0.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--sage);
  color: var(--ink);
}
.cx-answer-mark svg { width: 15px; height: 15px; }
.cx-answer-verdict { font-size: 0.95rem; line-height: 1.5; color: var(--ink); }
.cx-answer-verdict strong { font-weight: 600; }
.cx-answer[data-kind="no"] .cx-answer-verdict { color: var(--text-dim); font-size: 0.9rem; }
.cx-answer[data-kind="no"] .cx-answer-verdict strong { color: var(--ink); }
.cx-answer-close {
  width: 36px;
  height: 36px;
  margin: -0.4rem -0.45rem 0 0;
  display: grid;
  place-items: center;
  color: var(--text-dim);
  border-radius: var(--radius);
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.cx-answer-close svg { width: 16px; height: 16px; }
.cx-answer-close:hover { background: var(--off-card); color: var(--ink); }
.cx-answer-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.3rem 1rem;
  margin-top: 0.95rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--line);
}
.cx-answer-label,
.cx-phone-label {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.cx-answer-phone {
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  border-bottom: 1px solid var(--sage);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.cx-answer-phone:hover { color: var(--sage-deep); border-color: var(--sage-deep); }
.cx-answer-cta { margin-top: 1rem; width: 100%; justify-content: center; }
.cx-answer-alts { display: grid; gap: 0.5rem; margin-top: 0.95rem; }
.cx-answer-alt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.65rem 0.8rem;
  background: var(--off-card);
  border-radius: var(--radius);
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--text-dim);
  transition: background-color 0.2s var(--ease);
}
.cx-answer-alt strong {
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.cx-answer-alt:hover { background: var(--off-dim); }

/* --- Regions and towns --------------------------------------------------- */

.cx-browse { padding: 0.35rem 1.7rem 1.4rem; }
.cx-region { padding: 0.95rem 0 1rem; border-bottom: 1px solid var(--line); }
.cx-region-head { font-size: inherit; line-height: inherit; letter-spacing: normal; }
.cx-region-toggle {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  min-height: 44px;
  text-align: left;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.25;
  color: var(--ink);
}
.cx-region-name { flex: 1; }
.cx-region-count {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}
.cx-chevron {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--text-dim);
  transition: transform 0.35s var(--ease);
}
.cx-region-toggle[aria-expanded="true"] .cx-chevron { transform: rotate(180deg); }
.cx-region-toggle:hover .cx-region-name { color: var(--sage-deep); }

.cx-phone {
  display: inline-flex;
  align-items: baseline;
  gap: 0.65rem;
  margin-left: calc(13px + 0.65rem);
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  transition: color 0.2s var(--ease);
}
.cx-phone:hover { color: var(--sage-deep); }

.cx-towns {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.3rem;
  margin-top: 0.85rem;
}
.cx-town {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 34px;
  padding: 0.3rem 0.65rem 0.3rem 0.55rem;
  font-size: 0.85rem;
  line-height: 1.2;
  color: var(--text-dim);
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: default;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.cx-town.is-core { color: var(--ink); font-weight: 500; }
.cx--live .cx-town { cursor: pointer; }
.cx--live .cx-town:hover { background: rgba(255, 255, 255, 0.75); border-color: var(--line); color: var(--ink); }
.cx-town[aria-pressed="true"],
.cx--live .cx-town[aria-pressed="true"]:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--off);
}

.cx-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #f6f5f1;
  border: 1.6px solid var(--mid-green);
}
.cx-dot--core,
.cx-town.is-core .cx-dot { background: var(--mid-green); }
.cx-dot--base,
.cx-town.is-base .cx-dot {
  width: 11px;
  height: 11px;
  background: var(--ink);
  border: 0;
  box-shadow: 0 0 0 1.5px #f6f5f1, 0 0 0 3px var(--sage);
}
.cx-town[aria-pressed="true"] .cx-dot { border-color: var(--sage-pale); }

.cx-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.15rem;
  margin-top: 1.15rem;
  font-size: 0.76rem;
  color: var(--text-dim);
}
.cx-key > span { display: inline-flex; align-items: center; gap: 0.5rem; }

/* --- Note under the map --------------------------------------------------- */

.coverage-note { background: var(--off-dim); padding-top: 3.5rem; }
.coverage-note .note-panel { background: var(--off); }

/* --- No JavaScript, or no map --------------------------------------------- */

html:not(.js) .cx-stage,
.cx--no-map .cx-stage { display: none; }
html:not(.js) .cx-overlay,
.cx--no-map .cx-overlay { pointer-events: auto; padding-block: 0; }
html:not(.js) .cx-panel,
.cx--no-map .cx-panel { width: auto; max-width: 760px; }
html:not(.js) .cx-finder,
html:not(.js) .cx-chevron { display: none; }

/* --- Narrow screens: the panel stops floating ----------------------------- */

@media (max-width: 899px) {
  .coverage-explorer { padding-top: 5rem; }
  .coverage-explorer .section-head { margin-bottom: 2rem; }

  .cx { display: flex; flex-direction: column; }
  .cx-overlay,
  .cx-overlay > .container,
  .cx-panel { display: contents; }

  .cx-finder,
  .cx-browse {
    margin-inline: var(--gutter);
    background: rgba(243, 242, 238, 0.9);
    border: 1px solid rgba(18, 21, 29, 0.09);
    border-radius: var(--radius);
  }
  .cx-finder { order: 1; padding: 1.4rem 1.3rem 1.3rem; }
  .cx-stage {
    order: 2;
    min-height: 0;
    height: clamp(400px, 64svh, 560px);
    margin-top: 1.5rem;
  }
  .cx-browse { order: 3; padding: 0.3rem 1.3rem 1.2rem; margin-top: 1.5rem; }
  .cx-stage::before, .cx-stage::after { height: 56px; }
  .cx-tools { top: 1.1rem; right: 1rem; }
  .cx-tool { width: 42px; height: 42px; }

  .cx-finder-label { font-size: 1.2rem; }
  .cx-phone { font-size: 1.25rem; margin-left: 0; }
  .cx-town { font-size: 0.84rem; }
}

/* ==========================================================================
   Cookie consent banner (only rendered when GA_MEASUREMENT_ID is set; see
   content/analytics.py and js/consent.js). Dark, like the rest of the site.
   Accept and reject share one style so neither is the easier choice.
   ========================================================================== */

.consent-banner {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 110;
  background: var(--ink-soft);
  color: var(--off);
  border-top: 1px solid var(--line-on-dark);
  box-shadow: 0 -10px 30px rgba(18, 21, 29, 0.35);
  padding: 1rem var(--gutter) calc(1rem + env(safe-area-inset-bottom, 0));
}
.consent-banner[hidden] { display: none; }
.consent-inner {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}
.consent-inner p { font-size: 0.92rem; line-height: 1.5; color: var(--off-dim); margin: 0; }
.consent-inner p strong { color: var(--off); }
.consent-inner a { color: var(--sage-pale); text-decoration: underline; }
.consent-actions { display: flex; gap: 0.6rem; flex-shrink: 0; }
.consent-btn {
  border: 1px solid var(--sage-light);
  color: var(--off);
  background: transparent;
  cursor: pointer;
}
.consent-btn:hover,
.consent-btn:focus-visible { background: var(--sage-dark); border-color: var(--sage-dark); }

@media (max-width: 760px) {
  .consent-banner { padding-top: 0.85rem; padding-bottom: calc(0.85rem + env(safe-area-inset-bottom, 0)); }
  .consent-inner { flex-direction: column; align-items: stretch; gap: 0.7rem; }
  .consent-inner p { font-size: 0.86rem; }
  /* Side by side, so the banner covers less of the call bar beneath it. */
  .consent-btn { flex: 1; justify-content: center; padding: 0.7rem 0.6rem; white-space: normal; }
}
