/* ═══ 1 · TOKENS (Phase 3 — locked) ═══════════════════════ */
:root{
  --ground:#F4F1EB; --surface:#FFFFFF; --primary:#152238; --accent:#A93826; --tertiary:#A8834D;
  --ink:#1A1F26; --text-2:#6B6558; --text-muted:#A39C8C; --border:#DDD6C7; --border-strong:#C4BBA5;
  /* Brand marks. The company colours are RED AND BLUE — both are on the van,
     and the site previously carried only the red rule, which is why Charles
     asked "where's my logo?" in the review. --brand-blue is sampled from the
     wordmark on the van (assets/img/about/van.jpg).
     The two -on-dark variants exist because #A93826 sits at 1.73:1 on
     --primary: on the hero and in the footer the red rule was technically
     present and practically invisible. Same hues, lifted only far enough to
     read against navy. Do not use these on the cream or white planes. */
  --brand-red:#A93826; --brand-blue:#24437F;
  --brand-red-on-dark:#C55139; --brand-blue-on-dark:#4771BE;
  /* --text-muted is 2.42:1 on --ground. It is currently referenced nowhere,
     which is why nothing on the site fails contrast. Do not reach for it for
     text — use --text-2 (5.14:1 on ground, 5.79:1 on white). Borders only. */
  --radius-sm:2px; --radius-md:4px;
  --shadow-1:0 4px 24px rgba(21,34,56,.06);
  --shadow-2:0 2px 12px rgba(21,34,56,.08);
  --display:"Fraunces",Georgia,serif;
  --body:"Instrument Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --ease:cubic-bezier(.22,1,.36,1);
  --gutter:clamp(20px,5vw,64px);
  --measure:65ch;
}

/* ═══ 2 · RESET ═══════════════════════════════════════════ */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* 100px, not 88px. The mark is two lines plus TWO rules now (P1/P2), which
   took the fixed nav from 87px to 91px — at 88px every in-page anchor landed
   ~3px UNDER the header. 100px clears it with a small deliberate gap. Measured
   at 1025px, the narrowest width the desktop nav runs at. */
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:100px}
body{background:var(--ground);color:var(--ink);font-family:var(--body);font-size:17px;
  line-height:1.65;-webkit-font-smoothing:antialiased;overflow-x:hidden}
img,svg{display:block;max-width:100%}img{height:auto}
button,input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}ul,ol{list-style:none}
:focus-visible{outline:2px solid rgba(21,34,56,.45);outline-offset:2px;border-radius:var(--radius-sm)}
::selection{background:var(--primary);color:var(--ground)}
/* Visually hidden, still announced. Was inlined on every live region. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.skip{position:absolute;left:var(--gutter);top:-100px;z-index:200;background:var(--primary);
  color:var(--ground);padding:14px 24px;border-radius:var(--radius-sm);font-size:13px;
  letter-spacing:.12em;text-transform:uppercase;font-weight:500;transition:top .2s var(--ease)}
.skip:focus{top:16px}

/* ═══ 3 · TYPE ════════════════════════════════════════════ */
.d-xl,.d-l,.d-m{font-family:var(--display);font-weight:400}
.d-xl{font-size:clamp(38px,6.6vw,80px);line-height:1.06;letter-spacing:-.015em}
.d-l{font-size:clamp(30px,4.4vw,52px);line-height:1.1;letter-spacing:-.01em}
.d-m{font-size:clamp(22px,2.8vw,32px);line-height:1.2}
.lead{font-size:clamp(17px,1.5vw,20px);line-height:1.6;color:var(--text-2);max-width:var(--measure)}
p{max-width:var(--measure)}
.eyebrow{font-size:13px;letter-spacing:.12em;text-transform:uppercase;font-weight:500;color:var(--text-2)}
.wrap{max-width:1360px;margin-inline:auto;padding-inline:var(--gutter)}
section{padding-block:clamp(64px,9.5vw,150px)}
/* Lifts a section onto the white plane. .about and .rev each hard-coded this;
   new sections use the utility so the alternation stays adjustable in one place. */
.surface{background:var(--surface)}
.head{max-width:36ch}
.head h2{margin-top:14px}
.head .lead{margin-top:18px}

/* ═══ 4 · CTA ═════════════════════════════════════════════ */
.btn{display:inline-flex;align-items:center;gap:11px;padding:16px 30px;min-height:52px;
  border-radius:var(--radius-sm);border:1px solid transparent;font-size:13px;font-weight:500;
  letter-spacing:.12em;text-transform:uppercase;cursor:pointer;
  transition:background-color .3s var(--ease),border-color .3s var(--ease),color .3s var(--ease),transform .1s var(--ease)}
.btn svg{width:16px;height:16px;transition:stroke .3s var(--ease),transform .3s var(--ease)}
.btn--primary{background:var(--primary);color:var(--ground);border-color:var(--primary)}
.btn--primary:hover{border-color:var(--accent)}
.btn--primary:hover svg{stroke:var(--accent);transform:translateX(3px)}
.btn--ghost{border-color:var(--primary);color:var(--primary)}
.btn--ghost:hover{background:rgba(21,34,56,.07)}
.btn--onDark{color:var(--ground);border-color:rgba(244,241,235,.5)}
.btn--onDark:hover{background:rgba(244,241,235,.12);border-color:var(--ground)}
/* Solid cream fill — the primary action on any navy plane. .btn--primary is
   navy-on-navy over the hero scrim and reads as an outline, not a button,
   so every dark-section primary uses this and .btn--onDark drops to secondary. */
