/* ===========================================================================
   Avis Builders and Design — stylesheet.

   This is a VISUAL SYSTEM PORT, not a restyle. Method:
   ~/.claude/skills/website-create/references/visual-system-port.md

   The system was measured out of ~/hero-rebuild/src/index.html on 2026-09-02 by
   reading its code (NOT from memory, NOT from a screenshot). What was measured:

     D1 Spacing      one closed set of section paddings, all on a 24px gutter:
                     78 / 74 / 70 / 56 / 54px vertical. Inner boxes reuse
                     24 / 28 / 34 / 44px. Sections do not each invent a padding.
     D2 Containers   radius ladder is THREE rungs — 14px x29 (containers),
                     100px x12 (pills), 10px x11 (small chrome).
                     Shadow ladder is TWO rungs, both neutral and both low-alpha:
                     0 8px 24px rgba(30,34,40,.05) x9 and the same at .06 x9.
     D3 Background   brand gradient linear-gradient(90deg,#E4202A,#F7911E) x6 and
                     the 180deg form x4; ONE neutral dark band
                     linear-gradient(120deg,#23272E,#2E333B); ONE near-white band
                     linear-gradient(#EEF0F3,#E9EBEE). Section separation is by
                     band colour alternating white / #F4F5F7, plus radial accent
                     washes on the hero and the closing band. Low-alpha NEUTRALS
                     do the container work.
     D4 Accent       accent alpha is reserved for accent objects. Every
                     accent-tinted shadow measured sits under an accent button:
                     rgba(228,32,42,.3) / .32 / .4 / .42. The accent glows under
                     itself and nowhere else.
     D5 Type         THREE weights in near-equal use — 700 x43, 600 x40, 800 x39.
                     Hierarchy comes from weight contrast, not size alone.
     D6 Images       rounded, no border, no caption, object-fit; the photo column
                     alternates against the text column.
     D7 Separation   band colour changes and dark cards, never rules. Motion is
                     THREE named @keyframes, decorative, none of it gating content.

   🛑 WHAT DOES NOT PORT: Hero's palette, copy, photos and review figures. Hero's
   stylesheet also has ZERO custom properties and ZERO clamp() — that part is
   deliberately NOT copied. The system lands on this fork's existing token names
   (--navy / --orange / --sky, per fork checklist §D6) so there is no
   find-replace churn, and the fork's fluid type is kept.
   ⚠️ Hero has NO prefers-reduced-motion block. Its restraint ports; its missing
   guard does not. The guard is added below.

   ---------------------------------------------------------------------------
   AVIS PALETTE — sampled from real sources on 2026-09-02, never borrowed:
     #014421  the brand primary. Read off the client's OWN live site
              (avisbuildersanddesign.com): active-nav background, FAQ summary
              colour and its "+" marker, list bullet markers, outlined button
              border + text. 11 `color:` declarations + 1 `background-color:`.
     #0A5C31  a second live green on the same site (`color:#0A5C31` x2).
     #F5F5DB  THE LOGO MARK. Pixel-sampled from the real logo PNG
              (.../2024/11/WhatsApp_Image_2025-10-22...removebg-preview...png):
              14,470 opaque pixels, effectively the only colour in the mark. The
              logo is cream-on-transparent, i.e. drawn for a dark ground.
     #E7A52A  the only warm accent on the live site (`color:#E7A52A`).
              ⚠️ SINGLE OCCURRENCE — thin evidence. It is not invented, but it is
              not ratified either. NEEDS DYLAN: confirm the CTA accent.
   Hover/tint variants below are DERIVATIONS of those values (a hover state is a
   rendering decision, not a claim about the business), and are marked as such.

   FONTS: the live site's families sit behind Elementor CSS variables in an
   external kit stylesheet and could not be read from the page source. Per fork
   checklist §A8 the base's Montserrat/Inter is kept.
   NEEDS DYLAN: the real brand fonts, if the brand has any.
   =========================================================================== */

