/* ── Icons ──────────────────────────────────────────────────────────────
   <svg class="ic"><use href="#i-name"/></svg> against the per-page <symbol> sprite
   inlined by generate-icons.mjs from the vendored icons/ folder. No CDN, no JS.
   Stroke props live here once; inner fill attrs (e.g. star-filled) still win. */
.ic {
  width: 1em; height: 1em;
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* the client — shared site styles
 * Layered on top of colors_and_type.css.
 * Layouts and components shared across homepage variants and service pages.
 */

*, *::before, *::after { box-sizing: border-box; }
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

/* Prevent stray horizontal overflow on mobile from any element that
   forgets a max-width. Belt-and-suspenders alongside container rules. */
html, body { overflow-x: clip; }

/* Body lock while the mobile menu is open. */
body.menu-open-lock { overflow: hidden; }

/* ── Container ────────────────────────────────────────────────────── */
.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad-x-desk);
}
@media (max-width: 900px) {
  .container { padding: 0 var(--container-pad-x-tab); }
}
@media (max-width: 640px) {
  .container { padding: 0 var(--container-pad-x-mob); }
}

/* ── Promo bar ────────────────────────────────────────────────────── */
.promo-bar {
  background-image: var(--gradient-brand-dark);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.55rem 0;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.promo-bar .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--color-brand-secondary);
  display: inline-block;
  box-shadow: 0 0 0 0 rgba(150,192,225,0.6);
  animation: pulse 1.8s ease-out infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(150,192,225,0.55); }
  70% { box-shadow: 0 0 0 8px rgba(150,192,225,0); }
  100% { box-shadow: 0 0 0 0 rgba(150,192,225,0); }
}

/* ── Navbar ───────────────────────────────────────────────────────── */
/* 16 Sep 2026 (D9): `--nav-height` used to exist only inside `.nav-wrap`
   (for the mobile panel's own padding), so nothing outside the nav knew how
   tall the sticky header actually was. Defined on `:root` instead — at the
   same three steps the nav itself takes below — so any `[id]` target on the
   page can clear it. */
:root { --nav-height: 140px; }
@media (max-width: 1280px) { :root { --nav-height: 116px; } }
@media (max-width: 1180px) { :root { --nav-height: 72px; } }
[id] { scroll-margin-top: calc(var(--nav-height) + 1.5rem); }
.nav-wrap {
  position: sticky; top: 0; z-index: 50;
}
.navbar {
  position: relative;
  z-index: 10;  /* sit above the mobile menu panel inside .nav-wrap */
  background-color: var(--color-glass-bg);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--color-border-subtle);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 140px;
}
.nav-logo img { height: 96px; width: auto; }
.nav-links {
  display: flex; align-items: center; gap: 2.4rem;
  list-style: none; margin: 0; padding: 0;
}
.nav-links > li { position: relative; }
.nav-links a {
  font-size: 15.5px;
  font-weight: 600;
  color: var(--color-text-secondary);
  letter-spacing: -0.005em;
  transition: color 0.2s ease;
  display: inline-flex; align-items: center; gap: 4px;
}
.nav-links a:hover { color: var(--color-text-primary); }
.nav-links .chev {
  width: 16px; height: 16px; opacity: 0.6;
  transition: transform 200ms var(--ease-out);
}
.nav-links .has-dropdown:hover > a .chev,
.nav-links .has-dropdown.open > a .chev {
  transform: rotate(180deg);
}

/* Dropdown menu — opens below trigger, always light-themed so it
   reads cleanly against both light and dark hero navbars. */
.nav-links .dropdown {
  position: absolute;
  top: 100%;
  left: -0.75rem;
  min-width: 260px;
  background: #fff;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-menu);
  padding: 0.5rem;
  margin-top: 0.5rem;
  list-style: none;
  display: flex; flex-direction: column;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 200ms var(--ease-out),
              transform 200ms var(--ease-out),
              visibility 200ms;
  z-index: 60;
}
.nav-links .has-dropdown:hover > .dropdown,
.nav-links .has-dropdown:focus-within > .dropdown,
.nav-links .has-dropdown.open > .dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* Dropdown items live on a light surface and must render dark text
   even when the parent nav is in overlay (white-on-hero) state. We
   anchor the selector on `.dropdown` so the same rule wins regardless
   of which nav variant is rendering above. */
.nav-links .dropdown li a,
.nav-wrap.nav-overlay .nav-links .dropdown li a,
.nav-wrap.nav-overlay.is-solid .nav-links .dropdown li a,
.svc-hero-wrap .nav-links .dropdown li a {
  display: block;
  padding: 0.7rem 0.9rem;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-text-body);
  border-radius: var(--radius-sm);
  letter-spacing: -0.005em;
  transition: background 0.15s ease, color 0.15s ease;
}
.nav-links .dropdown li a:hover,
.nav-wrap.nav-overlay .nav-links .dropdown li a:hover,
.nav-wrap.nav-overlay.is-solid .nav-links .dropdown li a:hover,
.svc-hero-wrap .nav-links .dropdown li a:hover {
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
}

.nav-cta {
  display: flex; align-items: center; gap: 0.75rem;
}
.nav-phone {
  font-weight: 700; font-size: 15px;
  color: var(--color-brand-primary-700);
  display: inline-flex; align-items: center; gap: 6px;
}
.nav-phone svg { width: 16px; height: 16px; }

/* Phone-as-button variant — outline button with transparent fill,
   used in service-page navs so the call CTA reads as a button rather
   than a bare link. Same visual weight as the secondary "See How It
   Works" button. */
.btn.nav-phone-btn {
  padding: 0.7rem 1.25rem;
  font-size: 14.5px;
  border-radius: var(--radius-button);
  background: transparent;
  color: var(--color-brand-primary-700);
  border: 1px solid var(--color-border-default);
}
.btn.nav-phone-btn:hover {
  border-color: var(--color-brand-primary);
  background: rgba(10,37,65,0.04);
  color: var(--color-brand-primary-700);
}
.btn.nav-phone-btn svg { width: 16px; height: 16px; }

/* Overlay nav (homepage hero over dark video): flip the phone button
   to white-on-transparent with a white outline. No hover change while
   the video is visible. Reverts to the default navy state once the
   nav goes solid on scroll. */
.nav-wrap.nav-overlay .btn.nav-phone-btn,
.nav-wrap.nav-overlay .btn.nav-phone-btn:hover {
  background: transparent;
  border-color: var(--color-white-outline);
  color: #fff;
}
.nav-wrap.nav-overlay.is-solid .btn.nav-phone-btn {
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
}
.nav-wrap.nav-overlay.is-solid .btn.nav-phone-btn:hover {
  background: rgba(10,37,65,0.04);
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
}

/* Outline nav CTA — adapts to nav state. White outline + translucent
   white hover over the dark hero; flips to brand-navy outline + tinted
   hover once the nav goes solid on scroll. Matches the hero's
   "See How It Works" ghost button. */
.btn.nav-cta-outline {
  background: transparent;
  border: 1px solid var(--color-white-outline);
  color: #fff;
  padding: 0.7rem 1.25rem;
  font-size: 14.5px;
}
.btn.nav-cta-outline svg { width: 16px; height: 16px; }
.btn.nav-cta-outline:hover {
  background: var(--color-white-border);
  border-color: #fff;
  color: #fff;
}
.nav-wrap.nav-overlay.is-solid .btn.nav-cta-outline {
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
}
.nav-wrap.nav-overlay.is-solid .btn.nav-cta-outline:hover {
  background: rgba(10,37,65,0.08);
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
}

/* ── Buttons ──────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 1.5rem;
  border-radius: var(--radius-button);
  font-size: var(--font-size-button);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: all 0.3s var(--ease-brand);
  border: none;
}
.btn svg { width: 18px; height: 18px; }
.btn-sm {
  padding: 0.75rem 1.35rem;
  font-size: 14.5px;
}
.btn-sm svg { width: 16px; height: 16px; }
.btn-lg {
  padding: 1.05rem 1.85rem;
  font-size: 1.0625rem;
}

/* Brand CTA — light logo gradient (sky → pale, diagonal) with deep-ink
   label. Light gradient is the dominant button treatment site-wide;
   dark gradients are reserved for section backgrounds. */
.btn-brand {
  background-image: var(--gradient-brand);
  color: var(--color-brand-secondary);
  box-shadow: 0 4px 14px -4px rgba(10,37,65,0.35);
}
.btn-brand:hover {
  filter: brightness(1.04);
  box-shadow: 0 12px 28px -8px rgba(10,37,65,0.45);
  transform: translateY(-1px);
}
.btn-brand svg { stroke: var(--color-brand-secondary); }
/* Primary accent CTA — same light gradient + dark label. Legacy class
   name kept to avoid HTML churn; visually equivalent to .btn-brand and
   .btn-soft, just with a heavier shadow for prominence. */