.btn--light{background:var(--ground);color:var(--primary);border-color:var(--ground)}
.btn--light:hover{background:#FFFFFF;border-color:#FFFFFF}
.btn--light:hover svg{transform:translateX(3px)}
.btn:active{transform:scale(.98)}
.btn--block{width:100%;justify-content:center}
.tel{display:inline-flex;align-items:center;gap:9px;min-height:44px;font-weight:500;
  transition:color .3s var(--ease)}
.tel svg{width:18px;height:18px;stroke:var(--accent)}
.tel:hover{color:var(--accent)}

/* ═══ 5 · NAV ═════════════════════════════════════════════ */
.nav{position:fixed;inset:0 0 auto;z-index:100;transition:background-color .4s var(--ease),box-shadow .4s var(--ease)}
.nav__in{display:flex;align-items:center;gap:28px;padding-block:18px}
.nav.is-stuck{background:var(--surface);box-shadow:var(--shadow-2)}
.nav.is-open{background:transparent;box-shadow:none}
.mark{margin-right:auto;display:flex;flex-direction:column;line-height:1}
.mark__a{font-family:var(--display);font-size:21px;font-weight:500;letter-spacing:-.005em;color:var(--primary)}
/* TWO RULES, RED OVER BLUE — the company's actual colours, not the red alone.
   Same 74px width and the same slanted clip on both so they read as one mark
   rather than two stacked bars. Kept thin (3px / 2px) on purpose: the point is
   that the blue is present, not that the logo got heavier. */
.mark__r,.mark__l{width:74px;background:var(--brand-red);
  clip-path:polygon(0 40%,100% 0,100% 70%,0 100%)}
.mark__r{height:3px;margin-top:5px}
.mark__l{height:2px;background:var(--brand-blue);margin:2px 0 4px}
/* P1 — "I can't really read the Painting & Decorating part." Was 8.5px at
   .24em and 72% opacity over photography. Now 10px / 500 / 96%, tracking
   pulled back to .18em so the extra size does not widen the mark past the
   nav's slack at 1025px, and a 1px shadow that only exists over the hero. */
.mark__b{font-size:10px;letter-spacing:.18em;text-transform:uppercase;font-weight:500;
  color:var(--text-2)}
.nav:not(.is-stuck) .mark__a,.nav.is-open .mark__a{color:var(--ground);
  text-shadow:0 1px 3px rgba(10,16,28,.42)}
.nav:not(.is-stuck) .mark__b,.nav.is-open .mark__b{color:rgba(244,241,235,.96);
  text-shadow:0 1px 3px rgba(10,16,28,.55)}
/* Both rules are near-invisible at their light-plane values over a photograph
   or on the navy footer. Lifted, not recoloured. */
.nav:not(.is-stuck) .mark__r,.nav.is-open .mark__r,.foot .mark__r{background:var(--brand-red-on-dark)}
.nav:not(.is-stuck) .mark__l,.nav.is-open .mark__l,.foot .mark__l{background:var(--brand-blue-on-dark)}
.nav__links{display:none;gap:26px;align-items:center}
.nav__links a{position:relative;font-size:15px;font-weight:500;padding-block:6px}
.nav__links a::after{content:"";position:absolute;left:0;bottom:0;width:100%;height:1.5px;
  background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.nav__links a:hover::after{transform:scaleX(1)}
.nav:not(.is-stuck) .nav__links a{color:var(--ground)}
.nav__cta{display:none}
/* While the nav is transparent, the CTA is navy sitting on navy — the same
   problem §4 already flagged for .btn--primary on any dark plane. Against the
   homepage's daylight skies it survives; against the Holiday Lighting night
   frames the button loses its edge and reads as a smudge. A cream hairline
   holds the shape on both, and it stops existing the moment the nav goes
   solid, so the scrolled state is untouched. */
.nav:not(.is-stuck) .nav__cta .btn--primary{border-color:rgba(244,241,235,.65)}
.nav:not(.is-stuck) .nav__cta .btn--primary:hover{border-color:var(--ground)}
.burger{display:inline-flex;flex-direction:column;justify-content:center;gap:5px;width:44px;
  height:44px;padding:0 10px;background:none;border:0;cursor:pointer}
.burger span{height:1.5px;background:var(--primary);transition:transform .3s var(--ease),opacity .2s}
.nav:not(.is-stuck) .burger span,.nav.is-open .burger span{background:var(--ground)}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6.5px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6.5px) rotate(-45deg)}
/* The visibility delay is split deliberately.
   CLOSING: visibility flips to hidden only AFTER the 0.4s fade (0s .4s), so the
   panel is not yanked out from under the animation.
   OPENING: it flips immediately (0s 0s). It used to inherit the .4s on both
   sides, which meant the drawer was still visibility:hidden for 400ms after it
   opened — and main.js focuses the first link the instant it opens. focus() on
   a visibility:hidden element does nothing, silently, so opening the menu with a
   keyboard left focus stranded on the burger behind the overlay. */
.drawer{position:fixed;inset:0;z-index:99;background:var(--primary);display:flex;
  flex-direction:column;justify-content:center;align-items:center;gap:2px;opacity:0;
  visibility:hidden;transition:opacity .4s var(--ease),visibility 0s .4s}
.drawer[data-open="true"]{opacity:1;visibility:visible;
  transition:opacity .4s var(--ease),visibility 0s 0s}
.drawer a{font-family:var(--display);font-size:clamp(24px,6vw,34px);color:var(--ground);padding:9px 20px}
.drawer .btn{margin-top:22px}
.drawer .tel{color:var(--ground);margin-top:6px}.drawer .tel svg{stroke:var(--ground)}

/* ═══ 6 · HERO SLIDESHOW ══════════════════════════════════ */
.hero{position:relative;min-height:100dvh;display:flex;align-items:flex-end;padding-block:0;
  overflow:hidden;background:var(--primary)}
.slides{position:absolute;inset:0}
.slides img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;
  transition:opacity 1.4s var(--ease)}
.slides img.is-on{opacity:1}
/* Two layers on one element. The lower gradient is the original art direction.
   The upper one is a short nav band: the fixed nav sits over whichever slide
   happens to be showing, and the twilight-sky frame in the holiday set put
   cream nav text at 3.2:1 — below AA. This lifts it past 5:1 without
   darkening the rest of the photograph. Second element would need a z-index
   that also paints over the hero copy, so it is a background layer instead. */