:root {
  /* --- brand (fork token names kept; values re-pointed — §D6) --- */
  --navy: #2f2f2f;          /* THEIR nav/dark surface, measured live (#2f2f2f) */
  --navy-2: #014421;        /* THEIR deep green, used as an ACCENT not a surface */
  --navy-3: #157351;        /* THEIR lighter green, measured live */
  --orange: #d2cbb1;        /* THEIR sand — the colour of EVERY CTA on the live site (x9) */
  --orange-2: #c2b99a;      /* DERIVED from the sand — hover only */
  --sky: #f5f5db;           /* the logo mark, pixel-sampled */

  /* --- review stars: deliberately NOT the brand sand -----------------------
     The stars were painted `fill: var(--orange)`, and .proof-strip's background is that
     SAME token — so five stars sat on the strip at a measured contrast ratio of 1.00.
     Literally invisible, directly under the words "5.0 out of 5", on every page: the whole
     social-proof layer rendered as nothing. Gold is also what a reader already recognises
     as a rating, which the sand never could. Both values are measured, not guessed:
     Values are measured in the browser, not picked by eye. --star's first candidate (#c8860d)
     cleared white at 3.06 but only reached 2.80 on --tint, which is the background of the HERO
     form's rating strip — the single most load-bearing instance on the site. Darkened until
     every context clears 3:1:
       --star           on white 3.39:1 · on --tint 3.10:1
       --star-on-accent on the sand proof strip 3.39:1 */
  --star: #bd7f0b;
  --star-on-accent: #8f5f08;

  /* --- the brand mark on a LIGHT surface ---------------------------------
     His actual logo file is pale cream on transparent, drawn for a dark background: it works
     as-is in the footer and cannot go in the white header at all. So the header carries a
     TYPE treatment of his name instead, and that treatment is ours, not his brand.
     It was painted --orange-2 (#c2b99a) on white, which measures 1.96:1 — the word "Builders"
     and the hammer mark were both barely legible in the top-left of all 80 pages. Darkened
     into the same sand family until it clears normal-text AA on white: 4.78:1 measured. */
  --mark-on-light: #8a6f3c;

  /* --- neutrals (warm, to sit with the cream mark) --- */
  --ink: #23261f;
  --muted: #63615a;         /* live-verified warm grey */
  --line: #e7e5da;
  --tint: #f6f5f1;          /* THEIR light background, measured live */
  --band: linear-gradient(#f7f6ef, #f1efe4);   /* D3: the near-white band */
  --white: #fff;

  /* --- D2: the radius ladder, three rungs, exactly as measured --- */
  --r: 12px;        /* theirs: 12px x29 */
  --r-pill: 4px;    /* theirs: buttons are 3-4px rectangles, NOT pills */
  --r-sm: 6px;      /* theirs: 6px x40, the dominant radius */

  /* --- D2: the shadow ladder, two rungs, both neutral + low-alpha --- */
  --sh-1: 0 8px 24px rgba(35, 38, 31, 0.05);
  --sh-2: 0 8px 24px rgba(35, 38, 31, 0.06);
  --sh-lift: 0 16px 38px rgba(35, 38, 31, 0.13);

  /* --- D4: accent alpha, reserved for accent objects ONLY --- */
  --sh-accent: 0 4px 12px rgba(47, 47, 47, 0.14);   /* sand needs no glow */
  --sh-accent-hi: 0 6px 18px rgba(47, 47, 47, 0.20);

  /* --- D3: the gradients --- */
  --grad-brand: linear-gradient(90deg, var(--navy-2), var(--navy-3));  /* clean green accent band */
  --grad-brand-v: linear-gradient(180deg, var(--navy-2), var(--navy-3));
  --grad-dark: linear-gradient(120deg, #2b2b29, #3a3a37);  /* THEIR charcoal (#2f2f2f nav), not green.
     Green is an ACCENT on the live site (2 backgrounds, 11 colour decls) — using it as a full-bleed
     surface overstated it and buried the warm neutral identity. */

  /* --- D1: the spacing scale, one closed set --- */
  --pad-xl: 78px;
  --pad-lg: 70px;
  --pad-md: 56px;
  --gutter: 24px;

  --font-head: "Poppins", system-ui, sans-serif;   /* THEIR family, measured live */
  --font-body: "Poppins", system-ui, sans-serif;   /* THEIR family, measured live */
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body); color: var(--ink); line-height: 1.65;
  background: var(--white); font-size: 16.5px; font-weight: 400;
}
img, svg, iframe { max-width: 100%; }
iframe { border: 0; }
a { color: var(--navy-2); }

/* --- D5: THREE weights in near-equal use. Hierarchy is weight contrast, not
   size alone. The fork's fluid clamp() type is kept — Hero hardcodes and that
   part does not port. --- */
h1, h2, h3, .btn, .logo, .kicker, .step-badge { font-family: var(--font-head); }
h1 { font-size: clamp(2rem, 4.2vw, 3.05rem); line-height: 1.12; font-weight: 600; color: var(--navy); letter-spacing: -0.5px; }
h2 { font-size: clamp(1.5rem, 2.8vw, 2.1rem); line-height: 1.18; font-weight: 600; color: var(--navy); letter-spacing: -0.3px; margin-bottom: 0.6em; }
h3 { font-size: 1.1rem; font-weight: 700; color: var(--navy); }
strong, b { font-weight: 700; }
.lede { font-size: 1.06rem; font-weight: 500; color: var(--muted); max-width: 62ch; }
p { font-weight: 400; }

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 var(--gutter); }
.wrap-narrow {
  max-width: 860px;
  /* 🛑 margin auto is NOT optional. `.wrap-narrow` only capped the width, so the two call sites
     that use it WITHOUT `.wrap` (the home services intro, the gallery group lede) sat flush against
     their parent .wrap's left edge instead of centring: measured live at 1440, four home-page
     paragraphs rendered at x=154 while every other prose element on the page sat at x=314. That is
     two prose left edges on one page, which the checklist scores as a defect. Centring here fixes
     both call sites and stops the next one recurring - paired with `.wrap` it changes nothing,
     because `.wrap` already sets the same auto margins. */
  margin-left: auto;
  margin-right: auto;
  /* ...and the SAME gutter as `.wrap`. Centring alone still left the two nested call sites 24px
     out (x=290 vs x=314), because a nested `.wrap-narrow` has no horizontal padding of its own
     while `.wrap wrap-narrow` inherits `.wrap`'s. Declaring it here is idempotent for the paired
     case - identical value - and makes every prose column on the site resolve to ONE left edge. */
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}
.center { text-align: center; }
.center-box { text-align: center; margin-left: auto; margin-right: auto; }
.center-box .lede, .center-box .hero-sub { margin-left: auto; margin-right: auto; }
.muted { color: var(--muted); }
.skip { position: absolute; left: -999px; }
.skip:focus { left: 12px; top: 12px; background: var(--white); padding: 8px 14px; z-index: 99; }

/* --- D1 + D7: the closed padding set, and section separation by BAND, never a
   rule. No two consecutive sections may share a treatment (static check S3). --- */
.section { padding: var(--pad-xl) 0; }
.section-tint { background: var(--tint); }
.section-dark { background: var(--grad-dark); }
.section-dark h2, .section-dark h3, .section-dark p, .section-dark li { color: var(--white); }
.section-dark .kicker { color: var(--sky); }
.reviews { background: var(--band); }
.entity-sec { padding: 30px 0; background: var(--navy); }
/* D7 / static check S3: no two CONSECUTIVE sections may share a treatment, or
   nothing on the page has hierarchy. These two carry their own band precisely
   because they sit next to a white section in the canonical stacks — the offer
   block follows the services grid on home, and the team block follows the intro
   on /about/. gates/static-check.js asserts it on every page, every build. */
/* (the band itself comes from the `section-tint` CLASS on these sections in
   build.js — the gate reads markup, so the class is the source of truth) */

.kicker {
  display: inline-block; font-weight: 700; font-size: 0.78rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--navy-2);
  margin-bottom: 10px;
}
/* D4: gold never sets text on a light ground — it fails contrast there. */

.ic { width: 22px; height: 22px; flex: 0 0 auto; }