.btn-accent {
  background-image: var(--gradient-brand);
  color: var(--color-brand-secondary);
  box-shadow: 0 8px 22px -8px rgba(10,37,65,0.55);
}
.btn-accent:hover {
  filter: brightness(1.04);
  box-shadow: 0 12px 28px -8px rgba(10,37,65,0.65);
  transform: translateY(-1px);
}
.btn-accent svg { stroke: var(--color-brand-secondary); }
.btn-secondary {
  background: #fff;
  color: var(--color-text-primary);
  border: 1px solid var(--color-border-default);
}
.btn-secondary:hover {
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
  background: #fdfdff;
}
.btn-ghost {
  background: transparent;
  color: var(--color-text-primary);
  border: 1px solid var(--color-white-outline);
}
.btn-ghost.on-dark { color: #fff; }
.btn-ghost.on-dark:hover { background: var(--color-white-surface-hi); }

/* Light/logo-gradient CTA — sky → pale wash mirroring the logo badge.
   The background is light, so the label must be the deep brand ink. Pairs
   well with the dark hero so the button reads as a bright lift, not as
   another dark slab. */
.btn-soft {
  background-image: var(--gradient-brand);
  color: var(--color-brand-secondary);
  box-shadow: 0 8px 22px -8px rgba(10,37,65,0.35);
}
.btn-soft:hover {
  filter: brightness(1.04);
  box-shadow: 0 12px 28px -8px rgba(10,37,65,0.45);
  transform: translateY(-1px);
}
.btn-soft svg { stroke: var(--color-brand-secondary); }

/* ── Eyebrow pill ─────────────────────────────────────────────────── */
/* Eyebrow pill on light surfaces.
   Uses the logo sky tint as the chip fill, the deep ink as the text, and
   the deep brand-blue as the pulse dot — so the chip itself "wears the
   brand color" and the label still reads with strong contrast. */
.eyebrow-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--color-brand-primary-200);
  border: 1px solid var(--color-brand-primary-300);
  border-radius: var(--radius-pill);
  padding: 0.4rem 0.85rem 0.4rem 0.55rem;
  font-size: var(--font-size-eyebrow);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
  color: var(--color-brand-secondary);
  box-shadow: var(--shadow-subtle);
}
.eyebrow-pill .dot-wrap {
  position: relative;
  width: 8px; height: 8px;
}
.eyebrow-pill .dot-wrap .dot {
  position: absolute; inset: 0;
  background: var(--color-brand-primary);
  border-radius: 50%;
}
.eyebrow-pill .dot-wrap .ring {
  position: absolute; inset: 0;
  background: var(--color-brand-primary);
  border-radius: 50%;
  animation: pm-ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
/* Accent variant — uses deep brand-blue text (legacy `.green` class kept
   for HTML markup compatibility; styling is brand-blue, not green). */
.eyebrow-pill.green { color: var(--color-brand-primary-700); }
.eyebrow-pill.green .dot, .eyebrow-pill.green .ring { background: var(--color-brand-primary); }
.eyebrow-pill.on-dark {
  background: var(--color-white-surface-hi);
  border-color: var(--color-white-border);
  color: #fff;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.eyebrow-pill.on-dark .dot, .eyebrow-pill.on-dark .ring {
  background: var(--color-brand-primary);
}
@media (max-width: 640px) {
  .eyebrow-pill {
    padding: 0.3rem 0.65rem 0.3rem 0.45rem;
    font-size: 0.75rem;
    letter-spacing: 0.18em;
  }
}

/* ── Section heading block ────────────────────────────────────────── */
.section-heading {
  text-align: center;
  max-width: 760px;
  margin: 0 auto 4rem;
}
.section-heading .eyebrow-pill { margin-bottom: 1.5rem; }
.section-heading h1, .section-heading h2 { margin-bottom: 1.25rem; }
.section-heading .t-lead {
  text-wrap: pretty;
}

/* ── Star rating ──────────────────────────────────────────────────── */
.stars {
  display: inline-flex; gap: 2px;
  color: var(--color-accent-gold);
}
.stars svg { width: 16px; height: 16px; fill: currentColor; }

/* ── Card ─────────────────────────────────────────────────────────── */
.card {
  background: var(--color-surface-card);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  padding: 2.5rem;
  transition: all 0.5s ease;
}
.card:hover {
  box-shadow: var(--shadow-card-hover);
  border-color: var(--color-border-brand-30);
  transform: translateY(-4px);
}
.card .icon-chip {
  width: 48px; height: 48px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: 1.5rem;
}
.card .icon-chip.green {
  background: var(--color-surface-subtle-2);
  color: var(--color-brand-secondary);
}
.card .icon-chip svg { width: 24px; height: 24px; }
.card h3 { margin-bottom: 0.75rem; font-size: 1.5rem; }
.card p { font-size: 1rem; }

/* ── Footer ───────────────────────────────────────────────────────── */
/* Uses the same dark gradient as every other dark band, so the footer
   reads as the continuation of the brand-blue family rather than a
   separate navy.
   Text colour rule (QA 8 Sep 2026): the footer is a DARK surface, so
   every label in it takes a white-alpha token. It used to reuse
   --color-text-tertiary / --color-text-secondary, which are slate greys
   scaled for WHITE backgrounds — on the dark band they measured
   1.1–2.8:1. Never reach for --color-text-* inside .footer. */
.footer {
  background-color: var(--color-brand-primary-950);
  background-image: var(--gradient-brand-dark);
  color: rgba(255,255,255,0.78);
  padding: 5rem 0 2.5rem;
}
.footer h4 {
  color: #fff;
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  margin: 0 0 1.25rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { margin-bottom: 0.6rem; }
.footer a { font-size: 0.9375rem; color: var(--color-white-text-muted); transition: color 0.2s; }
.footer a:hover { color: #fff; }
.footer .footer-static li { font-size: 0.9375rem; color: var(--color-white-text-muted); }
/* Buttons keep their own label colour. `.footer a` (0,1,1) out-specifies
   `.btn-accent` (0,1,0), which is why the footer CTA rendered as grey
   text on a near-white pill (~1.5:1) — QA 8 Sep 2026. */
.footer a.btn,
.footer a.btn:hover { color: var(--color-brand-secondary); }
.footer a.btn svg { stroke: var(--color-brand-secondary); }
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr 1.2fr;
  gap: 3rem;
  padding-bottom: 3rem;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.footer-grid .brand img { height: 96px; width: auto; margin-bottom: 1.5rem; }
.footer-grid .brand p { color: var(--color-white-text-muted); font-size: 0.9375rem; max-width: 28ch; }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  padding-top: 2rem;
  font-size: 0.8125rem;
  /* space-between drops Privacy / Terms / Sitemap on the lightest end of
     the sweep, so this row needs the strongest of the muted tokens. */
  color: var(--color-white-text-muted);
}
.footer-bottom a { font-size: inherit; }
.footer-bottom .legal { display: flex; gap: 1.5rem; }

/* ── Fade-up animation on scroll ──────────────────────────────────── */
.fade-up {
  opacity: 0;
  transform: translateY(32px);
  transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out);
  will-change: opacity, transform;
}
.fade-up.in {
  opacity: 1;
  transform: translateY(0);
}
/* Gentler variant for tight rows of cards / small text blocks. */
.fade-up.soft {
  transform: translateY(18px);
  transition-duration: 0.8s;
}
/* `.fade-up.soft` and `.fade-up.in` have equal specificity, so without
   this rule the later `.soft` declaration would pin the element at
   +18px even after fade-in — turning small hover lifts into 20px+
   jumps. Restore the intended landing position. */
.fade-up.soft.in { transform: translateY(0); }
/* Cascade children with a small per-row delay so a grid of cards
   reveals row-by-row instead of landing as a block. Apply `.stagger`
   on the parent, `.fade-up` on each child. */
.stagger > .fade-up:nth-child(1) { transition-delay: 0ms; }
.stagger > .fade-up:nth-child(2) { transition-delay: 80ms; }
.stagger > .fade-up:nth-child(3) { transition-delay: 160ms; }
.stagger > .fade-up:nth-child(4) { transition-delay: 240ms; }
.stagger > .fade-up:nth-child(5) { transition-delay: 320ms; }
.stagger > .fade-up:nth-child(6) { transition-delay: 400ms; }
.stagger > .fade-up:nth-child(7) { transition-delay: 480ms; }
.stagger > .fade-up:nth-child(8) { transition-delay: 560ms; }

@media (prefers-reduced-motion: reduce) {
  .fade-up, .fade-up.soft {
    transform: none !important;
    transition: opacity 0.3s ease !important;
    transition-delay: 0ms !important;
  }
}

/* ── Comparison table ─────────────────────────────────────────────── */
/* Three-column "feature vs. competitor vs. brand" table with a
   gradient-filled brand header cell. Pages set their own column
   labels and row contents; this owns the visual chrome. */
.compare {
  background: #fff;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-card-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
.compare-row {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr;
  align-items: center;
  border-bottom: 1px solid var(--color-border-subtle);
}
.compare-row:last-child { border-bottom: 0; }
.compare-row > div { padding: 1.25rem 2rem; }
.compare-row.head > div {
  padding: 1.75rem 2rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-weight: 800;
  color: var(--color-text-secondary);
  background: var(--color-surface-app);
  border-bottom: 1px solid var(--color-border-default);
  text-align: center;
}
.compare-row.head > div:first-child {
  text-align: left;
}
.compare-row.head .ours {
  color: #fff;
  border-bottom-color: var(--color-brand-primary);
  position: relative;
}
.compare-row.head .ours::before {
  content: "";
  position: absolute; inset: 0;
  background: var(--gradient-brand-deep);   /* Header cell carries white text — needs the dark variant for legibility. */
  z-index: 0;
}
.compare-row.head .ours span { position: relative; z-index: 1; }
.compare-row .feature {
  font-weight: 700;
  color: var(--color-text-primary);
  font-size: 1rem;
  letter-spacing: -0.01em;
}
.compare-row .feature small {
  display: block;
  font-size: 0.8125rem;
  color: var(--color-text-secondary);
  font-weight: 500;
  margin-top: 0.25rem;
}
.compare-row .cell {
  font-size: 0.9375rem;
  color: var(--color-text-body);
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
.compare-row .check {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--color-surface-subtle-2);
  color: var(--color-brand-secondary);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.compare-row .check svg { width: 14px; height: 14px; }
.compare-row .x {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff1f3;
  color: var(--color-status-danger);
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.compare-row .x svg { width: 14px; height: 14px; }
.compare-row .ours {
  background: linear-gradient(180deg, rgba(10,37,65,0.025), transparent);
  border-left: 1px solid var(--color-border-subtle);
}
.compare-row .ours .check { background: var(--color-brand-secondary); color: #fff; }
/* When a compare row is also a fade-up target, only opacity should
   animate — sliding a table row reads as broken. */
.compare-row.fade-up { transform: none; }

/* ── Stat utility ─────────────────────────────────────────────────── */
/* Big gradient-text number recipe used in hero trust stats, review
   aggregate, testimonial result rows, and stage numerals. Page sets
   font-size; this owns the weight, tracking, and gradient fill. */
.t-stat {
  font-weight: 900;
  letter-spacing: -0.04em;
  line-height: 1;
  background-image: var(--gradient-brand-deep);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}
.t-stat.accent {
  background-image: var(--gradient-accent);
}

/* ── Reviews carousel ─────────────────────────────────────────────── */
/* ── Review card (used inside the reviews carousel) ───────────── */
.svc-review {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  padding: 2rem;
  display: flex; flex-direction: column;
  gap: 1.25rem;
  transition: all 0.4s var(--ease-brand);
}
.svc-review:hover {
  box-shadow: var(--shadow-card);
  border-color: var(--color-border-brand-30);
  transform: translateY(-4px);
}
.svc-review .stars { color: var(--color-accent-gold); display: flex; gap: 2px; }
.svc-review .stars svg { width: 16px; height: 16px; fill: currentColor; }
.svc-review blockquote {
  margin: 0;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--color-text-primary);
  font-weight: 500;
  letter-spacing: -0.005em;
  text-wrap: pretty;
  flex: 1;
  /* Clamp to 6 lines: real GBP reviews run 80-1200 chars, and one long review
     otherwise stretches every card in the row (QA 28 Jul). Full text stays in the
     DOM; the reviews page shows it unclamped. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
          line-clamp: 6;
  overflow: hidden;
}
.reviews-wall .svc-review blockquote { display: block; -webkit-line-clamp: none; line-clamp: none; overflow: visible; }
.svc-review .meta {
  display: flex; align-items: center; gap: 0.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-border-subtle);
}
.svc-review .avatar {
  width: 36px; height: 36px;
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 0.8125rem;
  flex: 0 0 auto;
}
.svc-review .name { font-size: 0.9375rem; font-weight: 700; color: var(--color-text-primary); }
.svc-review .where { font-size: 0.8125rem; color: var(--color-text-secondary); }
.svc-review .verified {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--color-brand-secondary);
  text-transform: uppercase;
  letter-spacing: 0.12em;
}
.svc-review .verified svg { width: 11px; height: 11px; }

/* Horizontal scroll-snap track with arrow nav. Three cards visible on
   desktop, two on tablet, one on mobile. Arrows scroll one card at a
   time and disable at the ends. */
.reviews-carousel { position: relative; }
.reviews-carousel .carousel-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * 1.25rem) / 3);
  gap: 1.25rem;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  padding: 0.5rem 0 1rem;
}
.reviews-carousel .carousel-track::-webkit-scrollbar { display: none; }
.reviews-carousel .carousel-track > * { scroll-snap-align: start; }
.reviews-carousel .carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px; height: 48px;
  background: #fff;
  border: 1px solid var(--color-border-default);
  border-radius: 50%;
  color: var(--color-text-primary);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-card);
  z-index: 2;
  transition: all 0.3s var(--ease-brand);
}
.reviews-carousel .carousel-arrow:hover {
  border-color: var(--color-brand-primary);
  color: var(--color-brand-primary-700);
}
.reviews-carousel .carousel-arrow:disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}
.reviews-carousel .carousel-arrow svg { width: 20px; height: 20px; }
.reviews-carousel .carousel-arrow.prev { left: -24px; }
.reviews-carousel .carousel-arrow.next { right: -24px; }
@media (max-width: 900px) {
  .reviews-carousel .carousel-track { grid-auto-columns: calc((100% - 1rem) / 2); }
}
@media (max-width: 640px) {
  .reviews-carousel .carousel-track { grid-auto-columns: 100%; }
  /* Arrows would overlap card content at this width — rely on
     scroll-snap touch behavior and hide them. */
  .reviews-carousel .carousel-arrow { display: none; }
}

