/* ==========================================================================
   Up To Date Construction Group — site stylesheet (from tools/website_starter)

   Header style: LIGHT — matches the client's legacy Elementor site (white header,
   dark logo). The mobile slide-in panel is dark, so every nav color is restated
   inside the ≤980px block (the light-to-dark port trap, Rule 12 / standards §4).

   ── COLOR CONTRACT (all ratios computed 2026-09-16, WCAG 2.x relative luminance) ──
   Brand: teal #13A89E, green #8CC63F, grey #6D6E71, Viga + Open Sans.
   White on teal is 2.95:1 and white on green is 2.05:1 — BOTH FAIL as text. So the
   brand colors are GROUNDS with a dark label, never white labels:
     --accent #8CC63F  fill, --on-accent = --ink #23272A on it        7.36:1
     --accent-hover #9BD452  (brighter, hover never worse than resting)  8.55:1 with ink
     --primary #13A89E  as a fill takes --ink on top                    5.11:1
   Teal as INK on white is too light (2.95:1), so links/icons use a darkened teal:
     --accent-ink #0C6F69 on white 6.01:1, on --surface #F6F7F8 5.60:1
     --accent-ink-dark #0A605B on white 7.40:1
   Dark grounds are a deep teal-black, not slate:
     --primary-deep #0C2B29: white 15.07:1, --on-dark #5FD3C9 8.36:1, --accent 7.37:1,
                             --muted-on-dark #C2C6C8 8.76:1
     --primary-dark #10312F (hero overlay ground): white 13.98:1
     --footer-bg #081F1D one step below deep; every footer token is higher there.
     --panel-bg = --primary-deep (mobile nav panel); --on-dark measured on it, 8.36:1.
   Light header: --header-text = --ink on white 15.9:1; nav hover tint rgba(35,39,42,.06).
   ========================================================================== */

:root {
  --primary: #13A89E;       /* brand teal — a GROUND (fills, badges); label on it is --ink (5.11:1) */
  --primary-dark: #10312F;    /* hero overlay ground; white on it is 13.98:1 */
  --primary-deep: #0C2B29;    /* every dark band: trust bar, stats, CTA band, dark sidebar card, sticky bar */
  --footer-bg: #081F1D;       /* one step below --primary-deep so the footer reads as its own band */

  --accent: #8CC63F;          /* brand green — FILLS, with --on-accent on top (7.36:1) */
  --on-accent: #23272A;       /* DARK label on green; white would be 2.05:1 */
  --accent-hover: #9BD452;    /* brighter on hover; ink on it 8.55:1 (never worse than resting) */
  --accent-ink: #0C6F69;      /* teal TEXT on white — links, icons (6.01:1; 5.60:1 on --surface) */
  --accent-ink-dark: #0A605B; /* hover shade for teal text (7.40:1) */
  --accent-tint: #E7F6F5;     /* pale teal ground; --accent-ink on it 5.41:1 */
  --on-dark: #5FD3C9;         /* teal text/icons on --primary-deep (8.36:1) and --panel-bg */
  --muted-on-dark: #C2C6C8;   /* secondary text on the dark grounds (8.76:1 on deep) */
  --warm: #F1F8E6;            /* pale green ground */

  --ink: #23272A;
  --body: #3F464B;
  --muted: #5C646A;
  --line: #E1E4E6;
  --surface: #F6F7F8;
  --surface-2: #ECEEF0;
  --white: #ffffff;

  /* LIGHT header — the legacy site's white header with the dark logo. */
  --header-bg: #ffffff;
  --header-text: #23272A;
  --header-line: #E1E4E6;
  /* The slide-in mobile panel is the deep teal; every nav color is restated for it
     in the ≤980px block below — a :root swap cannot do this. */
  --panel-bg: #0C2B29;
  --panel-line: #1E4744;

  /* Rule 26: the tokens hold TEXT faces. Viga (single weight, display) is bound to the
     heading rules only, further down. */
  --font-head: 'Open Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Open Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Viga', 'Open Sans', system-ui, sans-serif;

  --wrap: 1180px;
  --radius: 14px;
  --shadow: 0 1px 2px rgba(35,39,42,.05), 0 8px 24px rgba(35,39,42,.07);
  --shadow-lg: 0 2px 4px rgba(35,39,42,.05), 0 22px 50px rgba(35,39,42,.16);
}

/* ── reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}
body {
  margin: 0;
  font-synthesis: none;   /* Viga ships one weight; never let the browser faux-bold it */
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  color: var(--body);
  background: var(--white);
}
img { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { font-family: var(--font-head); color: var(--ink); line-height: 1.2; margin: 0 0 .55em; }
/* Display face on the two heading levels that carry the brand voice, at weight 400
   (Viga's only cut). h3 and smaller stay on Open Sans, where reading-size legibility wins. */
h1, h2 { font-family: var(--font-display); font-weight: 400; }
h1 { font-size: clamp(2.1rem, 4.6vw, 3.3rem); letter-spacing: .005em; }
h2 { font-size: clamp(1.6rem, 3vw, 2.3rem); letter-spacing: .005em; text-wrap: balance; }
h3 { font-size: 1.2rem; font-weight: 700; }
p { margin: 0 0 1.1em; }
a { color: var(--accent-ink); }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 3px solid var(--accent-ink); outline-offset: 2px; border-radius: 3px;
}
.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.section { padding: clamp(3.5rem, 7vw, 6rem) 0; }
.section-alt { background: var(--surface); }
.section-sub { max-width: 64ch; color: var(--muted); font-size: 1.06rem; margin-bottom: 2.4rem; }
.icon { flex: none; vertical-align: -.18em; }

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

/* ── buttons ──────────────────────────────────────────────────────────────
   Rule 12: base colors carry NO !important, so a container can restyle a
   button by specificity rather than by fighting it. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-family: var(--font-head); font-weight: 700; font-size: 1rem;
  padding: .95rem 1.7rem; border-radius: 999px; border: 2px solid transparent;
  text-decoration: none; cursor: pointer; transition: background-color .15s, color .15s, border-color .15s;
  white-space: nowrap; flex: 0 0 auto;   /* a label or a phone number is one token (checklist 39b) */
}
.btn-accent { background: var(--accent); color: var(--on-accent); }
.btn-accent:hover, .btn-accent:focus-visible { background: var(--accent-hover); color: var(--on-accent); }
.btn-outline { background: transparent; color: var(--accent-ink); border-color: var(--accent-ink); }
.btn-outline:hover, .btn-outline:focus-visible { background: var(--accent-ink); color: var(--white); }
/* on slate grounds: a white-outlined button, since copper fails there */
.btn-ondark { background: transparent; color: var(--white); border-color: rgba(255,255,255,.75); }
.btn-ondark:hover, .btn-ondark:focus-visible { background: var(--white); color: var(--primary-deep); border-color: var(--white); }  /* deep on white 15.07:1 — brand teal on white is 2.95 and fails */
.btn-block { width: 100%; }

/* ── trust bar ────────────────────────────────────────────────────────────── */
.trust-bar { background: var(--primary-deep); color: var(--muted-on-dark); font-size: .875rem; }
.trust-bar-inner { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem 1.6rem; padding: .55rem 0; }
.trust-item { display: inline-flex; align-items: center; gap: .4rem; }
.trust-item .icon { color: var(--on-dark); }
.trust-call { margin-left: auto; color: var(--white); font-weight: 600; text-decoration: none; }
.trust-call:hover { text-decoration: underline; }
@media (max-width: 720px) { .trust-bar { display: none; } }

/* ── header ───────────────────────────────────────────────────────────────── */
header {
  position: sticky; top: 0; z-index: 100;
  background: var(--header-bg); border-bottom: 1px solid var(--header-line);
  box-shadow: 0 1px 3px rgba(35,39,42,.05);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.2rem; padding: .6rem 0; min-height: 76px; }
.logo { display: block; flex: none; }
.logo img { width: auto; height: 60px; }
@media (max-width: 600px) { .logo img { height: 48px; } }

