/* =============================================================================
   No Clog Today — styles.css
   Vanilla CSS, mobile-first, no build step. Palette derived from nctplumbing.com
   (NCT green) so the new brand reads as NCT family. Every brand value is a CSS
   custom property below — re-skin the whole site from :root.
   ============================================================================= */

/* ---- Self-hosted Mulish (matches NCT's typeface) ---- */
@font-face {
  font-family: 'Mulish';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url('/fonts/mulish/mulish-latin.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: 'Mulish';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url('/fonts/mulish/mulish-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- Design tokens ---- */
:root {
  /* Brand — NCT green family */
  --brand:        #108B44;
  --brand-dark:   #0A6531;
  --brand-darker: #00461F;
  --brand-bright: #1BA354;
  --brand-tint:   #E8F3EC;
  --brand-tint-2: #D6E9DC;

  /* CTA — all-green per NCT palette. A deeper green than --brand so call
     buttons carry white text at AA contrast on light sections; on the dark-green
     hero/CTA bands the primary button flips to white-fill (see overrides below). */
  --cta:        #0E7C3C;
  --cta-dark:   #0A5F2D;

  /* Accents — gold + red, sampled to match the client logo. Green stays the baseline;
     these are ACCENTS and must never read as co-equal with it.
     ⚠ CHRISTMAS GUARD (binding):
       1. Red is EMERGENCY/URGENT ONLY. It never becomes a general CTA or decoration.
       2. Red only ever sits on INK or WHITE. Never put red on a green surface, and
          never place a red fill directly beside a green fill of similar size.
       3. Gold is the buffer between them — green/gold/cream reads heritage trade,
          green/red reads holiday. When in doubt, reach for gold, not red. */
  --gold:        #F1BD00;   /* logo gold — accents on dark surfaces */
  --gold-dark:   #715C11;   /* logo's deep gold — text-safe on light */
  --gold-tint:   #FDF4DA;   /* soft gold surface (occasional panels) */
  --gold-line:   #EBD9A6;   /* gold border on light */
  --red-deep:    #250000;   /* EMERGENCY PANEL SURFACE — near-black red */
  --red-deep-2:  #160000;   /* gradient end for the emergency panel */
  --red:         #C40000;   /* glove's dominant red — accents/rules. Pure hue, no pink */
  --red-dark:    #8C0000;   /* deeper red — hover */
  --red-bright:  #E00000;   /* bright pure red — CTA fill (white text 5.0:1) */
  --red-light:   #FF1F14;   /* vivid pure red — small TEXT on the deep panel (5.0:1) */
  --red-tint:    #FBEAE8;   /* soft red surface (emergency icon tile on white) */

  /* Neutrals */
  --ink:    #1B2A21;
  --text:   #2C3A31;
  --muted:  #5C6B62;
  --line:   #E2E7E3;
  --bg:     #FFFFFF;
  --bg-alt: #F4F7F5;
  --bg-dark:#0C2417;

  /* Type */
  --font: 'Mulish', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  /* Modular type scale — ONE source of truth. Every element sizes from these; no
     ad-hoc font-sizes in component rules. Fluid where it earns it (headings/lead). */
  --fs-xs:   .8rem;      /* eyebrows, labels, fine print */
  --fs-sm:   .93rem;     /* small body, meta, card body */
  --fs-body: 1.0625rem;  /* running text (17px) */
  --fs-md:   1.15rem;    /* large body, sidebar/widget headings (h4) */
  --fs-card: 1.3rem;     /* content card / blog titles (~20.8px) — above the lead */
  /* Lead = a gentle step over body, NOT a headline. Was maxing at 1.45rem (23px),
     which read as outrageously oversized body on multi-line opening paragraphs. */
  --fs-lead: clamp(1.1rem, 1rem + .4vw, 1.2rem);
  --fs-h3:   clamp(1.3rem, 1.18rem + .5vw, 1.55rem);
  --fs-h2:   clamp(1.75rem, 1.28rem + 1.9vw, 2.55rem);
  --fs-h1:   clamp(2.3rem, 1.5rem + 3.4vw, 3.6rem);
  /* Line-heights + tracking, paired to size (tight display → open body) */
  --lh-h1: 1.03; --lh-h2: 1.1; --lh-h3: 1.22; --lh-body: 1.62; --lh-lead: 1.5;
  --ls-h1: -.03em; --ls-h2: -.025em; --ls-h3: -.015em;
  --measure: 68ch;       /* readable line length for body columns */

  /* Layout */
  --container: 1180px;
  --container-narrow: 820px;
  /* Bold trade confidence: main panels are sharp/squared; only small floater
     elements soften slightly. Large buttons stay confident pills (.btn-lg);
     smaller buttons refine to --radius. NOTE: --radius-lg is intentionally
     *smaller* than --radius — "lg" = large surfaces, which read sharpest. */
  --radius:   5px;   /* small floaters: inputs, small buttons, chips, icon tiles */
  --radius-lg:2px;   /* main panels / cards — squared for spine */
  /* NEVER hardcode a radius. Every rounded thing is one of exactly three:
     --radius (floaters/chips/icon tiles), --radius-lg (panels/cards), or 999px
     (large buttons + small pill badges only). Hardcoded values are how the site
     ended up speaking two design languages — an icon chip at 14px next to
     identical chips at 5px, and a footer social circle next to topbar squares.
     Utilities (focus ring, 3px rule caps) are the only exceptions. */
  --shadow:   0 2px 8px rgba(16, 43, 28, .07);
  --shadow-md:0 10px 30px rgba(16, 43, 28, .12);
  /* The rest of the elevation set. These were hardcoded at their use sites until
     2026-09-15, which meant the shadow palette could not be read in one place the
     way the radii and the type scale can. NEVER hardcode a box-shadow: every
     shadow on this site is one of these, or `none`. */
  --shadow-tile:     0 6px 14px rgba(10, 101, 49, .28);  /* 58px brand icon tiles */
  --shadow-tile-red: 0 6px 14px rgba(140, 0, 0, .3);     /* the emergency variant */
  --shadow-header:   0 6px 22px rgba(0, 0, 0, .28);      /* the sticky masthead */
  --shadow-drawer:  -10px 0 30px rgba(0, 0, 0, .15);     /* mobile nav, casts left */
  --ring:            0 0 0 3px rgba(27, 163, 84, .35);   /* form focus */
  /* ONE padding for the dark feature panels. It was three separate clamps
     (.rail-card 1.5-1.9rem, .contact-card 1.6-2.1rem, .guarantee-card
     1.75-2.4rem) computing 30 / 34 / 35px at 1440: three formulas, one role, and
     differences too small to be read as intent. White content cards stay at their
     own flat 1.75rem. */
  --pad-panel: clamp(1.6rem, 3vw, 2rem);
  --header-h: 84px;
  --section-pad: clamp(3rem, 6vw, 5.5rem);

  /* ---------------------------------------------------------------------------
     TEXTURE — the material the surfaces are made of
     ---------------------------------------------------------------------------
     Three tiles, no more. They exist so a tinted band reads as a SURFACE rather
     than a swatch of paint, and so the big green fields have some body. They are
     material, not decoration: nothing here is a pattern the eye should notice on
     its own, and nothing here moves.

     RULES (binding — see "Texture application" near the bottom of this file):
       1. SECTION GROUNDS AND FULL-WIDTH BANDS ONLY. Cards get nothing. A 45deg
          line at card size reads as stripes laid ON the object instead of grain
          IN it, and every panel wearing it turns into a deck of humbugs.
       2. Sections on plain white stay BARE on purpose, so a textured band still
          reads as a change of surface rather than the whole page becoming one
          woven field.
       3. Nothing moves on hover, ever.
       4. Never over the map, a photo, or the logo.

     Drawn as SVG TILES, never as repeating-linear-gradient. A 1px line at 45
     degrees lands on a different subpixel offset in every repeat, so the
     gradient version renders some lines visibly heavier than others (the
     browser antialiases each one differently). A tile is rasterised once and
     repeated, so every line is identical. Allowed by the CSP: img-src carries
     'self' data:, and a CSS background-image data: URI is an img-src fetch. */

  /* The 45deg weave for LIGHT grounds (--bg-alt, --brand-tint), cut in the ink
     green. 6px period: tight enough to read as woven cloth, loose enough not to
     shimmer. .035 is the point where it gives the band a material without
     becoming a visible stripe — measured, not guessed. */
  --tex-hatch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Cpath d='M-1 1 L1 -1 M0 6 L6 0 M5 7 L7 5' stroke='%231B2A21' stroke-opacity='0.035' stroke-width='1'/%3E%3C/svg%3E");

  /* The same weave for DARK grounds (the green bands, the ink strip, the
     footer), cut in white. Slightly stronger than the light one because a light
     line on a saturated green loses more to the eye than an ink line on cream. */
  --tex-hatch-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6'%3E%3Cpath d='M-1 1 L1 -1 M0 6 L6 0 M5 7 L7 5' stroke='%23FFFFFF' stroke-opacity='0.055' stroke-width='1'/%3E%3C/svg%3E");

  /* PAPER GRAIN — fractal noise, the thing that stops a large green gradient
     reading as flat vinyl. Used only where a big uninterrupted field of colour
     would otherwise have no surface at all: the hero and the interior page
     headers. 200px tile with stitchTiles so the repeat seam does not show.
     Applied under `overlay` so it modulates the gradient underneath rather than
     greying it out.

     DESATURATED on purpose (feColorMatrix saturate 0). Raw feTurbulence emits
     COLOUR noise, and colour noise blended over a saturated green field reads as
     rainbow speckle, not as paper. Neutral grain is the only kind that survives
     contact with a brand colour. */
  --tex-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
}

/* ---- Reset / base ---- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 1rem); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; } }
body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Avoid typographic runts/widows in running text (single-word last lines).
     Inherited, so it reaches paragraphs/leads; headings override with balance. */
  text-wrap: pretty;
}
img { max-width: 100%; height: auto; display: block; }
/* Kill runts site-wide: short display paragraphs wrap *balanced* so no line is
   left a lone short word. Long-form article/prose body keeps `pretty` (on body).
   Structural per the typography standard — never by editing copy. */
.hero-tagline, .hero-bridge, .page-header-subtitle, .section-lead, .section-title p,
.lead, .article-lead, .svc-intro, .guarantee-card p, .cta-text, .area-lead,
.svc-callout-body p, .service-card p, .why-item p, .plan-card > p, .plan-note,
.benefits-grid li, .rail-card > p, .rail-emergency, .contact-card-note, .faq-a p,
.footer-brand p, .footer-bridge, .footer-locations address,
.svc-featured-body p, .article-main p, .article-main li, .prose p, .prose li,
.service-lead, .contact-lead { text-wrap: balance; }
a { color: var(--brand-dark); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Links inside running text must be distinguishable without relying on colour
   (WCAG 1.4.1) — underline prose links; nav/buttons/cards opt out by not being in <p>. */
p a:not(.btn), .lead a:not(.btn) { text-decoration: underline; text-underline-offset: .15em; }
/* ...which makes the global `a:hover { text-decoration: underline }` a NO-OP for
   them: they are already underlined. Measured 2026-09-15 across the site, every
   inline link in running text had ZERO hover feedback, 26 of them. Colour plus a
   thicker rule; neither can reflow text the way a weight change would.
   Dark grounds override the colour below (.hero-bridge, .footer-bridge,
   .svc-featured-body), where deepening the green would be invisible. */
p a:not(.btn):hover, .lead a:not(.btn):hover { color: var(--brand-darker); text-decoration-thickness: 2px; }
h1, h2, h3, h4 { font-family: var(--font); color: var(--ink); font-weight: 900; margin: 0 0 .5em; text-wrap: balance; }
h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); letter-spacing: var(--ls-h1); }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); letter-spacing: var(--ls-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); letter-spacing: var(--ls-h3); }
h4 { font-size: var(--fs-md); line-height: var(--lh-h3); letter-spacing: var(--ls-h3); }
p { margin: 0 0 1rem; }
/* Readable measure only where it matters: long-form article/prose body. */
.article-main p, .article-main li, .prose p:not(.article-lead), .prose li { max-width: var(--measure); }
ul, ol { margin: 0 0 1rem; padding-left: 1.25rem; }
strong { font-weight: 800; color: var(--ink); }