/* ── Why the client reasons grid ────────────────────────────────────── */
.why-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
}
.why-card {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  padding: 2.25rem 1.75rem;
  display: flex; flex-direction: column; gap: 0.75rem;
  transition:
    transform 0.3s var(--ease-out),
    box-shadow 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out);
}
.why-card.fade-up.soft:hover {
  border-color: var(--color-border-brand-30);
  background:
    linear-gradient(#fff, #fff) padding-box,
    var(--gradient-brand) border-box;
  box-shadow: 0 14px 26px -18px rgba(10,37,65,0.28);
  transform: translateY(-4px);
}
.why-card .icon-chip {
  width: 48px; height: 48px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: 0.5rem;
  transition: background 0.35s var(--ease-brand), color 0.35s var(--ease-brand);
}
.why-card .icon-chip.accent {
  background: var(--color-surface-subtle-2);
  color: var(--color-brand-secondary);
}
.why-card.fade-up.soft:hover .icon-chip {
  background: var(--gradient-brand);   /* 48px chip — light variant; dark icon for legibility. */
  color: var(--color-brand-secondary);
}
.why-card .icon-chip svg { width: 22px; height: 22px; }
.why-card h3 {
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  margin: 0;
}
.why-card p {
  font-size: 0.9375rem;
  color: var(--color-text-secondary);
  margin: 0;
  line-height: 1.55;
}
@media (max-width: 1100px) { .why-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .why-grid { grid-template-columns: 1fr; } }

/* ── Service areas + map ─────────────────────────────────────────── */
/* Shared across homepage and service pages. Two-column grid: stacked
   area rows on the left, embedded Google Map on the right with a
   floating overlay badge. */
.svc-areas-grid {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 3rem;
  align-items: stretch;
}
.svc-areas-list {
  display: grid; gap: 0.75rem;
  align-content: start;
}
.svc-area-row {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  padding: 1.75rem;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1rem;
  align-items: start;
  transition: all 0.4s ease;
}
.svc-area-row:hover {
  border-color: var(--color-border-brand-30);
  box-shadow: var(--shadow-card);
}
.svc-area-row .name {
  font-size: 1.25rem;
  font-weight: 900;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
  margin-bottom: 0.5rem;
}
.svc-area-row .neighborhoods {
  font-size: 0.875rem;
  color: var(--color-text-secondary);
  line-height: 1.55;
}
.svc-area-row .pill {
  align-self: start;
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.35rem 0.65rem;
  background: var(--color-surface-subtle-2);
  color: var(--color-brand-secondary);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.svc-area-row .pill .dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--color-brand-secondary);
}
.svc-map {
  border-radius: var(--radius-card-lg);
  overflow: hidden;
  box-shadow: var(--shadow-image);
  position: relative;
  min-height: 460px;
  background: var(--color-surface-subtle);
}
.svc-map iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
  filter: saturate(0.9) contrast(0.96);
}
.svc-map .map-overlay {
  position: absolute;
  bottom: 1.5rem; left: 1.5rem;
  background: #fff;
  padding: 1rem 1.25rem;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  display: flex; align-items: center; gap: 0.75rem;
  pointer-events: none;
}
.svc-map .map-overlay .pin {
  width: 32px; height: 32px;
  background-image: var(--gradient-brand);   /* 32px pin — light variant; dark icon for legibility. */
  color: var(--color-brand-secondary);
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
}
.svc-map .map-overlay .pin svg { width: 16px; height: 16px; }
.svc-map .map-overlay .label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-weight: 800;
  color: var(--color-text-secondary);
}
.svc-map .map-overlay strong {
  font-size: 0.9375rem;
  color: var(--color-text-primary);
  font-weight: 800;
  letter-spacing: -0.01em;
}
@media (max-width: 1100px) {
  .svc-areas-grid { grid-template-columns: 1fr; }
}

/* ── Nav overlay (homepage) ───────────────────────────────────────── */
/* Used when the nav floats transparent over a hero video and then
   becomes solid on scroll. Service pages still use the default nav. */
.nav-wrap.nav-overlay {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  transition: background 350ms var(--ease-out),
              border-color 350ms var(--ease-out),
              backdrop-filter 350ms var(--ease-out);
}
.nav-wrap.nav-overlay .navbar {
  background: transparent;
  border-bottom: 1px solid transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.nav-wrap.nav-overlay .nav-links a { color: rgba(255,255,255,0.75); }
.nav-wrap.nav-overlay .nav-links a:hover { color: #fff; }
.nav-wrap.nav-overlay .nav-phone { color: #fff; }
.nav-wrap.nav-overlay .nav-logo .logo-dark { display: none; }
.nav-wrap.nav-overlay .nav-logo .logo-light { display: block; }
/* lift the logo off busy hero imagery while the nav is transparent */
.nav-wrap.nav-overlay:not(.is-solid) .nav-logo img { filter: drop-shadow(0 2px 12px rgba(9, 20, 35, 0.35)); }

.nav-wrap.nav-overlay.is-solid .navbar {
  background: var(--color-glass-bg);
  border-bottom-color: var(--color-border-subtle);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
.nav-wrap.nav-overlay.is-solid .nav-links a { color: var(--color-text-secondary); }
.nav-wrap.nav-overlay.is-solid .nav-links a:hover { color: var(--color-text-primary); }
.nav-wrap.nav-overlay.is-solid .nav-phone { color: var(--color-brand-primary-700); }
.nav-wrap.nav-overlay.is-solid .nav-logo .logo-light { display: none; }
.nav-wrap.nav-overlay.is-solid .nav-logo .logo-dark { display: block; }

/* ── Section base (shared by homepage and service pages) ──────────── */
section.svc-section { padding: 7rem 0; position: relative; }
section.svc-alt     { background: var(--color-surface-section); }

/* ── Vapure section (video + intro), shared layout ────────────────── */
/* Used on homepage and service pages alike. Pages may override the
   media side (some show video, some show a photo with a play overlay)
   but the grid + copy rules are the same everywhere. */
.vapure {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 4rem;
  align-items: center;
}
.vapure-video {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-card-lg);
  overflow: hidden;
  box-shadow: var(--shadow-image-large);
  background: var(--color-brand-primary-900);
}
.vapure-video iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}
.vapure-copy .eyebrow-pill { margin-bottom: 1.5rem; }
.vapure-copy h2            { margin-bottom: 1.5rem; text-wrap: balance; }
.vapure-copy .t-lead       { margin-bottom: 2rem; max-width: 56ch; }
.vapure-features {
  list-style: none;
  margin: 0 0 2rem;
  padding: 0;
  display: grid; gap: 0.6rem;
}
.vapure-features li {
  display: flex; align-items: center; gap: 0.6rem;
  font-size: 0.9375rem;
  color: var(--color-text-body);
}
.vapure-features li svg {
  width: 18px; height: 18px;
  color: var(--color-brand-secondary);
  flex: 0 0 auto;
}
.vapure-cta-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
@media (max-width: 1100px) { .vapure { grid-template-columns: 1fr; } }

/* ── Dark-section grid overlay (paint via ::before) ───────────────── */
/* Add this class to any dark navy section that wants the subtle
   grid-pattern stamp + radial mask. Set --grid-opacity inline to tune
   if the section needs a more or less prominent overlay. */
.surface-grid-dark {
  position: relative;
  overflow: hidden;
}
.surface-grid-dark::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    linear-gradient(var(--color-white-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--color-white-grid) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, black 30%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, black 30%, transparent 80%);
  pointer-events: none;
  opacity: var(--grid-opacity, 0.5);
}

/* ── Glass card on dark backgrounds ───────────────────────────────── */
/* Reusable recipe for cards that sit on navy/dark sections — used by
   .health-card, .b-closing-card, and similar one-off panels. */
.card-on-dark {
  background: var(--color-white-surface);
  border: 1px solid var(--color-white-border);
  border-radius: var(--radius-card);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition:
    transform 0.3s var(--ease-out),
    background 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out);
}
/* Two selectors so the hover works both for plain cards and for
   `.fade-up.soft` cards. The 3-class form is needed because
   `.fade-up.soft.in` (0,2,0) would otherwise pin the transform after
   scroll-in. See memory: hover-on-fade-up-soft-cards. */
.card-on-dark:hover,
.card-on-dark.fade-up.soft:hover {
  background: var(--color-white-surface-hi);
  border-color: var(--color-white-border-hi);
  transform: translateY(-4px);
}

/* ── Dark navy section variant ────────────────────────────────────── */
/* Apply `.is-dark` (with `.surface-grid-dark` for the grid overlay) to
   any `.svc-section` that should sit on the brand navy band — used by
   the home-health "Why the client" intro on the homepage and service
   pages, the testing-options 3-up grid, the closing CTA, etc. */