.nav-desktop { display: flex; align-items: center; gap: .25rem; list-style: none; margin: 0; padding: 0; }
/* Rule 12 again: :not(.btn) sits on BOTH the base rule and the hover rule.
   Without it on the base rule, .nav-call's resting label inherits this color.

   ── THE `> li >` IS LOAD-BEARING. DO NOT FLATTEN IT BACK TO A DESCENDANT. ───
   This rule is for TOP-LEVEL links only. Written as a descendant selector it
   also matched every link inside .sub, and it BEAT both submenu rules on
   specificity, because :not() contributes its argument's weight:

     .nav-desktop a:not(.btn):not(.nav-call):not(.nav-quote)   (0,4,1)  WON
     .nav-desktop .sub a:not(.btn)                             (0,3,1)  lost
     .nav-desktop .sub-all a                                   (0,2,1)  lost

   So every dropdown link took --header-text (white) and rendered white-on-white
   on the .sub card: both desktop dropdowns were BLANK, sitewide. Nothing caught
   it, and nothing was going to — the dropdown is visibility:hidden at rest, so
   axe never measures it, and mobile masks it because white on the slate panel
   is perfectly readable. Only hovering it on desktop shows the bug.

   The child combinator is the fix rather than more :not()s: these links are not
   in .sub, so the rule should never have reached them in the first place. */
.nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote) {
  display: block; padding: .6rem .8rem; border-radius: 8px;
  color: var(--header-text); text-decoration: none; font-weight: 600; font-size: .97rem;
}
.nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):hover,
.nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):focus-visible { background: rgba(35,39,42,.06); color: var(--accent-ink-dark); }
/* The call button is excluded from BOTH rules above rather than overriding them
   with !important. A hover-only exclusion would leave its RESTING label taking
   the nav link color — the failure mode that reads fine on screen and fails
   axe at the base state. */
.nav-cta { margin-left: .35rem; }
/* outlined quote button, then the filled phone button — the estate's pair */
.nav-desktop a.nav-quote { display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--accent-ink); border: 1.5px solid var(--accent-ink);
  padding: .62rem 1.15rem; border-radius: 8px; text-decoration: none; font-weight: 700; font-size: .95rem; }
.nav-desktop a.nav-quote:hover, .nav-desktop a.nav-quote:focus-visible {
  background: var(--accent-ink); border-color: var(--accent-ink); color: var(--white); }
.nav-desktop a.nav-call { display: inline-flex; align-items: center; gap: .45rem;
  background: var(--accent); color: var(--on-accent); padding: .62rem 1.15rem; border-radius: 8px;
  text-decoration: none; font-weight: 700; font-size: .95rem; }
.nav-desktop a.nav-call:hover, .nav-desktop a.nav-call:focus-visible { background: var(--accent-hover); color: var(--on-accent); }

.has-sub { position: relative; }
.sub {
  position: absolute; left: 0; top: 100%; min-width: 250px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); list-style: none; margin: 0; padding: .4rem;
  opacity: 0; visibility: hidden; transform: translateY(6px); transition: opacity .15s, transform .15s, visibility .15s;
}
.has-sub:hover > .sub, .has-sub:focus-within > .sub { opacity: 1; visibility: visible; transform: translateY(0); }
/* These now style themselves completely: the base rule above no longer
   reaches them, so display, text-decoration and radius have to be restated
   here or the dropdown renders as underlined inline text. */
.nav-desktop .sub a:not(.btn) { display: block; padding: .55rem .7rem; border-radius: 8px;
  color: var(--body); text-decoration: none; font-weight: 500; font-size: .97rem; }
.nav-desktop .sub a:not(.btn):hover,
.nav-desktop .sub a:not(.btn):focus-visible { background: var(--surface); color: var(--accent-ink); }
/* :not(.btn) here only to match the specificity of .sub a:not(.btn) above —
   at (0,3,1) they tie and this one wins on source order. Without it this rule
   is (0,2,1) and the emphasis link silently takes the ordinary link color. */
.nav-desktop .sub-all a:not(.btn) { font-weight: 700; color: var(--accent-ink); border-top: 1px solid var(--line); margin-top: .25rem; }

/* Border at .55 not .40: composited over the #4E5255 header, .40 gives
   #959799 = 2.69:1 against it, under the 3:1 that WCAG 1.4.11 wants for a
   control boundary. .55 gives #AFB1B2 = 3.66:1, and matches .nav-quote. */
/* Light header: dark burger. Border #8A9094 on white is 3.35:1 (WCAG 1.4.11 wants 3:1). */
.hamburger { display: none; flex-direction: column; justify-content: center; gap: 5px; width: 46px; height: 42px; margin-left: auto; padding: 0 10px; background: transparent; border: 1px solid #8A9094; border-radius: 10px; cursor: pointer; }
.hamburger span { display: block; height: 2px; background: var(--ink); border-radius: 2px; transition: transform .2s, opacity .2s; }

/* ── mobile nav (Rule 25) ─────────────────────────────────────────────────── */
@media (max-width: 980px) {
  .hamburger { display: flex; }
  .nav {
    position: fixed; inset: 0 0 0 auto; width: min(86vw, 360px);
    background: var(--panel-bg); border-left: 1px solid var(--panel-line);
    padding: 5.5rem 1.1rem 2rem; overflow-y: auto;
    /* the closed state */
    transform: translateX(100%); transition: transform .25s ease; z-index: 120;
  }
  /* Rule 25: EVERY state resets the transform explicitly — never rely on the
     desktop rule not having applied. */
  body.nav-open .nav { transform: translateX(0); }
  .nav-desktop { flex-direction: column; align-items: stretch; gap: .15rem; }
  /* the panel is DARK while the header is light: restate every nav color here */
  .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote) { padding: .85rem .7rem; font-size: 1.05rem; color: var(--white); }
  .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):hover,
  .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):focus-visible { background: rgba(255,255,255,.12); color: var(--white); }
  .nav-desktop a.nav-quote { color: var(--white); border-color: rgba(255,255,255,.7); }
  .nav-desktop a.nav-quote:hover, .nav-desktop a.nav-quote:focus-visible { background: var(--white); color: var(--primary-deep); border-color: var(--white); }
  .nav-cta { margin-left: 0; }
  .nav-desktop a.nav-quote, .nav-desktop a.nav-call { width: 100%; margin-top: .6rem; padding: .8rem 1rem; }

  /* submenus fold rather than float */
  .sub {
    position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: 0; border-left: 2px solid var(--panel-line);
    border-radius: 0; background: transparent; margin: 0 0 .4rem .7rem; padding: 0 0 0 .5rem;
    display: none;
  }
  .has-sub.open > .sub { display: block; }
  .has-sub:hover > .sub, .has-sub:focus-within > .sub { transform: none; }
  /* submenu links sit on the SLATE panel here, not on white — the desktop rule
     above sets them to --body, which is invisible on slate. This is the exact
     bug a light-to-dark header port leaves behind. */
  /* The desktop submenu rule sets these links to --body, which is invisible
     on a dark panel, so each color is restated here for the dark ground rather
     than swapped at :root. Measured on --panel-bg #2F3234: --muted-on-dark
     7.50:1, --white on the hover ground 8.80:1, --on-dark 5.52:1.
     (The previous note here claimed --on-dark was 4.51:1 on #4E5255. It is
     3.37:1. That number was never true, and it was the reason the panel ground
     looked safe.) */
  .nav-desktop .sub a:not(.btn) { color: var(--muted-on-dark); }
  .nav-desktop .sub a:not(.btn):hover,
  .nav-desktop .sub a:not(.btn):focus-visible { background: rgba(255,255,255,.12); color: var(--white); }
  .nav-desktop .sub-all a:not(.btn) { color: var(--on-dark); border-top-color: var(--panel-line); }

  /* The panel is z-index 120 and the header only 100, so the burger was BURIED
     under the open panel — the X was unclickable and Escape was the only way
     out. Lift it above the panel while open. Rule 25 wants burger -> X, and an
     X you cannot press is not a close control. */
  body.nav-open .hamburger { position: relative; z-index: 130; border-color: rgba(255,255,255,.55); background: var(--primary-deep); }
  body.nav-open .hamburger span { background: var(--white); }
  body.nav-open .hamburger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  body.nav-open .hamburger span:nth-child(2) { opacity: 0; }
  body.nav-open .hamburger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
  body.nav-open { overflow: hidden; }
}