/* ---------------- buttons ---------------- */
.btn {
  display: inline-flex; align-items: center; gap: 9px; font-weight: 700; font-size: 0.95rem;
  padding: 14px 24px; border-radius: var(--r-pill); text-decoration: none;
  border: 2px solid transparent; cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
/* D4: the accent glows under ITSELF and nowhere else. */
.btn-cta { background: var(--orange); color: #484848; box-shadow: var(--sh-accent); }
.btn-cta:hover { background: var(--orange-2); box-shadow: var(--sh-accent-hi); transform: translateY(-2px); }
.btn-ghost { border-color: var(--navy); color: var(--navy); background: transparent; }
.btn-ghost:hover { transform: translateY(-2px); box-shadow: var(--sh-1); }
.hero .btn-ghost, .page-head .btn-ghost { border-color: rgba(245, 245, 219, 0.7); color: var(--sky); }
.btn-light { background: var(--white); color: var(--navy); box-shadow: var(--sh-2); }
.btn-lg { padding: 16px 30px; font-size: 1.05rem; }
.btn-sm { padding: 10px 18px; font-size: 0.86rem; }
.btn-block { width: 100%; justify-content: center; }

/* ---------------- header ---------------- */
.site-header {
  position: sticky; top: 0; z-index: 50; background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--line);
}
.header-in { display: flex; align-items: center; gap: 22px; height: 76px; }
/* The header CTA wrapped "Free Estimate" onto two lines, making the button 82px tall inside a
   76px header, so it overhung the band on every page. Pre-existing, not introduced by the
   wordmark change - measured identical on the old live build. Removing the Español switcher
   freed ~78px here, which is what makes nowrap affordable now. flex-shrink:0 stops it being
   squeezed back into wrapping at the tight widths just above the 1180px nav breakpoint. */
.header-cta { white-space: nowrap; flex-shrink: 0; }
/* The wordmark carries the FULL registered name (Avi's request, 2026-09-09). That is 24
   characters against a header the comment below measures at needing ~1350px already, so it
   must not be allowed to wrap - left to itself it broke onto three lines and blew the 76px
   header height. nowrap + flex:0 0 auto keeps it one line; the clamp buys back the ~90px
   the longer name costs, without touching the nav breakpoint. */
.logo { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; font-size: clamp(0.9rem, 0.95vw, 1.12rem); font-weight: 600; color: var(--navy); white-space: nowrap; flex: 0 0 auto; }
.logo b { color: var(--mark-on-light); font-weight: 600; }
.logo-ic { width: 30px; height: 30px; color: var(--mark-on-light); }
.logo-light { color: var(--sky); }
.logo-light b { color: var(--orange); }
.logo-light .logo-ic { color: var(--orange); }

.nav { margin-left: auto; }
.nav ul { display: flex; gap: 2px; list-style: none; }
.nav a { display: block; padding: 10px 12px; text-decoration: none; font-weight: 600; font-size: 0.93rem; color: var(--navy); border-radius: var(--r-sm); white-space: nowrap; }
.nav a:hover { background: var(--tint); color: var(--navy-2); }
.has-sub { position: relative; }
.has-sub > a::after { content: " ▾"; font-size: 0.7em; }
.sub {
  position: absolute; top: 100%; left: 0; min-width: 290px; background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--sh-lift);
  padding: 8px; display: none !important; flex-direction: column;
}
.has-sub:hover .sub, .has-sub:focus-within .sub { display: flex !important; }
.sub a { padding: 9px 12px; font-weight: 500; }

.lang-switch { font-weight: 700; font-size: 0.86rem; color: var(--navy-2); text-decoration: none; border: 1px solid var(--line); border-radius: var(--r-pill); padding: 6px 14px; }
/* nowrap: a phone number is one token to a reader. Without this it broke across three lines
   the moment the nav got wider, which is exactly what happened when Gallery and Reviews were
   added — the header grew to 74px of wrapped digits at 1440. */
.header-phone { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; color: var(--navy); text-decoration: none; font-size: 0.94rem; white-space: nowrap; }
.header-phone .ic { color: var(--mark-on-light); }
/* The sole navigation control below 1180px. The bars are 24x29; the padding is what takes the
   tap target to ~44x45, and the negative margin keeps it visually where it was. */
.nav-toggle { display: none; background: none; border: 0; cursor: pointer; padding: 8px 10px; margin: -8px -10px; }
.nav-toggle span { display: block; width: 24px; height: 3px; background: var(--navy); margin: 5px 0; border-radius: 2px; }

/* ---------------- social-proof strip (§5 position 1) ---------------- */
/* THEIR treatment: the bar under the nav on the live site is the sand #d2cbb1 with dark
   text. It was a full-bleed green band here, which overstated an accent colour that appears
   on their site only as small marks. */
.proof-strip { background: var(--orange); }
/* The strip is an <a>, so its colour comes from the link rules — force the whole
   subtree dark. #3a3a37 on the sand #d2cbb1 measures ~7.4:1, well past AA. */
.proof-strip, .proof-strip .proof-in, .proof-strip .proof-text,
.proof-strip .proof-count, .proof-strip .stars, .proof-strip a { color: #3a3a37; }
/* This must set FILL, not colour. The stars are filled paths, and the earlier
   `color: #6b6350` here never reached them — `fill` on .stars .ic won, unchallenged. */
.proof-strip .stars .ic { color: var(--star-on-accent); fill: var(--star-on-accent); }
.proof-in {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap; padding: 13px var(--gutter); text-decoration: none;
  color: var(--sky); font-weight: 600; font-size: 0.95rem;
}
.proof-in:hover { color: var(--white); }
.proof-count { font-weight: 700; }
.stars { display: inline-flex; gap: 3px; }
.stars .ic { width: 18px; height: 18px; color: var(--star); fill: var(--star); stroke: none; }

/* ---------------- hero ---------------- */
/* D3: the accent wash is a RADIAL over the brand gradient — accent alpha on an
   accent surface. The hero never carries an inline photo (§13): the photo is a
   background behind both columns and the right half is the form. */
.hero {
  background:
    radial-gradient(900px 460px at 88% -12%, rgba(210, 203, 177, 0.20), transparent 62%),  /* THEIR sand, was gold */
    radial-gradient(700px 420px at 0% 118%, rgba(245, 245, 219, 0.1), transparent 58%),
    var(--grad-dark);
  padding: 84px 0 92px;
}
.hero.hero-photo { background-size: cover; background-position: center; }
.hero-grid { display: grid; grid-template-columns: 1.12fr 0.88fr; gap: 54px; align-items: center; }
.hero-copy h1 { color: var(--white); margin: 6px 0 18px; }
.hero .kicker { color: var(--sky); }
.hero-sub { font-size: 1.08rem; color: rgba(255, 255, 255, 0.86); max-width: 56ch; font-weight: 400; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 14px; margin: 26px 0 30px; }
.badge-row { display: flex; flex-wrap: wrap; gap: 10px 22px; list-style: none; }
.badge-row li {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 0.87rem; font-weight: 600; color: rgba(255, 255, 255, 0.9);
  /* 🛑 Each chip must be a DISCRETE, visually bounded element, not a run of text with an icon -
     measured on the deployed hero at 1440 and 375, every chip returned border 0px none, background
     transparent, padding 0px and radius 0px, so four separate claims read as one wrapped sentence.
     Bounded here in BOTH contexts: light-on-dark in the hero, dark-on-light under .badge-dark. */
  padding: 6px 12px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.10);
  border-radius: var(--r-pill);
}
.badge-row .ic { width: 17px; height: 17px; color: var(--mark-on-light); }
.badge-dark li { color: var(--navy); border-color: var(--line); background: var(--white); }
.badge-dark .ic { color: var(--mark-on-light); }

/* ---------------- the lead form card ---------------- */
.form-card {
  background: var(--white); border-radius: var(--r); box-shadow: var(--sh-lift);
  padding: 28px 26px 24px; border-top: 5px solid var(--orange);
}
.form-kicker { font-family: var(--font-head); font-weight: 700; font-size: 0.74rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--navy-2); }
.form-title { font-size: 1.34rem; margin: 4px 0 10px; }
/* §5 position 2: the rating strip sits between the H2 and the first field. */
.form-proof {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  font-size: 0.86rem; font-weight: 600; color: var(--muted);
  padding: 9px 14px; margin-bottom: 14px;
  background: var(--tint); border-radius: var(--r-sm);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.form-card label { display: block; font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); margin-bottom: 12px; }