.svc-section.is-dark {
  background-color: var(--color-brand-primary-950);
  background-image: var(--gradient-brand-dark);   /* Clean two-stop sweep — no overlays that introduce off-family tints. */
  color: #fff;
  --grid-opacity: 0.6;
}
.svc-section.is-dark .container { position: relative; }
.svc-section.is-dark .section-heading h1, .svc-section.is-dark .section-heading h2 { color: #fff; text-wrap: balance; }
.svc-section.is-dark .section-heading .t-lead{ color: var(--color-white-text-lead); }
.health-grid {
  display: grid;
  grid-template-columns: 1fr 1.35fr;
  gap: 4rem;
  align-items: start;
}
.health-intro h2 {
  text-wrap: balance;
  margin-bottom: 1.25rem;
  color: #fff;
}
.health-intro .t-lead {
  max-width: 42ch;
  color: var(--color-white-text-lead);
}
.health-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
/* `.health-card` inherits chrome from `.card-on-dark`. */
.health-card {
  display: flex;
  gap: 1.1rem;
  align-items: flex-start;
  padding: 1.6rem 1.5rem;
}
.health-icon {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  border-radius: var(--radius-md);
  background: rgba(150,192,225,0.22);
  color: var(--color-brand-primary-300);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.health-icon svg { width: 22px; height: 22px; }
.health-card h3 {
  font-size: 1.0625rem;
  letter-spacing: -0.015em;
  margin: 0 0 0.35rem;
  line-height: 1.25;
  color: #fff;
}
.health-card p {
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--color-white-text-muted);
  margin: 0;
}
/* Left column pins while the benefit cards scroll (post-PMM improvement from the
   NoMoldWNC / PAR-Utah builds). align-self:start is required — a stretched grid item
   is as tall as its track, which makes position:sticky a no-op. */
@media (min-width: 1101px) { .health-intro { align-self: start; position: sticky; top: 7rem; } }
@media (max-width: 1100px) { .health-grid { grid-template-columns: 1fr; } }
@media (max-width: 640px)  { .health-cards { grid-template-columns: 1fr; } }

/* ── "The Evidence" section · study cards ────────────────────────── */
/* Shared by index.html and mold-remediation.html. */
.studies {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
}
.study {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  padding: 2.25rem;
  display: flex; flex-direction: column; gap: 1rem;
  position: relative;
  overflow: hidden;
  transition: all 0.4s ease;
}
.study:hover {
  border-color: var(--color-border-brand-30);
  box-shadow: var(--shadow-card);
  transform: translateY(-4px);
}
.study::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 3px;
  background-image: var(--gradient-brand);
}
/* Full-bleed photo header — pulls against the card padding so the image
   meets the card edges; the card's overflow:hidden rounds the corners. */
.study-photo {
  margin: -2.25rem -2.25rem 0.5rem;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  position: relative;
}
.study-photo img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(15%);
  transition: transform 0.8s var(--ease-out), filter 0.6s var(--ease-out);
}
.study:hover .study-photo img {
  transform: scale(1.04);
  filter: grayscale(0%);
}
.study-photo .doc-tag {
  position: absolute;
  bottom: 0.85rem; left: 0.85rem;
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius-pill);
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-brand-primary-700);
  box-shadow: var(--shadow-subtle);
}
.study-photo .doc-tag svg { width: 12px; height: 12px; }
.study .meta-row {
  display: flex; align-items: center; gap: 0.75rem;
  font-size: 0.75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--color-text-secondary);
}
.study .meta-row .journal {
  color: var(--color-brand-primary-700);
  padding: 0.35rem 0.7rem;
  background: var(--color-surface-subtle);
  border-radius: var(--radius-pill);
}
.study h3 {
  font-size: 1.1875rem;
  letter-spacing: -0.02em;
  line-height: 1.3;
  text-wrap: balance;
}
.study .finding {
  font-size: 0.9375rem;
  color: var(--color-text-body);
  line-height: 1.6;
  flex: 1;
}
.study .finding-stat {
  display: flex; align-items: baseline; gap: 0.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-border-subtle);
  margin-top: 0.5rem;
}
.study .finding-stat .num {
  font-size: 1.75rem;
  font-weight: 900;
  letter-spacing: -0.03em;
  background-image: var(--gradient-brand-deep);
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
.study .finding-stat .lbl {
  font-size: 0.75rem;
  color: var(--color-text-secondary);
  font-weight: 600;
}
.study .source {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-brand-primary-700);
}
.study .source svg { width: 12px; height: 12px; }
.evidence-coda {
  text-align: center;
  margin-top: 2.5rem;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-text-secondary);
  font-style: italic;
  letter-spacing: -0.005em;
}
@media (max-width: 1100px) { .studies { grid-template-columns: 1fr 1fr; } }
@media (max-width: 720px)  { .studies { grid-template-columns: 1fr; } }

/* ── FAQ accordion ────────────────────────────────────────────────── */
/* Shared by faqs.html and the FAQ section on mold-remediation.html. */
.faq-list {
  max-width: 820px;
  margin: 0 auto;
  display: flex; flex-direction: column;
  gap: 0.75rem;
}
.faq-item {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  overflow: hidden;
  transition: border-color 0.3s var(--ease-brand), box-shadow 0.3s var(--ease-brand);
}
.faq-item:hover { border-color: var(--color-border-default); }
.faq-q {
  width: 100%;
  background: none; border: 0; cursor: pointer;
  text-align: left;
  font: inherit;
  padding: 1.4rem 1.6rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  font-weight: 700;
  font-size: 1.0625rem;
  color: var(--color-text-primary);
  letter-spacing: -0.01em;
}
.faq-q .faq-icon {
  width: 20px; height: 20px;
  flex: 0 0 auto;
  color: var(--color-brand-primary-700);
  transition: transform 0.3s var(--ease-brand);
}
.faq-q.open .faq-icon { transform: rotate(180deg); }
.faq-a {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s var(--ease-brand);
}
.faq-a p {
  padding: 0 1.6rem 1.4rem;
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--color-text-body);
}

/* ── Heading-after-eyebrow rhythm ─────────────────────────────────── */
/* Standard 1.5rem gap between an eyebrow-pill and the heading that
   immediately follows it. Replaces inline margin-top declarations
   scattered across pages. */
.eyebrow-pill + h1,
.eyebrow-pill + h2,
.eyebrow-pill + h3 { margin-top: 1.5rem; }

/* ── Inline body links ────────────────────────────────────────────── */
/* Long-form prose links (about, science, faqs) need underline
   affordance. Apply `.prose a` or use the class directly. */
.prose a,
a.body-link {
  border-bottom: 1px dotted var(--color-border-brand-30);
  transition: color 0.2s ease, border-color 0.2s ease;
}
.prose a:hover,
a.body-link:hover {
  color: var(--color-brand-primary-700);
  border-bottom-color: var(--color-brand-primary);
}

/* ── Footer phone CTA ─────────────────────────────────────────────── */
/* The footer phone number is the loudest legal/contact mark in the
   footer. Replaces an inline style="" repeated across every page. */
.footer .footer-static li.phone {
  color: #fff;
  font-weight: 800;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
}
.footer .footer-static li.book-cta { margin-top: 1rem; }
.footer .footer-static li.book-cta .btn { margin-top: 0.5rem; }