/* ── hero ─────────────────────────────────────────────────────────────────── */
.hero { position: relative; background: var(--primary-deep); overflow: hidden; }
.hero-bg { position: absolute; inset: 0; }
.hero-bg img { width: 100%; height: 100%; object-fit: cover; }
/* Directional veil. Stops chosen from measurement, not taste: at 0.72 over the
   photo's BRIGHTEST pixel white still reads 6.16:1, and over the roof itself
   11.5:1. It then falls to 0.18 on the form side, where no text sits, so the
   photograph is actually visible. A flat 0.9 wash — the first attempt here —
   makes the hero a gray box and wastes the only asset a competitor cannot copy. */
.hero-overlay { position: absolute; inset: 0; background:
  linear-gradient(100deg,
    rgba(12,43,41,.90) 0%,
    rgba(12,43,41,.80) 34%,
    rgba(12,43,41,.50) 62%,
    rgba(12,43,41,.28) 100%); }
/* bottom scrim so the trust row stays legible over a bright lower edge */
.hero-overlay::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(8,31,29,.6) 0%, transparent 34%); }
/* Hero VIDEO (the client's one explicit request from the old site). Sits under the same
   overlay as the poster; the poster <img> stays in the DOM underneath so the frame is
   painted before the first video byte arrives and stays painted when the video never
   loads. Autoplay is muted + playsinline; preload=none so a phone on data pays nothing
   until the page decides to play. Solid --primary-deep behind everything (gate 48). */
.hero-bg video, .hero-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (prefers-reduced-motion: reduce) { .hero-bg video, .hero-video { display: none; } }

.hero-inner { position: relative; z-index: 1; display: grid; grid-template-columns: 1.02fr .98fr;
  gap: clamp(2rem, 4.5vw, 4rem); align-items: center; padding: clamp(3rem, 6vw, 5.5rem) 0; }
.hero h1 { color: var(--white); line-height: 1.04; letter-spacing: -.03em;
  font-size: clamp(2.4rem, 5.2vw, 4rem); text-wrap: balance; }
.hero-sub { color: #EDEFF0; font-size: 1.16rem; line-height: 1.62; max-width: 48ch; }

/* pill eyebrow badge on a white chip, like the rest of the estate */
.eyebrow-chip { display: inline-flex; align-items: center; gap: .5rem; background: var(--white);
  border: 1px solid var(--line); border-radius: 999px; padding: .5rem 1rem; margin-bottom: 1.4rem;
  font-family: var(--font-head); font-weight: 700; font-size: .8rem; letter-spacing: .07em;
  text-transform: uppercase; color: var(--accent-ink); box-shadow: 0 1px 3px rgba(35,39,42,.06); }
.eyebrow-chip .icon { color: var(--accent-ink); }

.hero-actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 1.8rem; }
.hero .btn-outline { color: var(--white); border-color: rgba(255,255,255,.7); background: transparent; }
.hero .btn-outline:hover, .hero .btn-outline:focus-visible { background: var(--white); color: var(--primary-deep); border-color: var(--white); }
/* trust row reads as checked facts, not as a list of badges */
.hero-checks { display: flex; flex-wrap: wrap; gap: .6rem 1.8rem; list-style: none; margin: 1.8rem 0 0; padding: 0;
  color: #EDEFF0; font-size: .95rem; font-weight: 500; }
.hero-checks li { display: inline-flex; align-items: center; gap: .5rem; }
.hero-checks .icon { color: var(--on-dark); }

@media (max-width: 900px) { .hero-inner { grid-template-columns: 1fr; } }

/* ── forms ────────────────────────────────────────────────────────────────── */
.form-card { background: var(--white); border: 1px solid var(--line); border-radius: 18px; padding: 1.6rem; box-shadow: var(--shadow-lg); color: var(--body); }
.hero .form-card { border: 1px solid rgba(35,39,42,.07); }
.form-card h2 { font-size: 1.3rem; margin-bottom: .4rem; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
.form-stack { display: grid; gap: .8rem; }
@media (max-width: 560px) { .form-row { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: .3rem; margin-bottom: .8rem; }
.field label { font-size: .88rem; font-weight: 600; color: var(--ink); }
.field input, .field select, .field textarea {
  font-family: inherit; font-size: 1rem; color: var(--ink);
  padding: .7rem .8rem; border: 1px solid #C9CED2; border-radius: 8px; background: var(--white);
}
.field textarea { resize: vertical; }
.field-check { flex-direction: row; align-items: center; gap: .5rem; }
.field-check label { font-weight: 500; }
.form-note { font-size: .87rem; color: var(--muted); margin: .4rem 0 0; font-weight: 400; }
.form-error { background: #FDECEC; border-left: 3px solid #B3261E; color: #8C1D18; padding: .7rem .9rem; border-radius: 6px; font-size: .92rem; }
.form-consent { display: flex; gap: .55rem; align-items: flex-start; font-size: .8rem; color: var(--muted); margin: .9rem 0 0; line-height: 1.5; }
.form-consent input { margin-top: .25rem; flex: none; }
/* Rule 8 honeypot — display:none, neutral name. Never rename to a
   profile-shaped field (website/url/company/address): Chrome autofills those
   regardless of autocomplete=off and a filled honeypot silently drops a REAL
   lead, with the visitor still seeing the thank-you page. */
.hp-field { display: none !important; }
.form-compact .form-note:first-of-type { display: none; }

/* ── cards, grids ─────────────────────────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.4rem; }
/* Explicit counts, because auto-fit strands an orphan row whenever the item
   count does not divide by the column count — 8 cards in 3 columns leaves 2
   marooned, which reads as broken layout rather than as a grid. */
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.grid-2-up { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.6rem; }
/* six service cards: three columns so no row is left with orphans */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.3rem; }
@media (max-width: 1080px) { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .grid-3 { grid-template-columns: 1fr; } }
@media (max-width: 1080px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .grid-4, .grid-2-up { grid-template-columns: 1fr; } }
.card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; box-shadow: var(--shadow); }
.card h3 { margin-bottom: .4rem; }
.card p { color: var(--muted); font-size: .96rem; margin-bottom: .8rem; }
.card-link { font-weight: 700; text-decoration: none; }
.card-link:hover { text-decoration: underline; }
.card-media { border-radius: var(--radius) var(--radius) 0 0; margin: -1.5rem -1.5rem 1.1rem; overflow: hidden; }
.icon-badge { display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; border-radius: 12px; background: var(--accent); color: var(--on-accent); margin-bottom: 1rem; }

/* Rule 14: the stat bar stays ONE row at every viewport — it shrinks rather
   than wrapping, because a wrapped stat bar reads as broken layout. */
.stats-bar { background: var(--primary-deep); color: var(--white); }
/* Rule 14: repeat(4,1fr) at EVERY breakpoint. Fonts shrink; the row never wraps.
   Cells separate with a hairline rather than by whitespace, which is what keeps
   four narrow columns legible on a phone. Every colour restated: this sits on
   --primary-deep, and the number, label and sub each choose their own. */
.stats-bar-inner { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; padding: clamp(1.6rem, 3.5vw, 2.6rem) 0; text-align: center; }
.stat { min-width: 0; padding: .2rem clamp(.35rem, 1.5vw, 1.2rem); border-left: 1px solid var(--panel-line); }
.stat:first-child { border-left: 0; }
.stat-num { display: block; font-family: var(--font-head); font-weight: 800; letter-spacing: -.03em; line-height: 1; font-size: clamp(1.6rem, 4.6vw, 3rem); color: var(--white); }
.stat-label { display: block; margin-top: .45rem; font-family: var(--font-head); font-weight: 700; font-size: clamp(.72rem, 1.5vw, .98rem); line-height: 1.25; color: var(--white); }
.stat-sub { display: block; margin-top: .3rem; font-size: clamp(.72rem, 1.3vw, .88rem); line-height: 1.35; color: var(--muted-on-dark); }
@media (max-width: 480px) { .stat-sub { display: none; } }

/* ── two-column body + sticky sidebar ─────────────────────────────────────── */
.content-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: clamp(2rem, 4vw, 3.5rem); }
/* Rule 13: a sticky child in a CSS grid needs align-self:start, or the stretched
   grid item is already full-height and `position:sticky` can never engage. */
.sidebar-cta { align-self: start; position: sticky; top: 96px; }
@media (max-width: 980px) { .content-wrap { grid-template-columns: 1fr; } .sidebar-cta { position: static; } }

/* ── reviews ──────────────────────────────────────────────────────────────── */
.review-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 1.4rem; }
.rev-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.4rem; margin: 0; box-shadow: var(--shadow); }
.rev-card blockquote { margin: .6rem 0 .9rem; }
.rev-card p { color: var(--body); font-size: .96rem; margin: 0; }
.rev-card figcaption { font-weight: 700; color: var(--ink); font-family: var(--font-head); font-size: .95rem; }
/* Stars are inline SVG (Rule 22) — never the character U+2605, which renders as
   a yellow color-emoji on iOS and ignores `color`. */