.skip-link { position: absolute; left: -999px; top: 0; background: var(--brand-darker); color: #fff; padding: .6rem 1rem; z-index: 200; border-radius: 0 0 var(--radius) 0; }
.skip-link:focus { left: 0; }
/* The only radius on the site that is not one of the three surface tokens, and
   deliberately so: this is the focus RING, a mechanism rather than a surface, and
   it has to round the outline of elements whose own radii differ. Leave it. */
:focus-visible { outline: 3px solid var(--brand-bright); outline-offset: 2px; border-radius: 3px; }

/* ---- Layout helpers ---- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: clamp(1rem, 4vw, 2rem); }
.section { padding-block: var(--section-pad); }
.section--alt { background: var(--bg-alt); }
.section--tint { background: var(--brand-tint); }
.section-title { text-align: center; margin-bottom: 2.5rem; }
/* Readable measure. These sit in the full 1180px container, so unconstrained they ran
   129 characters per line at 1920 (comfortable is 45-75). Centred, because the
   section titles they belong to are centred. */
.section-title p, .section-lead { color: var(--muted); max-width: 62ch; margin-inline: auto; }
.lead { font-size: var(--fs-lead); color: var(--muted); }
/* brand-dark (not brand) so the small uppercase eyebrow clears WCAG AA 4.5:1 on
   white (#108B44 was 4.37) and on --bg-alt (was 4.05); brand-dark is 7.2 / 6.7. */
.eyebrow { display: inline-flex; align-items: center; gap: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; font-size: var(--fs-xs); color: var(--brand-dark); margin-bottom: 1.15rem; }
/* Gold tick marks flanking the eyebrow thread the brand accent through every section
   header, so the boldness of the top carries down the page. Centred titles get both;
   left-aligned ones (featured/emergency) only the trailing side via ::after. */
.section-title .eyebrow::before, .section-title .eyebrow::after { content: ""; width: 1.6rem; height: 2px; background: var(--gold); border-radius: 999px; }

/* ---- Buttons ---- */
/* Buttons: refined (squared) at default/small size; confident pill only when
   large (.btn-lg). Trade-brand solidity, not app-store softness. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: .5rem; min-height: 44px; font-weight: 800; font-size: 1rem; line-height: 1; padding: .85rem 1.5rem; border-radius: var(--radius); border: 2px solid transparent; cursor: pointer; transition: background .15s, color .15s, border-color .15s, transform .1s; text-align: center; white-space: nowrap; }
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn .ico { font-size: .95em; }
.btn-cta { background: var(--cta); color: #fff; border-color: var(--cta); }
.btn-cta:hover { background: var(--cta-dark); border-color: var(--cta-dark); color: #fff; }
/* Emergency panels are deep-red surfaces, so their CTA flips to white — same rule as
   the other dark panels (.hero, .cta-band, .rail-card). */
.svc-emergency .btn-cta, .contact-emergency .btn-cta { background: var(--red-bright); color: #fff; border-color: var(--red-bright); }
.svc-emergency .btn-cta:hover, .contact-emergency .btn-cta:hover { background: #FF1F14; color: #fff; border-color: #FF1F14; }
.btn-outline { background: transparent; color: #fff; border-color: rgba(255,255,255,.6); }
.btn-outline:hover { background: #fff; color: var(--brand-darker); border-color: #fff; }
.btn-outline-dark { background: transparent; color: var(--brand-dark); border-color: var(--brand); }
.btn-outline-dark:hover { background: var(--brand); color: #fff; }
.btn-lg { padding: 1rem 1.85rem; font-size: 1.05rem; border-radius: 999px; }
/* On dark-green bands a green button goes flat — flip the primary button to
   white-fill (the way NCT's buttons sit on white), so it reads at full contrast. */
.hero .btn-cta, .cta-band .btn-cta { background: #fff; color: var(--brand-darker); border-color: #fff; }
.hero .btn-cta:hover, .cta-band .btn-cta:hover { background: var(--brand-tint); color: var(--brand-darker); border-color: var(--brand-tint); }

/* =============================================================================
   Icons — self-hosted SVGs (www/icon), recolored via CSS mask so they take
   currentColor and size with font-size (1em). Replaces the FontAwesome webfont.
   ============================================================================= */
.ico { display: inline-block; width: 1em; height: 1em; flex: none; background-color: currentColor; -webkit-mask: center / contain no-repeat; mask: center / contain no-repeat; vertical-align: -.125em; }
.ico--bolt { -webkit-mask-image: url("/icon/bolt.svg"); mask-image: url("/icon/bolt.svg"); }
.ico--calendar-check { -webkit-mask-image: url("/icon/calendar-check.svg"); mask-image: url("/icon/calendar-check.svg"); }
.ico--calendar-day { -webkit-mask-image: url("/icon/calendar-day.svg"); mask-image: url("/icon/calendar-day.svg"); }
.ico--camera { -webkit-mask-image: url("/icon/camera.svg"); mask-image: url("/icon/camera.svg"); }
.ico--chevron-down { -webkit-mask-image: url("/icon/chevron-down.svg"); mask-image: url("/icon/chevron-down.svg"); }
.ico--circle-check { -webkit-mask-image: url("/icon/circle-check.svg"); mask-image: url("/icon/circle-check.svg"); }
.ico--clipboard-check { -webkit-mask-image: url("/icon/clipboard-check.svg"); mask-image: url("/icon/clipboard-check.svg"); }
.ico--clock { -webkit-mask-image: url("/icon/clock.svg"); mask-image: url("/icon/clock.svg"); }
.ico--envelope { -webkit-mask-image: url("/icon/envelope.svg"); mask-image: url("/icon/envelope.svg"); }
.ico--gauge-high { -webkit-mask-image: url("/icon/gauge-high.svg"); mask-image: url("/icon/gauge-high.svg"); }
.ico--graduation-cap { -webkit-mask-image: url("/icon/graduation-cap.svg"); mask-image: url("/icon/graduation-cap.svg"); }
.ico--hand-holding-heart { -webkit-mask-image: url("/icon/hand-holding-heart.svg"); mask-image: url("/icon/hand-holding-heart.svg"); }
.ico--handshake { -webkit-mask-image: url("/icon/handshake.svg"); mask-image: url("/icon/handshake.svg"); }
.ico--house-chimney { -webkit-mask-image: url("/icon/house-chimney.svg"); mask-image: url("/icon/house-chimney.svg"); }
.ico--house-flood-water { -webkit-mask-image: url("/icon/house-flood-water.svg"); mask-image: url("/icon/house-flood-water.svg"); }
.ico--lightbulb { -webkit-mask-image: url("/icon/lightbulb.svg"); mask-image: url("/icon/lightbulb.svg"); }
.ico--location-dot { -webkit-mask-image: url("/icon/location-dot.svg"); mask-image: url("/icon/location-dot.svg"); }
.ico--people-group { -webkit-mask-image: url("/icon/people-group.svg"); mask-image: url("/icon/people-group.svg"); }
.ico--people-roof { -webkit-mask-image: url("/icon/people-roof.svg"); mask-image: url("/icon/people-roof.svg"); }
.ico--phone { -webkit-mask-image: url("/icon/phone.svg"); mask-image: url("/icon/phone.svg"); }
.ico--screwdriver-wrench { -webkit-mask-image: url("/icon/screwdriver-wrench.svg"); mask-image: url("/icon/screwdriver-wrench.svg"); }
.ico--shield-halved { -webkit-mask-image: url("/icon/shield-halved.svg"); mask-image: url("/icon/shield-halved.svg"); }
.ico--shower { -webkit-mask-image: url("/icon/shower.svg"); mask-image: url("/icon/shower.svg"); }
.ico--sink { -webkit-mask-image: url("/icon/sink.svg"); mask-image: url("/icon/sink.svg"); }
.ico--snowflake { -webkit-mask-image: url("/icon/snowflake.svg"); mask-image: url("/icon/snowflake.svg"); }
.ico--tag { -webkit-mask-image: url("/icon/tag.svg"); mask-image: url("/icon/tag.svg"); }
.ico--toilet { -webkit-mask-image: url("/icon/toilet.svg"); mask-image: url("/icon/toilet.svg"); }
.ico--toolbox { -webkit-mask-image: url("/icon/toolbox.svg"); mask-image: url("/icon/toolbox.svg"); }
.ico--tree { -webkit-mask-image: url("/icon/tree.svg"); mask-image: url("/icon/tree.svg"); }
.ico--triangle-exclamation { -webkit-mask-image: url("/icon/triangle-exclamation.svg"); mask-image: url("/icon/triangle-exclamation.svg"); }
.ico--truck-fast { -webkit-mask-image: url("/icon/truck-fast.svg"); mask-image: url("/icon/truck-fast.svg"); }
.ico--wrench { -webkit-mask-image: url("/icon/wrench.svg"); mask-image: url("/icon/wrench.svg"); }
.ico--bars { -webkit-mask-image: url("/icon/bars.svg"); mask-image: url("/icon/bars.svg"); }
.ico--arrow-right { -webkit-mask-image: url("/icon/arrow-right.svg"); mask-image: url("/icon/arrow-right.svg"); }
/* ONE trailing-link arrow for the whole site. Every "Read the article", "Join
   through NCT", "Visit NCT Plumbing" and service-card CTA used a text &rarr;
   until 2026-09-15; the services band introduced a real SVG arrow and two arrow
   languages on one page is exactly the drift this project keeps getting burned
   by, so the whole class was swept at once. Sized in em so it tracks its label.
   Every one of these sits inside an <a>, so a single hover rule reaches them all,
   including the ones inside whole-card links. */
.lnk-arrow { font-size: .85em; margin-left: .45em; vertical-align: -.05em; transition: transform .15s; }
/* .btn is inline-flex with its own .5rem gap, so the margin would space it twice. */
.btn .lnk-arrow { margin-left: 0; }
a:hover .lnk-arrow { transform: translateX(.25rem); }
@media (prefers-reduced-motion: reduce) { a:hover .lnk-arrow { transform: none; } }

/* =============================================================================
   Secondary nav — slim utility bar above the header (mirrors NCT's top row):
   emergency line, service area, social profiles.
   ============================================================================= */
.topbar { background: var(--ink); color: rgba(255,255,255,.82); font-size: var(--fs-xs); overflow: hidden; }
/* The bar runs edge to edge so the emergency block starts at the viewport edge and its
   TEXT sits at the far left. Painting a bleed behind centred content left a slab of empty
   red beside it. Inset comes from the block's own padding. */
.topbar > .container { max-width: none; padding-inline: 0; }
.topbar-inner { display: flex; align-items: stretch; justify-content: space-between; gap: 1.5rem; min-height: 50px; }
/* Info items group together on the left with a hairline between them, so the bar
   reads as two deliberate halves rather than three items floating apart. */
.topbar-info { display: flex; align-items: stretch; gap: 1.15rem; min-width: 0; }
/* No divider needed: the emergency block is separated by its own red-black surface. */
.topbar-sep { display: none; }
.topbar p { margin: 0; display: inline-flex; align-items: center; gap: .45rem; white-space: nowrap; }
.topbar .ico { color: var(--brand-bright); font-size: .95rem; }
.topbar-label { color: rgba(255,255,255,.62); text-transform: uppercase; letter-spacing: .05em; font-weight: 800; }
/* Emergency owns the far left as its own full-height red-black block, bled to the viewport
   edge. The rest of the bar stays green - this is not an emergency-coloured nav.
   .topbar is overflow:hidden so the bleed cannot create horizontal scroll. */
/* The emergency block must READ RED. It used to be
   `linear-gradient(180deg, var(--red-deep), var(--red-deep-2))`, which measures
   rgb(37,0,0) to rgb(22,0,0): near-black. That is the right surface for the
   emergency PANELS on /services and /contact, because those are cards on a white
   ground, where near-black-red plus a --red-bright top rule reads as danger.

   In this bar it failed, and the failure is specific: --ink is rgb(27,42,33), a
   green-tinted dark. So the block and the bar around it were both effectively
   black, the bar's green was the only hue on the strip, and the whole 24/7 line
   read GREEN with no red in it but a 14px triangle. Reported as a bug, and it
   was one.

   Solid --red-dark, not a gradient: over a 50px strip any falloff deep enough to
   matter just reintroduces the black end that caused this. */
.topbar .topbar-emergency { padding-inline: clamp(1rem, 3vw, 1.75rem) 1.25rem; background: var(--red-dark); }
/* --red-light on a red ground is a red mark on red. White carries the alert here;
   the surface is now what says "emergency". */
.topbar-emergency .ico { color: #fff; }
/* Raised from .62: that alpha was set against a near-black surface. Composited
   over --red-dark it drops under 4.5:1, so the lighter surface has to buy the
   label back. Verified by audit.py's alpha-composited contrast check. */
.topbar-emergency .topbar-label { color: rgba(255,255,255,.78); }
.topbar-emergency a { display: inline-block; padding-block: .5rem; margin-block: -.5rem; color: #fff; font-weight: 800; text-decoration: underline; text-underline-offset: .15em; }
/* Was --red-light, which on the old near-black surface was a visible lift. On
   --red-dark it is red-on-red and the hover reads as nothing happening. Gold is
   the site's established hover-off-a-dark-brand-surface (see .hero-bridge and
   .svc-featured-body), and it is the one accent that is neither the surface nor
   the text.
   SPECIFICITY IS LOAD-BEARING HERE. `p a:not(.btn):hover` (line 215) is (0,2,2)
   and this markup is <p class="topbar-emergency"><a>, so a plain
   `.topbar-emergency a:hover` at (0,2,1) LOSES and the number went
   --brand-darker green on hover: green text on the red emergency bar. That is
   the bug reported on 2026-09-17. Matching the shape the other dark-ground
   overrides use takes this to (0,3,2). Do not "simplify" this selector. */
p.topbar-emergency a:not(.btn):hover { color: var(--gold); }
/* Real buttons: visible container, adequate hit area, legible glyph. */
.topbar-social { list-style: none; display: flex; align-self: center; align-items: center; gap: .45rem; margin: 0; padding: 0 clamp(1rem, 3vw, 1.75rem) 0 0; flex: none; }
.topbar-social a { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: var(--radius); background: rgba(255,255,255,.09); border: 1px solid rgba(255,255,255,.16); color: #fff; transition: background .15s, border-color .15s, color .15s; }
.topbar-social a:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
.topbar-social .social-ico { width: 17px; height: 17px; }
/* Mobile: keep the emergency line (it matters most), drop the rest — the drawer
   and footer carry area + socials. */
@media (max-width: 700px) {
  /* Emergency is the only item on the bar here, so it fills it edge to edge. */
  .topbar-inner { justify-content: flex-start; }
  .topbar-info { flex: 1; }
  .topbar .topbar-emergency { flex: 1; justify-content: center; padding-inline: .75rem; }
  /* `.topbar .topbar-area` (0,2,0) is needed to beat `.topbar p` (0,1,1) above —
     a plain `.topbar-area` loses the specificity fight and the nowrap text then
     overflows the viewport. */
  .topbar .topbar-area, .topbar-sep, .topbar-social { display: none; }
}

/* =============================================================================
   Header / nav
   ============================================================================= */
/* Weighty dark-green nav bar (the lesson from nctplumbing.com, executed cleaner). The
   logo is a dark-plate illustration and actually reads BETTER on dark green than white.
   Gold hairline separates the bar from the dark hero below and adds the brand accent. */
.site-header { position: sticky; top: 0; z-index: 100;
  background: linear-gradient(180deg, var(--brand-dark), var(--brand-darker));
  border-bottom: 2px solid var(--gold); box-shadow: var(--shadow-header); }
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: var(--header-h); }

/* Wordmark */
.logo { display: inline-flex; flex-direction: column; line-height: 1; flex: none; }
.logo-img { display: block; width: clamp(158px, 15vw, 196px); height: auto; }
.nav-drawer-logo .logo-img { width: 170px; }
.footer-logo.logo-img { width: 190px; margin-bottom: .25rem; }

.logo:hover { text-decoration: none; opacity: .88; }
.logo { transition: opacity .15s; }
/* Two-tone green wordmark, echoing NCT's logo */

.nav-toggle { display: none; align-items: center; justify-content: center; width: 46px; height: 46px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.22); border-radius: var(--radius); color: #fff; cursor: pointer; flex: none; }
.hamburger, .hamburger::before, .hamburger::after { display: block; width: 22px; height: 2.5px; background: var(--ink); border-radius: 999px; position: relative; transition: transform .2s, opacity .2s; }
.hamburger::before, .hamburger::after { content: ''; position: absolute; left: 0; }
.hamburger::before { top: -7px; } .hamburger::after { top: 7px; }

.nav-list { list-style: none; display: flex; align-items: center; gap: .15rem; margin: 0; padding: 0; }
.nav-link { display: inline-block; padding: .7rem .8rem; font-weight: 800; color: rgba(255,255,255,.82); letter-spacing: .01em; white-space: nowrap; box-shadow: inset 0 -3px 0 transparent; transition: color .15s, background .15s, box-shadow .15s; }
.nav-link:hover { color: #fff; background: rgba(255,255,255,.1); text-decoration: none; }
.nav-link.active { color: #fff; box-shadow: inset 0 -3px 0 var(--gold); }
.nav-link.active:hover { color: #fff; background: rgba(255,255,255,.1); box-shadow: inset 0 -3px 0 var(--gold); }
.nav-call { background: #fff; color: var(--brand-darker); border-color: #fff; padding: .6rem 1rem; margin-left: .5rem; }
.nav-call:hover { background: var(--gold); color: #3A2A05; border-color: var(--gold); }

.nav-scrim { display: none; }

/* =============================================================================
   Hero (home)
   ============================================================================= */
/* The grain is blended INTO the gradient (background-blend-mode), so it is part
   of the material rather than a film sitting on it. The weave goes on top as a
   separate ::after, unblended — see "Texture application". */
.hero { position: relative; background-color: var(--brand-dark); background-image: var(--tex-grain), linear-gradient(160deg, var(--brand-darker) 0%, var(--brand-dark) 55%, var(--brand) 100%); background-size: 200px 200px, cover; background-repeat: repeat, no-repeat; background-blend-mode: overlay, normal; color: #fff; overflow: hidden; }
.hero::after { content: ''; position: absolute; inset: 0; background-image: var(--tex-hatch-dark); background-size: 6px 6px; background-repeat: repeat; pointer-events: none; }
.hero-inner { position: relative; z-index: 1; display: grid; gap: 2.5rem; padding-block: clamp(3rem, 7vw, 5.5rem); }
/* The two columns carried very different mass: 610px of copy against a 275px card in a
   786px hero, which reads lopsided however the card is aligned (top-aligned left a hole
   under it, centred left holes above AND below). Widening the copy column shortens it
   (the headline takes fewer lines) and narrowing the card column makes the card taller,
   so the two masses converge. Anchored to the top so it reads deliberate. */
@media (min-width: 900px) { .hero-inner { grid-template-columns: 1.3fr .9fr; align-items: center; gap: 3.25rem; } }
/* Take the slack out of the hero itself rather than stretching a short card to fill it. */
@media (min-width: 1200px) { .hero-inner { padding-block: clamp(2.5rem, 4vw, 4rem); } }
.guarantee-card h2 { text-wrap: balance; }
.guarantee-card .terms { text-wrap: pretty; }
.hero h1 { color: #fff; }
.hero-tagline { font-size: var(--fs-lead); color: rgba(255,255,255,.9); max-width: 38ch; }
.hero-eyebrow { color: #BFE6CD; }
.hero-actions { display: flex; flex-wrap: wrap; gap: .85rem; margin-top: 1.75rem; }
.hero-bridge { margin-top: 1.75rem; font-size: var(--fs-sm); color: rgba(255,255,255,.8); border-left: 3px solid var(--cta); padding-left: .9rem; max-width: 70ch; }
.hero-bridge a { color: #fff; text-decoration: underline; }
/* Scoped tight enough to beat the generic prose-link hover (which would deepen the
   green and vanish against this ground). Gold is the established hover for a white
   prose link on a dark panel; see .svc-featured-body p a:hover. */
p.hero-bridge a:not(.btn):hover { color: var(--gold); }

/* Guarantee card in hero */
/* The card read as a faint floating panel against a big green field, which is what made
   the hero look lopsided. More opacity, a gold edge and more padding give the right
   column real visual mass without inventing content to fill it. */
.guarantee-card { background: rgba(0,0,0,.22); border: 1px solid rgba(255,255,255,.22);
  border-top: 3px solid var(--gold); border-radius: var(--radius-lg);
  padding: var(--pad-panel); backdrop-filter: blur(2px); box-shadow: var(--shadow-md); }
.guarantee-card .badge { display: inline-flex; align-items: center; gap: .5rem; background: var(--gold); color: #3A2A05; font-weight: 800; padding: .4rem .9rem; border-radius: 999px; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em; }
.guarantee-card h2 { color: #fff; font-size: 1.9rem; margin-top: 1rem; }
.guarantee-card p { color: rgba(255,255,255,.88); margin-bottom: 0; }
/* .85 (not .6) so the fine print clears AA 4.5:1 on the lightened card (.6 was 3.2). */
.guarantee-card .terms { font-size: var(--fs-xs); color: rgba(255,255,255,.85); margin-top: 1rem; }
.guarantee-card .guarantee-link { display: inline-block; margin-top: 1.1rem; font-weight: 800; color: var(--gold); font-size: var(--fs-sm); }
.guarantee-card .guarantee-link:hover { color: #fff; text-decoration: underline; }

/* =============================================================================
   The three guarantees — includes/guarantees.php
   ONE component, used on Emerald Club, Drain Cleaning and Contact. Rows, not a
   3-up grid: the client's exact terms run long, and cards would either clip them
   or leave the row ragged. The named guarantee is marked by a gold rule and the
   only link, never by a different badge (that would be two design languages).
   ============================================================================= */
/* 42rem, not the narrow container: the client's exact terms are the longest text
   on the page, and at 820px wide they ran a 95ch measure (the audit caught it).
   A tighter column keeps every line inside a readable measure. */
.guarantee-list { list-style: none; margin: 0 auto; padding: 0; display: grid; gap: 1.25rem; max-width: 42rem; }
.guarantee-row { display: flex; gap: 1.4rem; align-items: flex-start; background: #fff; border: 1px solid var(--line); border-left: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.6rem; box-shadow: var(--shadow); }
/* Gold (not gold-dark) so the named guarantee reads as a brand accent on white
   rather than an olive-brown rule that looks like a mistake beside the greens. */
.guarantee-row--named { border-left-color: var(--gold); }
.guarantee-mark { flex: none; }  /* shares the one icon-badge rule above */
.guarantee-body { min-width: 0; }
.guarantee-body h3 { margin: 0 0 .5rem; font-size: var(--fs-card); color: var(--ink); text-wrap: balance; }
.guarantee-short { margin: 0 0 .6rem; font-weight: 700; color: var(--text); text-wrap: pretty; }
.guarantee-terms { margin: 0; font-size: var(--fs-sm); color: var(--muted); text-wrap: pretty; }
.guarantee-link { display: inline-block; margin-top: .9rem; font-weight: 800; color: var(--brand-dark); font-size: var(--fs-sm); }
.guarantee-link:hover { color: var(--brand-darker); text-decoration: underline; }
@media (max-width: 560px) { .guarantee-row { flex-direction: column; gap: 1rem; } }

/* =============================================================================
   Trust strip
   ============================================================================= */
.trust-strip { background: var(--ink); color: #fff; }
.trust-strip .container { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 1.25rem 2.5rem; padding-block: 1.25rem; text-align: center; }
.trust-item { display: inline-flex; align-items: center; gap: .6rem; font-weight: 700; font-size: var(--fs-sm); }
.trust-item .ico { color: var(--gold); font-size: var(--fs-lead); }
.trust-item strong { color: #fff; }

/* =============================================================================
   Services hub
   ============================================================================= */
.services-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 600px) { .services-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .services-grid { grid-template-columns: repeat(3, 1fr); } }
.service-card { display: flex; flex-direction: column; gap: .75rem; background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.75rem; box-shadow: var(--shadow); transition: transform .15s, box-shadow .15s, border-color .15s; scroll-margin-top: calc(var(--header-h) + 1rem); }
a.service-card:hover { border-top-color: var(--brand-dark); }
a.service-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: var(--brand-tint-2); text-decoration: none; }
/* Icon tile: ONE treatment on light surfaces (service cards, callouts, why-us). Was two
   (tint+dark-green vs solid-green+white) at two sizes — unified so it can't drift. */
/* Bold icon badge: a solid brand-green anchor with a white glyph gives each card real
   presence (the pale-tint square read as generic template). One rule across service
   cards, callouts and why-items so it stays consistent. */
.service-icon, .why-item .why-icon, .guarantee-mark { display: inline-flex; align-items: center; justify-content: center; width: 58px; height: 58px; border-radius: var(--radius); background: linear-gradient(150deg, var(--brand), var(--brand-dark)); color: #fff; font-size: 1.5rem; box-shadow: var(--shadow-tile); }
/* Emergency variant of the card + icon tile: same shape and size, red fill for the one
   card that is genuinely urgent. This is the red presence on the home page, and it sits
   on white — never on green. Semantic variant, not drift. */
.service-card--emergency { border-top-color: var(--red); }
.service-card--emergency .service-icon { background: linear-gradient(150deg, var(--red), var(--red-deep)); color: #fff; box-shadow: var(--shadow-tile-red); }
a.service-card--emergency:hover { border-top-color: var(--red-dark); }
.service-icon { margin-bottom: .25rem; }
.service-card h3 { margin: 0; font-size: var(--fs-card); color: var(--ink); }
/* Exactly two lines: a floor (min-height) let a 3-line title break the row; a clamp
   both reserves AND caps, so body copy shares a baseline. ONLY once the cards are
   actually a row: stacked on one column that reservation is 25px of dead space
   under every single-line title (measured at 390). */
@media (min-width: 600px) { .service-card h3 { min-height: 2.44em; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; } }
.service-card p { color: var(--muted); margin: 0; font-size: var(--fs-sm); }
.service-card-link { margin-top: auto; padding-top: .6rem; font-weight: 800; color: var(--brand-dark); font-size: var(--fs-sm); }
/* The emergency card is red end to end: rule, icon tile AND its call to action. */
.service-card--emergency .service-card-link { color: var(--red); }
a.service-card--emergency:hover .service-card-link { color: var(--red-dark); }
/* ---- Services page — Drain Cleaning FEATURED ("king") + five callout cards
   (plan of record §2). The featured block is the one built page's summary and
   is never a grid-equal. Callouts are honest blurbs with id={slug} anchors; as
   Stage 2 elevates a service into its own page, its callout gains a link —
   Drain Cleaning stays the featured item throughout. ---- */
.svc-intro { max-width: var(--container-narrow); margin-inline: auto; font-size: var(--fs-lead); color: var(--text); }
/* The cornerstone as a bold, flat dark-green block — high-contrast, squared,
   unmistakably the "king" above the white callout cards. Strong color fills the
   panel with intent instead of a pale dead half. */
.svc-featured { position: relative; overflow: hidden; background: linear-gradient(150deg, var(--brand-dark), var(--brand-darker)); color: #fff; border-radius: var(--radius-lg); padding: clamp(2rem, 4.5vw, 3.5rem); margin: 2.5rem 0 3.75rem; scroll-margin-top: calc(var(--header-h) + 1rem); box-shadow: var(--shadow-md); }
/* Large sink glyph filling the right — same watermark motif as the page headers,
   so the panel's width reads as intentional brand space, not a dead half. */
.svc-featured-watermark { position: absolute; z-index: 0; right: clamp(-3rem, -2vw, -1rem); top: 50%; transform: translateY(-50%); width: clamp(190px, 26vw, 340px); height: clamp(190px, 26vw, 340px); color: #fff; opacity: .07; pointer-events: none; }
.svc-featured-watermark .ico { display: block; width: 100%; height: 100%; }
@media (max-width: 640px) { .svc-featured-watermark { display: none; } }
.svc-featured-body { position: relative; z-index: 1; max-width: 42rem; }
.svc-featured .eyebrow { color: #BFE6CD; }
.svc-featured-body h2 { color: #fff; margin: 0 0 1rem; }
.svc-featured-body p { color: rgba(255,255,255,.9); margin: 0 0 1.75rem; }
/* Inline link inside the dark featured panel: same treatment as .hero-bridge a, so
   dark-panel links read one way sitewide. */
.svc-featured-body p a { color: #fff; text-decoration: underline; }
.svc-featured-body p a:hover { color: var(--gold); }
.svc-featured-body .btn { background: #fff; color: var(--brand-darker); border-color: #fff; }
.svc-featured-body .btn:hover { background: var(--brand-tint); color: var(--brand-darker); border-color: var(--brand-tint); }
.svc-callouts { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 700px) { .svc-callouts { grid-template-columns: repeat(2, 1fr); } }
.svc-callout { display: flex; flex-direction: column; gap: .9rem; background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.75rem; box-shadow: var(--shadow); scroll-margin-top: calc(var(--header-h) + 1rem); }
.svc-callout-body h3 { margin: 0 0 .8rem; font-size: var(--fs-card); color: var(--ink); }
@media (min-width: 700px) { .svc-callout-body h3 { min-height: 2.44em; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; } }
.svc-callout-body p { margin: 0; color: var(--muted); }
/* Four callouts sit in a clean 2x2; Drain Cleaning is featured above and Emergency
   is the band above, so neither is a grid-equal here. */

/* Emergency band (services page) + emergency CTA (contact sidebar) are ONE visual
   language: ink panel, bright-green top accent, uppercase eyebrow, single 24/7 CTA.
   The eyebrow rule is shared so the two can't drift apart. */
.emergency-eyebrow { display: inline-flex; align-items: center; gap: .45rem; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--red-light); margin-bottom: .85rem; }

/* Top of the services page: intro leads (left), emergency is a compact SIDE card
   (right) — high enough to catch an active backup, subordinate to the services below. */
.svc-top { display: grid; gap: 1.75rem; grid-template-columns: 1fr; align-items: start; margin-bottom: clamp(2.5rem, 5vw, 3.75rem); }
@media (min-width: 880px) { .svc-top { grid-template-columns: 1.6fr 1fr; gap: 2.5rem; } }
.svc-top .svc-intro { max-width: none; margin: 0; }

.svc-emergency { background: linear-gradient(160deg, var(--red-deep), var(--red-deep-2)); color: #fff; border-top: 3px solid var(--red-bright); border-radius: var(--radius-lg); box-shadow: var(--shadow-md); padding: clamp(1.4rem, 2.5vw, 1.75rem); scroll-margin-top: calc(var(--header-h) + 1rem); }
.svc-emergency h2 { color: #fff; font-size: var(--fs-h3); margin: 0 0 .5rem; }
.svc-emergency p { color: rgba(255,255,255,.8); font-size: var(--fs-sm); margin: 0 0 1.2rem; text-wrap: pretty; }
.svc-emergency .btn { width: 100%; }
.svc-emergency-note { display: block; text-align: center; margin-top: .65rem; font-size: var(--fs-sm); color: rgba(255,255,255,.65); }

/* =============================================================================
   Why us
   ============================================================================= */
.why-grid { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 640px) { .why-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .why-grid { grid-template-columns: repeat(4, 1fr); } }
.why-item { text-align: left; }
.why-item .why-icon { margin-bottom: .85rem; }
/* Reserve two lines for the heading so the body copy shares a baseline across the row.
   Without it, one heading wrapping ("No pressure, honest answers") shoves its column
   down and the row reads ragged/accidental. Measured ragged at 1920/1440/1200/1024. */
.why-item h3 { font-size: var(--fs-card); margin-bottom: .7rem; }
@media (min-width: 640px) { .why-item h3 { min-height: 2.44em; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; } }
.why-item p { color: var(--muted); font-size: var(--fs-sm); margin: 0; }

/* =============================================================================
   FAQ accordion
   ============================================================================= */
.faq-list { max-width: var(--container-narrow); margin-inline: auto; display: grid; gap: .75rem; }
/* No overflow:hidden here — it clipped the focusable button's focus ring. The answer
   panel (.faq-a) clips its own max-height animation, so the rounding is preserved. */
.faq-item { border: 1px solid var(--line); border-radius: var(--radius-lg); background: #fff; }
.faq-q { width: 100%; text-align: left; display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 1.1rem 1.25rem; background: none; border: 0; border-radius: var(--radius); font: inherit; font-weight: 800; color: var(--ink); cursor: pointer; }
.faq-q:hover { color: var(--brand-dark); background: var(--bg-alt); }
.faq-item.open { border-color: var(--brand-tint-2); }
.faq-item.open .faq-q { color: var(--brand-dark); }
.faq-q .faq-caret { transition: transform .2s; color: var(--brand); flex: none; }
.faq-item.open .faq-caret { transform: rotate(180deg); }
.faq-a { padding: 0 1.25rem; max-height: 0; overflow: hidden; transition: max-height .25s ease, padding .25s ease; }
.faq-item.open .faq-a { padding: 0 1.25rem 1.25rem; max-height: 600px; }
.faq-a p { color: var(--muted); margin: 0; max-width: var(--measure); }

/* =============================================================================
   Service area
   ============================================================================= */
.area-lead { max-width: 60ch; margin-inline: auto; text-align: center; color: var(--muted); margin-bottom: 2.5rem; }
.area-counties { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .area-counties { grid-template-columns: repeat(3, 1fr); } }
.area-county-note { font-size: var(--fs-sm); color: var(--muted); margin: .5rem 0 0; }
.area-county h3 { color: var(--brand-dark); border-bottom: 2px solid var(--brand-tint-2); padding-bottom: .5rem; }
.area-county ul { list-style: none; padding: 0; margin: 0; columns: 2; column-gap: 1rem; }
/* Hanging indent: a wrapped city name must line up with the text above it, not tuck
   under the pin. */
.area-county li { font-size: var(--fs-sm); color: var(--text); padding: .2rem 0; break-inside: avoid;
  display: flex; align-items: flex-start; gap: .45rem; }
.area-county li::before { content: ''; display: block; flex: none; width: .8rem; height: .8rem; margin-top: .28em; background-color: var(--brand); -webkit-mask: url("/icon/location-dot.svg") center / contain no-repeat; mask: url("/icon/location-dot.svg") center / contain no-repeat; }

/* ── Interactive service-area map (MapLibre GL; logic in /js/service-map.js,
   assets self-hosted, home page only). The .service-map div is the map canvas
   host; the poster <img> inside it is the no-JS/no-WebGL2/failure fallback and
   fades out once the live map has loaded (.is-live). ── */
.service-map-block { width: 100%; margin: 0 0 3rem; } /* full-bleed band — sits outside .container */
/* Height scales with WIDTH on large screens. The map fits the 25-mile ring vertically,
   so on a short full-bleed frame (3.6:1 at 1920) the rings shrink to a blob and the
   spare width fills with territory nowhere near the service area. */
.service-map { position: relative; width: 100%; height: clamp(340px, 48vh, 540px);
  background: #fff; border-block: 1px solid var(--brand-tint-2); overflow: hidden; }
@media (min-width: 1200px) { .service-map { height: clamp(520px, 34vw, 680px); } }
/* Ultrawide: the height cap stops tracking width, so the frame goes back to ~3.8:1 and
   the rings shrink again. Rein the band in rather than make the map absurdly tall. */
@media (min-width: 2200px) { .service-map-block { max-width: 2100px; margin-inline: auto; } }
.service-map-poster { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%;
  object-fit: cover; transition: opacity .3s ease; }
.service-map.is-live .service-map-poster { opacity: 0; pointer-events: none; }
.service-map .maplibregl-canvas.is-pointer { cursor: pointer; }
.service-map .maplibregl-canvas:focus-visible { outline: 3px solid var(--brand-bright); outline-offset: -3px; }

/* MapLibre chrome, skinned to the site (class-based — CSP style-src 'self').
   Popups: city name bold, the rest regular in the standard text color. */
.sm-popup .maplibregl-popup-content { font-family: inherit; font-size: var(--fs-sm);
  font-weight: 400; color: var(--text); background: #fff; padding: .55rem .8rem;
  border-radius: var(--radius); box-shadow: var(--shadow-md); pointer-events: none; }
.sm-popup .maplibregl-popup-content strong { font-weight: 600; }
.sm-pin { cursor: pointer; }

/* Cursor distance readout — follows the pointer (JS sets transform via CSSOM). */
.sm-readout { position: absolute; top: 0; left: 0; z-index: 3; pointer-events: none;
  white-space: nowrap; background: rgba(255, 255, 255, .95); color: var(--text);
  font-size: var(--fs-xs); padding: .3rem .6rem; border: 1px solid var(--brand-tint-2);
  border-radius: var(--radius); box-shadow: var(--shadow); opacity: 0;
  transition: opacity .12s ease; will-change: transform; }
.sm-readout.is-visible { opacity: 1; }
.sm-readout strong { font-weight: 600; color: var(--brand-darker); }
.service-map .maplibregl-ctrl-group { border-radius: var(--radius); box-shadow: var(--shadow); }
.service-map .maplibregl-ctrl-attrib { font-size: .68rem; }
.service-map .maplibregl-ctrl-attrib a { color: var(--muted); }

/* The shop pin marker (custom element — the default MapLibre marker SVG clips
   flat where its artwork is tangent to the viewBox; ours carries padding). */
.sm-pin svg { display: block; }

/* Legend under the map — real artwork chips (the actual pin + ring geometry),
   centered within the reading measure even though the band is full-bleed. */
.service-map-legend { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .5rem 1.4rem; margin-top: 1.1rem; padding-inline: 1rem;
  font-size: var(--fs-sm); color: var(--muted); }
.sm-legend-item { display: inline-flex; align-items: center; font-weight: 700; color: var(--text); }
.sm-legend-chip { display: block; margin-right: .5rem; flex-shrink: 0; }
.sm-legend-note { flex-basis: 100%; text-align: center; font-style: italic; }
/* The service-rate note is a real answer to "do you come out to me?", not a
   caption aside: upright, weighted, and first in the block. */
.sm-legend-rates { font-style: normal; font-weight: 700; color: var(--text); order: -1; max-width: 62ch; margin-inline: auto; text-wrap: pretty; }
/* A capped-width note stops filling its flex line, so the legend chips ride up
   beside it. This zero-height break holds the line for it. */
.sm-legend-break { order: -1; flex-basis: 100%; height: 0; }
/* The number is the action in that line — keep it upright so it reads as one. */
/* padding-block on an inline element grows the touch target without moving layout */
.sm-legend-note a { font-style: normal; font-weight: 700; color: var(--brand-dark);
  padding-block: .3rem; }
.sm-legend-note a:hover, .sm-legend-note a:focus-visible { text-decoration: underline; }

/* Phones: tall map, full width already from the band; cooperativeGestures keeps
   page scroll unhijacked. */
@media (max-width: 640px) {
  .service-map { height: min(62vh, 480px); }
}

.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; }

/* =============================================================================
   CTA band
   ============================================================================= */
.cta-band { background: linear-gradient(150deg, var(--brand-dark), var(--brand-darker)); color: #fff; }
.cta-inner { display: grid; gap: 1.75rem; align-items: center; }
/* Cap and centre the band content. Left across the full 1180px container the short
   statement and the buttons flew to opposite edges, leaving a ~240px void at 1920. */
@media (min-width: 820px) { .cta-inner { grid-template-columns: 1fr auto; max-width: 960px; margin-inline: auto; column-gap: 3rem; } }
.cta-band h2 { color: #fff; }
.cta-text { color: rgba(255,255,255,.85); margin: 0; }
.cta-actions { display: flex; flex-wrap: wrap; gap: .85rem; }
@media (min-width: 820px) { .cta-actions { justify-content: flex-end; } }

/* =============================================================================
   Page header (interior hero band)
   ============================================================================= */
.page-header { position: relative; background-color: var(--brand-dark); background-image: var(--tex-grain), linear-gradient(150deg, var(--brand-darker), var(--brand-dark)); background-size: 200px 200px, cover; background-repeat: repeat, no-repeat; background-blend-mode: overlay, normal; color: #fff; padding-block: clamp(2.5rem, 6vw, 4.5rem); overflow: hidden; }
.page-header-inner { position: relative; z-index: 2; }
.page-header h1 { color: #fff; }
.page-header-subtitle { font-size: var(--fs-lead); color: rgba(255,255,255,.9); max-width: 60ch; margin: 0; }

/* Breadcrumbs */
.breadcrumbs ol { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; padding: 0; margin: 0 0 1rem; font-size: var(--fs-sm); }
/* balance: the current-page crumb carries the full page title, and a long article
   title wrapped to a lone trailing word on narrow screens. */
.breadcrumbs li { display: inline-flex; align-items: center; gap: .4rem; color: rgba(255,255,255,.75); text-wrap: balance; }
.breadcrumbs li:not(:last-child)::after { content: '/'; color: rgba(255,255,255,.4); }
.breadcrumbs a { display: inline-block; padding-block: .35rem; margin-block: -.35rem; color: rgba(255,255,255,.9); }
.breadcrumbs [aria-current="page"] { color: #fff; font-weight: 700; }

/* Interior page header — dot-grid texture + an oversized, low-opacity subject
   watermark so the wide band reads as intentional brand space, not a dead right
   half. $ph_icon (page-header.php) picks the glyph per page. */
.page-header::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background-image: var(--tex-hatch-dark); background-size: 6px 6px; background-repeat: repeat; }
.page-header-watermark { position: absolute; z-index: 1; right: clamp(-2rem, -1vw, 0rem); top: 50%; transform: translateY(-50%); width: clamp(150px, 20vw, 260px); height: clamp(150px, 20vw, 260px); color: #fff; opacity: .06; pointer-events: none; }
.page-header-watermark .ico { display: block; width: 100%; height: 100%; }
@media (max-width: 700px) { .page-header-watermark { display: none; } }

/* =============================================================================
   Interior article layout — story + a supporting call rail. Replaces the lone
   centered .prose ribbon on content pages so the page uses its width and carries
   a persistent, on-brand way to reach the crew (also the single CTA on the page,
   so these pages don't end in yet another green band).
   ============================================================================= */
.article-layout { display: grid; gap: clamp(2rem, 5vw, 3.5rem); grid-template-columns: 1fr; align-items: start; }
/* Mobile: the contents list belongs ABOVE the article, not stacked after it.
   Desktop: it returns to the top of the rail column. */
.toc-card { order: -1; }
/* Sticky sidebar. Pattern taken from the bcfoundations blog, which solved this
   already and wrote down why (its .blog-post-aside):
     - The aside sticks as ONE piece and carries NO max-height/overflow clamp. A
       `calc(100vh - ...)` cap silently guillotines the bottom card, which reads as
       broken rather than as "scroll me", and a second scrollbar beside an article
       is its own problem. Both were built here first and both were wrong.
     - It does NOT have to fit. If the aside is taller than the viewport, the last
       block simply runs off the bottom, which is what that site does and is fine.
       Do not build machinery to prevent it.
   Measured 2026-09-15, ours against theirs: their toc 319px + cta 234px = 573px;
   ours toc 443px + call card 449px = 911px. Ours is the bigger column, so it
   overruns sooner. The services list moved out to a band at the foot of the
   article, which is most of what bought the room back. */
.article-side { display: contents; }
@media (min-width: 920px) {
  .article-layout { grid-template-columns: minmax(0, 1fr) 21rem; }
  .article-main { grid-column: 1; }
  .article-side {
    display: block;
    grid-column: 2;
    align-self: start;
    position: sticky;
    top: calc(var(--header-h) + 1.5rem);
  }
  .toc-card { order: 0; }
}
/* ONE height guard, matching the reference build, and no attempt to guarantee the
   whole aside fits. A three-step ladder that dismantled the call card block by
   block lived here briefly and was over-engineered: measured on
   bcfoundations.ca/blog at 1440x900, its own aside overruns the viewport bottom by
   111px and is simply left to. The last block runs off the screen and reads as a
   block that continues, which is what scrolling is for. Only the lowest-priority
   item gets dropped outright, and only on genuinely short screens. Same here: the
   creds repeat the trust strip a few hundred pixels up the page, so they are what
   goes. Everything else is allowed to overrun. */
@media (min-width: 920px) and (max-height: 850px) {
  .article-side .rail-creds { display: none; }
}

/* Services band at the foot of an article: the rail-links list, but as a row. */
/* Services band at the foot of an article. The SECTION carries the tinted, hatched
   ground (.section--alt) and the card sits on top of it, which is the texture rule:
   grounds and full-width bands take the weave, cards never do. That separation is
   the whole point of the band, so do not move the background onto the card. */
.article-services .rail-links { background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); box-shadow: var(--shadow-md); padding: clamp(1.5rem, 3vw, 2rem); margin-top: 0; }
.article-services .rail-links > h2 { font-size: var(--fs-h3); margin: 0 0 1.1rem; }
.article-services .rail-links ul { display: grid; gap: 0; grid-template-columns: 1fr; }
@media (min-width: 620px) { .article-services .rail-links ul { grid-template-columns: repeat(2, 1fr); column-gap: 2rem; } }
@media (min-width: 960px) { .article-services .rail-links ul { grid-template-columns: repeat(3, 1fr); } }
/* Row, not a chip: label left, arrow parked right, hairline underneath. Bigger and
   with more air than the sidebar version, because down here these are the next step
   rather than a reference list. */
/* The right padding is deliberately bigger than the left: it is the runway the
   arrow slides into. Measured at .5rem it left the arrow 3px off the row's painted
   edge on hover, which reads as the arrow hitting the wall. 1.15rem leaves ~14px
   after the travel.
   Hover no longer shifts padding-left. Moving the label on hover re-flows text
   under the cursor, and the arrow is already carrying the affordance. */
.article-services .rail-links a { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin: 0; padding: .85rem 1.15rem .85rem .5rem; border-bottom: 1px solid var(--line); border-radius: 0; font-size: var(--fs-body); color: var(--ink); transition: background-color .15s, color .15s; }
.article-services .rail-links a:hover { background: var(--brand-tint); color: var(--brand-dark); }
.article-services .rail-links .lnk-arrow { color: var(--brand); font-size: .9em; }
.article-services .rail-links a:hover .lnk-arrow { color: var(--brand-dark); }
.article-main { min-width: 0; max-width: 70ch; }
.article-lead { font-size: var(--fs-lead); line-height: var(--lh-lead); color: var(--ink); font-weight: 600; margin-bottom: 1.75rem; }
.article-main h2 { color: var(--brand-darker); margin-top: 2.5rem; }
.article-main h2:first-of-type { margin-top: 0; }
.article-main h2::before { content: ''; display: block; width: 2.25rem; height: 3px; border-radius: 999px; background: var(--brand); margin-bottom: .85rem; }
.article-main > p:last-child { margin-bottom: 0; }

/* NOT sticky: at 844px it overflowed a 720px viewport when it was, and it was also
   taking the sticky slot from the contents list, which is what a reader actually
   needs held in view. See the grid note above. */
.article-rail { display: block; }
.rail-card { background: linear-gradient(160deg, var(--brand-dark), var(--brand-darker)); color: #fff; border-radius: var(--radius-lg); padding: var(--pad-panel); box-shadow: var(--shadow-md); }
.rail-card h2 { color: #fff; font-size: var(--fs-h3); margin: 0 0 .6rem; }
.rail-card > p { color: rgba(255,255,255,.85); font-size: var(--fs-sm); margin: 0 0 1.15rem; }
.rail-card .btn-cta { width: 100%; background: #fff; color: var(--brand-darker); border-color: #fff; }
.rail-card .btn-cta:hover { background: var(--brand-tint); color: var(--brand-darker); border-color: var(--brand-tint); }
/* Higher specificity than `.rail-card > p` (which otherwise zeroes the top margin
   and jams this line under the call button). */
.rail-card .rail-emergency { margin: 1.5rem 0 0; font-size: var(--fs-sm); color: rgba(255,255,255,.85); }
.rail-emergency a { color: #fff; font-weight: 800; text-decoration: underline; text-underline-offset: .15em; }
.rail-creds { list-style: none; margin: 1.25rem 0 0; padding: 1.25rem 0 0; border-top: 1px solid rgba(255,255,255,.2); display: grid; gap: .7rem; }
/* flex-start, not center: on a two-line credential ("Family owned & operated, 50+ years")
   a centred icon floats between the lines instead of sitting on the first one. Matches
   .contact-rows, .footer-contact-list and .nav-extra-creds. */
.rail-creds li { display: flex; gap: .6rem; align-items: flex-start; font-size: var(--fs-sm); color: rgba(255,255,255,.9); }
.rail-creds .ico { color: var(--brand-bright); font-size: 1.05rem; flex: none; margin-top: .12em; }
/* Quick service links beneath the call card — fills the rail column on tall pages
   (no dead space) and gives every article page real internal linking. */
.rail-links { margin-top: 1.25rem; background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.5rem; box-shadow: var(--shadow); }
.rail-links h2 { font-size: var(--fs-md); margin: 0 0 1rem; color: var(--ink); }
.rail-links ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .1rem; }
/* ONE arrow behaviour for every rail-links list. The compact sidebar version and
   the big band at the foot of an article differ in scale and ground, not in how a
   row works: label left, arrow parked right, arrow slides on hover. */
/* Asymmetric on purpose: the negative margin is what aligns the hover tint with
   the card's padding edge, and it does not depend on padding, so the extra right
   padding buys the arrow its runway without moving the label. */
.rail-links a { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .7rem .95rem .7rem .65rem; margin: 0 -.65rem; border-radius: var(--radius); color: var(--text); font-weight: 700; font-size: var(--fs-sm); transition: background-color .15s, color .15s; }
.rail-links a:hover { background: var(--brand-tint); color: var(--brand-dark); text-decoration: none; }
.rail-links a[aria-current] { color: var(--brand-dark); background: var(--brand-tint); }
.rail-links-label { min-width: 0; }
.rail-links .lnk-arrow { color: var(--brand); font-size: .85em; transition: transform .15s, color .15s; }
.rail-links a:hover .lnk-arrow { transform: translateX(.25rem); color: var(--brand-dark); }
@media (prefers-reduced-motion: reduce) { .rail-links a:hover .lnk-arrow { transform: none; } }

/* ---- Emerald Club: benefits checklist + plan tiers (modern, not a raw bullet
   list buried in prose). Full-width sections, bold blocking. ---- */
.benefits-grid { display: grid; gap: 1rem 2rem; grid-template-columns: 1fr; margin: 0; padding: 0; list-style: none; }
@media (min-width: 680px) { .benefits-grid { grid-template-columns: 1fr 1fr; } }
.benefits-grid li { display: flex; gap: .75rem; align-items: flex-start; }
.benefits-grid li::before { content: ''; flex: none; width: 1.4rem; height: 1.4rem; margin-top: .1rem; background-color: var(--brand); -webkit-mask: url("/icon/circle-check.svg") center / contain no-repeat; mask: url("/icon/circle-check.svg") center / contain no-repeat; }
.plan-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
/* Three cards with real body copy need ~285px each to avoid ragged 4-word lines and
   hyphenated breaks. At the old 720px breakpoint each card was ~220px. */
@media (min-width: 960px) { .plan-grid { grid-template-columns: repeat(3, 1fr); } }
.plan-card { display: flex; flex-direction: column; background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.75rem; box-shadow: var(--shadow); }
.plan-card--feature { background: linear-gradient(160deg, var(--brand-dark), var(--brand-darker)); border: 0; color: #fff; box-shadow: var(--shadow-md); }
.plan-card--feature h3, .plan-card--feature p { color: #fff; }
.plan-tag, .tag { align-self: flex-start; line-height: 1.2; font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: #3A2A05; background: var(--gold); padding: .25rem .6rem; border-radius: var(--radius); margin-bottom: .9rem; }
/* The feature card carries a "Most coverage" tag above its title, which pushed its
   heading (and therefore its body copy) 37px below the other two. Reserve the same
   space on the untagged cards so all three share a baseline. Height tracks the tag's
   own type + padding + margin so it stays correct if those change. Only while they
   ARE a row: stacked on one column, that reserved space is a hole above the
   heading. */
@media (min-width: 960px) {
  .plan-card:not(.plan-card--feature)::before { content: ""; display: block; height: calc(.7rem * 1.2 + .5rem + .9rem); flex: none; }
}
.plan-card h3 { font-size: var(--fs-card); margin: 0 0 .6rem; color: var(--ink); }
.plan-card > p { margin: 0; color: var(--muted); font-size: var(--fs-sm); }
/* Plan value + price (client's own published figures, getEmeraldPlans()). The
   value number is the loudest thing after the plan name, but never louder: it
   sits one step BELOW the card title so the type ladder holds. */
.plan-card > .plan-value { margin: 0 0 .1rem; }
.plan-value strong { font-size: var(--fs-md); font-weight: 900; color: var(--brand-dark); }
.plan-card > .plan-price { margin: 0 0 .9rem; font-weight: 700; color: var(--ink); }
/* Whole card is the link out to NCT, where people actually join. */
a.plan-card { text-decoration: none; transition: transform .15s, box-shadow .15s, border-color .15s; }
a.plan-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); text-decoration: none; }
a.plan-card:not(.plan-card--feature):hover { border-color: var(--brand-tint-2); border-top-color: var(--brand-dark); }
.plan-card-link { margin-top: auto; padding-top: .9rem; font-weight: 800; font-size: var(--fs-sm); color: var(--brand-dark); }
/* Placed after the base rules so it wins the equal-specificity source-order
   fight — the featured card is dark, so its text must be white. */
.plan-card--feature h3 { color: #fff; }
.plan-card--feature > p { color: rgba(255,255,255,.9); }
.plan-card--feature .plan-value strong { color: var(--gold); }
.plan-card--feature > .plan-price { color: #fff; }
.plan-card--feature .plan-card-link { color: var(--gold); }
.plan-note { color: var(--muted); font-size: var(--fs-sm); margin: 1.5rem 0 0; max-width: var(--measure); }

/* =============================================================================
   Brand lockups — the client's OWN artwork (NCT logo, Emerald Club mark),
   copied unaltered and served at their published sizes. ONE rule for both, so
   a second co-brand placement can't drift into a different treatment.
   ============================================================================= */
/* NO panel, NO border, NO plate. These marks are already framed artwork (a
   bordered tile, a scalloped seal); putting them in a box stacks a box on a box
   and reads like a sticker. They sit on the page's own light surface, sized
   against the type beside them, and the space around them does the work. */
.brand-lockup { margin: 1.75rem 0; display: flex; flex-direction: column; gap: 1rem; align-items: flex-start; }
.brand-lockup img { height: auto; }
.brand-lockup figcaption { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; font-weight: 800; color: var(--muted); line-height: 1.35; }
.brand-lockup--wordmark img { width: min(300px, 100%); }

/* Emerald Club opening: the mark and the statement are one block. The copy holds
   its own measure; the mark hangs beside it, unframed, on plain white. */
.club-intro { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); align-items: start;
  max-width: 58rem; margin-inline: auto; }
@media (min-width: 880px) { .club-intro { grid-template-columns: auto minmax(0, 1fr); } }
.club-mark { width: 96px; height: auto; }
@media (min-width: 880px) { .club-mark { width: 140px; } }
/* Hold the reading measure whether the mark is beside the copy or above it: the
   single-column stack at 834 ran the body to 89ch. */
.club-intro-copy { max-width: 62ch; }
.club-intro-copy > p:last-child { margin-bottom: 0; }
/* Wordmark + anniversary seal side by side, nothing behind them. */
.brand-lockup-marks { display: flex; align-items: center; gap: clamp(1.5rem, 3.5vw, 2.5rem); flex-wrap: wrap; }
.brand-lockup .brand-seal { width: 88px; flex: none; }


/* =============================================================================
   Generic prose blocks (club, service detail, 404, privacy policy)
   NOTE: this heading used to read "1968" — a founding year the site never had
   and is forbidden from claiming. Corrected so nobody reads it as a fact.
   ============================================================================= */
.prose { max-width: var(--container-narrow); margin-inline: auto; }
.prose h2 { margin-top: 2rem; }
.prose h2:first-child { margin-top: 0; }
/* A long policy page is the only thing here that needs a third level. Set tighter
   to its own h2 than the h2 is to the text above it, so the subheads read as
   belonging to the section rather than starting a new one. */
.prose h3 { margin-top: 1.5rem; }
.prose ul { padding-left: 1.25rem; }
/* One rule for prose list rhythm: the article body sets multi-line list items, which
   sat flush against each other without it. Same spacing as .prose, not a second one.
   `balance`, not `pretty`, for the same measured reason as the note below: these run
   three to five lines at 390px, well under Blink's cap, where balance is the stronger
   of the two. Without it the long cause/symptom items left single-word last lines. */
.prose li, .article-body li { margin-bottom: .4rem; text-wrap: balance; }
/* NOTE for the next person tempted by `text-wrap: pretty` on long prose:
   it was tried here on the privacy policy (2026-08-27) and MEASURED WORSE.
   Blink stops balancing past roughly six lines, so the instinct is that
   `balance` is doing nothing on a long paragraph and `pretty` should replace it.
   Measured across 8 widths: `balance` left 1 runt, `pretty` left 6, because
   `balance` is still working on every paragraph under the cap and `pretty` is
   the weaker of the two here. The fix for a paragraph over the cap is to not
   have a paragraph over the cap. Do not "improve" this back to pretty. */
/* Tap targets in list items (WCAG 2.2 SC 2.5.8, 24x24). A one-line <li> gives a
   link a 21px box. Links in running paragraphs are exempt by the criterion and
   stay untouched, as decided in the 2026-08-14 mobile pass, but a list of
   phone / email / "use the contact form" links is a control list, not a sentence.
   Padding an INLINE element grows its hit box without moving the line box, so
   this buys the height with no effect on rhythm and no display:inline-block
   (which would stop a long email address wrapping and overflow at 280). */
.prose li a { padding-block: .3rem; }
/* Revision line at the foot of a policy page. Deliberately quiet: it is a fact
   about the document, not part of the document. */
.policy-updated { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line); font-size: var(--fs-sm); color: var(--muted); }
/* Gold surface: the callout/guarantee quote. Green type on cream keeps the brand
   baseline while giving gold a real surface role (and buffering green from red). */
.pullquote { border-left: 4px solid var(--gold-dark); background: var(--gold-tint); padding: 1.25rem 1.5rem; border-radius: 0 var(--radius) var(--radius) 0; font-size: var(--fs-lead); font-weight: 700; color: var(--brand-darker); margin: 2rem 0; }

/* Two-col feature rows (1968 page) */

/* =============================================================================
   Club
   ============================================================================= */
@media (min-width: 640px) { .club-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .club-grid { grid-template-columns: repeat(3, 1fr); } }

/* =============================================================================
   Service detail (cornerstone)
   ============================================================================= */
.service-detail .service-lead { font-size: var(--fs-lead); color: var(--text); max-width: var(--container-narrow); }
.service-section { max-width: var(--container-narrow); margin: 2.25rem auto 0; }
/* Inside the split on Home the checklist is already in a sized column, so the
   narrow cap and the auto centring would only inset it a second time. */
.service-section--flush { max-width: none; margin: 0; }
/* Checklist and picture side by side, filling a band the list alone left mostly
   empty. One column until there is genuinely room for a ~19rem picture beside a
   readable measure. */
.mainline-split { display: grid; gap: clamp(1.75rem, 4vw, 3rem); margin-top: 2.25rem; align-items: start; }
@media (min-width: 900px) { .mainline-split { grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); } }
.mainline-split .photo-figure { margin: 0; }
.service-section h2 { color: var(--brand-darker); }
.service-section ul { padding-left: 0; list-style: none; }
.service-section ul li { position: relative; padding-left: 1.9rem; margin-bottom: .55rem; }
.service-section ul li::before { content: ''; position: absolute; left: 0; top: .2em; width: 1.05em; height: 1.05em; background-color: var(--brand); -webkit-mask: url("/icon/circle-check.svg") center / contain no-repeat; mask: url("/icon/circle-check.svg") center / contain no-repeat; }

/* =============================================================================
   Articles — cards match the service-card system exactly (no image dependency);
   single article reuses .article-layout + prose body + sticky rail. No drift.
   ============================================================================= */
.article-grid { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 620px) { .article-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 960px) { .article-grid { grid-template-columns: repeat(3, 1fr); } }
/* The related-articles grid holds however many siblings exist (two today). A fixed
   three-column track left an empty column and a visible hole on the right. */
@media (min-width: 620px) { .article-grid--fit[data-count="2"] { grid-template-columns: repeat(2, 1fr); } }
/* A single card centres under its (centred) section title. Left-aligned it read as
   lopsided on the home feed, which is what a one-article blog looks like at launch. */
@media (min-width: 620px) { .article-grid--fit[data-count="1"] { grid-template-columns: minmax(0, 520px); justify-content: center; } }
.article-card { display: flex; flex-direction: column; height: 100%; background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow); transition: transform .15s, box-shadow .15s, border-color .15s; }
a.article-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-top-color: var(--brand-dark); text-decoration: none; }
/* Featured-image space: honest branded placeholder until real photography lands. */
.article-card-media { position: relative; display: flex; align-items: center; justify-content: center; aspect-ratio: 16 / 9; background: linear-gradient(150deg, var(--brand-dark), var(--brand-darker)); border-bottom: 1px solid var(--line); }
.article-card-media .ico { width: 44px; height: 44px; color: rgba(255,255,255,.4); }
/* Photo variant. Every photo we hold is portrait or square (the originals are 1080p
   video frames and a phone photo), so a 16/9 card slot has to crop hard: the window
   is only about 42% of a 800x1067 frame's height.
   display:block, not the base flex: as a flex container the slot let the portrait
   img force it far past its own aspect-ratio and the card grew to ~700px tall.
   object-position 50% 55% is measured against THIS photo, whose subject (the
   root-matted pipe and the gloved hands on it) sits just below centre. A default
   50% 50% crop clips the hands; 38% landed on jeans and a boot. */
.article-card-media--photo { display: block; overflow: hidden; padding: 0; }
.article-card-media--photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 55%; }
a.article-card:hover .article-card-media .ico { color: rgba(255,255,255,.55); }
.article-card-body { display: flex; flex-direction: column; gap: .7rem; flex: 1; padding: 1.75rem; }
.article-card-meta { font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--brand-dark); }
/* Staging-only marker, overlaid on the media tile. It must NOT sit in the flow of
   the card body: in the meta line its padding made draft cards taller than real
   ones and broke the row's shared baseline. */
.article-card-draft { position: absolute; top: .6rem; left: .6rem; background: var(--red-deep); color: #fff; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .06em; padding: .2rem .5rem; border-radius: var(--radius); }
/* Blog titles run 2-3 lines, so reserve three. Without it the excerpts sit at different
   heights across the row (measured ragged at 1920/1440/768 on both Home and /articles). */
.article-card h3 { margin: 0; font-size: var(--fs-card); color: var(--ink); }
@media (min-width: 620px) { .article-card h3 { min-height: 3.66em; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; } }
/* balance, not pretty: a card excerpt is three or four lines, well under the cap
   where Blink stops balancing, and the measured result on this site is that
   `balance` clears runts where `pretty` leaves them. See the note by .prose. */
.article-card p { margin: 0; color: var(--muted); font-size: var(--fs-sm); text-wrap: balance; }
.article-card-more { margin-top: auto; padding-top: .35rem; font-weight: 800; color: var(--brand-dark); font-size: var(--fs-sm); }
.articles-more { text-align: center; margin-top: 2.5rem; }

/* Single article — meta line + placeholder flag */
.article-byline { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .9rem; color: var(--muted); font-weight: 700; font-size: var(--fs-sm); margin-bottom: 1.75rem; }
.article-byline .placeholder-flag { text-transform: uppercase; letter-spacing: .04em; font-size: var(--fs-xs); color: var(--brand-darker); background: var(--brand-tint); padding: .2rem .5rem; border-radius: var(--radius); }
.article-body > h2 { color: var(--brand-darker); margin-top: 2.5rem; scroll-margin-top: calc(var(--header-h) + 1rem); }
.article-body > h2::before { content: ''; display: block; width: 2.25rem; height: 3px; border-radius: 999px; background: var(--brand); margin-bottom: .85rem; }
.article-body > p:first-of-type { font-size: var(--fs-lead); line-height: var(--lh-lead); color: var(--ink); }

/* On-this-page table of contents (rail) */
/* Density pass 2026-09-15, calibrated against the reference build's .blog-toc
   (theirs 319px for seven items, ours was 443px). The surface STAYS: this project
   has been told before that stripping surfaces to bare text reads duller, so the
   saving comes from leading and padding, not from deleting the card.
   `line-height: 1.3` is most of it, because three of the seven labels wrap. */
.toc-card { background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-lg); padding: 1.25rem 1.35rem; box-shadow: var(--shadow); margin-bottom: 1.25rem; }
.toc-card h2 { font-size: var(--fs-md); margin: 0 0 .7rem; color: var(--ink); }
/* Tight leading WITHIN an item, a real gap BETWEEN them, so a wrapped second line
   reads as part of its entry rather than as a new one. */
.toc-card ol { list-style: none; margin: 0; padding: 0; counter-reset: toc; display: grid; gap: .25rem; }
.toc-card li { counter-increment: toc; }
.toc-card a { display: block; padding: .3rem .6rem .3rem 2rem; position: relative; color: var(--text); font-weight: 700; font-size: var(--fs-sm); line-height: 1.3; }
.toc-card a::before { content: counter(toc); position: absolute; left: .55rem; color: var(--brand); font-weight: 800; }
.toc-card a:hover { background: var(--brand-tint); color: var(--brand-dark); text-decoration: none; }
/* Scroll-spy: the section currently being read. Set by main.js, which also sets
   aria-current, so it is not colour-only. The left rule is the carrier of the
   state, so it still reads with a background the user's contrast settings drop. */
.toc-card a.is-active { background: var(--brand-tint); color: var(--brand-darker); box-shadow: inset 2px 0 0 var(--brand); }
.toc-card a.is-active::before { color: var(--brand-darker); }
/* The active item already wears the hover tint, so hovering it changed nothing and
   it was the last element on the site with no hover feedback. Deepen the rule
   instead; inset shadow cannot reflow the label the way a weight change would. */
.toc-card a.is-active:hover { color: var(--brand-darker); box-shadow: inset 3px 0 0 var(--brand-dark); }

/* =============================================================================
   Contact
   ============================================================================= */
.contact-grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
/* min-width:0 lets grid columns shrink below their content's min-content (the <select>'s
   longest option) — without it the form column overflows the viewport at ~320px. */
.contact-grid > * { min-width: 0; }
@media (min-width: 880px) { .contact-grid { grid-template-columns: 1.2fr .8fr; } }
/* Stacked (mobile/tablet): lead with the emergency CTA + contact details,
   then the form. Desktop keeps form-left / sidebar-right via source order. */
@media (max-width: 879px) { .contact-info { order: -1; } }
.contact-lead { font-size: var(--fs-lead); color: var(--muted); }
/* Stacked, the row's gap lands on top of .form-group's own margin, so Name to
   Phone was spaced twice as far apart as every other pair. The gap only exists
   once the row is actually a row. */
.contact-form .form-row { display: grid; gap: 0; grid-template-columns: 1fr; }
@media (min-width: 560px) { .contact-form .form-row { grid-template-columns: 1fr 1fr; gap: 1.1rem; } }
.form-group { display: flex; flex-direction: column; gap: .5rem; margin-bottom: 1.25rem; min-width: 0; }
.form-group label { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }
.form-group input, .form-group select, .form-group textarea { font: inherit; padding: .8rem 1rem; border: 1px solid var(--line); border-radius: var(--radius); background: #fff; color: var(--text); width: 100%; transition: border-color .15s, box-shadow .15s; }
.form-group input:hover, .form-group select:hover, .form-group textarea:hover { border-color: var(--brand-tint-2); }
/* Visible focus ring (the pale --brand-tint ring was ~1.1:1); brand-bright @ .35
   reads clearly while the border also flips to --brand. Keep :focus so the active
   field highlights for mouse users too. */
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { outline: none; border-color: var(--brand); box-shadow: var(--ring); }
.form-group .required { color: var(--cta); }
/* No per-field inline error text (it told visitors nothing and bloated the
   form). Invalid required fields just get a red border; one submit-level status
   message carries any real feedback. */
.is-invalid { border-color: #B42318 !important; }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.contact-form-status { padding: 1rem 1.25rem; border-radius: var(--radius); margin-bottom: 1.25rem; font-weight: 700; }
.contact-form-status.is-success { background: var(--brand-tint); color: var(--brand-darker); border: 1px solid var(--brand-tint-2); }
.contact-form-status.is-error { background: #FDECEA; color: #B42318; border: 1px solid #F4C7C2; }

/* "What happens next" under the form — balances the left column against the
   taller sidebar with content that actually helps, not filler. */
.contact-next { margin-top: 2.75rem; padding-top: 2rem; border-top: 1px solid var(--line); }
.contact-next h2 { font-size: var(--fs-h3); color: var(--brand-darker); margin: 0 0 1.4rem; }
.contact-steps { list-style: none; margin: 0; padding: 0; counter-reset: step; display: grid; gap: 1.15rem; }
.contact-steps li { counter-increment: step; position: relative; padding-left: 2.9rem; color: var(--muted); font-size: var(--fs-sm); }
.contact-steps li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 1.95rem; height: 1.95rem; display: inline-flex; align-items: center; justify-content: center; background: var(--brand-tint); color: var(--brand-dark); border-radius: var(--radius); font-weight: 800; font-size: var(--fs-xs); }
.contact-steps strong { color: var(--ink); }

/* The sidebar is ONE panel with two sections, not two floating cards — the ink
   emergency section sits flush on top of the green "not an emergency" section, so
   the two paths read as a single ranked choice. Radius + shadow belong to the
   wrapper; the sections inside are square and gapless. */
.contact-info { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-md); align-self: start; }

/* Emergency section — ink (near-black) so it out-ranks the green section beneath it
   and reads as the urgent path. On mobile the whole sidebar is ordered above the
   form (see .contact-grid order below). */
/* Extra ink below the red CTA: the green section sits flush underneath, so the
   near-black buffer is what stops red-above-green reading as Christmas. */
.contact-emergency { background: linear-gradient(160deg, var(--red-deep), var(--red-deep-2)); color: #fff; border-top: 3px solid var(--red-bright); padding: clamp(1.6rem, 3vw, 2rem); padding-bottom: clamp(2.4rem, 4.5vw, 3rem); }
.contact-emergency h2 { color: #fff; font-size: var(--fs-h3); margin: 0 0 .7rem; }
.contact-emergency p { color: rgba(255,255,255,.78); font-size: var(--fs-sm); margin: 0 0 1.4rem; text-wrap: balance; }
.contact-emergency .btn { width: 100%; }
.contact-emergency-note { text-align: center; font-size: var(--fs-xs); color: rgba(255,255,255,.6); margin: .85rem 0 0 !important; }

/* Second section of the same panel: the non-urgent path. Flush against the ink
   section above it — no radius, no shadow, no gap; the wrapper owns those. */
.contact-card { background: linear-gradient(155deg, var(--brand-dark), var(--brand-darker)); color: #fff; border: 0; padding: var(--pad-panel); }
.contact-card h2 { color: #fff; font-size: var(--fs-h3); margin: 0 0 .7rem; }
.contact-card-lead { color: rgba(255,255,255,.78); font-size: var(--fs-sm); margin: 0 0 1.4rem; text-wrap: balance; }
.contact-rows { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.25rem; }
.contact-rows li { display: flex; gap: .9rem; align-items: flex-start; }
/* Let the text column shrink and long values (email) break, so the row never
   overflows on narrow screens (the icon is flex:none). */
.contact-rows li > div { min-width: 0; }
.contact-row-value { overflow-wrap: anywhere; }
.contact-row-icon { display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: var(--radius); background: rgba(255,255,255,.12); color: #fff; flex: none; }
/* Office hours: two day-ranges stacked under one label, with the weekend note in
   the same fine-print treatment as the card note below it. */
/* Day range and time each hold together, so a narrow card breaks at the comma
   instead of stranding "to 4:30pm" on its own line. */
.contact-row-hours { display: block; }
.contact-row-hours > span { white-space: nowrap; }
.contact-row-note { display: block; margin-top: .35rem; font-size: var(--fs-xs); color: rgba(255,255,255,.78); text-wrap: pretty; }
.contact-row-label { display: block; font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; color: rgba(255,255,255,.7); font-weight: 700; }
.contact-row-value { font-weight: 800; color: #fff; }
a.contact-row-value { display: inline-block; padding-block: .4rem; margin-block: -.4rem; text-decoration: underline; text-underline-offset: .15em; }
/* Was color:#fff, which is the base colour, so hovering did nothing at all.
   These sit on the dark green card, so lift toward the brand tint. */
a.contact-row-value:hover { color: var(--brand-tint); text-decoration-thickness: 2px; }
/* Replacements for former inline style="" attributes — our CSP (style-src 'self')
   blocks inline styles, so these silently did nothing (causing collapsed margins /
   left-aligned CTAs). Moved here so they actually apply. */
/* Tablet stacks these panels to full width, where their small type ran 93-99ch.
   Cap the measure; at desktop the column is already narrower than this. */
.contact-card-lead, .contact-card-note, .contact-emergency p, .svc-emergency p,
.contact-steps li, .service-section p, .rail-card > p, .plan-card > p { max-width: 64ch; }
.contact-card-note { color: rgba(255,255,255,.8); margin: 1.4rem 0 0; padding-top: 1.4rem; border-top: 1px solid rgba(255,255,255,.18); font-size: var(--fs-sm); }
.prose--cta { text-align: center; }
.prose--cta .hero-actions { justify-content: center; }

/* =============================================================================
   404 / not found
   ============================================================================= */
@media (min-width: 700px) { .not-found-grid { grid-template-columns: repeat(2, 1fr); } }

/* =============================================================================
   Footer
   ============================================================================= */
.site-footer { background: var(--bg-dark); color: rgba(255,255,255,.78); border-top: 4px solid var(--brand); }
/* Global `strong` is dark ink for light sections; on the dark footer it must flip
   to white or it's illegible (was --ink #1B2A21 on #0C2417 ≈ 1.1:1). */
.site-footer strong { color: #fff; }
.footer-inner { display: grid; gap: 2.25rem; padding-block: clamp(2.5rem, 5vw, 4rem); grid-template-columns: 1fr; }
/* 2×2 in the tablet range — 4 blocks in 3 cols stranded "Get in Touch" in an empty
   row; 4 cols don't fit the email until ~1000px. */
@media (min-width: 700px) { .footer-inner { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .footer-inner { grid-template-columns: 1.7fr .9fr 1.2fr 1.3fr; gap: 2.5rem 2rem; } }
.footer-brand .footer-logo { font-size: 1.5rem; }
/* Wordmark on the dark footer: lighten the two greens so both clear AA large-text
   contrast on --bg-dark (header values were 3.75:1 / 2.28:1 — "Today" failed). */
.footer-brand p { font-size: var(--fs-sm); margin-top: .85rem; }
.footer-bridge { font-size: var(--fs-sm); color: rgba(255,255,255,.65); border-left: 3px solid var(--cta); padding-left: .85rem; }
.footer-bridge a { color: #fff; display: inline-block; margin-top: .25rem; font-weight: 700; }
/* Same reason as .hero-bridge: white on dark, so the generic deepen-the-green
   hover would be invisible. Scoped to outrank it. */
p.footer-bridge a:not(.btn):hover { color: var(--gold); }
.site-footer h2 { color: #fff; font-size: var(--fs-md); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 1rem; }
/* Tap targets (WCAG 2.2 target size, 24x24 minimum; 44 is the comfortable size
   on a phone). List links were 19px tall stacked in a column, which is a thumb
   miss waiting to happen. The link, not the li, carries the padding so the whole
   row is tappable, and the gap shrinks to keep the block from ballooning. */
.footer-nav ul { list-style: none; padding: 0; margin: 0; display: grid; gap: .55rem; }
.footer-nav a { display: block; padding-block: .45rem; margin-block: -.45rem; color: rgba(255,255,255,.78); font-size: var(--fs-sm); }
@media (max-width: 900px) { .footer-nav ul { gap: .15rem; } .footer-nav a { padding-block: .7rem; margin-block: 0; } }
.footer-nav a:hover { color: #fff; }
/* "Full guide" badge (footer + 404 lists, both on the dark surface) + the honest
   "see all on our overview" link that frames the topic list as jumps, not a page index. */
.footer-contact-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .8rem; }
.footer-contact-list a { display: inline-block; padding-block: .35rem; margin-block: -.35rem; }
@media (max-width: 900px) { .footer-contact-list a { padding-block: .55rem; margin-block: 0; } }
.footer-contact-list li { display: flex; gap: .6rem; align-items: flex-start; font-size: var(--fs-sm); color: rgba(255,255,255,.78); }
.footer-contact-list .ico { color: var(--brand-bright); flex: none; margin-top: .15rem; font-size: 1rem; }
/* Underline in-text footer contact links so they're distinguishable without relying
   on colour alone (WCAG 1.4.1 — axe link-in-text-block). */
.footer-locations a { color: rgba(255,255,255,.85); text-decoration: underline; text-underline-offset: .15em; }
.footer-locations a:hover { color: #fff; }
.footer-locations .ico { color: var(--brand-bright); margin-right: .4rem; }
.footer-social { list-style: none; display: flex; gap: .6rem; padding: 0; margin: 1.1rem 0 0; }
/* Social chips are ONE component in three places (topbar, footer, drawer). They had
   drifted into three treatments — the footer was a borderless circle while the other
   two were bordered semi-squares. Shape/border/background are shared here; only the
   size and the surface (dark vs light) differ. Keep it that way. */
.topbar-social a, .footer-social a, .nav-extra-social a {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius); border: 1px solid transparent;
  transition: background .15s, border-color .15s, color .15s;
}
.topbar-social a, .footer-social a {
  background: rgba(255,255,255,.09); border-color: rgba(255,255,255,.16); color: #fff;
}
.topbar-social a:hover, .footer-social a:hover {
  background: var(--brand); border-color: var(--brand); color: #fff; text-decoration: none;
}
.footer-social a { width: 44px; height: 44px; }
.footer-social .social-ico { width: 18px; height: 18px; }
/* Self-hosted brand glyphs (www/icon) recolored via CSS mask so they take the
   link's currentColor — FontAwesome's brand webfont is not bundled. */
.social-ico { width: 18px; height: 18px; background-color: currentColor; -webkit-mask: center / contain no-repeat; mask: center / contain no-repeat; }
.social-ico--facebook  { -webkit-mask-image: url("/icon/facebook.svg");  mask-image: url("/icon/facebook.svg"); }
.social-ico--instagram { -webkit-mask-image: url("/icon/instagram.svg"); mask-image: url("/icon/instagram.svg"); }
.social-ico--youtube   { -webkit-mask-image: url("/icon/youtube.svg");   mask-image: url("/icon/youtube.svg"); }
.social-ico--x-twitter { -webkit-mask-image: url("/icon/x-twitter.svg"); mask-image: url("/icon/x-twitter.svg"); }
.social-ico--linkedin  { -webkit-mask-image: url("/icon/linkedin.svg");  mask-image: url("/icon/linkedin.svg"); }
.footer-bottom { border-top: 1px solid rgba(255,255,255,.12); }
.footer-bottom .container { padding-block: 1.25rem; }
.footer-bottom p { margin: 0; font-size: var(--fs-sm); color: rgba(255,255,255,.6); max-width: var(--measure); }
/* Copyright at one end, policy + agency credit at the other. Stacks and left-sets
   on a phone rather than centring, so the bar keeps the same left edge as every
   column above it. */
.footer-bottom-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .35rem 1.5rem; }
.footer-legal { display: flex; align-items: center; flex-wrap: wrap; gap: .1rem .75rem; }
/* Same tap-target treatment as .footer-nav a: the link carries the padding and
   pulls it back with a negative margin, so the row clears 24px without the bar
   growing. */
.footer-legal a { display: inline-block; padding-block: .45rem; margin-block: -.45rem; color: rgba(255,255,255,.78); }
/* (0,3,2), for the same reason as p.topbar-emergency: `p a:not(.btn):hover` is
   (0,2,2) and .footer-legal IS a <p>, so at (0,2,1) this lost and both links
   went --brand-darker green on the dark footer on hover. Found by measuring
   every light-on-dark link's forced :hover colour, not by reading the file. */
p.footer-legal a:not(.btn):hover { color: #fff; }
.footer-legal-sep { width: 3px; height: 3px; border-radius: 999px; background: rgba(255,255,255,.3); }

/* =============================================================================
   Mobile nav (drawer)
   ============================================================================= */
/* Drawer-only enrichment block — hidden on the desktop horizontal nav. */
.nav-extra, .nav-drawer-head { display: none; }
@media (max-width: 1000px) {
  .nav-toggle { display: inline-flex; }
  .nav-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; padding-bottom: 1.1rem; border-bottom: 1px solid var(--line); }
  .nav-drawer-close { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; flex: none; background: none; border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink); font-size: 1.15rem; line-height: 1; cursor: pointer; }
  .nav-drawer-close:hover { background: var(--bg-alt); color: var(--brand-dark); border-color: var(--brand-tint-2); }
  /* Desktop needs the subline on one line for the nav-fit maths; on mobile the nav is
     off-canvas, so let it wrap — keeps the header from overflowing at ~280px. */
  .main-nav { position: fixed; top: 0; right: 0; bottom: 0; width: min(86vw, 360px); background: #fff; transform: translateX(100%); transition: transform .25s ease; z-index: 130; padding: 1rem 1.25rem 2rem; overflow-y: auto; box-shadow: var(--shadow-drawer); }
  .is-resizing .main-nav { transition: none; }
  .main-nav.open { transform: translateX(0); }
  .nav-list { flex-direction: column; align-items: stretch; gap: .15rem; }
  /* The drawer is a WHITE off-canvas panel, so the header bar's white links would be
     invisible here. Restore the dark-on-tint treatment inside the drawer. */
  .nav-link { display: block; padding: .8rem .6rem; border-radius: var(--radius); color: var(--text); box-shadow: none; }
  .nav-link:hover { color: var(--brand-dark); background: var(--brand-tint); }
  .nav-link.active { color: var(--brand-dark); background: var(--brand-tint); box-shadow: none; }
  .nav-link.active:hover { background: var(--brand-tint-2); box-shadow: none; }
  .nav-cta-item { margin-top: 1rem; }
  /* CTA in the white drawer goes back to the solid green fill (white-on-white otherwise). */
  .nav-call { background: var(--cta); color: #fff; border-color: var(--cta); margin-left: 0; }
  .nav-call:hover { background: var(--cta-dark); color: #fff; border-color: var(--cta-dark); }
  .nav-call { justify-content: center; width: 100%; }
  .nav-scrim { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.45); opacity: 0; visibility: hidden; transition: opacity .25s; z-index: 120; }
  .nav-scrim.open { opacity: 1; visibility: visible; }

  /* Fill the drawer with real, useful content (was empty white space): a quote
     CTA, emergency/email/area, and credentials. Uses the site's own facts. */
  .nav-extra { display: block; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }
  .nav-extra-cta { width: 100%; margin-bottom: 1.75rem; }
  .nav-extra-title { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .06em; font-weight: 800; color: var(--muted); margin: 0 0 .9rem; }
  .nav-extra-contact { list-style: none; margin: 0; padding: 0; display: grid; gap: .9rem; }
  .nav-extra-contact a { display: inline-block; padding-block: .5rem; margin-block: -.5rem; }
  .nav-extra-contact li { display: flex; gap: .65rem; align-items: flex-start; font-size: var(--fs-sm); color: var(--text); }
  .nav-extra-contact .ico { color: var(--brand); flex: none; margin-top: .15rem; font-size: 1.05rem; }
  .nav-extra-label { display: block; font-size: var(--fs-xs); color: var(--muted); font-weight: 700; }
  .nav-extra-contact a { color: var(--brand-dark); font-weight: 800; }
  .nav-extra-creds { display: flex; gap: .55rem; align-items: flex-start; margin: 1.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--line); font-size: var(--fs-xs); color: var(--muted); text-wrap: balance; }
  .nav-extra-creds .ico { color: var(--brand); flex: none; margin-top: .1rem; }
  .nav-extra-social { list-style: none; display: flex; gap: .5rem; margin: 1.25rem 0 0; padding: 0; }
  /* Same chip as topbar/footer, on a light surface: tinted fill + border, not bare. */
  .nav-extra-social a { width: 42px; height: 42px; background: var(--brand-tint); border-color: var(--line); color: var(--brand-dark); }
  .nav-extra-social a:hover { background: var(--brand); border-color: var(--brand); color: #fff; }
  .nav-extra-social .social-ico { width: 17px; height: 17px; }
}

/* Ultra-narrow (< 320px, e.g. Galaxy Fold cover screen — below the WCAG reflow floor):
   the fixed-width wordmark (229px) + hamburger overflow. Shrink the mark so the header
   still fits. Scoped below 320 so the standard mobile design is untouched. */
@media (max-width: 319px) {
  .header-inner { gap: .5rem; }
  /* .btn is nowrap by design so labels stay on one line. Below 320 a long label
     ("Read about drain cleaning") is 303px and forces horizontal scroll, so let it wrap
     and tighten the large-button padding. */
  .btn { white-space: normal; }
  .btn-lg { padding-inline: 1.1rem; }
  .logo-img { width: 132px; }
  /* Long unbroken strings (URLs, #M5944, phone numbers) must not push the column wider. */
  .article-main, .prose, .article-main p, .article-main li { overflow-wrap: anywhere; }
}

/* Forced-colors / Windows High Contrast: the browser forces background-color to the
   system Canvas colour, so our mask icons (which paint via background-color:currentColor)
   become invisible. Re-assert an explicit system foreground colour so they show. */
@media (forced-colors: active) {
  .ico,
  .social-ico,
  .area-county li::before,
  .service-section ul li::before { background-color: CanvasText; }
  .btn .ico,
  .nav-call .ico { background-color: ButtonText; }
}

/* =============================================================================
   EDITORIAL SPIKE — Emerald Club (branch spike/emerald-editorial, 2026-08-13)
   The site's every section is centred eyebrow → centred h2 → centred lead → a
   row of cards, six times a page, five card systems deep. That rhythm is what
   reads as a template. This is the alternative, scoped to .ed-* so no other page
   moves until the direction is chosen: section heads left-set and indexed,
   content in an asymmetric split, and money set as type rather than boxed.
   ============================================================================= */
.ed-section { padding-block: clamp(3.5rem, 7vw, 6.5rem); }
.ed-section--alt { background: var(--bg-alt); }

/* Asymmetric split: the head holds a third, the body takes the rest and keeps a
   real measure. Stacks below 900. */
.ed-split { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
@media (min-width: 900px) { .ed-split { grid-template-columns: minmax(15rem, 22rem) minmax(0, 1fr); gap: clamp(3rem, 6vw, 6rem); } }

/* Indexed, left-set section head. The numeral is the device: it paces the page
   and replaces the eyebrow-pill rhythm. */
.ed-head { position: relative; }
.ed-head h2 { font-size: var(--fs-h2); line-height: 1.05; letter-spacing: -.03em; margin: 0; text-wrap: balance; }
.ed-index { display: block; font-size: var(--fs-sm); font-weight: 900; letter-spacing: .18em; color: var(--gold-dark); margin-bottom: .9rem; }
.ed-index::after { content: ''; display: block; width: 2.5rem; height: 3px; background: var(--gold); margin-top: .55rem; }
.ed-head-note { margin: 1rem 0 0; color: var(--muted); font-size: var(--fs-sm); max-width: 34ch; text-wrap: pretty; }
/* Pages that are not a numbered sequence get the rule without the numeral. */
.ed-head--rule > h2::before { content: ''; display: block; width: 2.5rem; height: 3px; background: var(--gold); margin-bottom: 1.1rem; border-radius: 999px; }
.ed-head--wide { margin-bottom: clamp(2rem, 4vw, 3rem); max-width: 46rem; }
.ed-head--wide .ed-head-note { max-width: 60ch; font-size: var(--fs-body); }
/* Mark at the TOP of the head column, above the index: hanging it below the
   heading left the column bottom-heavy against the copy beside it. */
.ed-head .club-mark { width: 112px; height: auto; margin-bottom: 1.5rem; }

/* Copy sits on a surface, not bare on the page field. */
.ed-body { max-width: 62ch; }
.ed-panel { max-width: none; background: var(--brand-tint); border-top: 3px solid var(--gold);
  border-radius: var(--radius-lg); padding: clamp(1.6rem, 3vw, 2.4rem); }
.ed-panel > p { max-width: 60ch; }
/* When the panel IS the lead paragraph (article pages), keep the lead's type but
   give it the surface. */
p.ed-panel { margin-bottom: 2rem; }
.ed-panel--white { background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand); box-shadow: var(--shadow); }
.ed-lead { font-size: var(--fs-lead); color: var(--text); font-weight: 600; text-wrap: pretty; }
.ed-body > p:last-child { margin-bottom: 0; }

/* Benefit list: hairline rows, not a two-column tick grid in a card. */
.ed-checks { list-style: none; margin: 0; padding: 0; }
.ed-checks li { position: relative; padding: .95rem 0 .95rem 2.25rem; border-top: 1px solid var(--line); color: var(--text); text-wrap: balance; }
.ed-checks li:last-child { border-bottom: 1px solid var(--line); }
.ed-checks li::before { content: ''; position: absolute; left: 0; top: 1.15rem; width: 1.25rem; height: 1.25rem;
  background-color: var(--brand); -webkit-mask: url("/icon/circle-check.svg") center / contain no-repeat; mask: url("/icon/circle-check.svg") center / contain no-repeat; }

/* The company's own standards, quoted (About). Deliberately the SAME hairline
   row as .ed-checks above it, because the two lists sit in one band and are one
   idea: the values, then the standards. Making this a grid of three cards would
   have been a sixth card system for three sentences.

   The mark is the shared .ico--<slug> mask set, sized by font-size and coloured
   by `color` the way every other .ico on the site is, so adding a standard with
   a different icon needs no new CSS.

   Two lines per row on purpose: the client's sentence, then our plain-language
   note. The quote is his and is never shortened or paraphrased; the gloss may
   sit beside it and never in place of it. Same rule the guarantees run under. */
.ed-creed-intro { margin: 2.25rem 0 1.1rem; color: var(--muted); font-size: var(--fs-sm); }
.ed-creed { list-style: none; margin: 0; padding: 0; }
.ed-creed li { position: relative; padding: 1.15rem 0 1.15rem 2.75rem; border-top: 1px solid var(--line); }
.ed-creed li:last-child { border-bottom: 1px solid var(--line); }
.ed-creed-mark { position: absolute; left: 0; top: 1.3rem; font-size: 1.4rem; color: var(--brand); }
.ed-creed-quote { margin: 0; font-size: var(--fs-body); font-weight: 700; color: var(--ink); text-wrap: pretty; }
.ed-creed-gloss { margin: .45rem 0 0; font-size: var(--fs-sm); color: var(--muted); text-wrap: pretty; }
/* The values list follows the lead paragraph directly; without this it sits on
   the paragraph's 1rem and reads as part of it rather than as a list. */
.ed-body > .ed-checks { margin-top: 1.75rem; }

/* Plans as CARDS with the value figure as the hero. Bare rows on a flat field
   read as a spreadsheet; these are surfaces, with the recommended level carrying
   the brand fill the way the services page's featured block does. */
.ed-plans { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 820px) { .ed-plans { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ed-plan { display: flex; }
.ed-plan > a { display: flex; flex-direction: column; align-items: flex-start; gap: .5rem; width: 100%;
  background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); color: inherit;
  padding: clamp(1.5rem, 2.4vw, 2rem); transition: transform .15s, box-shadow .15s; }
.ed-plan > a:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); text-decoration: none; }
.ed-plan .tag { margin-bottom: .35rem; }
.ed-plan-name { font-size: var(--fs-card); font-weight: 800; color: var(--ink); }
.ed-plan-figure { display: flex; align-items: baseline; gap: .5rem; font-size: clamp(2.1rem, 1.3rem + 2.4vw, 3rem);
  font-weight: 900; letter-spacing: -.035em; line-height: 1.05; color: var(--brand-dark); }
.ed-plan-unit { font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0; color: var(--muted); }
.ed-plan-price { font-weight: 700; color: var(--text); font-size: var(--fs-body); }
.ed-plan-blurb { color: var(--muted); font-size: var(--fs-sm); margin-top: .35rem; text-wrap: pretty; }
.ed-plan-go { margin-top: auto; padding-top: 1rem; font-weight: 800; color: var(--brand-dark); font-size: var(--fs-sm); }
.ed-plan > a:hover .ed-plan-go { text-decoration: underline; }
/* The recommended level: brand surface, gold accents, same card shape. */
.ed-plan--feature > a { background: linear-gradient(155deg, var(--brand-dark), var(--brand-darker)); border: 0; border-top: 3px solid var(--gold); box-shadow: var(--shadow-md); }
.ed-plan--feature .ed-plan-name { color: #fff; }
.ed-plan--feature .ed-plan-figure { color: var(--gold); }
.ed-plan--feature .ed-plan-unit { color: rgba(255,255,255,.8); }
.ed-plan--feature .ed-plan-price { color: #fff; }
.ed-plan--feature .ed-plan-blurb { color: rgba(255,255,255,.85); }
.ed-plan--feature .ed-plan-go { color: var(--gold); }
.ed-foot { margin: 1.75rem 0 0; color: var(--muted); font-size: var(--fs-sm); max-width: var(--measure); }

/* Guarantees, editorial mode: same component, no boxes. */
.guarantees--editorial .guarantee-list { max-width: none; gap: 0; }
.guarantees--editorial .guarantee-row { background: none; border: 0; border-top: 1px solid var(--line); border-radius: 0; box-shadow: none; padding: clamp(1.5rem, 2.5vw, 2.25rem) 0; }
.guarantees--editorial .guarantee-row:last-child { border-bottom: 1px solid var(--line); }
.guarantees--editorial .guarantee-row--named { border-top-color: var(--gold); border-top-width: 3px; }
.guarantees--editorial .guarantee-mark { display: none; }
/* SURFACES, not naked text. The first editorial pass stripped every box out and
   left three text blocks on a flat field, which is duller than what it replaced.
   These are real panels: the named guarantee is a full dark-green surface that
   runs the height of the block, the other two are white cards beside it. That is
   an asymmetric composition made of components (icon badge, panel, tag, link),
   not a grid of three equal rectangles and not a wall of paragraphs. */
.guarantees--editorial .guarantee-list { max-width: none; display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 820px) {
  /* Named guarantee as a wide feature BAND across the top, then the other two as
     cards beneath. Spanning it down a column left a lake of empty green; laid
     across, it fills its surface and the row reads as a hierarchy. */
  .guarantees--editorial .guarantee-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .guarantees--editorial .guarantee-row--named { grid-column: 1 / -1; flex-direction: row; align-items: center; gap: clamp(1.5rem, 3vw, 2.75rem); }
  /* Copy left, action right, both inside the band: the CTA lives inside the body
     in the shared markup, so the body itself carries the two-column split. */
  .guarantees--editorial .guarantee-row--named .guarantee-body { flex: 1 1 auto; height: auto;
    display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
  .guarantees--editorial .guarantee-row--named .guarantee-body h3 { grid-column: 1; grid-row: 1; }
  .guarantees--editorial .guarantee-row--named .guarantee-short { grid-column: 1; grid-row: 2; }
  .guarantees--editorial .guarantee-row--named .guarantee-terms { grid-column: 1; grid-row: 3; }
  .guarantees--editorial .guarantee-cta { grid-column: 2; grid-row: 1 / span 3; margin: 0; align-self: center; white-space: nowrap; }
}
.guarantees--editorial .guarantee-row { display: flex; flex-direction: column; align-items: flex-start; gap: .9rem;
  background: #fff; border: 1px solid var(--line); border-top: 3px solid var(--brand);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: clamp(1.5rem, 2.6vw, 2.25rem); }
.guarantees--editorial .guarantee-mark { display: inline-flex; }
.guarantees--editorial .guarantee-body { display: block; }
.guarantees--editorial .guarantee-body h3 { font-size: var(--fs-h3); margin: 0 0 .5rem; text-wrap: balance; }
.guarantees--editorial .guarantee-short { margin: 0 0 .75rem; color: var(--ink); font-weight: 700; font-size: var(--fs-body); }
.guarantees--editorial .guarantee-terms { margin: 0; font-size: var(--fs-sm); color: var(--muted); text-wrap: balance; }

/* The named guarantee is the page's subject, so it gets the brand surface. */
.guarantees--editorial .guarantee-row--named { background: linear-gradient(155deg, var(--brand-dark), var(--brand-darker));
  border: 0; border-top: 3px solid var(--gold); box-shadow: var(--shadow-md); justify-content: flex-start; }
.guarantees--editorial .guarantee-row--named .guarantee-mark { background: var(--gold); color: #3A2A05; box-shadow: none; }
.guarantees--editorial .guarantee-row--named h3 { color: #fff; font-size: var(--fs-h3); }
.guarantees--editorial .guarantee-row--named .guarantee-short { color: #fff; font-size: var(--fs-lead); }
.guarantees--editorial .guarantee-row--named .guarantee-terms { color: rgba(255,255,255,.88); font-size: var(--fs-body); text-wrap: balance; }
/* Measured, not guessed: at phone widths the band's terms are the longest
   paragraph in the narrowest column. Matching the cards' fine-print size there
   is worth three runts across the three pages that carry this band. */
@media (max-width: 560px) { .guarantees--editorial .guarantee-row--named .guarantee-terms { font-size: var(--fs-sm); } }
.guarantees--editorial .guarantee-row--named .guarantee-link { color: var(--gold); margin-top: auto; padding-top: 1.25rem; }
.guarantees--editorial .guarantee-row--named .guarantee-link:hover { color: #fff; }
/* White fill on the dark panel, same as the services featured block. */
.guarantees--editorial .guarantee-cta { margin-top: auto; align-self: flex-start; background: #fff; color: var(--brand-darker); border-color: #fff; }
.guarantees--editorial .guarantee-cta:hover { background: var(--brand-tint); color: var(--brand-darker); border-color: var(--brand-tint); text-decoration: none; }
.guarantees--editorial .guarantee-row--named .guarantee-mark { align-self: flex-start; flex: none; }
.guarantees--editorial .guarantee-row--named .guarantee-terms { max-width: 68ch; }

/* Masthead aside: one fact, set as type on the band's empty right side. */
.page-header-inner--split { display: grid; gap: clamp(1.5rem, 3vw, 3rem); align-items: end; }
@media (min-width: 900px) { .page-header-inner--split { grid-template-columns: minmax(0, 1fr) minmax(15rem, 20rem); } }
.page-header-aside { margin: 0; font-size: var(--fs-lead); line-height: 1.35; text-wrap: balance;
  border-top: 3px solid var(--gold); padding-top: 1rem; }
/* `strong` is inked globally, which on this band was near-invisible. */
.page-header-aside strong { color: #fff; font-weight: 800; }

/* =============================================================================
   Texture application
   =============================================================================
   The tiles are defined with the tokens at the top of this file; this is the
   ONLY place they are switched on, so the set of textured surfaces is one list
   you can read rather than a decision scattered through thirty components.

   Set LAST, and touching only background-image/size/repeat, because several of
   these families fill themselves with the `background:` shorthand — which
   resets background-image to none and would silently strip the weave.

   WHAT IS DELIBERATELY BARE:
     - Every section on plain white. If everything is woven, nothing reads as a
       change of surface and the whole page becomes one field of cloth.
     - Every card and panel: .service-card, .rail-card, .plan-card,
       .svc-featured, .svc-emergency, .article-card, .toc-card, .faq-item.
       A 45deg line at card size reads as stripes ON the object.
     - The service map, photographs, and the logo lockups.
   ========================================================================== */

/* Light grounds. These are colour-only backgrounds, so background-image is free. */
.section--alt,
.section--tint {
  background-image: var(--tex-hatch);
  background-size: 6px 6px;
  background-repeat: repeat;
}

/* Ink grounds: the trust strip under the masthead and the footer. Both are
   full-width bands, not cards, so they take the weave. */
.trust-strip,
.site-footer {
  background-image: var(--tex-hatch-dark);
  background-size: 6px 6px;
  background-repeat: repeat;
}

/* The CTA band fills itself with a gradient via the `background:` shorthand, so
   its weave rides on a pseudo-element instead of a second background layer. */
.cta-band { position: relative; }
.cta-band::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: var(--tex-hatch-dark);
  background-size: 6px 6px;
  background-repeat: repeat;
}
.cta-inner { position: relative; z-index: 1; }

/* =============================================================================
   Photography — includes/photo-figure.php
   =============================================================================
   ONE rule for every photograph on the site. See the include for why there are
   no variants: the figure is its own container, so it reads the width of the
   column it was dropped into and picks its own arrangement. About's 690px prose
   column gets caption-beside-picture; Home's 460px section column gets
   caption-under-picture; both from these declarations.

   Every photo we have is PORTRAIT or SQUARE — there is not one landscape frame
   in the client's set — so nothing here assumes a wide picture, and no rule
   crops one to fake it. The image keeps its own aspect ratio and imgDims()
   writes the intrinsic width/height, so there is no layout shift either way.
   ========================================================================== */
/* The wrapper is the container; the figure inside it is what reacts. An element
   cannot query itself, so these have to be two elements — see the include. */
.photo-figure { container-type: inline-size; margin: 2.5rem 0; }
.photo-figure-body { display: grid; gap: 1.1rem; margin: 0; }
.photo-figure-img { display: block; width: 100%; height: auto; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); }

/* The caption carries the same gold rule and uppercase label as the masthead
   aside and the editorial section heads, so a photo reads as part of the page's
   editorial furniture rather than as an image someone dropped in. */
/* Capped on the CAPTION, not on the text inside it, so the gold rule ends where
   the sentence ends. The column is a `1fr` and takes whatever the host gives
   it: in the full 1180px container on /services that was a 101-character
   measure under an 880px rule pointing at nothing. Capping the text alone would
   have fixed the measure and left the rule overhanging it. */
.photo-figure-caption { border-top: 3px solid var(--gold); padding-top: .85rem; max-width: 32rem; }
.photo-figure-label { display: block; font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-dark); margin-bottom: .3rem; }
.photo-figure-text { margin: 0; font-size: var(--fs-sm); color: var(--muted); line-height: 1.55; text-wrap: balance; }

/* Wide enough to seat the caption beside the picture. The picture is capped at
   19rem because these are tall: a 3:4 frame run full width of a 690px prose
   column stands 920px high and swallows the page it is illustrating. */
/* Aligned to the TOP, so the caption's gold rule lands on the photo's top edge
   and the two columns share a real horizontal line. Bottom-aligning them was
   tried first and left the rule floating in the middle of an empty column with
   nothing to agree with. */
@container (min-width: 34rem) {
  .photo-figure-body { grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); gap: 1.9rem; align-items: start; }
  .photo-figure-caption { border-top-width: 4px; padding-top: 1rem; }
}

/* On a dark ground the muted caption colour and the ink label both disappear. */
.hero .photo-figure-text,
.cta-band .photo-figure-text,
.svc-featured .photo-figure-text { color: rgba(255,255,255,.85); }
.hero .photo-figure-label,
.cta-band .photo-figure-label,
.svc-featured .photo-figure-label { color: var(--gold); }