/* ── Form input focus ring on dark sections ───────────────────────── */
.form-on-dark input,
.form-on-dark textarea {
  background: var(--color-white-surface-hi);
  border: 1px solid var(--color-white-border);
  border-radius: var(--radius-md);
  padding: 0.95rem 1.1rem;
  color: #fff;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 500;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.form-on-dark input::placeholder,
.form-on-dark textarea::placeholder { color: rgba(255,255,255,0.45); }
.form-on-dark input:focus,
.form-on-dark textarea:focus {
  outline: none;
  border-color: rgba(255,255,255,0.4);
  box-shadow: 0 0 0 3px rgba(255,255,255,0.1);
}

/* ── Hamburger toggle (mobile/tablet nav) ─────────────────────────── */
/* The button is rendered on every nav by site.js at runtime. It sits in
   `.nav-inner` after the desktop CTAs and is hidden on desktop. */
.nav-toggle {
  display: none;
  background: none;
  border: 0;
  cursor: pointer;
  width: 44px; height: 44px;
  padding: 0;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 5px;
  color: var(--color-text-primary);
  transition: color 0.2s ease;
}
.nav-toggle span {
  display: block;
  width: 26px; height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 0.3s var(--ease-brand), opacity 0.2s ease;
}
.nav-wrap.menu-open .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-wrap.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
.nav-wrap.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* On the homepage overlay variant, the toggle sits over the dark video
   hero — flip it to white. Goes back to navy once the nav scrolls solid. */
.nav-wrap.nav-overlay .nav-toggle { color: #fff; }
.nav-wrap.nav-overlay.is-solid .nav-toggle { color: var(--color-text-primary); }
.nav-wrap.nav-overlay.menu-open .nav-toggle { color: var(--color-text-primary); }

/* ── Mobile menu panel (≤900px) ───────────────────────────────────── */
/* Slides down from below the navbar. Content is generated by site.js
   from the desktop nav structure so this CSS only owns presentation. */
.nav-mobile-panel {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: var(--color-surface-app);
  padding: calc(var(--nav-height, 72px) + 1.5rem) 1.5rem 2rem;
  overflow-y: auto;
  /* 16 Sep 2026 (M15): column flex so the CTA block below can pin itself to
     the bottom with `margin-top: auto` — the link list is long enough on
     every page that the panel used to end with both CTAs 200px+ off screen
     and no cue that it scrolled. */
  display: flex;
  flex-direction: column;
  transform: translateY(-100%);
  visibility: hidden;
  transition: transform 0.4s var(--ease-brand), visibility 0s linear 0.4s;
  z-index: 5;  /* below the navbar inside .nav-wrap so the toggle stays clickable */
  -webkit-overflow-scrolling: touch;
}
.nav-wrap.menu-open .nav-mobile-panel {
  transform: translateY(0);
  visibility: visible;
  transition: transform 0.4s var(--ease-brand), visibility 0s;
}
.nav-mobile-panel ul {
  list-style: none;
  padding: 0; margin: 0;
}
.nav-mobile-panel .nav-mobile-section-title {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-weight: 800;
  color: var(--color-text-secondary);
  padding: 1.25rem 0.5rem 0.35rem;
}
.nav-mobile-panel li a {
  display: block;
  padding: 0.95rem 0.5rem;
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--color-text-primary);
  border-bottom: 1px solid var(--color-border-subtle);
  letter-spacing: -0.01em;
  transition: color 0.2s ease;
}
.nav-mobile-panel li a:hover {
  color: var(--color-brand-primary-700);
}
/* Dropdown children are indented and set lighter so they read as members of
   the section label above them. Without this every row is identical, so a
   standalone top-level link that happens to follow a section ("Reviews"
   after Resources) looks like one of its children. */
.nav-mobile-panel .nav-mobile-sub a {
  padding-left: 1.5rem;
  font-weight: 600;
  color: var(--color-text-body);
}
/* A top-level link directly after a section's children starts a new group. */
.nav-mobile-panel .nav-mobile-sub + .nav-mobile-top a {
  margin-top: 0.75rem;
}
.nav-mobile-panel .nav-mobile-cta {
  /* 16 Sep 2026 (M15): pinned to the panel's bottom edge — see the flex
     column + sticky note on .nav-mobile-panel above. `margin-top: auto`
     keeps it flush with the last link on a short page and lets the link
     list (not the CTA block) be what scrolls behind it on a long one. */
  margin-top: auto;
  position: sticky;
  bottom: 0;
  background: var(--color-surface-app);
  padding-top: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.nav-mobile-panel .nav-mobile-cta .btn {
  width: 100%;
  justify-content: center;
}
/* The mobile panel is a light surface, but it lives inside .nav-wrap —
   which on the homepage carries .nav-overlay, whose rule flips the phone
   button to white-on-transparent (invisible here). Force the dark/default
   voice for the cloned phone CTA regardless of the overlay state. */
.nav-wrap .nav-mobile-panel .nav-mobile-cta .btn.nav-phone-btn,
.nav-wrap.nav-overlay .nav-mobile-panel .nav-mobile-cta .btn.nav-phone-btn {
  background: transparent;
  color: var(--color-brand-primary-700);
  border-color: var(--color-border-default);
}

/* Nav height on tablet/mobile — compressed so the mobile menu panel
   has more breathing room. The CSS custom prop is read by the panel
   to offset its top padding. */
@media (max-width: 1180px) {
  .nav-wrap { --nav-height: 72px; }
  .nav-inner { height: 72px; }
  .nav-logo img { height: 48px; }
  .nav-inner nav,
  .nav-cta { display: none; }
  .nav-toggle {
    display: inline-flex;
    position: relative;
    z-index: 11;  /* sits above the panel even within navbar's stack */
  }
}

/* The overlay nav on the homepage stays transparent over the video on
   mobile by default; once the user opens the menu, we promote it to
   the same light-themed glass treatment used post-scroll so links stay
   readable when the panel is open behind it. */
@media (max-width: 1180px) {
  .nav-wrap.nav-overlay.menu-open .navbar {
    background: var(--color-glass-bg);
    border-bottom-color: var(--color-border-subtle);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
  }
  .nav-wrap.nav-overlay.menu-open .nav-logo .logo-light { display: none; }
  .nav-wrap.nav-overlay.menu-open .nav-logo .logo-dark  { display: block; }
}

/* ── Pulsating call FAB (mobile only) ─────────────────────────────── */
/* Rendered by site.js so it ships once and applies to every page. */

/* Height the FAB occludes in the bottom-right corner: 60px button +
   1.1rem offset + 1.2rem breathing room, PLUS the device's own bottom inset.
   Zero above the FAB's breakpoint so desktop layout is untouched. Consumed by
   the page-end clearance rules below; change it here and every page follows.
   safe-area-inset-bottom (15 Sep 2026): the FAB is offset from the VIEWPORT
   bottom, and on a phone with a home indicator or gesture bar the last ~34px
   of that viewport is the system's. Without the inset the button sits in the
   indicator's swipe zone and the reserved strip is that much too short — so
   both the offset and the reservation carry it. Falls back to 0px everywhere
   the environment variable does not exist, which is the old behaviour. */
:root { --call-fab-clearance: 0px; }
/* 16 Sep 2026: widened 640->1180 so the FAB's clearance tracks its own visibility band (M2). */
@media (max-width: 1180px) {
  :root { --call-fab-clearance: calc(6rem + env(safe-area-inset-bottom, 0px)); }
}

/* Page-end clearance (QA 8 Sep 2026 — D2).
   The FAB is position:fixed, so it sits over whatever happens to be in
   the bottom-right corner. Mid-page that is harmless (scroll and the
   control comes clear); at the very END of the document it is
   PERMANENT — the page cannot scroll any further, so the last
   interactive controls stay buried under the button forever. Reserving
   the FAB's height at the foot of every page guarantees a reachable
   scroll offset for every element on it.
   .footer covers the 14 standard pages; .err-foot is the 404's
   self-contained footer. The paid landing pages carry .lp-sticky-bar
   instead and site.js skips the FAB there entirely. */
/* 16 Sep 2026: widened 640->1180 to match the FAB's own breakpoint (M2). */
@media (max-width: 1180px) {
  .footer   { padding-bottom: calc(2rem + var(--call-fab-clearance)); }
  .err-foot { padding-bottom: calc(2rem + var(--call-fab-clearance)); }
}

/* Second guard: while a form control has focus the FAB steps out of the
   way, so it can never sit on top of the field the visitor is filling in
   (the /contact Zip input was the reported case). Browsers without :has()
   drop the rule and still get the clearance above. */
/* 16 Sep 2026: widened 640->1180 to match the FAB's own breakpoint (M2). */
@media (max-width: 1180px) {
  body:has(input:focus, select:focus, textarea:focus) .call-fab { display: none; }
}

.call-fab { display: none; }
body.menu-open-lock .call-fab { display: none !important; }
/* 16 Sep 2026: widened 640->1180 so the FAB fills the dead zone where the header CTA hides (M2). */
@media (max-width: 1180px) {
  .call-fab {
    position: fixed;
    right: 1.1rem;
    bottom: calc(1.1rem + env(safe-area-inset-bottom, 0px));
    z-index: 200;
    width: 60px; height: 60px;
    border-radius: 50%;
    background-image: var(--gradient-accent);
    /* Dark ink so the phone icon reads clearly on the light gradient. */
    color: var(--color-brand-secondary);
    /* Faint dark outline + a soft dark drop shadow to lift it off the page. */
    border: 1px solid rgba(10,37,65,0.18);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 14px 28px -10px rgba(150,192,225,0.55), 0 6px 16px -4px rgba(10,37,65,0.30), 0 2px 6px rgba(0,0,0,0.12);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }
  .call-fab:hover,
  .call-fab:focus-visible {
    transform: scale(1.06);
    outline: none;
  }
  .call-fab svg {
    width: 26px; height: 26px;
    position: relative;
    z-index: 2;
  }
  .call-fab::before,
  .call-fab::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--color-brand-secondary);
    z-index: 0;
    animation: pm-fab-pulse 2.4s cubic-bezier(0, 0, 0.2, 1) infinite;
  }
  .call-fab::after { animation-delay: 1.2s; }
}
@keyframes pm-fab-pulse {
  0%   { transform: scale(0.97); opacity: 0.38; }
  80%  { transform: scale(1.6);  opacity: 0;    }
  100% { transform: scale(1.6);  opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
  .call-fab::before, .call-fab::after { animation: none; opacity: 0; }
}

/* ── Footer responsive ────────────────────────────────────────────── */
/* 16 Sep 2026 (D3/D4): the five-column track held until 900px, so from 1100
   down to 950 every column was too narrow — six of thirteen links wrapped,
   the "Areas Served" heading wrapped when its siblings didn't (breaking the
   row of column heads), and the brand paragraph ran seven lines. An
   intermediate three-column step keeps every column above ~180px until the
   real two-column collapse at 900. (`.brand-marks` itself is now `nowrap`
   unconditionally — see the D4 note beside its base rule further down.) */
@media (max-width: 1180px) {
  /* Two by two, not three: with the brand block spanning the row, three
     columns left "Get in touch" stranded alone on a second row (supervisor
     check, 16 Sep 2026). */
  .footer-grid {
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem 2rem;
  }
  .footer-grid .brand { grid-column: 1 / -1; }
}
@media (max-width: 900px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
    gap: 2.5rem 2rem;
  }
  .footer-grid .brand { grid-column: 1 / -1; }
  .footer-grid .brand p { max-width: none; }
  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }
  .footer-bottom .legal { flex-wrap: wrap; gap: 1rem 1.5rem; }
}
@media (max-width: 640px) {
  /* Bottom pad keeps the call FAB's clearance — see --call-fab-clearance. */
  .footer { padding: 4rem 0 calc(2rem + var(--call-fab-clearance)); }
  .footer-grid {
    grid-template-columns: 1fr;
    gap: 2rem;
    padding-bottom: 2.25rem;
  }
  .footer-grid .brand img { height: 72px; }
}

/* ── Compare table on mobile ──────────────────────────────────────── */
/* 5-column grid is too wide for phones. Wrap in horizontal scroll so
   users can still see every column by sliding sideways within the
   table rather than losing data via stacking. */
@media (max-width: 900px) {
  .compare {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .compare-row { min-width: 640px; }
  .compare-row > div { padding: 1rem 1.25rem; }
}

/* ── Section padding compression on mobile ────────────────────────── */
/* The standard 7rem section padding crushes vertical rhythm on phones. */
@media (max-width: 640px) {
  section.svc-section { padding: 4rem 0; }
}

/* ── Utility ──────────────────────────────────────────────────────── */
.text-center { text-align: center; }
.muted { color: var(--color-text-secondary); }
.divider {
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--color-border-default), transparent);
  margin: 0;
}