.stars { display: inline-flex; gap: 2px; color: var(--accent-ink); }
.stars .icon { fill: currentColor; }
.reviews-cta { margin-top: 1.8rem; }

/* ── FAQ ──────────────────────────────────────────────────────────────────── */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); padding: .3rem 0; }
.faq summary { cursor: pointer; font-family: var(--font-head); font-weight: 700; color: var(--ink); padding: 1rem 2rem 1rem 0; position: relative; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: ""; position: absolute; right: .4rem; top: 1.5rem; width: 9px; height: 9px; border-right: 2px solid var(--accent-ink); border-bottom: 2px solid var(--accent-ink); transform: rotate(45deg); transition: transform .18s; }
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq details p { color: var(--muted); margin: 0 0 1rem; }

/* ── CTA band ─────────────────────────────────────────────────────────────── */
.cta-banner { background: var(--primary-deep); color: var(--white); }
.cta-banner-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem; padding: clamp(2rem, 4vw, 3rem) 0; }
.cta-banner h2 { color: var(--white); margin-bottom: .3rem; }
.cta-banner p { color: var(--muted-on-dark); margin: 0; max-width: 52ch; }
.cta-banner-actions { display: flex; flex-wrap: wrap; gap: .8rem; }

/* ── footer ───────────────────────────────────────────────────────────────── */
.site-footer { background: var(--footer-bg); color: var(--muted-on-dark); font-size: .95rem; }
.footer-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1.2fr; gap: 2.5rem; padding: clamp(2.5rem, 5vw, 4rem) 0 2rem; }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; gap: 2rem; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }
.footer-brand img { width: auto; height: 52px; margin-bottom: 1.1rem; }
.footer-brand p { max-width: 42ch; }
.footer-license { display: flex; align-items: center; gap: .45rem; color: var(--white); font-weight: 600; }
.footer-license .icon { color: var(--on-dark); }
.footer-h { font-size: 1rem; color: var(--white); margin-bottom: .9rem; }
.footer-links, .footer-contact, .footer-social, .footer-legal { list-style: none; margin: 0; padding: 0; }
.footer-links li, .footer-contact li { margin-bottom: .55rem; }
.footer-contact li { display: flex; gap: .6rem; align-items: flex-start; }
.footer-contact .icon { color: var(--on-dark); margin-top: .28rem; }
.footer-office { display: block; font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted-on-dark); }
.footer-contact a[href^="tel:"], .contact-list a[href^="tel:"], .article a[href^="tel:"] { white-space: nowrap; display: inline-block; }
/* :not(.btn) on BOTH rules — see the color contract, trap 2. */
.site-footer a:not(.btn) { color: var(--muted-on-dark); text-decoration: none; }
.site-footer a:not(.btn):hover, .site-footer a:not(.btn):focus-visible { color: var(--white); text-decoration: underline; }
.footer-social { display: flex; gap: .6rem; margin-top: 1.1rem; }
.footer-social a { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border: 1px solid var(--panel-line); border-radius: 8px; color: var(--muted-on-dark); }
.footer-social a:hover { background: var(--white); color: var(--primary-deep); border-color: var(--white); }
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .8rem; padding: 1.3rem 0 1.6rem; border-top: 1px solid var(--panel-line); font-size: .88rem; }
.footer-bottom p { margin: 0; }
.footer-legal { display: flex; flex-wrap: wrap; gap: 1.2rem; }

/* ── mobile sticky bar (Rules 10 + 24) ────────────────────────────────────── */
.sticky-bar { display: none; }
.sticky-clearance { display: none; }
@media (max-width: 780px) {
  /* EXACTLY two elements: quote + phone (Rule 10). */
  .sticky-bar {
    display: grid; grid-template-columns: 1fr 1fr;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 110;
    background: var(--primary-deep); border-top: 1px solid var(--panel-line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .sticky-bar a { display: inline-flex; align-items: center; justify-content: center; gap: .45rem; padding: .95rem .5rem; font-family: var(--font-head); font-weight: 700; font-size: 1rem; text-decoration: none; }
  .sticky-quote { background: var(--accent); color: var(--on-accent); }
  .sticky-call { color: var(--white); }
  .sticky-call:hover { background: rgba(255,255,255,.12); }
  /* Rule 24: clearance is its own element in the SAME color as the footer, plus
     the safe-area inset — padding on the footer alone leaves a white strip under
     the bar on a notched device. */
  .sticky-clearance { display: block; height: calc(58px + env(safe-area-inset-bottom)); background: var(--primary-deep); }
}

/* ── utilities ────────────────────────────────────────────────────────────── */
.visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.breadcrumb { font-size: .87rem; color: var(--muted); padding: 1rem 0 0; }
.breadcrumb ol { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; margin: 0; padding: 0; }
.breadcrumb li::after { content: "/"; margin-left: .4rem; color: var(--line); }
.breadcrumb li:last-child::after { content: ""; }
.prose > h2 { margin-top: 2.2rem; }
.prose > h3 { margin-top: 1.6rem; }
.prose ul { padding-left: 1.2rem; }
.prose li { margin-bottom: .45rem; }
/* Rule 2: .page-hero carries NO background-image here — layout only. Each page
   supplies its own contextual image plus a BRAND-TINTED (not plain black)
   gradient via the inline multi-background shorthand. No ::before image layer,
   no var() on a pseudo-element. --primary-deep #0C2B29 is rgb(12,43,41). */
.page-hero { position: relative; overflow: hidden; color: var(--white); background-color: var(--primary-deep); padding: clamp(2.5rem, 5vw, 4rem) 0 clamp(2.5rem, 5vw, 3.5rem); }
.page-hero h1 { color: var(--white); margin-top: .6rem; }
.page-hero p { max-width: 62ch; color: #E8EAEB; margin: 0; font-size: 1.06rem; }
.page-hero .breadcrumb { color: var(--muted-on-dark); padding-top: 0; }
.page-hero .breadcrumb a { color: var(--muted-on-dark); }
.page-hero .breadcrumb a:hover { color: var(--white); }
.page-hero .breadcrumb li::after { color: rgba(255,255,255,.35); }

/* ── homepage: service link row + service-area list ───────────────────────── */
.more-services { margin-top: 1.6rem; color: var(--muted); font-size: .97rem; }
.more-services a { font-weight: 600; }
.area-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: .6rem 1.2rem; list-style: none; margin: 0; padding: 0; }
.area-list a { display: inline-flex; align-items: center; gap: .45rem; padding: .55rem .2rem; text-decoration: none; font-weight: 600; }
.area-list a:hover { text-decoration: underline; }
.area-list .icon { color: var(--accent-ink); }

/* ── contact list ─────────────────────────────────────────────────────────── */
.contact-list { list-style: none; margin: 0; padding: 0; }
.contact-list li { display: flex; gap: .8rem; align-items: flex-start; padding: .9rem 0; border-bottom: 1px solid var(--line); }
.contact-list li:last-child { border-bottom: 0; }
.contact-list .icon { color: var(--accent-ink); margin-top: .3rem; }
.contact-list strong { display: block; font-family: var(--font-head); color: var(--ink); font-size: .95rem; }
.contact-phone { display: inline-block; font-family: var(--font-head); font-weight: 700; font-size: 1.2rem; text-decoration: none; margin: .15rem 0; }
.contact-phone:hover { text-decoration: underline; }
.contact-note { display: block; font-size: .89rem; color: var(--muted); }

/* ── thank-you / 404 ──────────────────────────────────────────────────────── */
.thanks { max-width: 62ch; }
.thanks-mark { display: inline-flex; align-items: center; justify-content: center; width: 76px; height: 76px; border-radius: 50%; background: var(--accent); color: var(--on-accent); margin-bottom: 1.3rem; }
.thanks-lead { font-size: 1.12rem; color: var(--body); }
.thanks-actions { display: flex; flex-wrap: wrap; gap: .8rem; margin: 1.6rem 0 2.4rem; }
.thanks-next { border-top: 1px solid var(--line); padding-top: 1.8rem; }
.thanks-next ol, .thanks-next ul { padding-left: 1.2rem; }
.thanks-next li { margin-bottom: .6rem; }

/* ── sidebar three-card pattern (§6) ──────────────────────────────────────────
   Every sub-page carries the same three cards: a dark phone CTA, a form CTA to
   /contact/, and a related-page card that ROTATES BY PAGE TYPE to spread
   internal-link equity rather than pointing every page at the same target. */
.sidebar-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.4rem; margin-bottom: 1.1rem; box-shadow: var(--shadow); }
.sidebar-card h3 { font-size: 1.08rem; margin-bottom: .5rem; }
.sidebar-card p { color: var(--muted); font-size: .94rem; margin-bottom: 1rem; }
.sidebar-card .btn { width: 100%; }
.sidebar-card.dark { background: var(--primary-deep); border-color: var(--primary-deep); color: var(--muted-on-dark); }
.sidebar-card.dark h3 { color: var(--white); }
.sidebar-card.dark p { color: var(--muted-on-dark); }
.phone-big { display: block; font-family: var(--font-head); font-weight: 800; font-size: 1.6rem; color: var(--on-dark); text-decoration: none; margin: .2rem 0 .6rem; }
.phone-big:hover { text-decoration: underline; }

/* ── service / why cards (capture.py clips on these selectors) ────────────── */
.svc-card, .why-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; box-shadow: var(--shadow); }
.svc-card h3, .why-card h3 { margin-bottom: .4rem; }
.svc-card p, .why-card p { color: var(--muted); font-size: .96rem; margin-bottom: .8rem; }
.why-card p:last-child { margin-bottom: 0; }