.form-card input, .form-card textarea {
  display: block; width: 100%; margin-top: 5px; padding: 11px 13px; font: inherit;
  font-weight: 400; text-transform: none; letter-spacing: normal; color: var(--ink);
  border: 1.5px solid var(--line); border-radius: var(--r-sm); background: #fdfdf9;
}
.form-card input:focus, .form-card textarea:focus { outline: 2px solid var(--navy-2); border-color: var(--navy-2); }

/* ---------------- interior page heads ---------------- */
.page-head {
  background:
    radial-gradient(700px 320px at 92% -20%, rgba(231, 165, 42, 0.22), transparent 62%),
    var(--grad-dark);
  padding: var(--pad-lg) 0;
}
.page-head h1 { color: var(--white); margin: 6px 0 14px; }
.page-head .kicker { color: var(--sky); }
.page-head .hero-sub { color: rgba(255, 255, 255, 0.86); }

/* /thank-you/ is the only page that puts .prose and .license-line inside the dark
   .page-head band. Both inherit light-background colours (--ink, --muted) and render
   near-invisible on charcoal - measured live 2026-09-09. Every other .page-head child
   already carries an explicit light colour; these two were simply missed. */
.page-head .prose { color: rgba(255, 255, 255, 0.86); }
.page-head .prose a { color: var(--sky); }
.page-head .license-line { color: rgba(255, 255, 255, 0.70); }
.hub-hero { padding: 88px 0; }

.crumbs { font-size: 0.85rem; color: var(--muted); padding-top: 18px; }
.crumbs a { color: var(--muted); }

/* ---------------- cards ---------------- */
.card-grid { display: grid; gap: 22px; }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }

.svc-card, .offer-card, .place-card, .step-card, .map-card {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--sh-1); text-decoration: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.svc-card { padding: 26px 22px; display: flex; flex-direction: column; }
.svc-card:hover, .place-card:hover { transform: translateY(-4px); box-shadow: var(--sh-lift); border-color: var(--navy-2); }
.card-ic { width: 38px; height: 38px; color: var(--navy-2); margin-bottom: 13px; }
.svc-card h3 { margin-bottom: 8px; }
.svc-card p { font-size: 0.9rem; color: var(--muted); }
.link-more { display: inline-block; margin-top: 12px; font-weight: 700; font-size: 0.87rem; color: var(--navy-2); }

.offer-card { padding: 26px 22px; border-top: 5px solid var(--orange); display: flex; flex-direction: column; gap: 11px; }
.offer-card p { font-size: 0.9rem; color: var(--muted); }
.offer-card .btn { align-self: flex-start; margin-top: auto; }
.badge-pill {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--r-pill);
  background: rgba(231, 165, 42, 0.14); color: var(--mark-on-light);
}

/* ---------------- split blocks (D6: the alternation rule) ---------------- */
/* 🛑 STATIC CHECK S2. Side is controlled by ORDER of the two children, so the
   photo x-position genuinely differs between blocks. DOM order stays
   text-then-image for reading order; `order` flips the visual side. Three
   image/text blocks all painting the photo at the same x is the diagnosed
   "very static" defect (PowerClean shipped three at x=720). */
.split { display: grid; grid-template-columns: 0.86fr 1.14fr; gap: 52px; align-items: center; }
.split-media-first .split-media { order: 1; }
.split-media-first .split-copy { order: 2; }
.split-text-first { grid-template-columns: 1.14fr 0.86fr; }
.split-text-first .split-copy { order: 1; }
.split-text-first .split-media { order: 2; }

.media { width: 100%; height: auto; border-radius: var(--r); display: block; object-fit: cover; }
.media-tall { aspect-ratio: 4 / 5; }
.media-wide { aspect-ratio: 4 / 3; }
.card-photo { border-radius: var(--r-sm); margin-bottom: 14px; aspect-ratio: 16 / 10; }

/* Copy-heavy blocks that pair with a photo sit in a dark card so the block reads
   as one unit (§13). */
.dark-card { background: var(--grad-dark); color: var(--white); border-radius: var(--r); padding: 34px 28px; box-shadow: var(--sh-2); }
.dark-card h2, .dark-card h3 { color: var(--white); }
.dark-card p, .dark-card li { color: rgba(255, 255, 255, 0.86); }
.dark-card .ic { color: var(--orange); }

.why-list { list-style: none; display: grid; gap: 18px; margin-top: 20px; }
.why-list li { display: flex; gap: 14px; }
.why-list .ic { color: var(--navy-2); margin-top: 4px; }
.why-list p { color: var(--muted); font-size: 0.92rem; }
.dark-card .why-list p { color: rgba(255, 255, 255, 0.8); }

.check-list { list-style: none; display: grid; gap: 12px; margin: 18px 0; }
.check-list li { display: flex; gap: 11px; align-items: flex-start; font-weight: 500; }
.check-list .ic { color: var(--mark-on-light); margin-top: 3px; width: 19px; height: 19px; }
/* The homepage trust points: seven short verified claims. auto-fit rather than a fixed
   column count so the row reflows instead of stranding a lone item on its own line —
   the dead-column failure this book keeps hitting. Single column on narrow screens. */
.check-cols { grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: 12px 30px; }

/* ---------------- gallery ----------------
   The photo bank is overwhelmingly PORTRAIT phone photography: only 8 of 58 frames are true
   landscape. A fixed 3- or 4-up grid of equal boxes would letterbox almost everything, so this
   is a CSS-columns masonry — each photo keeps its own aspect ratio and the columns fill from
   the top. `auto-fill`/columns also means a group with an awkward count (5, 7, 11) reflows
   instead of stranding a lone tile in a dead column, which is the failure this book keeps
   hitting on fixed-track grids. */
.shot-grid { columns: 3 260px; column-gap: 16px; margin-top: 26px; }
.shot { margin: 0 0 16px; break-inside: avoid; border-radius: var(--r-sm); overflow: hidden; background: var(--tint); }
.shot img { display: block; width: 100%; height: auto; }
.gallery-lede { margin-bottom: 4px; }

/* Exterior / Interior category bands + the jump row that sits above them. The brand's radius
   is a 4px rectangle, not a pill, so these follow the buttons rather than inventing a shape. */