/* Lead-form success message (injected by site.js after webhook POST) */
.form-sent {
  padding: 1.25rem 1.5rem;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.04);
  font-size: 1.05rem;
  line-height: 1.55;
}
.form-sent strong { display: inline-block; font-size: 1.15rem; margin-bottom: 0.15rem; }
.form-sent--dark { background: rgba(255, 255, 255, 0.08); color: #fff; }
.form-sent--dark a { color: #fff; text-decoration: underline; }

/* Required-field marker on any form (contact + LPs). lp.css scopes its own copy to
   .lp-field, which never matched the contact form — this is the general rule. */
.req { color: var(--color-brand-primary-700); }

/* ── Meet the Team (PAA pattern, no photo/awards — those are client-specific) ── */
.lp-team-copy { display: grid; grid-template-columns: 1.1fr 1fr; gap: 3.5rem; margin-top: 2.5rem; }
.lp-team-trust h3, .lp-team-why h3 { font-size: 1.35rem; margin: 0 0 1rem; }
.lp-team-trust p { color: var(--color-text-secondary); line-height: 1.65; margin: 0 0 1rem; }
.lp-team-points { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1.1rem; }
.lp-team-points li { display: flex; gap: 0.85rem; align-items: flex-start; line-height: 1.55; color: var(--color-text-secondary); }
.lp-team-points b { color: var(--color-text-primary); }
.lp-team-points .pt-ic { flex-shrink: 0; display: inline-flex; width: 2.4rem; height: 2.4rem; border-radius: 0.8rem; background: var(--color-surface-subtle); color: var(--color-brand-primary-700); align-items: center; justify-content: center; }
.lp-team-points .pt-ic .ic { width: 1.2rem; height: 1.2rem; }
@media (max-width: 900px) { .lp-team-copy { grid-template-columns: 1fr; gap: 2rem; } }

/* ── Eyebrow pill + geo locator (pill stays short; territory sits beside it) ── */
.hero-eyebrow-row { display: flex; align-items: center; gap: 0.9rem 1.1rem; flex-wrap: wrap; }
div.hero-eyebrow-row > .eyebrow-pill { margin-bottom: 0; }
.svc-hero-stack .hero-eyebrow-row { justify-content: center; margin-bottom: 2rem; }
.hero-copy .hero-eyebrow-row { margin-bottom: 1.75rem; }
.hero-eyebrow-row + h1, .hero-eyebrow-row + h2 { margin-top: 1.5rem; }
.hero-geo {
  display: inline-flex; align-items: center; gap: 0.45rem;
  font-size: 14px; font-weight: 600; letter-spacing: 0.01em;
  color: var(--color-text-secondary);
}
.hero-geo .ic { width: 15px; height: 15px; flex: 0 0 auto; opacity: 0.85; }
.hero-geo.on-dark { color: rgba(255, 255, 255, 0.78); }


/* ── Audit fixes, 11 September 2026 (PureAire; ported to _template) ────── */
/* Nav between 900 and 1120px: the logo squashed (fixed height + max-width:100%)
   and "Reviews" collided with the phone pill. The squeeze rule that used to sit
   here was superseded on 15 September 2026 — it also matched at phone widths and
   won over the mobile nav height. See the nav block at the foot of this file. */
.nav-logo img { object-fit: contain; }
/* Card rows follow the card count: a fifth card centres on its own row instead
   of sitting beside three empty slots. */
.svc-overview-grid, .why-grid, .studies { display: flex; flex-wrap: wrap; justify-content: center; }
.svc-overview-grid > * { flex: 0 1 calc((100% - 3 * 1.25rem) / 4); }
.why-grid > * { flex: 0 1 calc((100% - 3 * 1.25rem) / 4); }
.studies > * { flex: 0 1 calc((100% - 2 * 1.25rem) / 3); }
@media (max-width: 1100px) {
  .svc-overview-grid > * { flex-basis: calc((100% - 1.25rem) / 2); }
  .why-grid > * { flex-basis: calc((100% - 1.25rem) / 2); }
  .studies > * { flex-basis: calc((100% - 1.25rem) / 2); }
}
@media (max-width: 640px) {
  .svc-overview-grid > *, .why-grid > *, .studies > * { flex-basis: 100%; }
}
/* Evidence tags wrap as a row, never inside a pill. */
.study .meta-row { flex-wrap: wrap; }
.study .meta-row .journal { white-space: nowrap; }
/* Headline figures never break on a hyphen. */
.study .finding-stat .num { white-space: nowrap; }
.study .finding-stat .lbl { font-size: 0.8125rem; }
/* Tap targets: footer lists, breadcrumbs and evidence links. */
.footer a { display: inline-block; padding: 0.45rem 0; } /* 16 Sep 2026 (D16/M17): 0.35rem left the link/legal lists a hair under a 44px tap target */
.footer a.btn { display: inline-flex; }
.hero-breadcrumb a, .svc-hero-breadcrumb a { display: inline-block; padding: 0.55rem 0; }
.study .source { display: inline-flex; align-items: center; padding: 0.55rem 0; font-size: 0.875rem; }
/* Mobile call button: hidden until the visitor has scrolled past the hero, and
   off entirely on pages whose whole body is a form or an accordion. */
/* 16 Sep 2026: widened 640->1180 to match the FAB's own breakpoint (M2). */
@media (max-width: 1180px) {
  .call-fab { opacity: 0; pointer-events: none; transform: translateY(12px); transition: opacity 0.25s ease, transform 0.25s ease; }
  .call-fab.is-visible { opacity: 1; pointer-events: auto; transform: translateY(0); }
}
body.no-fab .call-fab { display: none !important; }
/* Review carousel on phones: keep the arrows, below the track. */
@media (max-width: 640px) {
  .reviews-carousel { padding-bottom: 3.75rem; }
  .reviews-carousel .carousel-arrow { display: inline-flex; top: auto; bottom: 0; transform: none; }
  .reviews-carousel .carousel-arrow.prev { left: calc(50% - 56px); }
  .reviews-carousel .carousel-arrow.next { right: auto; left: calc(50% + 8px); }
}
/* Comparison table on phones: scroll it rather than clip it. */
@media (max-width: 640px) {
  .compare { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .compare-row { min-width: 600px; }
}
/* Visible keyboard focus everywhere. */
a:focus-visible, button:focus-visible, [tabindex="0"]:focus-visible { outline: 3px solid var(--color-brand-primary-500); outline-offset: 3px; }
/* Reduced motion: no pulsing dots. */
@media (prefers-reduced-motion: reduce) { .eyebrow-pill .ring, .eyebrow-pill .dot-wrap .ring, .dot-wrap .ring { animation: none !important; } }


/* ═══════════════════════════════════════════════════════════════════════
   Audit fixes, 15 September 2026 (fresh-eyes design sweep, Pure Maintenance
   of East Texas). Every rule here is a defect fix, not a restyle.
   ═══════════════════════════════════════════════════════════════════════ */

/* 1 · The nav row never squeezes the brand mark and never wraps a link.
   `.nav-logo` was a shrinkable flex item, so between the hamburger breakpoint
   and roughly 1270px the badge lost width instead of the row collapsing —
   measured at 27px wide on a 950px viewport, with "Services" printed over it.
   Pin the logo, hold the top-level links on one line, and step the gap down.
   The hamburger now arrives at 1180px (see the media queries above), which is
   where a six-item nav with long labels genuinely stops fitting. */
.nav-logo { flex: 0 0 auto; }
.nav-links > li > a { white-space: nowrap; }
@media (max-width: 1366px) { .nav-links { gap: 1.6rem; } }
@media (max-width: 1280px) {
  .nav-links { gap: 1.1rem; }
  .nav-links a { font-size: 14.5px; }
  .nav-cta .btn { padding-left: 0.95rem; padding-right: 0.95rem; }
}

/* 2 · The footer's primary button lost its horizontal padding.
   1.11.1's tap-target rule `.footer a { padding: 0.35rem 0 }` (0,1,1)
   out-specifies `.btn` (0,1,0); the follow-up line restored `display` but not
   the padding, so the label sat flush against both pill edges on every page. */
.footer a.btn { padding: 0.85rem 1.5rem; }

/* 3 · `.health-card` on a LIGHT section.
   The card's type colours were written for the dark band and were applied
   unconditionally, so a light-surface use rendered white on #fcfdfd — axe
   measured 1.01:1 and the whole section was invisible. The dark use always
   carries `.card-on-dark`; scope the light variant off that. */
.health-card:not(.card-on-dark) {
  background: var(--color-surface-card);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
}
.health-card:not(.card-on-dark) h3 { color: var(--color-text-primary); }
.health-card:not(.card-on-dark) p  { color: var(--color-text-body); }
.health-card:not(.card-on-dark) .health-icon {
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
}

/* 4 · Visible keyboard focus on form controls.
   1.11.1 gave links and buttons a 3px ring; inputs kept `outline: none` plus an
   8%-alpha halo nobody can see. WCAG 2.2 AA 2.4.7 / 2.4.11. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--color-brand-primary-500);
  outline-offset: 2px;
}
.form-on-dark input:focus,
.form-on-dark textarea:focus {
  outline: 3px solid rgba(255, 255, 255, 0.85);
  outline-offset: 2px;
}

/* 5 · A section heading that ends its section left 4rem of dead space on top of
   the section's own padding — 176px of nothing, and on a heading-only page that
   is the whole first screen. */
.section-heading:last-child { margin-bottom: 0; }

/* 6 · The map's coverage pill sat bottom-left, which is where the Google embed
   puts its Street View thumbnail and its attribution row. Top-right is the only
   corner the embed leaves free. */
.svc-map .map-overlay { top: 1.5rem; right: 1.5rem; bottom: auto; left: auto; }
@media (max-width: 640px) { .svc-map .map-overlay { top: 1rem; right: 1rem; } }

/* 7 · Comfortable tap targets. Every `.btn` measured 42px tall. */
.btn { min-height: 44px; }

/* 8 · A figure never breaks on its own hyphen. 1.11.1 fixed this on the
   evidence cards; the particle chart carries the same pattern. */
.particle-row .size { white-space: nowrap; }

/* 9 · `background-clip: text` clips each line fragment to its own box, so the
   italic overhang on the last word of a wrapped line is painted off — visible
   at 390px as a clipped "your". `clone` gives every fragment the gutter. */
.svc-story-body .story-emphasis {
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  padding-inline: 0.08em;
  margin-inline: -0.08em;
}

/* 10 · The comparison table scrolls sideways on a phone but said nothing about
   it, so three of five columns simply looked missing. A right-edge fade is the
   cue. */
@media (max-width: 900px) {
  .compare {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
  }
}

/* 12 · A mobile-panel row that opens a new tab now shows the same marker the
   desktop nav uses (site.js clones the icon across). */
.nav-mobile-panel li a.is-external { display: flex; align-items: center; gap: 0.45rem; }
.nav-mobile-panel li a.is-external .ic { width: 15px; height: 15px; opacity: 0.6; }

/* ═══════════════════════════════════════════════════════════════════════
   Pure Maintenance of East Texas — client-specific components.
   Everything below is content this licensee has and the template does not:
   the Enhancify financing block, the Trustpilot badge, their "Proudly
   Serving Texas" city list, the footer NAP + TDLR licence line, and the
   square badge logo (their mark is a circular badge, not a wordmark).
   ═══════════════════════════════════════════════════════════════════════ */

/* Nav logo — a near-square badge, not a horizontal wordmark, so it needs
   more of the nav height than a wordmark does to stay legible. A square mark
   also eats width the nav needs for six top-level links, so it steps down once
   before the hamburger takes over at 1180px (audit 15 September 2026). */
.nav-logo img { height: 112px; }
@media (min-width: 1181px) and (max-width: 1280px) { .nav-logo img { height: 88px; } .nav-inner { height: 116px; } }
@media (max-width: 1180px) { .nav-logo img { height: 60px; } }

/* ── Financing block (Enhancify) ─────────────────────────────────────── */
.fin-block {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 3rem;
  align-items: center;
  background: var(--color-surface-card);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 3.5rem;
}
.fin-copy h2 { margin: 1rem 0 1.5rem; }
.fin-bullets { list-style: none; margin: 0 0 2rem; padding: 0; display: grid; gap: 0.85rem; }
.fin-bullets li {
  display: flex; align-items: flex-start; gap: 0.75rem;
  color: var(--color-text-body); font-size: 1.0625rem; line-height: 1.5;
}
.fin-bullets li .ic { flex: 0 0 auto; margin-top: 0.2rem; color: var(--color-brand-primary-500); }
.fin-partner {
  margin: 1.25rem 0 0; font-size: 0.875rem; font-weight: 600;
  letter-spacing: 0.02em; text-transform: uppercase; color: var(--color-text-tertiary);
}
.fin-stat {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 2.5rem; border-radius: var(--radius-card-lg);
  background: var(--color-brand-primary-100);
  border: 1px solid var(--color-brand-primary-200);
}
.fin-stat .num { font-size: clamp(3rem, 6vw, 4.5rem); font-weight: 800; line-height: 1; color: var(--color-brand-primary-700); }
.fin-stat .lbl { font-size: 1.125rem; font-weight: 700; color: var(--color-text-primary); }
/* slate-500 measures 4.42:1 on this pale-blue tint, not the 4.76:1 it gives on
   white — the tinted surface needs the next rung down (axe, 15 Sep 2026). */
.fin-stat .note { font-size: 0.8125rem; line-height: 1.5; color: var(--color-text-body); }
@media (max-width: 900px) {
  .fin-block { grid-template-columns: 1fr; gap: 2rem; padding: 2rem; border-radius: var(--radius-card-lg); }
  .fin-stat { padding: 1.75rem; border-radius: var(--radius-md); }
}

/* Financing page: the Enhancify calculator is a third-party embed, so it
   gets a plain card to sit in rather than any styling of its own. */
.fin-embed {
  background: var(--color-surface-card);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 2.5rem;
  min-height: 420px;
}
@media (max-width: 900px) { .fin-embed { padding: 1.25rem; border-radius: var(--radius-md); } }

/* ── Trustpilot badge ────────────────────────────────────────────────── */
.trustpilot-badge {
  display: inline-flex; align-items: center; gap: 0.5rem;
  margin-top: 1.25rem; padding: 0.6rem 1.15rem;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-pill);
  background: var(--color-surface-card);
  font-size: 0.9375rem; font-weight: 600;
  color: var(--color-text-primary);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.trustpilot-badge:hover { border-color: var(--color-border-brand-30); box-shadow: var(--shadow-glass); }
.trustpilot-badge .tp-stars { display: inline-flex; gap: 2px; color: var(--color-accent-gold); }
.trustpilot-badge .tp-stars .ic { width: 16px; height: 16px; }

/* ── "Proudly Serving Texas" city list ───────────────────────────────── */
.svc-serving { margin-top: 3rem; text-align: center; }
.svc-serving h3 { margin: 0 0 0.75rem; }
.svc-serving p { color: var(--color-text-secondary); margin: 0 0 1.25rem; }
.city-list-inline {
  list-style: none; margin: 0 0 1.25rem; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem;
  max-width: 62rem; margin-inline: auto; /* 16 Sep 2026 (D14): an unbounded row balances to a lone trailing "And more!" pill at several widths */
}
.city-list-inline li {
  padding: 0.4rem 0.95rem;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-pill);
  background: var(--color-surface-card);
  font-size: 0.9375rem; font-weight: 600; color: var(--color-text-body);
}

/* ── Footer NAP + TDLR licence ───────────────────────────────────────── */
.footer-nap { line-height: 1.55; }
.footer-licence { font-weight: 700; letter-spacing: 0.02em; }

/* ── About: owner quote, guarantee + local-press panels ──────────────── */
.owner-quote {
  margin: 1.5rem 0; padding: 1.25rem 0 1.25rem 1.5rem;
  border-left: 3px solid var(--color-brand-primary-300);
}
.owner-quote blockquote { margin: 0; font-size: 1.0625rem; line-height: 1.65; color: var(--color-text-primary); font-style: italic; }
.about-extra { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-top: 2.5rem; align-items: start; } /* 16 Sep 2026 (D10): default stretch left the shorter panel with 229px of true void at 1366 */
.about-panel {
  background: var(--color-surface-card);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-card-lg);
  box-shadow: var(--shadow-subtle);
  padding: 2.25rem;
}
.about-panel h3 { margin: 0 0 1rem; }
.about-panel p { color: var(--color-text-body); }
.about-licence { font-weight: 700; letter-spacing: 0.02em; color: var(--color-text-tertiary) !important; }
@media (max-width: 900px) { .about-extra { grid-template-columns: 1fr; } .about-panel { padding: 1.5rem; border-radius: var(--radius-md); } }
/* Texas note: TDLR licenses mold assessment and mold remediation separately. */
.tdlr-note {
  margin-top: 1rem; padding: 0.9rem 1.15rem;
  background: var(--color-surface-subtle);
  border-left: 3px solid var(--color-brand-primary-300);
  border-radius: var(--radius-sm);
  font-size: 0.9375rem; line-height: 1.6; color: var(--color-text-body);
}
/* Image credits — CC BY attribution for the two Commons city photographs. */
.img-credit {
  margin: 1rem 0 0; text-align: center;
  font-size: 0.75rem; line-height: 1.6; color: var(--color-text-tertiary);
}
/* The two Commons credits were 15px-tall links. They are inline in a sentence,
   so WCAG 2.2 AA exempts them, but a hit area costs nothing (15 Sep 2026). */
.img-credit a { color: inherit; text-decoration: underline; display: inline-block; padding: 0.3rem 0; }
.about-panel-photo { width: 100%; height: auto; border-radius: var(--radius-md); margin-bottom: 1.25rem; }

/* ── Pillar card pair — promoted from the testing page's <style> block to
   site.css (template 1.11.4) so any page can use it. The East Texas build
   needed it on three pages; a page-local copy silently ships unstyled
   markup because check-styles.mjs only scans styles/*.css. ── */
/* ── "No more guesswork" pillar pair ──────────────────────────── */
.pillar-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  margin-top: 3rem;
}
.pillar-card {
  background: #fff;
  border: 1px solid var(--color-border-subtle);
  border-top: 3px solid transparent;
  border-radius: var(--radius-card-lg);
  padding: 2.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  transition:
    transform 0.3s var(--ease-out),
    box-shadow 0.3s var(--ease-out),
    border-color 0.3s var(--ease-out);
}
.pillar-card.fade-up.soft:hover {
  border-color: transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    var(--gradient-brand) border-box;
  box-shadow: 0 14px 26px -18px rgba(10,37,65,0.28);
  transform: translateY(-4px);
}
.pillar-icon {
  width: 56px; height: 56px;
  border-radius: var(--radius-md);
  background: var(--color-surface-subtle);
  color: var(--color-brand-primary-700);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.35s var(--ease-brand), color 0.35s var(--ease-brand);
}
.pillar-card.fade-up.soft:hover .pillar-icon {
  background: var(--gradient-brand-deep);   /* White-on-light unreadable — switch to deep variant on hover. */
  color: #fff;
}
.pillar-icon.accent { color: var(--color-brand-secondary); }
.pillar-icon svg { width: 28px; height: 28px; }
.pillar-card h3 {
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  line-height: 1.25;
  margin: 0;
  color: var(--color-text-primary);
}
.pillar-card p {
  font-size: 1rem;
  line-height: 1.6;
  color: var(--color-text-body);
  margin: 0;
}
.pillar-cta-row {
  display: flex; flex-wrap: wrap; gap: 0.75rem;
  margin-top: 2.5rem;
  justify-content: center;
}
@media (max-width: 900px) {
  .pillar-grid { grid-template-columns: 1fr; }
}
/* 16 Sep 2026 (M16): stacked CTA pairs kept content-sized widths on a phone,
   so the two buttons in a row never shared a left/right edge. The matching
   `.hero-ctas` / `.svc-closing-buttons` (service-page.css), `.study-cta-row`
   (science.html) and `.lp-cta-row` (lp.css) rules live beside the selectors
   they belong to; `.vapure-cta-row` and `.pillar-cta-row` are defined in
   this file, so their rule sits here. The border-padding compensation below
   is global — `.btn-secondary`/`.btn-ghost` measured 2px taller than the
   unbordered variants beside them in every one of these rows. */