/* ── feature list ─────────────────────────────────────────────────────────── */
.feature-list { list-style: none; margin: 1.2rem 0; padding: 0; }
.feature-list li { display: flex; gap: .6rem; align-items: flex-start; margin-bottom: .7rem; }
.feature-list .icon { color: var(--accent-ink); margin-top: .28rem; flex: none; }

/* ── FAQ (documented markup: .faq-item / .faq-q / .faq-a) ─────────────────── */
.faq-item { border-bottom: 1px solid var(--line); padding: .3rem 0; }
.faq-item:first-child { border-top: 1px solid var(--line); }
.faq-q { cursor: pointer; font-family: var(--font-head); font-weight: 700; color: var(--ink); padding: 1rem 2rem 1rem 0; position: relative; list-style: none; }
.faq-q::-webkit-details-marker { display: none; }
.faq-q::after { content: ""; position: absolute; right: .4rem; top: 1.5rem; width: 9px; height: 9px; border-right: 2px solid var(--accent-ink); border-bottom: 2px solid var(--accent-ink); transform: rotate(45deg); transition: transform .18s; }
.faq-item[open] .faq-q::after { transform: rotate(-135deg); }
.faq-a { color: var(--muted); margin: 0 0 1rem; }

/* ── in-body photo (Rule 32) ──────────────────────────────────────────────── */
.photo-frame { border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); margin: 1.8rem 0; }
.photo-frame img { width: 100%; }
.photo-frame figcaption { background: var(--surface); color: var(--muted); font-size: .87rem; padding: .7rem 1rem; }
.photo-inline { border-radius: var(--radius); overflow: hidden; margin: 1.6rem 0; }

/* ── section heading helper ───────────────────────────────────────────────── */
.section-h { margin-bottom: .5rem; }
/* Section eyebrow. --accent-ink at 12px uppercase reads near-black rather than
   orange, so on light grounds the eyebrow becomes an orange CHIP (fill + dark
   label, 5.62:1) to match the hero's, instead of dark-orange text nobody reads
   as orange. On dark grounds it stays text in --on-dark, which is genuinely
   orange there. */
.eyebrow { display: inline-flex; align-items: center; gap: .4rem;
  background: var(--accent); color: var(--on-accent); border-radius: 999px;
  padding: .38rem .8rem; font-family: var(--font-head); font-weight: 700;
  font-size: .76rem; letter-spacing: .09em; text-transform: uppercase; margin-bottom: .9rem; }
.hero .eyebrow, .cta-banner .eyebrow, .work .eyebrow {
  background: transparent; color: var(--on-dark); padding: 0; font-size: .9rem; }
.review-featured { background: var(--white); border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: var(--radius); padding: 1.6rem; margin-bottom: 1.4rem; box-shadow: var(--shadow); }
.review-by { font-weight: 700; color: var(--ink); font-family: var(--font-head); font-size: .95rem; margin: .6rem 0 0; }
.review-by span { font-weight: 500; color: var(--muted); }
.reviews-note { color: var(--muted); margin-bottom: 1.8rem; }

/* ── .content-wrap = .content-main + aside.sidebar-cta (§6) ───────────────── */
.content-main > h2:first-child { margin-top: 0; }
.content-main > h2 { margin-top: 2.2rem; }
.content-main > h3 { margin-top: 1.6rem; }
.content-main ul:not(.feature-list):not(.area-list) { padding-left: 1.2rem; }
.content-main li { margin-bottom: .45rem; }

/* ── footer column + reviews band ─────────────────────────────────────────── */
.footer-col { min-width: 0; }
.reviews { background: var(--surface); }

/* ── recent-work photo band (Rule 32) ─────────────────────────────────────────
   Nine authentic drone job photos exist and the page was using one. This puts
   the rest to work: it is the only thing on the page a competitor cannot copy,
   and it is the reason to trust the copy above it. */
.work { background: var(--primary-deep); color: var(--muted-on-dark); }
.work h2 { color: var(--white); }
.work .section-sub, .work .section-head p { color: var(--muted-on-dark); }
/* The work band is a DARK ground dropped into a page of light sections, so every
   color set for the light ground has to be restated here. .section-head p and
   .eyebrow both inherited --muted (2.14:1 on this ground) — the same light/dark
   trap as the header port, this time inside my own new section. */
.work .eyebrow .icon { color: var(--on-dark); }
.work-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
.work-item { position: relative; margin: 0; border-radius: var(--radius); overflow: hidden; background: var(--primary); }
.work-item img { width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform .4s ease; }
.work-item:hover img { transform: scale(1.04); }
@media (max-width: 900px) { .work-grid { grid-template-columns: repeat(2, 1fr); } }
@media (prefers-reduced-motion: reduce) { .work-item:hover img { transform: none; } }

/* ── section eyebrow label, used to lead a section rather than a bare H2 ───── */
.section-head { max-width: 64ch; margin-bottom: 2.4rem; }
.section-head .eyebrow { margin-bottom: .5rem; }
.section-head h2 { margin-bottom: .5rem; }
.section-head p { color: var(--muted); font-size: 1.06rem; margin: 0; }