.gallery-jump { padding: 22px 0 0; }
.gallery-jump-list { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; margin: 0; padding: 0; }
.gallery-jump-list a {
  display: inline-block; padding: 9px 16px; border: 1px solid var(--line);
  border-radius: var(--r-pill); background: var(--white); color: var(--ink);
  font-weight: 600; text-decoration: none;
}
.gallery-jump-list a:hover, .gallery-jump-list a:focus-visible { background: var(--tint); border-color: var(--muted); }
.gallery-area { padding-bottom: calc(var(--pad-xl) * 0.6); }
.gallery-area h2 { scroll-margin-top: 90px; }
@media (max-width: 860px) { .shot-grid { columns: 2 160px; column-gap: 12px; } .shot { margin-bottom: 12px; } }
@media (max-width: 520px) { .shot-grid { columns: 1; } }
.license-line { font-size: 0.85rem; color: var(--muted); margin-top: 18px; }
.muted { color: var(--muted); }
.inline-cta { margin-top: 18px; font-weight: 600; }

.answer-first { background: rgba(245, 245, 219, 0.14); border-left: 3px solid var(--orange); border-radius: var(--r-sm); padding: 16px 18px; margin: 16px 0; }
.answer-first ul { margin: 10px 0 0 18px; }

.chip-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 18px; }
.chip { display: flex; align-items: center; gap: 9px; padding: 11px 14px; background: var(--tint); border: 1px solid var(--line); border-radius: var(--r-sm); text-decoration: none; font-weight: 600; font-size: 0.89rem; color: var(--navy); }
.chip:hover { border-color: var(--navy-2); }
.chip .ic { width: 18px; height: 18px; color: var(--navy-2); }

/* ---------------- review proof / trust index ---------------- */
.trust-index { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 26px; align-items: stretch; margin-top: 14px; }
.trust-card {
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 10px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  padding: 30px 28px; box-shadow: var(--sh-2); text-decoration: none;
}
.trust-mark { font-family: var(--font-head); font-weight: 600; font-size: 1.4rem; color: var(--navy); }
.trust-rating { font-family: var(--font-head); font-weight: 600; font-size: 2.1rem; color: var(--navy); }
.trust-cta { font-weight: 700; font-size: 0.88rem; color: var(--navy-2); }
.trust-embed iframe, .footer-map iframe, .contact-map iframe, .area-map iframe { width: 100%; border-radius: var(--r); display: block; }
.trust-embed iframe { min-height: 300px; }

/* ---------------- how it works ---------------- */
.steps { list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; margin-top: 26px; }
.step-card { padding: 26px 22px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; }
/* 🛑 The badge is a SPAN, not an H2. Bluebird marks these up as H2s, which puts
   four extra H2s on every service page and fails our heading gate. */
.step-badge {
  display: inline-block; font-weight: 700; font-size: 0.72rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--navy); background: var(--orange);
  padding: 6px 15px; border-radius: var(--r-pill);
}
.step-ic { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: var(--r-sm); background: rgba(245, 245, 219, 0.16); color: var(--mark-on-light); }
.step-ic .ic { width: 26px; height: 26px; }
.step-card h3 { margin-top: 2px; }
.step-card p { font-size: 0.9rem; }
.step-timing { font-weight: 700; color: var(--orange); font-size: 0.85rem; }

/* ---------------- FAQ (§9: accordions, CENTERED container) ---------------- */
.faq-item { border: 1px solid var(--line); border-radius: var(--r); background: var(--white); margin-bottom: 12px; box-shadow: var(--sh-1); overflow: hidden; text-align: left; }
.faq-item summary {
  cursor: pointer; list-style: none; padding: 18px 46px 18px 22px; font-family: var(--font-head);
  font-weight: 700; color: var(--navy); position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: "+"; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); font-size: 1.4rem; color: var(--orange-2); font-weight: 700; }
.faq-item[open] summary::after { content: "–"; }
.faq-item p { padding: 0 22px 20px; color: var(--muted); }

/* ---------------- service areas ---------------- */
.areas-grid { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 40px; align-items: stretch; margin-top: 14px; }
.area-pills { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; align-content: start; }
.area-pill {
  display: inline-flex; align-items: center; gap: 8px; padding: 11px 16px;
  background: var(--orange); color: var(--navy); border-radius: var(--r-pill);
  text-decoration: none; font-weight: 700; font-size: 0.86rem;
}
.area-pill:hover { background: var(--sky); }
.area-pill .ic { width: 16px; height: 16px; }
.area-map iframe { min-height: 400px; height: 100%; }

/* ---------------- maps + local routes ---------------- */
.map-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 26px; }
.map-card { overflow: hidden; display: flex; flex-direction: column; }
.map-card iframe { width: 100%; height: 210px; display: block; }
.map-body { padding: 18px 18px 20px; }
.map-body h3 { margin-bottom: 10px; }
.route-row { display: grid; grid-template-columns: 1fr; gap: 1px; font-size: 0.84rem; margin-bottom: 8px; }
.route-k { font-weight: 700; color: var(--navy-2); text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.72rem; }
.route-v { color: var(--muted); }
.map-cid { max-width: 460px; margin: 26px auto 0; }
.map-cid iframe { width: 100%; height: 300px; border-radius: var(--r); display: block; }

/* ---------------- place-card grid (§4) ---------------- */
.place-card { overflow: hidden; display: flex; flex-direction: column; text-align: center; }
.place-photo { border-radius: 0; aspect-ratio: 4 / 3; }
.place-nophoto { aspect-ratio: 4 / 3; background: var(--tint); border-bottom: 1px solid var(--line); }
.place-body { background: var(--grad-dark); padding: 22px 18px 24px; display: flex; flex-direction: column; align-items: center; gap: 13px; flex: 1; }
.place-body h3 { color: var(--white); letter-spacing: 0.06em; font-size: 1rem; }

/* ---------------- entity strip ---------------- */
.entity-row { list-style: none; display: flex; flex-wrap: wrap; justify-content: center; gap: 14px 40px; }
.entity-row li { display: inline-flex; align-items: center; gap: 9px; color: var(--sky); font-weight: 600; font-size: 0.88rem; }
.entity-row .ic { width: 19px; height: 19px; color: var(--orange); }
.entity-row a { color: var(--sky); text-decoration: none; }
.entity-row a:hover { color: var(--white); text-decoration: underline; }

/* ---------------- real-job block ---------------- */
.job-card { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 30px; align-items: center; background: var(--white); border: 1px solid var(--line); border-radius: var(--r); padding: 26px; box-shadow: var(--sh-2); margin-top: 18px; }
.job-body p { margin-bottom: 9px; font-size: 0.94rem; }
.job-src { font-size: 0.8rem; margin-top: 12px; }

.knowledge-box { background: var(--white); border: 1px solid var(--line); border-radius: var(--r); padding: 30px 30px 32px; box-shadow: var(--sh-1); text-align: left; }