@media (max-width: 640px) {
  .vapure-cta-row, .pillar-cta-row { flex-direction: column; align-items: stretch; }
  .vapure-cta-row .btn, .pillar-cta-row .btn { width: 100%; justify-content: center; }
  .btn-secondary, .btn-ghost { padding-block: calc(0.85rem - 1px); }
  .btn-secondary.btn-lg, .btn-ghost.btn-lg { padding-block: calc(1.05rem - 1px); }
  .btn-secondary.btn-sm, .btn-ghost.btn-sm { padding-block: calc(0.75rem - 1px); }
}
/* Breadcrumb on pages with no hero band (financing, pure treatment, compare). */
.page-breadcrumb { padding-top: 2rem; }
/* Landing-page "real jobs" strip — the client's own site photographs.
   The frame was 260px tall, which cut a 720x960 portrait phone photo to a
   letterbox band: the shutter shot survived it, the window-sill shot kept only
   the view through the screen (audit 15 September 2026). A 4:3 frame with the
   crop pulled down to the sill holds the subject in both. */
.lp-jobs { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-top: 2.5rem; }
.lp-jobs figure { margin: 0; }
.lp-jobs img {
  width: 100%; aspect-ratio: 4 / 3; height: auto;
  object-fit: cover; object-position: center 65%;
  border-radius: var(--radius-md); display: block;
}
.lp-jobs figcaption { margin-top: 0.65rem; font-size: 0.8125rem; line-height: 1.5; color: var(--color-text-tertiary); }
@media (max-width: 700px) { .lp-jobs { grid-template-columns: 1fr; } }

/* The sanitization hero is 1146x1200 in a 4/5 frame, so `cover` trims the
   sides — and the two subjects stand right of centre, which left the desktop
   crop as about 60% empty cabinetry (audit 15 September 2026). */
.hero-media .photo img[src*="hero-sanitization-disinfection"] { object-position: 68% center; }

/* ── "Veteran Owned and Operated" badge ──────────────────────────────────
   The client's own mark, and one of the few things on the old site that said
   something no competitor could copy. It sat in three places there — the hero,
   the "Accredited & Certified" strip, and the footer of every page — and this
   build mirrors all three. It is a photograph of a badge, not an icon, so it
   stays out of the sprite and carries real alt text. */