/* ── svc-card: give the copper somewhere real to live ─────────────────────── */
.svc-card { position: relative; transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.svc-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--accent); border-radius: var(--radius) 0 0 var(--radius); opacity: 0; transition: opacity .18s ease; }
.svc-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: #D6D9DB; }
.svc-card:hover::before { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .svc-card, .svc-card:hover { transform: none; transition: none; } }

/* stat unit sits smaller and copper beside the numeral */

/* ── photo-led service cards (Rule 32) ────────────────────────────────────────
   The whole card is one <a>, so the inner "heading" and copy are spans styled
   to look like a heading rather than real h3/p — nesting block elements or a
   heading inside an anchor is invalid and axe flags the heading-order. */
.svc-card { display: flex; flex-direction: column; padding: 0; overflow: hidden; text-decoration: none; color: inherit; }
.svc-card .pic { display: block; overflow: hidden; background: var(--surface-2); }
.svc-card .pic img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; transition: transform .45s ease; }
.svc-card:hover .pic img { transform: scale(1.05); }
.svc-card .icon-badge { margin: 1.5rem 1.5rem 0; }
.svc-card .body { display: flex; flex-direction: column; gap: .45rem; padding: 1.25rem 1.5rem 1.5rem; }
.svc-card .h3 { font-family: var(--font-head); font-weight: 700; font-size: 1.15rem; color: var(--ink); line-height: 1.25; }
.svc-card .blurb { color: var(--muted); font-size: .95rem; line-height: 1.55; }
.svc-card .more { display: inline-flex; align-items: center; gap: .35rem; margin-top: .35rem;
  font-family: var(--font-head); font-weight: 700; font-size: .93rem; color: var(--accent-ink); }
.svc-card:hover .more { color: var(--accent-ink-dark); }
.svc-card:hover .more .icon { transform: translateX(3px); }
.svc-card .more .icon { transition: transform .18s ease; }
/* the copper edge only makes sense on the icon variant; a photo card already
   has its own top-edge weight */
.svc-card.has-pic::before { display: none; }
@media (prefers-reduced-motion: reduce) {
  .svc-card:hover .pic img, .svc-card:hover .more .icon { transform: none; }
}

/* Fallback panel for a service with no honest photo yet. Same 16/10 block as a
   real photo so a mixed grid keeps its rhythm, but visibly not a photograph. */
.svc-card .pic-placeholder { display: flex; align-items: center; justify-content: center;
  aspect-ratio: 16 / 10; background:
    linear-gradient(135deg, var(--primary) 0%, var(--primary-deep) 100%);
  color: var(--on-dark); }
.svc-card:hover .pic-placeholder { color: var(--accent); }

/* ── owner portraits (real photos, recovered from the legacy site) ─────────── */
.owner { float: left; margin: .4rem 1.4rem .8rem 0; text-align: center; }
.owner img { width: 132px; height: 132px; border-radius: 50%; object-fit: cover; border: 3px solid var(--accent); }
.owner figcaption { font-family: var(--font-head); font-weight: 700; font-size: .92rem; color: var(--ink); margin-top: .55rem; }
.owner figcaption span { display: block; font-weight: 500; font-size: .8rem; color: var(--muted); }
.content-main h3 { clear: none; }
@media (max-width: 560px) { .owner { float: none; margin: 0 auto 1rem; } }



/* ── process steps ────────────────────────────────────────────────────────────
   An ordered sequence, not a card grid. Own component because .svc-card now
   carries padding:0 for the photo layout — anything reusing it without a .body
   wrapper loses its inset entirely, which is exactly what happened here. */
.step-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; list-style: none; margin: 0; padding: 0; counter-reset: step; }
.step-card { position: relative; background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 2.4rem 1.5rem 1.6rem; box-shadow: var(--shadow); }
.step-num { position: absolute; top: -18px; left: 1.5rem;
  display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  border-radius: 12px; background: var(--accent); color: var(--on-accent);
  font-family: var(--font-head); font-weight: 800; font-size: 1.15rem; box-shadow: var(--shadow); }
.step-card h3 { font-size: 1.08rem; margin: .35rem 0 .45rem; }
.step-card p { color: var(--muted); font-size: .95rem; line-height: 1.55; margin: 0; }
/* the connecting rule between steps, desktop only */
.step-card::after { content: ""; position: absolute; top: 4px; right: -1.3rem; width: 1.3rem; height: 1px; background: var(--line); }
.step-card:last-child::after { display: none; }
@media (max-width: 1080px) { .step-grid { grid-template-columns: repeat(2, 1fr); row-gap: 2.4rem; } .step-card::after { display: none; } }
@media (max-width: 620px)  { .step-grid { grid-template-columns: 1fr; row-gap: 2.4rem; } }

/* The eyebrow is a <p> inside .section-head, and `.section-head p` (0,1,1) is
   declared later than `.eyebrow` (0,1,0), so it silently won and painted the
   chip label --muted — gray on orange at 2.25:1. Restated at (0,2,0) so the
   eyebrow's own color holds regardless of the container it sits in. */
.section-head .eyebrow { color: var(--on-accent); }
.work .section-head .eyebrow,
.hero .section-head .eyebrow,
.cta-banner .section-head .eyebrow { color: var(--on-dark); }

/* ── location cards (/locations/) ───────────────────────────────────────────
   A TEXT card, not a photo card, and that is a content-integrity decision
   rather than a design one — see the comment above site_city_card().

   Every color is restated on the card's own children rather than inherited.
   This build has already had four incidents where a rule on a CONTAINER
   silently repainted a component that had chosen its own color, and these
   cards sit on BOTH grounds: .section (white) in one block and .section-alt
   (--surface) in the other. Inheriting would make them diverge the first time
   either ground changes. */
.loc-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; }
@media (max-width: 900px) { .loc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .loc-grid { grid-template-columns: 1fr; } }

.loc-card {
  display: flex; flex-direction: column; position: relative;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.35rem; box-shadow: var(--shadow); text-decoration: none;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.loc-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--accent); border-radius: var(--radius) 0 0 var(--radius);
  opacity: 0; transition: opacity .18s ease; }
.loc-card:hover, .loc-card:focus-visible { transform: translateY(-2px); box-shadow: var(--shadow-lg); border-color: #D6D9DB; }
.loc-card:hover::before, .loc-card:focus-visible::before { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .loc-card, .loc-card:hover { transform: none; transition: none; } }

.loc-card .county { display: inline-flex; align-items: center; gap: .35rem;
  font-size: .73rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--accent-ink); margin-bottom: .55rem; }
.loc-card .county .icon { color: var(--accent-ink); }
.loc-card .h3 { display: block; font-family: var(--font-head); font-weight: 700;
  font-size: 1.14rem; line-height: 1.25; color: var(--ink); margin-bottom: .45rem; }
.loc-card .blurb { display: block; color: var(--muted); font-size: .94rem; line-height: 1.55;
  margin-bottom: 1rem; }
.loc-card .more { display: inline-flex; align-items: center; gap: .35rem; margin-top: auto;
  font-weight: 700; font-size: .92rem; color: var(--accent-ink); }
.loc-card .more .icon { color: var(--accent-ink); }
.loc-card:hover .more { text-decoration: underline; }

/* ── city-page service block (includes/city_render.php) ─────────────────────
   Three or four services chosen FOR that city, each with copy written for that
   market. It replaces the legacy eleven-service grid, which was identical on
   all eleven city pages and hardcoded to Sarasota.
   Every color restated rather than inherited — this block sits inside
   .content-main on a white ground, and .content-main sets its own p color. */
.city-svc { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem; margin: 1.4rem 0 .4rem; }
@media (max-width: 640px) { .city-svc { grid-template-columns: 1fr; } }
.city-svc-item { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.15rem 1.25rem; }
.city-svc-item h3 { margin: 0 0 .4rem; font-size: 1.06rem; color: var(--ink); }
.city-svc-item h3 a { color: var(--accent-ink); text-decoration: none; }
.city-svc-item h3 a:hover, .city-svc-item h3 a:focus-visible { text-decoration: underline; }
.city-svc-item p { margin: 0; color: var(--muted); font-size: .95rem; line-height: 1.6; }