/* ---------------- contact ---------------- */
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }
.contact-list { list-style: none; display: grid; gap: 20px; margin: 0 0 26px; }
.contact-list li { display: flex; gap: 14px; }
.contact-list .ic { color: var(--navy-2); margin-top: 4px; }
.contact-list a { font-weight: 700; color: var(--navy); }
.contact-list p { color: var(--muted); font-size: 0.94rem; }
.contact-map iframe { min-height: 320px; }

/* ---------------- CTA band ---------------- */
/* D3/D4: the closing band is the one place the brand gradient runs full-width,
   with the accent reserved for the button that sits on it. */
.cta-band { background: var(--grad-dark); padding: 54px 0; }   /* charcoal, not a green slab */
.cta-in { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px; }
.cta-band h2 { color: var(--white); margin-bottom: 0.2em; }
.cta-band .kicker { color: var(--sky); }
.cta-micro { color: rgba(255, 255, 255, 0.85); margin-top: 8px; font-size: 0.92rem; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 14px; }

/* ---------------- footer ---------------- */
.site-footer { background: var(--navy); color: rgba(255, 255, 255, 0.8); padding: 66px 0 0; }
.footer-grid { display: grid; grid-template-columns: 1.5fr 1fr 1.2fr 1fr 1fr; gap: 34px; padding-bottom: 44px; }
.f-col h2 { font-size: 0.92rem; color: var(--sky); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 14px; }
.f-col ul { list-style: none; display: grid; gap: 8px; }
.f-col a { color: rgba(255, 255, 255, 0.82); text-decoration: none; font-size: 0.92rem; }
.f-col a:hover { color: var(--sky); }
.f-col p { color: rgba(255, 255, 255, 0.8); }
.f-brand p { font-size: 0.92rem; margin-top: 12px; }
.nap { line-height: 1.8; }
.license { font-size: 0.8rem; color: rgba(255, 255, 255, 0.55); }
/* Social links. 40px hit target so it is usable on a phone, and the resting state is a
   quiet outline that lifts to the brand sand on hover — the row should read as available,
   not compete with the CTA. */
.social-row { display: flex; gap: 10px; margin-top: 16px; }
.social-row a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--r-sm);
  border: 1px solid rgba(255, 255, 255, 0.22); color: rgba(255, 255, 255, 0.82);
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.social-row a:hover { color: var(--navy); background: var(--orange); border-color: var(--orange); }
.social-row .ic { width: 19px; height: 19px; }
.footer-map { margin-top: 16px; }
.footer-map iframe { height: 220px; }
.footer-bottom {
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  border-top: 1px solid rgba(255, 255, 255, 0.12); padding: 20px var(--gutter);
  font-size: 0.82rem; color: rgba(255, 255, 255, 0.55);
}
.footer-bottom a { color: rgba(255, 255, 255, 0.7); }

.mobile-call {
  display: none; position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 60;
  align-items: center; justify-content: center; gap: 10px;
  background: var(--orange); color: var(--navy); font-family: var(--font-head); font-weight: 700;
  padding: 15px; border-radius: var(--r-pill); text-decoration: none; box-shadow: var(--sh-accent-hi);
}

/* ===========================================================================
   MOTION — restrained, and it can NEVER hide content.
   The script side lives in build.js revealScript(), ported verbatim from the
   shipped PowerClean implementation with all six guards. This is the CSS half
   and it is not optional: guard 1 needs `html.js`, guard 3 needs `.reveal-force`,
   guard 4 needs the reduced-motion block, guard 5 needs opacity+translate ONLY.
   🚫 Deliberately NOT built: carousels, autoplay, parallax, scroll-jacking,
   animated counters.
   =========================================================================== */

/* Static check S1: a stylesheet with zero @keyframes is the diagnosed "very
   static" defect. These three are decorative and none of them gates content. */
@keyframes rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes proof-sheen { 0%, 100% { opacity: 0.9; } 50% { opacity: 1; } }
@keyframes badge-settle { from { transform: translateY(-3px); } to { transform: translateY(0); } }

.section > .wrap > h2 { position: relative; }
.section-dark > .wrap > h2.center::after {
  content: ""; display: block; width: 64px; height: 3px; margin: 14px auto 0;
  background: var(--orange); border-radius: var(--r-pill);
  transform-origin: center; animation: rule-draw 0.6s ease-out both;
}
.proof-in .stars { animation: proof-sheen 4s ease-in-out infinite; }
.step-badge { animation: badge-settle 0.4s ease-out both; }

/* Guard 1: the hiding rule is scoped to html.js, a class only the script adds. */
html.js .reveal { opacity: 0; transform: translateY(14px); }
html.js .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.55s cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* Guard 3, the other half: the 1200ms failsafe reveals with transition:none, so
   a renderer that never ticks transitions still paints the content. */
html.js .reveal.reveal-force { opacity: 1 !important; transform: none !important; transition: none !important; }

/* Guard 4. Hero has no such block; this is the guard the reference is missing. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html.js .reveal, html.js .reveal.is-in { opacity: 1; transform: none; transition: none; }
  .svc-card, .place-card, .btn, .map-card { transition: none !important; }
  .svc-card:hover, .place-card:hover, .btn:hover { transform: none; }
  .section-dark > .wrap > h2.center::after,
  .proof-in .stars, .step-badge { animation: none; }
}
/* Guard 5: print output must never be blank. */
@media print {
  html.js .reveal, html.js .reveal.is-in { opacity: 1 !important; transform: none !important; }
}

/* ---------------- responsive ---------------- */

/* MID-WIDTH HEADER TIGHTENING — 1181px to 1400px.
   Seven nav items plus logo, language switch, phone and CTA need roughly 1350px at full
   spacing. Rather than send every 1280 and 1366 laptop to a hamburger (both are extremely
   common widths, and the horizontal nav is what Google reads for sitelinks), the header
   tightens first: less padding per item, a slightly smaller label, a narrower outer gap.
   That buys about 90px, which is what the measurements said was missing at 1200. */
@media (min-width: 1181px) and (max-width: 1400px) {
  .header-in { gap: 12px; }
  .nav a { padding: 10px 7px; font-size: 0.88rem; }
  .lang-switch { padding: 6px 10px; }
  .header-cta { padding-left: 16px; padding-right: 16px; }
}

/* NAV COLLAPSE — 1180px, not 860px.
   Measured, not guessed: the horizontal nav is 622px wide, and alongside the logo, language
   switch, phone and CTA it stops fitting at 1180. Below that the CTA was pushed clean off the
   right edge (+82px at 1100, +158px at 1024) while the nav still tried to render inline,
   because the hamburger did not appear until 860. So every laptop between 860 and 1180 got a
   header with its primary call-to-action off-screen.
   Adding Gallery and Reviews to the nav is what pushed this into common widths, but the
   header was already marginal there — the phone was wrapping to two lines at 1100 before
   those links existed. Collapsing earlier fixes both.
   `.logo { margin-right: auto }` does the alignment: `.nav` becomes position:fixed here, so it
   leaves the flex flow and its own margin-left:auto stops working. Pushing from the logo side
   keeps whatever is still visible right-aligned regardless of which pieces a page renders. */