.vet-badge { display: block; width: auto; flex: 0 0 auto; }

/* Hero: a third item in the CTA row, beside the two buttons rather than as
   another stacked block above them. */
.svc-hero-ctas .vet-badge { height: 104px; margin-left: 0.5rem; }
@media (max-width: 640px) {
  /* Single column on a phone (audit 16 September 2026, M3/M4 — replaces the
     prior two-column grid). Splitting the row into a button column and a
     badge column left the buttons fighting the badge for width until
     "Book Free Consultation" wrapped to two lines at 360px; full-width
     buttons with the badge centred underneath removes the competition
     entirely, so `white-space: normal` and the compensating side padding
     that existed only to cope with the squeeze are gone too. */
  /* The doubled class is deliberate: home.css loads after this file and sets
     `.svc-hero-ctas { display: flex }` at the same specificity, so a single
     class here loses the cascade and the badge drops to a third row below the
     fold. (0,2,0) wins without an !important. */
  .svc-hero-ctas.svc-hero-ctas {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.7rem 0.8rem;
    align-items: center;
  }
  .svc-hero-ctas.svc-hero-ctas .btn {
    width: 100%;
    justify-content: center;
    white-space: nowrap;
  }
  .svc-hero-ctas.svc-hero-ctas .vet-badge {
    height: 64px;
    justify-self: center;
    margin-left: 0;
  }
}

/* Service and About hero: alongside the guarantee line in the cert strip. */
.hero-cert-strip .vet-badge { height: 46px; }

/* Footer: beside the wordmark, on every page that has one. */
.footer-grid .brand .brand-marks {
  display: flex; align-items: center; gap: 1.25rem;
  margin-bottom: 1.5rem;
  /* 16 Sep 2026 (D4): was `wrap` — once the D3 fix (site.css ~1631) keeps the
     brand column from ever narrowing past ~180px, nowrap is safe everywhere
     and stops the wordmark and veteran badge stacking with no alignment
     relationship, as they did at 1100/1024/950 before that fix. */
  flex-wrap: nowrap;
}
.footer-grid .brand .brand-marks img { margin-bottom: 0; }
.footer-grid .brand .vet-badge { height: 76px; }
@media (max-width: 640px) { .footer-grid .brand .vet-badge { height: 64px; } }

/* Landing-page footer: the two marks travel together as one flex item so the
   row still reads as logo | contact | legal. */
.lp-footer-marks { display: flex; align-items: center; gap: 1rem; }
.lp-footer .lp-footer-marks .vet-badge { height: 42px; }

/* ── Accreditations & press band (client badges carried over from the old
   site, 16 September 2026). Seals sit in uniform boxes with a caption that
   names the credential; the captions are the information, so no cards,
   shadows or gradients compete with the seals' own colour. ──────────── */
/* 16 Sep 2026 (M13): a real surface step off the section above, so the band
   reads as its own block on every page instead of two pages (odor-removal,
   sanitization-disinfection) silently sharing a background with the section
   before it. Fixed white rather than continuing the svc-alt alternation,
   which only ever bought a 1.03:1 contrast step. */
.creds { border-top: 1px solid var(--color-border-default); background: var(--color-surface-card); }
.creds .section-heading { margin-bottom: 3rem; }
.creds-grid {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start;
  gap: 2.25rem 1.5rem;
}
.creds-grid li {
  flex: 0 1 160px;
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 0.9rem;
}
.creds-grid li.is-wide { flex-basis: 220px; }
/* 16 Sep 2026 (D2/M9): eleven items in free flex-wrap balanced wherever they
   landed — 5/5/1 at 1181 and 1100 stranded the FEMA photo alone on its own
   row. One shared flex-basis (`.is-wide` included) forces a steady 4/4/3
   for the whole tablet band; 1281 and up keeps the existing 6/5. */
@media (max-width: 1280px) {
  .creds-grid li, .creds-grid li.is-wide { flex: 0 1 calc(25% - 1.5rem); }
}
.creds-grid a {
  display: flex; flex-direction: column; align-items: center; gap: 0.9rem;
  color: inherit; text-decoration: none; border-radius: var(--radius-sm);
}
.creds-grid a:focus-visible { outline: 3px solid var(--color-brand-primary-700); outline-offset: 6px; } /* 16 Sep 2026 (D16): match the site's 3px ring */
.creds-grid img { width: 120px; height: 96px; object-fit: contain; display: block; }
.creds-grid .is-wide img { width: 200px; }
/* 16 Sep 2026 (D7/M8): the only photograph in a row of vector seals reads as
   an intentional tile instead of a stray white rectangle, in the band only. */
.creds-grid .is-photo img { border-radius: 6px; border: 1px solid var(--color-border-default); }
.creds-grid span {
  font-size: 0.8125rem; line-height: 1.4; color: var(--color-text-secondary);
  max-width: 180px; text-wrap: balance;
}
/* 16 Sep 2026 (D15): the underline made BBB's caption the one odd one out in
   a row of eleven identical labels — the existing hover/focus colour shift
   plus an inline external-link glyph (markup) now carry the "this is a
   link" signal instead. */
.creds-grid a span { display: inline-flex; align-items: center; gap: 0.3em; }
.creds-grid a span .ic { width: 12px; height: 12px; }
.creds-grid a:hover span, .creds-grid a:focus-visible span { color: var(--color-text-primary); }

.press {
  margin-top: 3.5rem; padding-top: 2.25rem; border-top: 1px solid var(--color-border-default);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem 2.5rem;
}
.press-label { margin: 0; font-size: 0.875rem; font-weight: 600; color: var(--color-text-tertiary); }
.press-logos {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.75rem 2rem;
}
/* 16 Sep 2026 (D5/M11): capped so seven logos break a clean 4/3 instead of
   stranding USA Today alone at 1100/1024. */
@media (max-width: 1180px) {
  .press-logos { max-width: 640px; margin-inline: auto; }
}
.press-logos a {
  display: block; border-radius: 4px;
  padding: 7px 0; /* 16 Sep 2026 (D16/M17): brings the link to a 44px tap target */
}
.press-logos a:focus-visible { outline: 3px solid var(--color-brand-primary-700); outline-offset: 4px; } /* 16 Sep 2026 (D16): match the site's 3px ring */
.press-logos img {
  width: 132px; height: 40px; object-fit: contain; display: block;
  filter: grayscale(1); opacity: 0.75; transition: opacity 0.2s ease, filter 0.2s ease; /* 16 Sep 2026 (D5/M11): 0.62 -> 0.75, the short wordmarks were vanishing */
}
.press-logos a:hover img, .press-logos a:focus-visible img { filter: none; opacity: 1; }
@media (max-width: 640px) {
  .creds .section-heading { margin-bottom: 2rem; }
  .creds-grid { gap: 1.5rem 0.75rem; }
  .creds-grid li, .creds-grid li.is-wide { flex: 0 0 calc(33.333% - 0.5rem); }
  /* 16 Sep 2026 (D2/M9): the 11-item 3-up row always ends 3/3/3/2 — let the
     trailing two fill the line instead of centring as orphans. */
  .creds-grid li:nth-last-child(-n+2) { flex-basis: calc(50% - 0.375rem); }
  .creds-grid img, .creds-grid .is-wide img { width: 100%; height: 64px; }
  .creds-grid span { font-size: 0.75rem; max-width: none; }
  .press { margin-top: 2rem; padding-top: 2rem; gap: 0.75rem 1.5rem; } /* 16 Sep 2026 (M20): one rhythm scale with the heading above */
  .press-logos { gap: 0.5rem 0.75rem; }
  /* 16 Sep 2026 (D5/M11): taller box so the short wordmarks (MarketWatch,
     Bloomberg) survive; still breaks 4/3. */
  .press-logos img { width: 80px; height: 32px; }
}
@media (prefers-reduced-motion: reduce) { .press-logos img { transition: none; } }

/* Footer: the same seals as a quiet row between the link grid and the legal
   line, on every page that has the standard footer. NORMI's navy wordmark
   would vanish on the navy footer, so it alone is knocked back to white. */
.footer-creds {
  list-style: none; margin: 0 0 2rem; padding: 2rem 0 0;
  border-top: 1px solid rgba(255,255,255,0.12);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1rem 1.75rem;
}
.footer-creds li { display: block; }
.footer-creds a {
  display: block; border-radius: 4px;
  padding: 7px 0; /* 16 Sep 2026 (D16/M17): brings the link to a 44px tap target */
}
.footer-creds a:focus-visible { outline: 3px solid #fff; outline-offset: 4px; } /* 16 Sep 2026 (D16): match the site's 3px ring */
.footer-creds img { width: 88px; height: 52px; object-fit: contain; display: block; }
/* 16 Sep 2026 (D12/M7): Gephardt's near-black ink is ~1.5:1 against the navy
   footer — tagging its `<li>` with the class already saving NORMI here
   saves it too, no new rule needed. */
.footer-creds .is-mono img { filter: brightness(0) invert(1); opacity: 0.85; }
/* 16 Sep 2026 (D7/M8): matches the band's photo-tile treatment; no border
   needed on the navy surface, the radius alone is enough. */
.footer-creds .is-photo img { border-radius: 6px; }
/* 16 Sep 2026 (supervisor check): between 641 and 1180 the ten seals broke
   9/1 (1100-1181), 7/3 (950) and 6/4 (768). Capped so they break 5/5. */
@media (max-width: 1280px) {
  /* Padding, not max-width, so the hairline above the row still spans the
     footer while the seals wrap inside a 560px lane. */
  /* `.footer ul { padding: 0 }` outranks a lone `.footer-creds`, hence the extra ancestor. */
  .footer .footer-creds { padding-left: calc((100% - 560px) / 2); padding-right: calc((100% - 560px) / 2); }
}
/* 641-760: the container is too narrow for four 132px logos, so the row
   fell to 3/3/1. Slightly smaller boxes keep the 4/3 break. */
@media (max-width: 760px) {
  .press-logos img { width: 112px; height: 36px; }
}
@media (max-width: 640px) {
  .footer-creds { gap: 0.75rem 1rem; }
  /* 16 Sep 2026 (D2/M9): 10 items at the old 64px box broke 4/4/2; sized to
     break 5/5 instead so the tail is a full row, not two stragglers. */
  .footer-creds img { width: 56px; height: 36px; }
}

/* 16 Sep 2026 (supervisor check): on 360px phones the press row broke 3/3/1
   and the footer seals 4/4/2. Tighter boxes give 4/3 and 5/5. */
@media (max-width: 400px) {
  .press-logos { gap: 0.5rem 0.5rem; }
  .press-logos img { width: 72px; height: 30px; }
  .footer-creds { gap: 0.75rem 0.75rem; }
  .footer-creds img { width: 52px; height: 34px; }
}