/* THREE layers, painted top-down in this order.

   Layer 2 is new, and it is the P3 fix. Opacity on the text could not solve
   it: at the 64% stop of the bottom-up gradient the scrim is only .32, so a
   bright pixel behind the location line (white siding, blown sky) leaves cream
   text at 2.2–2.6:1 no matter how opaque the text itself is. The brief ruled
   out an opaque box, and rightly — so instead of plating the text or flattening
   the whole photograph, this darkens only the column the copy actually sits in
   and fades out by 66% across. The right two-thirds of every slide is untouched.

   Measured at the location line: .50 (layer 3) composited with .58 (layer 2)
   = .77 effective, which puts cream at 4.5:1+ against the brightest pixel in
   the hero set. At the logo it reaches .85. Past 66% across, all three layers
   are exactly what they were before this revision. */
.hero__scrim{position:absolute;inset:0;background:
  /* 1 · nav band — keeps cream nav text off a twilight sky */
  linear-gradient(to bottom,rgba(21,34,56,.62) 0,rgba(21,34,56,0) 160px),
  /* 2 · copy-column wash — P3 */
  linear-gradient(to right,rgba(21,34,56,.58) 0,rgba(21,34,56,.34) 38%,rgba(21,34,56,0) 66%),
  /* 3 · original art direction, unchanged */
  linear-gradient(to top,
  rgba(21,34,56,.94) 0%,rgba(21,34,56,.76) 34%,rgba(21,34,56,.32) 64%,rgba(21,34,56,.44) 100%)}
/* padding-block, not the padding shorthand: the shorthand was resetting the
   inline padding .wrap sets, so the hero headline ran flush to the viewport
   edge while every other section sat on the 64px gutter. */
.hero__in{position:relative;width:100%;padding-block:150px clamp(84px,10vw,120px)}
/* P3 — the location line. It sits at the 64% band of the scrim, the weakest
   part of the gradient, and at 13px/82% it was the hardest thing on the page
   to read. Lifted to 14px, 96% cream and a shadow tight enough that it never
   reads as a box. No backdrop plate: the hero has to stay cinematic. */
.hero .eyebrow{color:rgba(244,241,235,.96);font-size:14px;letter-spacing:.14em;
  text-shadow:0 1px 4px rgba(10,16,28,.6)}
.hero h1{color:var(--ground);margin-top:18px;max-width:16ch;
  text-shadow:0 2px 18px rgba(10,16,28,.28)}
.hero__sub{color:rgba(244,241,235,.92);max-width:46ch;margin-top:22px;font-size:clamp(17px,1.5vw,20px);
  line-height:1.6;text-shadow:0 1px 10px rgba(10,16,28,.35)}
/* One action row, used by the hero, the holiday teaser and every section
   that closes on a primary + secondary pair. */
.hero__acts,.hol__acts,.acts{display:flex;flex-wrap:wrap;align-items:center;gap:14px 26px;margin-top:36px}
.acts{margin-top:clamp(30px,4vw,44px)}
.hero .tel{color:var(--ground)}.hero .tel svg{stroke:var(--ground)}
.dots{position:absolute;right:var(--gutter);bottom:clamp(84px,10vw,120px);display:flex;gap:8px}
/* The visible mark stays a 2px rule; the button around it is 26x24 so the
   target is actually hittable. It was 26x2 — a two-pixel-tall click target. */
.dots button{width:26px;height:24px;background:none;border:0;padding:0;cursor:pointer;
  display:grid;place-items:center}
/* .35 cream is 2.93:1 on the navy under the dots — below the 3:1 WCAG 1.4.11
   floor for a non-text control. .45 is 3.95:1 and looks identical. */
.dots button::after{content:"";display:block;width:26px;height:2px;
  background:rgba(244,241,235,.45);transition:background-color .4s var(--ease)}
.dots button[aria-current="true"]::after{background:var(--ground)}

/* ═══ 7 · TRUST BAR — four items, no mobile orphan ════════ */
.trust{background:var(--ground);padding-block:clamp(36px,4.5vw,56px);border-bottom:1px solid var(--border)}
.trust__grid{display:grid;grid-template-columns:repeat(2,1fr);gap:24px}
.trust__i{display:flex;gap:11px;align-items:flex-start}
.trust__i svg{width:19px;height:19px;flex:none;stroke:var(--primary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round;margin-top:3px}
.trust__i b{display:block;font-weight:500;font-size:15.5px}
.trust__i span{display:block;font-size:13.5px;color:var(--text-2);line-height:1.4;margin-top:1px}

/* ═══ 8 · ABOUT (portrait slot optional) ══════════════════ */
.about{background:var(--surface)}
.about__grid{display:grid;gap:40px}
.about__body>*+*{margin-top:22px}
.about__stat{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:38px;
  border-top:1px solid var(--border);padding-top:26px}
.about__stat b{display:block;font-family:var(--display);font-size:clamp(28px,3.4vw,40px);
  line-height:1;color:var(--primary)}
.about__stat span{display:block;font-size:13px;color:var(--text-2);margin-top:8px;line-height:1.35}
/* Charles's portrait has not been shot. This <figure> currently holds a real
   job photograph. Swapping in a portrait later is a src + alt change only —
   deleting it entirely collapses the grid to one finished column. */
.about__fig{margin:0;position:relative}
.about__fig img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-md)}
.about__fig figcaption{margin-top:12px;font-size:13px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-2)}
/* Van band — Charles asked for the van in the meeting notes. Cropped so the
   "#1 Custom Painter" wrap claim is not legible. */
/* Continuous with .about — both sit on the same white plane, so the doubled
   section rhythm between them would read as a hole rather than a pause. */