@media (max-width: 1180px) {
  /* 🛑 Fork gotcha §F, and it bit again on 2026-09-09. backdrop-filter on the header makes
     the header the containing block for the fixed nav overlay, so the menu is trapped inside
     the 76px header instead of filling the screen. This kill-switch lived ONLY in the 860px
     block while the nav goes fixed at 1180px, so from 861px to 1180px the hamburger opened a
     36px-tall panel with 0 of 20 links reachable - iPad landscape included. It must live at
     the SAME breakpoint the nav goes fixed at. If that breakpoint ever moves again, move this
     with it. */
  .site-header { backdrop-filter: none; background: var(--white); }
  .nav { position: fixed; inset: 76px 0 0 0; background: var(--white); padding: 18px var(--gutter); display: none; overflow: auto; margin-left: 0; }
  body.nav-open .nav { display: block; }
  .nav ul { flex-direction: column; }
  .sub { position: static; display: flex !important; box-shadow: none; border: 0; padding-left: 16px; }
  .nav-toggle { display: block; }
  .logo { margin-right: auto; }
}

@media (max-width: 1100px) {
  .map-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 1020px) {
  .cols-4 { grid-template-columns: repeat(2, 1fr); }
  .steps { grid-template-columns: repeat(2, 1fr); }
  .header-phone, .lang-switch { display: none; }
  .area-pills { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 860px) {
  .hero-grid, .split, .contact-grid, .areas-grid, .trust-index, .job-card { grid-template-columns: 1fr; gap: 34px; }
  .split-media-first .split-media, .split-text-first .split-media { order: 2; }
  .split-media-first .split-copy, .split-text-first .split-copy { order: 1; }
  .cols-3 { grid-template-columns: 1fr; }
  /* .site-header backdrop-filter is killed in the 1180px block above, which is where the nav
     actually goes fixed. Do not re-add it here - one copy, at the right breakpoint.
     padding-bottom clears the fixed .mobile-call bar, which is shown only at this width and
     occupies 69px at the bottom of the viewport (bottom:12px + ~57px tall). Without it the
     last nav item, Contact, sits underneath the bar and is 100% untappable even scrolled to
     the end. The footer already does exactly this with padding-bottom:78px. */
  .nav { position: fixed; inset: 76px 0 0 0; background: var(--white); padding: 18px var(--gutter) 78px; display: none; overflow: auto; }
  body.nav-open .nav { display: block; }
  .nav ul { flex-direction: column; }
  .sub { position: static; display: flex !important; box-shadow: none; border: 0; padding-left: 16px; }
  .nav-toggle { display: block; margin-left: auto; }
  .header-cta { display: none; }
  .mobile-call { display: flex; }
  .site-footer { padding-bottom: 78px; }
  .section { padding: var(--pad-md) 0; }
  .hero, .page-head { padding: 56px 0 64px; }
  .field-row { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .cols-4, .footer-grid, .steps, .map-grid, .area-pills, .chip-grid { grid-template-columns: 1fr; }
}

/* Card titles that sit directly under a page H1 with no section H2 above them
   (the services hub, the place-card grid) are marked up as H2 — an h1→h3 jump
   fails the heading gate. They keep the card's visual size. */
.card-h { font-size: 1.1rem; font-weight: 700; color: var(--navy); letter-spacing: 0; margin-bottom: 8px; }
.place-body .card-h { color: var(--white); letter-spacing: 0.06em; font-size: 1rem; margin-bottom: 0; }

/* .split-solo — a split with no real media collapses to one column so the copy
   fills the container instead of sitting in ~52% of it. Specificity (0,2,0)
   beats .split-text-first / .split-media-first (0,1,0). See splitCls() in build.js. */
.split.split-solo { grid-template-columns: 1fr; }

/* ---------------------------------------------------------------------------
   2026-09-02 — WHITE-ON-WHITE FIX inside dark sections.
   `.section-dark h2, h3, p, li { color: var(--white) }` (above) paints EVERY
   descendant white, but the card components (.step-card, .svc-card, .offer-card,
   .place-card, .map-card) carry a WHITE background of their own. Inside a dark
   section their text therefore rendered white-on-white — measured at exactly
   1:1 contrast on the homepage "Working With Avis Builders and Design" steps,
   where all four cards looked empty apart from the badge and icon.
   Same defect class as the Tagz homepage (1.01:1 invisible headings) found today.
   `.section-dark .step-card h3` (0,2,1) out-specifies `.section-dark h3` (0,1,1).
   `.dark-card` is deliberately excluded — it IS meant to be dark with light text.
   --------------------------------------------------------------------------- */
.section-dark .step-card h2,  .section-dark .step-card h3,  .section-dark .step-card p,  .section-dark .step-card li,
.section-dark .svc-card h2,   .section-dark .svc-card h3,   .section-dark .svc-card p,   .section-dark .svc-card li,
.section-dark .offer-card h2, .section-dark .offer-card h3, .section-dark .offer-card p, .section-dark .offer-card li,
.section-dark .place-card h2, .section-dark .place-card h3, .section-dark .place-card p, .section-dark .place-card li,
.section-dark .map-card h2,   .section-dark .map-card h3,   .section-dark .map-card p,   .section-dark .map-card li {
  color: var(--ink);
}
.section-dark .step-card p, .section-dark .svc-card p,
.section-dark .offer-card p, .section-dark .place-card p { color: var(--muted); }

/* Verbatim Google review cards (§5 position 4). Real quotes only — see `reviews` in data.js. */
.review-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; margin-top: 26px; }
.review-card { background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  padding: 24px 22px; box-shadow: var(--sh-1); display: flex; flex-direction: column; gap: 12px; margin: 0; }
.review-card blockquote { margin: 0; font-size: 0.95rem; line-height: 1.6; color: var(--ink); }
.review-card figcaption { font-size: 0.86rem; font-weight: 600; color: var(--ink); margin-top: auto; }
.review-card .muted { font-weight: 400; color: var(--muted); }
.reviews-cta { margin-top: 20px; text-align: center; }
@media (max-width: 900px) { .review-cards { grid-template-columns: 1fr; } }

/* The trust card sits alone when the GBP embed is present in its own cell; keep it
   from stretching to the full row on its own. */
.trust-index { align-items: start; }
.trust-card { max-width: 420px; }

/* Typographic place card — used when a town has no honest photograph. The standard:
   "A place with no honest photo gets a typographic card — never a neighbouring town's image." */
.place-typo { aspect-ratio: 4 / 3; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 6px; background: var(--grad-dark); border-radius: var(--r) var(--r) 0 0; }
.place-initial { font-family: var(--font-head); font-weight: 600; font-size: 3.2rem; line-height: 1;
  color: var(--orange); letter-spacing: 0.02em; }
.place-county { font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255,255,255,0.72); }