/* A phone number is one token. Inside running prose the inline tel link was
   wrapping mid-number at 390px on /locations/ and /privacy-policy/. */
.content-main a[href^="tel:"], .prose a[href^="tel:"], .contact-list a[href^="tel:"],
.sidebar-card a[href^="tel:"], .cta-banner a[href^="tel:"] { white-space: nowrap; }

/* ── blog index + article ─────────────────────────────────────────────────── */
.post-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.4rem; }
@media (max-width: 720px) { .post-list { grid-template-columns: 1fr; } }
.post-card { display: flex; flex-direction: column; background: var(--white); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; box-shadow: var(--shadow); text-decoration: none; color: inherit; }
.post-card .h3 { font-family: var(--font-display); font-weight: 400; font-size: 1.35rem; color: var(--ink); line-height: 1.25; margin-bottom: .5rem; }
.post-card .blurb { color: var(--muted); font-size: .96rem; margin-bottom: 1rem; }
.post-card .more { margin-top: auto; font-weight: 700; color: var(--accent-ink); }
.post-card:hover .more { text-decoration: underline; }
.article { max-width: 74ch; }
.article h2 { margin-top: 2.2rem; }
.article ul { padding-left: 1.2rem; }
.article li { margin-bottom: .55rem; }
.article-meta { color: var(--muted); font-size: .9rem; margin-bottom: 1.6rem; }
/* project cards on the legacy projects page */
.project { display: grid; grid-template-columns: 1.1fr .9fr; gap: 2rem; align-items: start; margin-bottom: 3rem; }
@media (max-width: 860px) { .project { grid-template-columns: 1fr; } }
.project dl { display: grid; grid-template-columns: auto 1fr; gap: .35rem 1rem; margin: 0 0 1rem; font-size: .95rem; }
.project dt { font-weight: 700; color: var(--ink); }
.project dd { margin: 0; color: var(--body); }
.gallery-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 900px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } }
.gallery-grid figure { margin: 0; border-radius: var(--radius); overflow: hidden; background: var(--surface-2); }
.gallery-grid img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
/* Rule 30: links inside running copy are underlined, not color-only */
main p a:not(.btn), .article li a:not(.btn) { text-decoration: underline; }
/* 44px tap targets on phones for list-style links (checklist 46) */
@media (max-width: 900px) {
  .footer-links a, .footer-legal a, .breadcrumb a, .area-list a, .nav-desktop .sub a:not(.btn) { display: inline-block; padding-top: .55rem; padding-bottom: .55rem; }
  .breadcrumb ol { align-items: center; }
}

/* ── cinematic hero (homepage) ────────────────────────────
   body.cine is set by index.php (the homepage). The header floats
   over the footage (white logo, white links) and turns solid --footer-bg once the page
   scrolls. NO backdrop-filter / transform on the header: either would make it the containing
   block for the position:fixed mobile nav panel inside it and clip the drawer to 76px. */
body.cine .trust-bar { display: none; }
/* A real tinted ground, not a gradient-only scrim: the sky frames are bright, and a nav label needs a
   background-color a contrast tool (and an eye) can resolve. White on .62 of #04100F over the
   brightest graded sky pixel (#8FB4BD) is 9.4:1. */
body.cine header { position: fixed; top: 0; left: 0; right: 0; background-color: rgba(4,16,15,.62); border-bottom-color: rgba(255,255,255,.12);
  box-shadow: none; transition: background-color .3s, box-shadow .3s; }
body.cine header.is-stuck { background-color: var(--footer-bg); box-shadow: 0 6px 24px rgba(0,0,0,.28); }
body.cine .hamburger { border-color: rgba(255,255,255,.6); }
body.cine .hamburger span { background: var(--white); }
@media (min-width: 981px) {
  body.cine .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote) { color: var(--white); }
  body.cine .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):hover,
  body.cine .nav-desktop > li > a:not(.btn):not(.nav-call):not(.nav-quote):focus-visible { background: rgba(4,16,15,.55); color: var(--white); box-shadow: inset 0 -2px 0 var(--accent); }  /* hover DARKENS: a lighter pill drops white to 4.05:1 */
  body.cine .nav-desktop a.nav-quote { color: var(--white); border-color: rgba(255,255,255,.7); }
  body.cine .nav-desktop a.nav-quote:hover, body.cine .nav-desktop a.nav-quote:focus-visible { background: var(--white); color: var(--primary-deep); border-color: var(--white); }
}
body.cine a:focus-visible, body.cine .hamburger:focus-visible { outline-color: var(--white); }
body.cine main a:focus-visible { outline-color: var(--accent-ink); }
body.cine .hero-cine a:focus-visible { outline-color: var(--white); }