.van{background:var(--surface);padding-top:0}
.van__fig{margin:0;border-radius:var(--radius-md);overflow:hidden}
.van__fig img{width:100%;aspect-ratio:1232/295;object-fit:cover}
.van__cap{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:14px;font-family:var(--mono);
  font-size:12.5px;letter-spacing:.02em;color:var(--text-2);text-transform:uppercase}

/* ═══ 9 · SERVICES GRID ═══════════════════════════════════ */
.svc__grid{display:grid;grid-template-columns:1fr;gap:0;margin-top:clamp(36px,5vw,56px);
  border-top:1px solid var(--border)}
.svc{padding:24px 0;border-bottom:1px solid var(--border);display:block}
.svc svg{width:26px;height:26px;stroke:var(--primary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round;transition:stroke .3s var(--ease)}
.svc h3{font-family:var(--display);font-weight:400;font-size:21px;line-height:1.25;margin-top:14px;
  display:inline-block;background-image:linear-gradient(var(--accent),var(--accent));
  background-size:0% 1px;background-position:0 100%;background-repeat:no-repeat;
  transition:background-size .35s var(--ease)}
.svc p{font-size:15.5px;color:var(--text-2);margin-top:6px;line-height:1.5;max-width:30ch}
.svc:hover h3{background-size:100% 1px}
.svc:hover svg{stroke:var(--accent)}

/* ═══ 10 · BENEFITS — six reasons, six icons ══════════════ */
.ben{background:var(--primary);color:var(--ground)}
.ben h2{color:var(--ground)}.ben .eyebrow{color:rgba(244,241,235,.6)}
.ben .lead{color:rgba(244,241,235,.75)}
.ben__grid{display:grid;gap:0 clamp(40px,6vw,88px);margin-top:clamp(36px,5vw,56px)}
.ben__i{padding-block:26px;border-bottom:1px solid rgba(244,241,235,.15)}
.ben__i svg{width:24px;height:24px;stroke:var(--tertiary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.ben__i b{display:block;font-family:var(--display);font-weight:400;font-size:clamp(20px,2.1vw,24px);
  line-height:1.25;margin-top:16px}
.ben__i p{font-size:15.5px;color:rgba(244,241,235,.7);line-height:1.6;margin-top:10px;max-width:40ch}

/* ═══ 11 · OUR WORK — records, filter, mid-job frame ══════ */
.filters{display:flex;flex-wrap:wrap;gap:8px;margin-top:clamp(28px,4vw,40px)}
.filters button{background:none;border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:9px 16px;min-height:44px;font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;
  font-weight:500;color:var(--text-2);cursor:pointer;
  transition:border-color .3s var(--ease),color .3s var(--ease),background-color .3s var(--ease)}
.filters button:hover{border-color:var(--border-strong);color:var(--ink)}
.filters button[aria-pressed="true"]{background:var(--primary);border-color:var(--primary);color:var(--ground)}
.work__grid{display:grid;grid-template-columns:1fr;gap:clamp(28px,4vw,44px);margin-top:clamp(32px,4.5vw,52px)}
.rec{margin:0}
.rec[hidden]{display:none}
.rec__img{border-radius:var(--radius-md);overflow:hidden;background:#E4DFD4}
.rec__img img{width:100%;aspect-ratio:4/3;object-fit:cover;transition:transform .6s var(--ease)}
.rec:hover .rec__img img{transform:scale(1.02)}
.rec figcaption{margin-top:18px}
.rec h3{font-family:var(--display);font-weight:400;font-size:clamp(19px,2vw,22px);line-height:1.3}
.rec p{font-size:15.5px;color:var(--text-2);line-height:1.55;margin-top:9px;max-width:44ch}
.rec dl{display:grid;grid-template-columns:auto 1fr;gap:4px 18px;margin-top:16px;
  border-top:1px solid var(--border);padding-top:14px;
  font-family:var(--mono);font-size:12px;letter-spacing:.02em;text-transform:uppercase}
.rec dt{color:var(--text-2)}
.rec dd{color:var(--primary)}
/* Single-frame mid-job photograph. Not a slider: no true before/after PAIR
   exists in the library yet, and faking one from two different houses would
   be dishonest. Restore the drag slider once the pair is shot. */
.midjob{position:relative;margin-top:clamp(32px,4.5vw,52px);border-radius:var(--radius-md);overflow:hidden}
.midjob img{width:100%;aspect-ratio:7/5;object-fit:cover}
.midjob__tag{position:absolute;bottom:16px;padding:7px 13px;border-radius:var(--radius-sm);
  background:rgba(21,34,56,.88);color:var(--ground);font-family:var(--mono);font-size:11.5px;
  letter-spacing:.08em;text-transform:uppercase}
.midjob__tag--b{left:16px}.midjob__tag--a{right:16px}
.midjob__cap{margin-top:14px;font-size:15px;color:var(--text-2);max-width:52ch}

/* ═══ 12 · PROCESS ════════════════════════════════════════ */
.proc__grid{display:grid;gap:0;margin-top:clamp(40px,5vw,64px);border-top:1px solid var(--border)}
.proc__s{display:grid;gap:6px 22px;align-items:start;padding-block:26px;border-bottom:1px solid var(--border)}
.proc__n{font-family:var(--mono);font-size:12.5px;letter-spacing:.08em;color:var(--accent);padding-top:5px}
.proc__s svg{width:24px;height:24px;stroke:var(--primary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.proc__s h3{font-family:var(--display);font-weight:400;font-size:clamp(21px,2.2vw,26px);line-height:1.25}
.proc__s p{color:var(--text-2);font-size:16px;margin-top:8px;max-width:52ch}
.proc__note{margin-top:clamp(36px,5vw,56px);border-left:2px solid var(--accent);
  padding:4px 0 4px 22px;font-family:var(--display);font-size:clamp(20px,2.4vw,28px);
  line-height:1.35;color:var(--primary);max-width:30ch}
.proc__pics{display:grid;grid-template-columns:1fr;gap:clamp(16px,2.4vw,26px);margin-top:clamp(36px,5vw,56px)}
.proc__pics figure{margin:0}
.proc__pics img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-md)}
.proc__pics figcaption{margin-top:11px;font-family:var(--mono);font-size:12px;letter-spacing:.02em;
  text-transform:uppercase;color:var(--text-2)}

/* ═══ 13 · REVIEWS — honest empty state ═══════════════════ */
.rev{background:var(--surface)}
.rev__box{margin-top:clamp(32px,4.5vw,52px);border:1px solid var(--border);border-radius:var(--radius-md);
  padding:clamp(28px,4vw,56px);max-width:62ch}
/* 62ch is a reading measure and fits one button. The closing card carries a
   primary and a secondary, which wrapped onto two rows at that width. */
.rev__box--wide{max-width:78ch}
.rev__stars{display:flex;gap:4px}
.rev__stars svg{width:19px;height:19px;fill:none;stroke:var(--border-strong);stroke-width:1.25;
  stroke-linejoin:round}
.rev__box h3{font-family:var(--display);font-weight:400;font-size:clamp(21px,2.4vw,28px);
  line-height:1.25;margin-top:20px}
.rev__box p{color:var(--text-2);margin-top:14px}
.rev__acts{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}
.rev__plat{display:flex;flex-wrap:wrap;gap:10px;margin-top:26px}
.rev__plat span{border:1px solid var(--border);border-radius:var(--radius-sm);padding:7px 14px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-2)}

/* ═══ 14 · FAQ ════════════════════════════════════════════ */
.faq{border-top:1px solid var(--border);margin-top:clamp(32px,4.5vw,52px);max-width:80ch}
.faq__i{border-bottom:1px solid var(--border)}
.faq__q{width:100%;display:flex;gap:24px;align-items:flex-start;justify-content:space-between;
  background:none;border:0;text-align:left;cursor:pointer;padding-block:24px;min-height:56px;
  font-family:var(--display);font-weight:400;font-size:clamp(19px,2vw,23px);line-height:1.3;color:var(--ink);
  transition:color .3s var(--ease)}
.faq__q:hover{color:var(--accent)}
.faq__ic{position:relative;width:18px;height:18px;flex:none;margin-top:6px;transition:transform .3s var(--ease)}
.faq__ic::before,.faq__ic::after{content:"";position:absolute;background:var(--primary)}
.faq__ic::before{left:0;top:8px;width:18px;height:1.5px}
.faq__ic::after{top:0;left:8px;width:1.5px;height:18px}
.faq__q[aria-expanded="true"] .faq__ic{transform:rotate(45deg)}
/* grid-template-rows avoids the old max-height clip on long answers */
.faq__a{display:grid;grid-template-rows:0fr;opacity:0;
  transition:grid-template-rows .3s var(--ease),opacity .3s var(--ease)}
.faq__a>div{overflow:hidden}
.faq__i[data-open] .faq__a{grid-template-rows:1fr;opacity:1}
.faq__a p{color:var(--text-2);padding-bottom:26px;max-width:62ch}

/* ═══ 15 · AREAS ══════════════════════════════════════════ */
.area__grid{display:grid;gap:28px;margin-top:clamp(32px,4.5vw,52px)}
.area__c{border-top:1px solid var(--border);padding-top:22px}
.area__c h3{font-family:var(--display);font-weight:400;font-size:clamp(24px,2.8vw,32px)}
.area__c p{font-size:15.5px;color:var(--text-2);margin-top:10px;line-height:1.6}

/* ═══ 16 · CONTACT ════════════════════════════════════════ */
.est{background:var(--primary);color:var(--ground)}
.est h2{color:var(--ground)}.est .eyebrow{color:rgba(244,241,235,.6)}
.est .lead{color:rgba(244,241,235,.75)}
.est__grid{display:grid;gap:clamp(36px,5vw,64px);align-items:start}
.est__i{display:flex;gap:12px;padding-block:14px;border-bottom:1px solid rgba(244,241,235,.15);
  font-size:16px;color:rgba(244,241,235,.86)}
.est__i svg{width:17px;height:17px;flex:none;stroke:var(--tertiary);stroke-width:1.5;fill:none;
  stroke-linecap:round;stroke-linejoin:round;margin-top:5px}
.est__ways{display:grid;gap:14px;margin-top:32px}
.est__w{display:flex;gap:13px;align-items:flex-start;color:rgba(244,241,235,.9);font-size:16px}
.est__w svg{width:18px;height:18px;flex:none;stroke:var(--tertiary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round;margin-top:5px}
.est__w a{position:relative}
.est__w a::after{content:"";position:absolute;left:0;bottom:-2px;width:100%;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.est__w a:hover::after{transform:scaleX(1)}
.est__w address{font-style:normal}
/* P6 — the credential line under the estimate lead. Mono and uppercase so it
   reads as a stamp rather than a sentence, gold on navy at 4.57:1 (AA at this
   size), and max-width released because the global `p` measure of 65ch would
   otherwise wrap "24+ Years" onto its own line at desktop. The separator is a
   real element, not a ::before, so it can be hidden from screen readers —
   "Fully Licensed and Insured middle dot Serving Metro Atlanta" is not a
   sentence anybody needs to hear. */
.est__trust{margin-top:18px;max-width:none;font-family:var(--mono);font-size:12.5px;
  letter-spacing:.06em;text-transform:uppercase;color:var(--tertiary);line-height:1.6}
.est__trust span{margin-inline:8px;color:rgba(244,241,235,.35)}
.form{background:var(--surface);color:var(--ink);border-radius:var(--radius-md);padding:clamp(24px,3.2vw,44px)}
.f{display:grid;gap:8px;margin-bottom:20px}
.f label{font-size:14px;font-weight:500}
.f .req{color:var(--accent)}
.f .hint{font-size:13px;color:var(--text-2);font-weight:400}
.f input,.f select,.f textarea{width:100%;padding:13px 14px;min-height:48px;background:var(--surface);
  border:1px solid var(--border);border-radius:var(--radius-sm);
  transition:border-color .25s var(--ease),box-shadow .25s var(--ease)}
.f textarea{min-height:112px;resize:vertical}
.f input:hover,.f select:hover,.f textarea:hover{border-color:var(--border-strong)}
.f input:focus,.f select:focus,.f textarea:focus{outline:0;border-color:var(--primary);
  box-shadow:0 0 0 2px rgba(21,34,56,.35)}
.f .err{display:none;font-size:13px;color:var(--accent);align-items:center;gap:6px}
.f .err svg{width:14px;height:14px;flex:none;stroke:var(--accent);stroke-width:1.5;fill:none}
.f[data-invalid] input,.f[data-invalid] select,.f[data-invalid] textarea{border-color:var(--accent)}
.f[data-invalid] .err{display:flex}
.f2{display:grid;grid-template-columns:1fr;gap:0 18px}
.form__foot{font-size:13px;color:var(--text-2);margin-top:14px;text-align:center}
/* Honeypot. Off-screen, not display:none, and never focusable. */
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}
/* Only ever shown when the POST to estimate.php actually failed. The form keeps
   its values underneath it so nobody has to retype the job. */
.form__fail{margin-top:16px;padding:13px 15px;border:1px solid var(--accent);
  border-left-width:2px;border-radius:var(--radius-sm);background:rgba(169,56,38,.05);
  font-size:14.5px;line-height:1.5;color:var(--accent)}
.form__fail[hidden]{display:none}
.btn[disabled]{opacity:.6;cursor:progress}
.done{display:none;text-align:center;padding-block:22px}
.done svg{width:42px;height:42px;stroke:var(--primary);stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round;margin:0 auto 18px}
.done h3{font-family:var(--display);font-weight:400;font-size:27px;line-height:1.2}
.done p{color:var(--text-2);margin:10px auto 0}
.form[data-sent] .form__live{display:none}
.form[data-sent] .done{display:block}

/* ═══ 17 · FOOTER ═════════════════════════════════════════ */
.foot{background:var(--primary);color:var(--ground);padding-block:clamp(52px,6vw,80px) 0;
  border-top:1px solid rgba(244,241,235,.15)}
.foot__grid{display:grid;gap:36px}
.foot h4{font-size:12.5px;letter-spacing:.12em;text-transform:uppercase;font-weight:500;
  color:rgba(244,241,235,.55);margin-bottom:16px}
.foot li+li{margin-top:9px}
.foot li,.foot address{color:rgba(244,241,235,.86);font-size:15.5px;font-style:normal;line-height:1.6}
/* Inline anchors are only hit-testable across their 19px content box, which
   is a thin target on a phone. inline-block + padding takes them to 25px;
   the underline offset is rebalanced so nothing moves visually. */
.foot li a{display:inline-block;padding-block:3px}
.foot li a::after{bottom:1px}
.foot a{position:relative}
.foot a::after{content:"";position:absolute;left:0;bottom:-2px;width:100%;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.foot a:hover::after{transform:scaleX(1)}
.foot .mark__a{color:var(--ground);font-size:23px}
/* Same P1 legibility problem as the header, same fix. 60% cream on navy is
   3.9:1 at 8.5px; 84% is 7.4:1 at 10px. */
.foot .mark__b{color:rgba(244,241,235,.84)}
/* P4 · FOOTER CREDENTIALS — a tick and a line of text. Deliberately not a
   badge, a seal or a bordered panel: the claim carries because it is stated
   plainly next to the company name, and a graphic would push it toward
   advertising. The tick is the site's own 1.25 stroke, gold like every other
   accent icon on a navy plane. Inherits .foot li+li for its row spacing. */
.foot__cred{margin-top:22px}
.foot__cred li{display:flex;gap:10px;align-items:flex-start;
  color:rgba(244,241,235,.78);font-size:14.5px;line-height:1.45}
.foot__cred svg{width:15px;height:15px;flex:none;margin-top:4px;stroke:var(--tertiary);
  stroke-width:1.5;fill:none;stroke-linecap:round;stroke-linejoin:round}

/* P5 · FOOTER SOCIAL — v1.2: dropped the permanent 44x44 outlined box (read as
   an empty template slot with a thin mark adrift in it). No border at rest —
   the ivory glyph carries its own weight at 20px, close enough to the credential
   ticks above it to read as one family. The 44x44 hit target survives as a
   circular hover/focus backdrop only, so touch sizing (WCAG 2.5.8) doesn't
   regress. Hover/focus shifts the glyph to the footer's own gold (--tertiary),
   the same accent the credential ticks use, rather than either brand's colour.
   The accessible name lives in a .vh span because the <svg> is aria-hidden —
   an icon-only link with no text is the single most common a11y defect in a
   footer and it is not going to be one here. */
.foot__soc{display:flex;gap:2px;margin-top:24px}
.foot__soc li+li{margin-top:0}
.foot__soc a{display:grid;place-items:center;width:44px;height:44px;
  border-radius:50%;transition:background-color .3s var(--ease)}
.foot__soc a::after{display:none}
.foot__soc svg{width:20px;height:20px;stroke:rgba(244,241,235,.86);stroke-width:1.3;
  fill:none;stroke-linecap:round;stroke-linejoin:round;transition:stroke .3s var(--ease)}
.foot__soc a:hover,.foot__soc a:focus-visible{background:rgba(244,241,235,.08)}
.foot__soc a:hover svg,.foot__soc a:focus-visible svg{stroke:var(--tertiary)}
.foot__soc a:focus-visible{outline:2px solid rgba(244,241,235,.65);outline-offset:2px}
.foot__base{display:flex;flex-wrap:wrap;gap:8px 24px;justify-content:space-between;
  border-top:1px solid rgba(244,241,235,.15);margin-top:clamp(36px,4.5vw,56px);
  padding-block:22px 108px;font-size:12.5px;color:rgba(244,241,235,.55)}

/* ═══ 18 · MOBILE DOCK ════════════════════════════════════ */
.dock{position:fixed;left:0;right:0;bottom:0;z-index:98;display:grid;grid-template-columns:1fr 1fr;
  background:var(--surface);border-top:1px solid var(--border);
  padding:10px 12px calc(10px + env(safe-area-inset-bottom));gap:10px;
  transform:translateY(110%);transition:transform .4s var(--ease)}
.dock[data-show="true"]{transform:translateY(0)}
.dock a{display:flex;align-items:center;justify-content:center;gap:8px;min-height:48px;
  border-radius:var(--radius-sm);font-size:14px;font-weight:500}
.dock__call{background:var(--accent);color:#fff}
.dock__est{background:var(--primary);color:var(--ground);font-size:12px;letter-spacing:.1em;text-transform:uppercase}
.dock svg{width:17px;height:17px;stroke:currentColor;stroke-width:1.25;fill:none;
  stroke-linecap:round;stroke-linejoin:round}

/* ═══ 18.5 · HOLIDAY LIGHTING TEASER (homepage) ═══════════
   Same navy .ben/.est plane — the one deliberate dark break the rest of
   the daylight site doesn't have, because night photography reads
   correctly against dark, not the warm --ground cream. */
.hol{background:var(--primary);color:var(--ground)}
.hol .eyebrow{color:rgba(244,241,235,.6)}
.hol h2{color:var(--ground)}
.hol .lead{color:rgba(244,241,235,.75)}
.hol__grid{display:grid;grid-template-columns:1fr;gap:16px;margin-top:clamp(36px,5vw,56px)}
.hol__grid a{display:block;border-radius:var(--radius-md);overflow:hidden}
/* Square, not 4:3. Every holiday frame in the library is a 414px square, and
   the 4:3 crop was cutting the driveway edging out of the bottom of each one. */
.hol__grid img{width:100%;aspect-ratio:1/1;object-fit:cover;transition:transform .6s var(--ease)}
.hol__grid a:hover img{transform:scale(1.03)}
/* Teaser and Contact are both navy. Without this they read as one slab. */
.hol + .est{border-top:1px solid rgba(244,241,235,.15)}

/* ═══ 18.6 · HOLIDAY CURATION LAYER ═══════════════════════
   The whole holiday library is 414px square. Nothing below is allowed to
   display wider than ~1.2× that, which is why the lead installation is a
   470px square beside editorial type rather than a full-bleed estate photo
   — at this resolution the big version would look cheap, not expensive.
   Restore a true full-bleed hero the day full-resolution files exist. */

/* Seasonal capacity — a real operational constraint, not a countdown. */
.season{background:var(--ground);border-bottom:1px solid var(--border);
  padding-block:clamp(30px,3.6vw,44px)}
.season__in{display:grid;gap:22px 48px;align-items:center}
.season__b{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-size:12px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--accent)}
.season__b i{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:none}
.season h2{font-family:var(--display);font-weight:400;font-size:clamp(20px,2.3vw,26px);
  line-height:1.25;margin-top:12px}
.season p{font-size:15.5px;color:var(--text-2);line-height:1.55;margin-top:8px;max-width:56ch}

/* Lead installation — the one frame the gallery opens on. */
.feat{display:grid;gap:clamp(26px,3.6vw,52px);align-items:center;margin-top:clamp(38px,5vw,60px)}
.feat__fig{margin:0;border-radius:var(--radius-md);overflow:hidden;background:#0E1727}
.feat__fig img{width:100%;aspect-ratio:1/1;object-fit:cover}
.feat h3{font-family:var(--display);font-weight:400;font-size:clamp(24px,3vw,36px);line-height:1.2}
.feat p{color:var(--text-2);margin-top:16px;max-width:46ch}
.feat dl{display:grid;grid-template-columns:auto 1fr;gap:5px 20px;margin-top:24px;
  border-top:1px solid var(--border);padding-top:16px;
  font-family:var(--mono);font-size:12px;letter-spacing:.02em;text-transform:uppercase}
.feat dt{color:var(--text-2)}
.feat dd{color:var(--primary)}

/* Gallery tiers. Featured records keep the editorial .rec treatment; the
   browse index drops to a quiet label so the tier above stays dominant and
   every row lands at exactly the same height. */
.gal .rec__img img{aspect-ratio:1/1}
.gal--index .rec figcaption{margin-top:12px}
.gal--index .rec h3{font-family:var(--body);font-size:12.5px;font-weight:500;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-2);line-height:1.45;
  transition:color .3s var(--ease)}
.gal--index .rec:hover h3{color:var(--ink)}

/* Cinematic break — full-bleed night band between the curated tier and the
   index. Three frames abreast keeps each one near its native width. */
.break{background:var(--primary);color:var(--ground);padding-block:0;overflow:hidden}
.break__strip{display:grid;grid-template-columns:1fr}
.break__strip img{width:100%;aspect-ratio:4/3;object-fit:cover}
.break__say{padding-block:clamp(48px,6.5vw,88px)}
.break__say p{font-family:var(--display);font-size:clamp(22px,3.1vw,38px);line-height:1.3;
  color:var(--ground);max-width:24ch}
.break__say span{display:block;margin-top:20px;font-size:16px;line-height:1.6;
  color:rgba(244,241,235,.7);max-width:56ch}

/* ═══ 18.7 · STANDALONE MESSAGE PAGES (404, thank-you) ════
   No nav, no footer, no dock — these are dead ends by design and every extra
   control on them is one more thing between a lost visitor and the way back.
   Centred, full-height, and built entirely from components that already exist
   so neither page can drift away from the site as it changes. */
.msg{min-height:100dvh;display:grid;place-items:center;background:var(--ground);
  padding-block:clamp(56px,10vw,96px)}
.msg__in{max-width:60ch;text-align:center}
.msg .mark{display:inline-flex;align-items:center;margin:0 0 clamp(36px,6vw,56px)}
.msg .mark__r,.msg .mark__l{margin-inline:auto}
.msg__ic{width:46px;height:46px;stroke:var(--primary);margin:0 auto 26px}
.msg h1{margin-top:14px}
.msg .lead{margin:18px auto 0}
.msg .acts{justify-content:center}
.msg .est__w a,.msg .lead a{text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:var(--accent)}
.msg__links{display:flex;flex-wrap:wrap;justify-content:center;gap:8px 24px;
  margin-top:clamp(36px,5vw,52px);padding-top:26px;border-top:1px solid var(--border);
  font-size:14.5px;color:var(--text-2)}
.msg__links a{padding-block:4px;transition:color .3s var(--ease)}
.msg__links a:hover{color:var(--accent)}

/* ═══ 19 · MOTION ═════════════════════════════════════════ */
[data-reveal]{opacity:0;transform:translateY(18px);
  transition:opacity .75s var(--ease),transform .75s var(--ease);
  transition-delay:calc(var(--i,0) * 70ms)}
[data-reveal].is-in{opacity:1;transform:none}

/* ═══ 20 · BREAKPOINTS ════════════════════════════════════ */
@media (min-width:640px){
  .svc__grid{grid-template-columns:1fr 1fr;column-gap:clamp(28px,4vw,56px)}
  .ben__grid{grid-template-columns:1fr 1fr}
  .f2{grid-template-columns:1fr 1fr}
  .work__grid{grid-template-columns:1fr 1fr}
  .proc__pics{grid-template-columns:1fr 1fr}
  .area__grid{grid-template-columns:repeat(3,1fr)}
  .foot__grid{grid-template-columns:1fr 1fr}
  .about__stat{gap:28px}
  .proc__s{grid-template-columns:auto auto 1fr}
  .hol__grid{grid-template-columns:repeat(3,1fr)}
  .break__strip{grid-template-columns:repeat(3,1fr)}
  /* Three featured records in a two-column grid leave the third stranded on
     its own row. They stay stacked until the grid can carry all three. */
  .gal--feat{grid-template-columns:1fr}
}
@media (min-width:900px){
  .season__in{grid-template-columns:1fr auto}
  /* Mirrors .about__grid so the lead installation sits on the same rhythm as
     the rest of the site — image 5 columns, type 7. */
  .feat{grid-template-columns:5fr 7fr}
}
@media (min-width:1025px){
  .nav__links,.nav__cta{display:flex}
  .burger,.dock{display:none}
  .trust__grid{grid-template-columns:repeat(4,1fr);gap:30px}
  .about__grid{grid-template-columns:7fr 5fr;gap:clamp(48px,6vw,88px);align-items:center}
  .svc__grid{grid-template-columns:repeat(4,1fr)}
  .work__grid{grid-template-columns:repeat(3,1fr)}
  .gal--feat{grid-template-columns:repeat(3,1fr)}
  /* Two lines reserved so one-line and two-line titles start their body copy
     on the same baseline across the row. Only needed once they share a row. */
  .gal--feat .rec h3{min-height:2.6em}
  .proc__pics{grid-template-columns:1fr 1fr}
  .est__grid{grid-template-columns:5fr 7fr}
  .foot__grid{grid-template-columns:1.6fr 1fr 1fr 1fr}
  .foot__base{padding-bottom:22px}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  [data-reveal]{opacity:1;transform:none}
  .slides img{transition:none}
}
/* Images are plain <img>, so every photograph renders without JS.
   The reveals did NOT: [data-reveal] starts at opacity:0 and only the
   IntersectionObserver ever adds .is-in, so with JavaScript off, 46 elements
   on the homepage and 30 here stayed invisible — About, Services, Our Work,
   Process, Areas and the holiday teaser among them. The page claimed to be
   legible without JS and was not. Reveals are an enhancement, so they are
   opt-in: no JS means no animation and everything simply shows.
   main.js applies the same fallback if IntersectionObserver is unavailable. */
.no-js [data-reveal]{opacity:1;transform:none}
.no-js .slides img:not(.is-on){opacity:0}
.no-js .dots{display:none}

/* The nav was the hole left in that same argument. Everything above made the
   page LEGIBLE without JavaScript; the header was still depending on it.
   .is-stuck — the class that gives the nav its solid white background and dark
   text — is only ever added by main.js. With JavaScript off it is never added,
   so the nav kept its cream-on-transparent hero treatment for the whole page
   and turned into invisible cream text on the cream ground the moment the hero
   scrolled away. Five links and the logo, gone.
   With no JavaScript the nav is simply solid from the first pixel. Every
   selector below is deliberately weighted to match the .nav:not(.is-stuck)
   rules in §5 and win on source order, not by reaching for !important. */
.no-js .nav{background:var(--surface);box-shadow:var(--shadow-2)}
.no-js .nav .mark__a{color:var(--primary);text-shadow:none}
.no-js .nav .mark__b{color:var(--text-2);text-shadow:none}
.no-js .nav .mark__r{background:var(--brand-red)}
.no-js .nav .mark__l{background:var(--brand-blue)}
.no-js .nav .nav__links a{color:var(--ink)}
.no-js .nav .burger span{background:var(--primary)}
.no-js .nav .nav__cta .btn--primary{border-color:var(--primary)}
/* Same root cause: the mobile call bar is revealed by the same observer, so
   without JavaScript it stayed translated off-screen permanently. On a phone
   that is the primary way to reach Charles. */
.no-js .dock{transform:none}