/* Platform trust badges — his own badge files, kept per Dylan 2026-09-03. */
.badge-strip-sec { padding: 34px 0; background: var(--tint); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.platform-badges { list-style: none; display: flex; flex-wrap: wrap; align-items: center;
  justify-content: center; gap: 34px; margin: 0; padding: 0; }
.platform-badges img { height: 46px; width: auto; opacity: 0.9; }
.platform-badges li:first-child img { height: 40px; }
@media (max-width: 640px) { .platform-badges { gap: 20px; } .platform-badges img { height: 34px; } }

/* Honeypot — off-screen, never shown to a human; bots fill it and lead.php drops the request. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------------- blog index + blog post (2026-09-03) ----------------
   Added with the blog-post page type that closes the BLOG-POSTS cutover blocker.
   The three live posts render at their exact live slugs, so they need a reading
   layout: one measured column, generous line-height, real spacing between the
   client's own H2s and the paragraphs under them. */
.post-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 26px; }
.post-card { border-bottom: 1px solid var(--line); padding-bottom: 24px; }
.post-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.post-card h2, .post-card h3 { margin: 0 0 6px; font-size: 1.32rem; line-height: 1.3; }
.post-card h3 { font-size: 1.08rem; }
.post-card h2 a, .post-card h3 a { color: var(--ink); text-decoration: none; }
.post-card h2 a:hover, .post-card h3 a:hover { color: var(--navy-2); text-decoration: underline; }
.post-card p { margin: 0; color: var(--muted); }

.post-meta { font-size: 0.86rem; color: var(--muted); margin: 0; }
.page-head .post-meta { color: rgba(255, 255, 255, 0.78); margin-top: 10px; }

.post-body { font-size: 1.05rem; line-height: 1.75; }
.post-body p { margin: 0 0 20px; }
.post-body h2 { font-size: 1.42rem; line-height: 1.32; margin: 38px 0 12px; }
.post-body h3 { font-size: 1.14rem; margin: 28px 0 10px; }
.post-body ul, .post-body ol { margin: 0 0 20px; padding-left: 22px; }
.post-body li { margin-bottom: 8px; }
.post-body a { color: var(--navy-2); font-weight: 500; }
.post-body p:last-child { margin-bottom: 0; }
@media (max-width: 640px) {
  .post-body { font-size: 1rem; }
  .post-body h2 { font-size: 1.24rem; margin-top: 30px; }
}


/* ---------------- progressive disclosure ("Read more") ----------------
   The SOP's answer to a wall of text is the accordion. The hidden half stays in the DOM inside
   <details>, so every word is still indexed; this shortens the READ, not the content.
   The summary is styled as a real control with a 44px hit target, because a toggle nobody
   notices is worse than no toggle. */
.prose .more { margin-top: 6px; }
.prose .more > summary {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  font-family: var(--font-head); font-weight: 700; font-size: 0.92rem; color: var(--navy-2);
  padding: 11px 0; min-height: 44px; user-select: none;
}
.prose .more > summary::-webkit-details-marker { display: none; }
.prose .more > summary::after {
  content: ""; width: 8px; height: 8px; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.18s ease;
}
.prose .more[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.prose .more > summary:hover { color: var(--navy); }
.prose .more > summary:focus-visible { outline: 2px solid var(--navy-2); outline-offset: 3px; border-radius: var(--r-sm); }
.prose .more-body { padding-top: 2px; }
/* Print and reduced-motion must never hide content behind a control. */
@media print { .prose .more > summary { display: none; } .prose .more-body { display: block !important; } }

/* The Spanish nav's services parent is a label, not a link (the hub is English-only).
   It must still look and behave like the other nav items, including opening the submenu. */
.nav-label { display: block; padding: 10px 12px; font-weight: 600; font-size: 0.93rem;
  color: var(--navy); white-space: nowrap; cursor: default; }

/* Before/after proof block (canon v19). Two phases side by side on a dark band, stacking on
   mobile with the phase chip staying on the image so a label can never separate from its frame. */
.ba-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 26px; }
.ba-frame { margin: 0; position: relative; }
.ba-frame img { display: block; width: 100%; height: auto; border-radius: var(--r-sm); }
.ba-phase {
  position: absolute; top: 12px; left: 12px; z-index: 1;
  background: var(--orange); color: var(--ink);
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 5px 12px; border-radius: var(--r-pill);
}
.ba-frame figcaption { margin-top: 10px; font-size: 0.92rem; line-height: 1.5; }

/* 🛑 CONTRAST. `.section-dark` whitens h2, h3, p and li - it does NOT whiten figcaption, so the
   first render put dark-grey captions and a dark-green link on the dark band, effectively
   invisible. Same failure class as the review stars that were painted in the brand sand ON the
   brand sand at a measured 1.00 ratio. Both are declared explicitly here rather than relying on
   the generic dark-section rule to grow a new selector. */
.section-dark .ba-frame figcaption { color: var(--white); }
.section-dark .inline-cta a { color: var(--sky); text-decoration: underline; }
.section-dark .inline-cta a:hover { color: var(--white); }

/* The before frame is portrait and the after frame landscape, which left one column half empty and
   the captions on different baselines. A shared 4:3 box with object-fit: cover puts both frames on
   the same footprint without distorting either photograph. */
.ba-frame img { aspect-ratio: 4 / 3; object-fit: cover; }
.ba-frame figcaption { min-height: 3em; }

@media (max-width: 700px) { .ba-grid { grid-template-columns: 1fr; gap: 26px; } }

/* ---------------------------------------------------------------------------
   2026-09-09 — INTERIOR / EXTERIOR CATEGORY GROUPING.
   The nav dropdown and the footer service column are now grouped under the two
   category hubs. Both lists stay FLAT markup (a nested <ul> inside .sub would
   inherit the dropdown's flex column and render as a second, narrower menu), so
   the group heading is a sibling <li> that is styled to read as a heading and
   still behaves as the link to its hub.
   --------------------------------------------------------------------------- */
.sub .sub-head {
  font-weight: 700; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--navy-2); padding: 8px 12px 6px;
}
.sub .sub-group + li a { padding-left: 12px; }
.sub .sub-group:not(:first-child) .sub-head { margin-top: 6px; border-top: 1px solid var(--line); padding-top: 12px; }
.f-col .f-group a { color: var(--sky); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; }
.f-col .f-group:not(:first-child) a { display: inline-block; margin-top: 10px; }
/* A category lede sits between the section H2 and its card grid. */
.lede + .card-grid { margin-top: 26px; }