.hero-cine { min-height: 100vh; min-height: 100svh; display: flex; align-items: flex-end; background: #04100F; }
/* The grade: the clip is a bright midday aerial, which reads as stock at full exposure.
   Pulled down, desaturated and warmed slightly it reads as footage. */
.hero-cine .hero-bg img, .hero-cine .hero-video { filter: brightness(.9) saturate(.86) contrast(1.06) sepia(.08); }
.hero-cine .hero-video { opacity: 0; transition: opacity 1.4s ease; }
.hero-cine .hero-video.is-playing { opacity: 1; }
/* light vignette + a floor for the type. The words sit in the bottom 28%, under >= .5 of #04100F
   on a frame at .9 brightness: white on the brightest grass there still measures > 8:1. */
.hero-cine .hero-overlay { background:
  radial-gradient(120% 90% at 70% 35%, transparent 45%, rgba(4,16,15,.42) 100%),
  linear-gradient(to top, rgba(4,16,15,.82) 0%, rgba(4,16,15,.5) 28%, rgba(4,16,15,.08) 58%, transparent 100%); }
/* film grain */
.hero-cine .hero-overlay::after { opacity: .08; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay; }
.hero-cine-inner { position: relative; z-index: 1; padding: 8.5rem 0 clamp(4.5rem, 10vh, 7.5rem); }
.hero-cine h1 { margin: 0; color: var(--white); letter-spacing: 0; }
.cine-kicker { display: flex; align-items: center; gap: .9rem; font-family: var(--font-head); font-weight: 700;
  font-size: clamp(.74rem, 1.1vw, .9rem); letter-spacing: .26em; text-transform: uppercase; color: var(--white); margin-bottom: 1.3rem; }
.cine-kicker::before { content: ""; width: 3.2rem; height: 2px; background: var(--accent); flex: none; }
.cine-line { display: block; font-family: var(--font-display); font-size: clamp(3.1rem, 10.5vw, 9.2rem); line-height: .92;
  letter-spacing: -.035em; text-transform: uppercase; text-wrap: balance; text-shadow: 0 2px 40px rgba(0,0,0,.45); }
.cine-sub { color: #F1F3F3; font-size: clamp(1.05rem, 1.6vw, 1.3rem); line-height: 1.5; max-width: 40ch; margin: 1.6rem 0 0;
  text-shadow: 0 1px 12px rgba(0,0,0,.6); }
.hero-cine .hero-actions { margin-top: 2.2rem; }
.hero-cine .btn { padding: 1.05rem 2rem; }
@keyframes cine-rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
.cine-kicker, .cine-line, .cine-sub, .hero-cine .hero-actions { animation: cine-rise 1s cubic-bezier(.2,.7,.2,1) both; }
.cine-line { animation-delay: .15s; } .cine-sub { animation-delay: .4s; } .hero-cine .hero-actions { animation-delay: .55s; }

.cine-scroll { position: absolute; z-index: 1; right: max(1.25rem, calc((100% - var(--wrap)) / 2)); bottom: clamp(4.5rem, 10vh, 7.5rem);
  width: 28px; height: 46px; border: 2px solid rgba(255,255,255,.75); border-radius: 16px; display: block; }
.cine-scroll span { position: absolute; left: 50%; top: 8px; width: 4px; height: 9px; margin-left: -2px; border-radius: 2px;
  background: var(--white); animation: cine-wheel 1.9s ease-in-out infinite; }
@keyframes cine-wheel { 0% { opacity: 0; transform: translateY(0); } 30% { opacity: 1; } 100% { opacity: 0; transform: translateY(16px); } }
/* The hero is the video alone at every width: the header (and on phones the bottom call bar) stays
   off-screen until the first scroll, then slides in. The header moves on `top`, NOT transform: a transformed header
   becomes the containing block for the position:fixed nav drawer inside it. The bar has no fixed
   children, so it may translate. body.nav-open pins the header so the drawer's close button
   never slides away with it. */
body.cine header { top: -120px; opacity: 0; transition: top .35s ease, opacity .3s, background-color .3s, box-shadow .3s; }
body.cine.is-scrolled header, body.cine.nav-open header { top: 0; opacity: 1; }
body.cine .hero-cine-inner { padding-top: 5rem; }
@media (max-width: 980px) {
  body.cine .cine-scroll { display: block; right: auto; left: 50%; margin-left: -14px; bottom: 1.4rem; width: 24px; height: 40px; transition: opacity .3s; }
  body.cine .cine-scroll span { top: 6px; height: 8px; }
  body.cine.is-scrolled .cine-scroll { opacity: 0; }
}
@media (max-width: 780px) {
  body.cine .sticky-bar { transform: translateY(110%); transition: transform .35s ease; }
  body.cine.is-scrolled .sticky-bar { transform: none; }
}
@media (max-width: 767px) { .hero-cine-inner { padding-bottom: 5.5rem; } }
@media (prefers-reduced-motion: reduce) { body.cine header, body.cine .sticky-bar, body.cine .cine-scroll, body.cine .hero-badges { transition: none; } }
#after-hero { display: block; scroll-margin-top: 76px; }

.cine-quote { background: var(--primary-deep); }
.cine-quote-inner { max-width: 680px; }
body.cine #quote-form { scroll-margin-top: 100px; }

/* Homepage "areas" band (house template, shared with IE Pro and Arolite): county cards + highlighted NC map.
   On --primary-deep: white 15.07:1, --on-dark 8.36:1, --muted-on-dark 8.76:1. */
.areas-band { background: var(--primary-deep); color: var(--muted-on-dark); }
.areas-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
@media (max-width: 980px) { .areas-grid { grid-template-columns: 1fr; } }
.areas-band .eyebrow { background: transparent; padding: 0; border-radius: 0; display: inline-flex; align-items: center; gap: .45rem; color: var(--on-dark); font-size: .9rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; margin: 0 0 .4rem; }
.areas-band h2 { color: var(--white); margin-bottom: .6rem; }
.areas-lead { color: var(--muted-on-dark); max-width: 52ch; }
.county-cards { display: grid; gap: .7rem; margin-top: 1.4rem; }
.county-card { background: rgba(255,255,255,.04); border: 1px solid var(--panel-line); border-radius: var(--radius); padding: .9rem 1.2rem 1rem; }
.county-card h3 { color: var(--white); font-size: 1rem; margin: 0 0 .55rem; padding-bottom: .55rem; border-bottom: 1px solid var(--panel-line); display: flex; align-items: center; gap: .45rem; }
.county-card h3 .icon { color: var(--on-dark); }
.pin-list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .15rem 1rem; list-style: none; margin: 0; padding: 0; font-size: .95rem; }
@media (max-width: 560px) { .pin-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.pin-list li { display: flex; align-items: center; gap: .4rem; color: var(--muted-on-dark); }
.pin-list .icon { color: var(--on-dark); flex: none; }
.pin-list a { color: var(--white); text-decoration: none; font-weight: 600; padding: .2rem 0; display: inline-block; }
.pin-list a:hover, .pin-list a:focus-visible { text-decoration: underline; color: var(--on-dark); }
.areas-actions { margin-top: 1.2rem; display: flex; flex-wrap: wrap; gap: .8rem; }
.areas-map { display: block; }
.nc-map { width: 100%; height: auto; display: block; }
.nc-map .c { fill: #123A37; stroke: #2B5753; stroke-width: .9; vector-effect: non-scaling-stroke; }
.nc-map .c.served { fill: var(--accent); stroke: var(--white); stroke-width: 1.4; }
.map-note { font-size: .85rem; color: var(--muted-on-dark); margin: .8rem 0 0; text-align: center; }

/* ── hero badge strip (Arolite's rotating seals, asked for 2026-10-03; replaces the stats bar)
   A white card that overlaps the cinematic hero's bottom edge and scrolls a row of badges
   continuously (CSS marquee, duplicated list, pauses on hover, static under reduced motion).
   Only CHECKABLE facts (Rule 27); each seal is an inline SVG icon, never a borrowed logo.
   The wrap straddles the seam: its top 2.6rem is transparent (the hero shows through), the
   rest is the page ground, so the card sits half on the video and half on the page.
   On the homepage the card behaves like the header (Max, 2026-10-03): the first screen is the
   video alone, and the first scroll (body.is-scrolled, set by the hero script) slides the header
   down and this card up together. Keyboard focus inside it shows it too. */
.hero-badges-wrap { position: relative; z-index: 2; margin-top: -2.6rem; width: 100%; max-width: none; padding: 0 1.25rem .4rem;
  background: linear-gradient(to bottom, transparent 2.6rem, var(--white) 2.6rem); }
body.cine .hero-badges { opacity: 0; transform: translateY(18px); transition: opacity .35s ease, transform .35s ease; }
body.cine.is-scrolled .hero-badges, body.cine .hero-badges:focus-within { opacity: 1; transform: none; }
.badge-set { display: contents; }
/* The card stays solid; the badges fade out under two white edge veils. (Arolite masks the
   whole card, which on this site lets the dark video bleed through its rounded corners.) */
.hero-badges { position: relative; background: var(--white); border-radius: 18px; box-shadow: var(--shadow-lg); overflow: hidden;
  padding: .55rem 0; max-width: 1080px; margin: 0 auto; }
.hero-badges::before, .hero-badges::after { content: ""; position: absolute; top: 0; bottom: 0; width: 7%; z-index: 1; pointer-events: none; }
.hero-badges::before { left: 0; background: linear-gradient(to right, var(--white), rgba(255,255,255,0)); }
.hero-badges::after { right: 0; background: linear-gradient(to left, var(--white), rgba(255,255,255,0)); }
.badge-track { display: flex; gap: 2.2rem; width: max-content; padding: 0 1.1rem;
  animation: badge-scroll 40s linear infinite; }
.hero-badges:hover .badge-track, .hero-badges:focus-within .badge-track { animation-play-state: paused; }
@keyframes badge-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.badge { display: inline-flex; align-items: center; gap: .7rem; padding: .55rem .4rem; white-space: nowrap; color: var(--ink); text-decoration: none; }
.badge-seal { flex: none; width: 52px; height: 52px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--primary-deep); color: var(--on-dark); border: 3px solid var(--primary); }
.badge-seal.gold { background: var(--accent); color: var(--on-accent); border-color: var(--accent-hover); }
.badge-big { display: block; font-family: var(--font-head); font-weight: 800; font-size: 1.05rem; line-height: 1.1; color: var(--ink); }
.badge-small { display: block; font-size: .8rem; color: var(--muted); line-height: 1.2; margin-top: .15rem; }
a.badge:hover .badge-big, a.badge:focus-visible .badge-big { color: var(--accent-ink); text-decoration: underline; }
@media (prefers-reduced-motion: reduce) {
  .badge-track { animation: none; width: auto; flex-wrap: wrap; justify-content: center; gap: 1rem 1.6rem; padding: .3rem .8rem; }
  .badge-track [aria-hidden="true"] { display: none; }
  .hero-badges::before, .hero-badges::after { display: none; }
}
@media (max-width: 780px) {
  .hero-badges-wrap { margin-top: -1.6rem; background: linear-gradient(to bottom, transparent 1.6rem, var(--white) 1.6rem); }
  .badge-track { animation-duration: 30s; }
}
