
/* ─────────────────────────────────────────────────────────────
   501 Pressure Washing — home
   Ground: black. Accents: the brand blue #0274be brightened for a
   black ground, and the logo's own yellow #e4e400 — which does one
   job on this page and one only: the call button.
   ───────────────────────────────────────────────────────────── */
:root{
  --ink:#06080b;          /* base ground */
  --raise:#0c1118;        /* raised band */
  --raise-2:#121a23;      /* card face */
  --line:rgba(124,176,212,.17);
  --line-strong:rgba(124,176,212,.34);
  --white:#fff;
  --dim:#a3b8c8;          /* secondary text, tinted from the blue — never grey */
  --dimmer:#7d93a5;
  --neon:#2cb4ff;         /* #0274be lifted for a black ground */
  --neon-soft:#7cc4f2;
  --brand-deep:#0274be;   /* their actual blue, kept for depth */
  --yellow:#e4e400;       /* sampled from the logo */
  --yellow-deep:#b9b900;
  --display:"Space Grotesk",system-ui,sans-serif;
  --body:"Nunito Sans",system-ui,sans-serif;
  --wrap:1200px;
  --pad:clamp(18px,5vw,48px);
  --ease:cubic-bezier(.2,.7,.3,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--ink);color:var(--white);
  font-family:var(--body);font-size:17px;line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img,video,svg,iframe{max-width:100%;height:auto;display:block}

/* the parts nobody draws */
::selection{background:var(--neon);color:#04070a}
::-moz-selection{background:var(--neon);color:#04070a}
input,textarea{caret-color:var(--neon)}
:focus-visible{outline:2px solid var(--neon);outline-offset:3px;border-radius:2px}
@supports selector(::-webkit-scrollbar){
  ::-webkit-scrollbar{width:11px;height:11px}
  ::-webkit-scrollbar-track{background:#0a0e13}
  ::-webkit-scrollbar-thumb{background:#22303d;border-radius:99px;border:2px solid #0a0e13}
  ::-webkit-scrollbar-thumb:hover{background:#31465a}
}
html{scrollbar-color:#22303d #0a0e13}

h1,h2,h3,h4{font-family:var(--display);font-weight:600;color:var(--white);margin:0;line-height:1.12;letter-spacing:-.018em}
p{margin:0 0 1.05em}
a{color:var(--neon);text-decoration-thickness:1px;text-underline-offset:.22em;transition:color .16s var(--ease)}
a:hover{color:var(--neon-soft)}
.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
/* A phone number that wraps mid-digit reads as two broken numbers. */
a.num{white-space:nowrap}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--pad)}
section{scroll-margin-top:106px}
.sec{padding-block:clamp(60px,9vw,112px)}
.sec-raise{background:var(--raise)}
.rule{height:1px;background:var(--line);border:0;margin:0}

.eyebrow{
  font-family:var(--display);font-weight:600;font-size:.76rem;
  letter-spacing:.16em;text-transform:uppercase;color:var(--neon);
  margin:0 0 14px;display:flex;align-items:center;gap:12px;
}
.eyebrow::after{content:"";flex:1;height:1px;background:var(--line);max-width:120px}
h2{font-size:clamp(1.72rem,3.5vw,2.65rem);max-width:20ch}
.lede{color:var(--dim);font-size:clamp(1rem,1.5vw,1.12rem);max-width:64ch}

/* ── buttons ─────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:var(--display);font-weight:700;font-size:1.02rem;
  padding:16px 30px;border-radius:4px;border:1px solid transparent;
  text-decoration:none;cursor:pointer;white-space:nowrap;
  transition:background .18s var(--ease),color .18s var(--ease),
             border-color .18s var(--ease),transform .18s var(--ease),
             box-shadow .18s var(--ease);
}
/* the one yellow thing on this page */
.btn-call{
  background:var(--yellow);color:#0a0a00;border-color:var(--yellow);
  font-size:1.1rem;padding:18px 34px;letter-spacing:-.01em;
  box-shadow:0 10px 30px -14px rgba(228,228,0,.75);
}
.btn-call:hover{background:#f2f21a;border-color:#f2f21a;transform:translateY(-2px);box-shadow:0 16px 38px -14px rgba(228,228,0,.9)}
.btn-call:active{transform:translateY(0);background:var(--yellow-deep);border-color:var(--yellow-deep)}
.btn-ghost{background:transparent;color:var(--white);border-color:var(--line-strong)}
.btn-ghost:hover{border-color:var(--neon);color:var(--neon);transform:translateY(-2px)}
.btn-ghost:active{transform:translateY(0)}
.btn-row{display:flex;flex-wrap:wrap;gap:14px;align-items:center}

/* ── header ──────────────────────────────────────────────── */
/* not yellow: on this page yellow means "call", and nothing else */
.skip{position:absolute;left:-9999px;top:0;background:var(--neon);color:#04070a;padding:12px 20px;z-index:200;font-weight:700}
.skip:focus{left:12px;top:12px}
.hdr{
  position:sticky;top:0;z-index:100;
  background:rgba(6,8,11,.9);backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
.hdr-in{display:flex;align-items:center;gap:18px;min-height:64px;flex-wrap:wrap;padding-block:9px}
.brand{display:flex;align-items:center;text-decoration:none;flex-shrink:0}
.brand img{width:auto;height:36px;max-height:36px}
/* one row that scrolls sideways rather than two rows that eat the fold —
   a wrapped nav pushed the mobile header to 157px and the hero down with it */
.nav{
  display:flex;flex-wrap:nowrap;gap:22px;order:3;flex-basis:100%;
  overflow-x:auto;overscroll-behavior-x:contain;
  scrollbar-width:none;-ms-overflow-style:none;
  margin-inline:calc(var(--pad) * -1);padding-inline:var(--pad);
}
.nav::-webkit-scrollbar{display:none}
.nav a{
  font-family:var(--display);font-weight:500;font-size:.9rem;color:var(--dim);
  text-decoration:none;padding:5px 0;white-space:nowrap;flex-shrink:0;
  border-bottom:1px solid transparent;transition:color .16s var(--ease),border-color .16s var(--ease);
}
.nav a:hover{color:var(--white);border-bottom-color:var(--neon)}
.hdr-tel{
  margin-left:auto;font-family:var(--display);font-weight:700;font-size:1rem;
  color:var(--white);text-decoration:none;white-space:nowrap;
  padding:8px 14px;border:1px solid var(--line-strong);border-radius:4px;
  transition:border-color .16s var(--ease),color .16s var(--ease),background .16s var(--ease);
}
.hdr-tel:hover{border-color:var(--yellow);color:var(--yellow);background:rgba(228,228,0,.07)}
@media (min-width:900px){
  .hdr-in{flex-wrap:nowrap;padding-block:0;min-height:72px}
  .nav{order:0;flex-basis:auto;gap:28px;margin-left:14px;margin-inline-end:0;padding-inline:0;overflow:visible}
  .nav a{font-size:.94rem}
  section{scroll-margin-top:92px}
}

/* ── hero ────────────────────────────────────────────────── */
.hero{
  padding-block:clamp(34px,6vw,70px) clamp(48px,7vw,84px);
  position:relative;overflow:hidden;
}
.hero::before{  /* a single soft pool of their blue, low and behind the video */
  content:"";position:absolute;inset:auto -20% -40% 30%;height:70%;
  background:radial-gradient(closest-side,rgba(2,116,190,.30),transparent 72%);
  pointer-events:none;filter:blur(10px);
}
/* On a phone the two hero columns become one, and the video is lifted to sit
   directly under the headline — otherwise the whole point of the page loads
   below the fold. The explaining paragraph drops below the buttons; the deck
   line above the video still says what the business does and where. */
.hero-grid{position:relative;display:flex;flex-direction:column;gap:clamp(24px,4vw,34px)}
.hero-copy{display:contents}
.hero-eyebrow{order:1}
.hero h1{order:2}
.hero-deck{order:3}
.hero-media{order:4;max-width:320px;width:100%;margin-inline:auto}
.hero .btn-row{order:5}
.hero-sub{order:6}
.hero-note{order:7}
.hero-eyebrow,.hero h1,.hero-deck,.hero-sub{margin-bottom:0}
.hero-note{margin-top:0}
@media (min-width:960px){
  .hero-grid{display:grid;grid-template-columns:1fr 0.78fr;gap:clamp(40px,5vw,72px);align-items:center}
  .hero-copy{display:block}
  .hero-media{order:0;max-width:none;margin-inline:0}
  .hero h1{margin-bottom:18px}
  .hero-deck{margin-bottom:20px}
  .hero-sub{margin-bottom:28px}
  .hero-eyebrow{margin-bottom:14px}
  .hero-note{margin-top:20px}
}

.hero h1{
  font-size:clamp(2.5rem,6.4vw,4.4rem);font-weight:700;letter-spacing:-.032em;
  line-height:1.03;margin:0 0 18px;max-width:14ch;
}
.hero h1 em{font-style:normal;color:var(--neon);display:block}
.hero-deck{
  font-family:var(--display);font-weight:500;
  font-size:clamp(1.06rem,2vw,1.4rem);line-height:1.34;
  color:var(--white);margin:0 0 20px;max-width:26ch;
}
.hero-sub{color:var(--dim);max-width:52ch;margin:0 0 28px}
.hero-note{margin:20px 0 0;font-size:.95rem;line-height:1.55;color:var(--dimmer);max-width:48ch}
.hero-note strong{color:var(--dim);font-weight:600}

/* video, in its own upright shape — never cropped to a banner */
.hero-media{position:relative}
.vframe{
  position:relative;border-radius:6px;overflow:hidden;
  background:#0a0e13;border:1px solid var(--line);
  box-shadow:0 40px 80px -50px rgba(0,0,0,.95),0 0 0 1px rgba(44,180,255,.07);
  aspect-ratio:3/4;
}
.vframe video{width:100%;height:100%;object-fit:cover;display:block}
.vframe::after{ /* holds the control legible over bright sky */
  content:"";position:absolute;inset:auto 0 0 0;height:38%;
  background:linear-gradient(to top,rgba(4,7,10,.88),rgba(4,7,10,.42) 42%,transparent);
  pointer-events:none;
}
/* Caption and sound control share ONE flex row. They were two absolutely
   positioned boxes, one anchored left with no right bound, so a long caption
   ran under the button. The script also swaps the caption at runtime, so the
   length is not fixed: laying them out against each other is what makes a
   collision impossible rather than merely unlikely at one width. */
.vbar{
  position:absolute;inset:auto 12px 11px 14px;z-index:3;
  display:flex;align-items:flex-end;gap:16px;
  pointer-events:none;
}
.vcap{
  flex:1 1 auto;min-width:0;padding-bottom:5px;
  font-family:var(--display);font-weight:600;font-size:.74rem;
  line-height:1.45;letter-spacing:.13em;text-transform:uppercase;color:var(--dim);
  text-shadow:0 1px 6px rgba(0,0,0,.9);text-wrap:balance;
}
.sound{
  flex:0 0 auto;pointer-events:auto;
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--display);font-weight:700;font-size:.84rem;
  color:#04070a;background:var(--white);
  border:1px solid var(--white);border-radius:99px;
  padding:9px 16px 9px 13px;cursor:pointer;
  transition:background .18s var(--ease),color .18s var(--ease),
             border-color .18s var(--ease),transform .18s var(--ease);
}
/* Play/pause. The clip starts on its own and runs about twelve seconds
   alongside the hero copy, which is over the five-second line in WCAG 2.2.2,
   so it needs a visible stop. It is also the only way to START the clip on
   reduced motion, where it never begins by itself. Kept as a small circle so
   the white sound pill stays the louder of the two. */
.vplay{
  flex:0 0 auto;pointer-events:auto;
  display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:50%;
  color:var(--white);background:rgba(4,7,10,.62);
  border:1px solid rgba(255,255,255,.42);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  cursor:pointer;
  transition:background .18s var(--ease),border-color .18s var(--ease),
             color .18s var(--ease),transform .18s var(--ease);
}
.vplay:hover{background:var(--neon);border-color:var(--neon);color:#04070a;transform:translateY(-1px)}
.vplay:active{transform:translateY(0)}
.vplay svg{width:16px;height:16px}
/* inline-flex would otherwise beat the hidden attribute */
.vplay[hidden]{display:none}
.vplay .i-play{display:none}
.vplay[data-state="paused"] .i-pause{display:none}
.vplay[data-state="paused"] .i-play{display:inline-block;margin-left:2px}
.sound:hover{background:var(--neon);border-color:var(--neon);color:#04070a;transform:translateY(-1px)}
.sound:active{transform:translateY(0)}
.sound svg{width:15px;height:15px;flex-shrink:0}
/* On a phone the sticky call bar sits over the foot of the video, so the
   sound control moves to the top of the frame and the scrim moves with it. */
@media (max-width:899px){
  .vbar{inset:11px 12px auto 14px;justify-content:flex-end}
  .vcap{display:none}
  .vframe::after{inset:0 0 auto 0;height:30%;background:linear-gradient(to bottom,rgba(4,7,10,.8),transparent)}
}
.sound .lbl-off{display:none}
.sound[aria-pressed="true"] .lbl-on{display:none}
.sound[aria-pressed="true"] .lbl-off{display:inline}

/* ── proof strip: a data band, not a card row ─────────────── */
.proof{background:var(--raise);border-block:1px solid var(--line)}
.proof-in{display:grid;grid-template-columns:1fr 1fr;gap:0}
@media (min-width:760px){.proof-in{grid-template-columns:repeat(4,1fr)}}
.pf{padding:26px clamp(14px,2.4vw,28px);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.pf:nth-child(2n){border-right:0}
@media (min-width:760px){
  .pf{border-bottom:0;padding-block:34px}
  .pf:nth-child(2n){border-right:1px solid var(--line)}
  .pf:last-child{border-right:0}
}
.pf b{
  display:block;font-family:var(--display);font-weight:700;
  font-size:clamp(1.85rem,4vw,2.5rem);line-height:1;color:var(--neon);
  letter-spacing:-.03em;margin-bottom:9px;
}
.pf span{display:block;font-size:.94rem;line-height:1.45;color:var(--dim)}

/* ── problem ─────────────────────────────────────────────── */
.prob-grid{display:grid;gap:clamp(28px,4vw,52px)}
@media (min-width:900px){.prob-grid{grid-template-columns:1.15fr .85fr;align-items:start}}
.prob-body p:last-child{margin-bottom:0}
.prob-figure{border-radius:6px;overflow:hidden;border:1px solid var(--line);background:#0a0e13}
.prob-figure img{width:100%;aspect-ratio:4/3;object-fit:cover}
.prob-list{list-style:none;margin:clamp(34px,5vw,54px) 0 0;padding:0;display:grid;gap:0;border-top:1px solid var(--line)}
@media (min-width:820px){.prob-list{grid-template-columns:1fr 1fr}}
.prob-list li{padding:26px 0 24px;border-bottom:1px solid var(--line)}
@media (min-width:820px){
  .prob-list li{padding-right:clamp(20px,4vw,52px)}
  .prob-list li:nth-child(2n){padding-right:0;padding-left:clamp(20px,4vw,52px);border-left:1px solid var(--line)}
}
.prob-list h3{font-size:1.1rem;margin:0 0 9px;font-weight:600}
.prob-list p{margin:0;color:var(--dim);font-size:.98rem;line-height:1.6}

/* ── the wash (authored moment) ──────────────────────────── */
.wash{background:var(--ink);position:relative;overflow:hidden}
.wash-grid{display:grid;gap:clamp(30px,5vw,60px);align-items:center}
@media (min-width:900px){.wash-grid{grid-template-columns:.92fr 1.08fr}}
.gd{
  position:relative;aspect-ratio:720/1388;
  margin-inline:auto;width:100%;max-width:430px;
  border-radius:6px;overflow:hidden;
  border:1px solid var(--line);background:#0a0e13;
  box-shadow:0 40px 90px -55px rgba(0,0,0,.95);
}
.gd picture,.gd img{position:absolute;inset:0;width:100%;height:100%}
.gd img{object-fit:cover}
.gd-clean{z-index:2;clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
/* the wet sheet, and the edge that rides the boundary — both idle
   until the observer fires, both harmless if it never does */
.gd-wet{
  position:absolute;inset:0;z-index:3;pointer-events:none;opacity:0;
  background:linear-gradient(105deg,rgba(6,18,28,.40),rgba(8,42,66,.24) 58%,rgba(8,42,66,.10));
  clip-path:polygon(0 0,125% 0,100% 100%,0 100%);
}
.gd-edge{
  position:absolute;top:-14%;bottom:-14%;left:0;width:22%;z-index:4;
  pointer-events:none;opacity:0;
  background:linear-gradient(90deg,
      transparent 0%,rgba(2,10,17,.55) 34%,
      rgba(2,116,190,.42) 62%,rgba(124,196,242,.72) 78%,
      rgba(255,255,255,.30) 88%,transparent 100%);
  transform:translateX(-140%) skewX(-9deg);
  filter:blur(1px);
}
.gd-tag{
  position:absolute;z-index:5;bottom:12px;left:12px;
  font-family:var(--display);font-weight:700;font-size:.7rem;
  letter-spacing:.15em;text-transform:uppercase;
  padding:6px 11px;border-radius:3px;
  background:rgba(6,8,11,.78);border:1px solid var(--line-strong);color:var(--dim);
  backdrop-filter:blur(6px);
}
.wash-body p:last-of-type{margin-bottom:0}
.wash-meta{
  margin-top:26px;padding-top:22px;border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:10px 30px;
}
.wash-meta div{min-width:120px}
.wash-meta dt{font-family:var(--display);font-weight:600;font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dimmer);margin-bottom:5px}
.wash-meta dd{margin:0;font-family:var(--display);font-weight:600;font-size:1.02rem;color:var(--white)}

/* ── services ────────────────────────────────────────────── */
.svc{display:grid;gap:1px;margin-top:clamp(34px,5vw,52px);background:var(--line)}
@media (min-width:700px){.svc{grid-template-columns:1fr 1fr}}
@media (min-width:1040px){.svc{grid-template-columns:repeat(3,1fr)}}
.svc article{
  background:var(--raise-2);padding:clamp(24px,3vw,34px);
  display:flex;flex-direction:column;position:relative;z-index:0;
  transition:background .22s var(--ease),
             transform .28s cubic-bezier(.16,1,.3,1),
             box-shadow .28s var(--ease);
}
/* The grid's 1px gaps ARE the hairlines, so a card that merely rises
   tears a seam in the sheet. The ring shadow redraws its own edge as it
   goes, which is what makes it read as lifting off rather than slipping. */
.svc article:hover{
  background:#182430;transform:translateY(-5px);z-index:2;
  box-shadow:0 22px 46px -26px rgba(0,0,0,.95),0 0 0 1px rgba(44,180,255,.30);
}
.svc .meth{
  align-self:flex-start;margin:0 0 14px;padding:5px 11px;
  border:1px solid rgba(44,180,255,.4);border-radius:99px;
  font-family:var(--display);font-weight:600;font-size:.7rem;
  letter-spacing:.13em;text-transform:uppercase;color:var(--neon);
  transition:background .22s var(--ease),border-color .22s var(--ease);
}
.svc article:hover .meth{background:rgba(44,180,255,.13);border-color:rgba(44,180,255,.78)}
.svc h3{font-size:1.24rem;margin:0 0 11px}
.svc p{margin:0;color:var(--dim);font-size:.98rem;line-height:1.62}
.svc-note{margin-top:clamp(28px,4vw,40px);color:var(--dim);max-width:74ch}

/* ── method ──────────────────────────────────────────────── */
.steps{list-style:none;margin:clamp(34px,5vw,52px) 0 0;padding:0;counter-reset:s}
.steps li{
  display:grid;grid-template-columns:auto 1fr;gap:18px clamp(18px,3vw,34px);
  padding:clamp(24px,3vw,32px) 0;border-top:1px solid var(--line);
  transition:background .2s var(--ease);
}
.steps li:last-child{border-bottom:1px solid var(--line)}
.steps li:hover{background:rgba(44,180,255,.035)}
.steps .n{
  font-family:var(--display);font-weight:700;font-size:clamp(1.5rem,3vw,2.1rem);
  line-height:1;color:transparent;-webkit-text-stroke:1px var(--neon);
  letter-spacing:-.02em;padding-top:2px;min-width:2.4ch;
}
.steps h3{font-size:1.16rem;margin:0 0 8px}
.steps p{margin:0;color:var(--dim);font-size:.99rem;line-height:1.62;max-width:70ch}
.steps-note{margin-top:26px;color:var(--dim)}

/* ── pressure table ──────────────────────────────────────── */
.ptab-shell{
  margin-top:clamp(30px,4vw,46px);border:1px solid var(--line);
  border-radius:6px;overflow:hidden;background:var(--raise-2);
}
.ptab-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:660px;font-size:.97rem}
caption{text-align:left;padding:18px clamp(16px,2.4vw,24px);color:var(--dim);font-size:.94rem;line-height:1.55;border-bottom:1px solid var(--line)}
th,td{padding:15px clamp(12px,2vw,20px);text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
thead th{
  font-family:var(--display);font-weight:600;font-size:.73rem;
  letter-spacing:.13em;text-transform:uppercase;color:var(--dimmer);
  background:#0a1017;white-space:nowrap;
}
tbody tr{transition:background .18s var(--ease)}
tbody tr:hover{background:rgba(44,180,255,.05)}
tbody tr:last-child td{border-bottom:0}
td.surf{font-family:var(--display);font-weight:600;color:var(--white);white-space:nowrap}
td.meth{color:var(--dim);white-space:nowrap}
td.psi{white-space:nowrap;min-width:170px}
td.psi b{display:block;font-family:var(--display);font-weight:700;color:var(--neon);font-size:1.02rem;margin-bottom:7px;letter-spacing:-.01em}
.bar{display:block;height:4px;border-radius:99px;background:rgba(124,176,212,.15);overflow:hidden;max-width:132px}
/* The bright stop sits at the right end of the fill, so when the bar draws
   itself the highlight rides the leading edge — the head of the stream. */
.bar i{
  display:block;height:100%;border-radius:99px;transform-origin:left center;
  background:linear-gradient(90deg,var(--brand-deep),var(--neon) 74%,#c8ebff);
}
td.why{color:var(--dim);font-size:.94rem;line-height:1.58;min-width:290px}
.ptab-note{margin-top:22px;color:var(--dim)}

/* ── gallery: photographs, nothing on top of them ─────────── */
/* Real columns, not CSS multi-column: loading="lazy" is unreliable inside a
   multi-column container - measured here, every gallery image stayed unloaded
   at 3 columns and loaded fine at 1. Flex columns keep the masonry look and
   keep lazy loading working. */
.gal{display:flex;gap:12px;margin-top:clamp(30px,4vw,46px);flex-direction:column}
.gal-col{display:flex;flex-direction:column;gap:12px;flex:1;min-width:0}
@media (min-width:640px){.gal{flex-direction:row}}
@media (max-width:639px){.gal-col{gap:12px}}
.gal figure{
  margin:0;border-radius:5px;overflow:hidden;
  background:#0a0e13;border:1px solid var(--line);
}
.gal img{width:100%;height:auto;display:block;transition:transform .5s var(--ease)}
.gal figure{transition:opacity .34s var(--ease),border-color .24s var(--ease)}
.gal figure:hover img{transform:scale(1.035)}
/* Hovering anywhere in the wall drops the rest back so the one under the
   cursor is the only one lit. Pointer devices only — on a touch screen
   there is no hover to end, and the wall would simply stay dimmed. */
@media (hover:hover){
  .gal:hover figure{opacity:.46}
  .gal figure:hover{opacity:1;border-color:var(--neon)}
}

/* ── faq ─────────────────────────────────────────────────── */
.faq{margin-top:clamp(30px,4vw,44px);border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  list-style:none;cursor:pointer;padding:22px 44px 22px 0;position:relative;
  font-family:var(--display);font-weight:600;font-size:1.06rem;line-height:1.35;
  color:var(--white);transition:color .18s var(--ease);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--neon)}
.faq summary::after{
  content:"";position:absolute;right:6px;top:50%;width:12px;height:12px;
  border-right:1.5px solid var(--neon);border-bottom:1.5px solid var(--neon);
  transform:translateY(-70%) rotate(45deg);transition:transform .22s var(--ease);
}
.faq details[open] summary::after{transform:translateY(-25%) rotate(-135deg)}
.faq .ans{padding:0 0 24px;color:var(--dim);max-width:74ch}
.faq .ans p{margin:0 0 .85em}
.faq .ans p:last-child{margin:0}
.faq .ans strong{color:var(--white);font-weight:700}
.faq-note{margin-top:26px;color:var(--dim)}

/* ── areas ───────────────────────────────────────────────── */
.towns{
  list-style:none;margin:clamp(28px,4vw,42px) 0 0;padding:0;
  display:flex;flex-wrap:wrap;gap:8px;
}
.towns li{
  font-family:var(--display);font-weight:600;font-size:.94rem;
  padding:9px 15px;border:1px solid var(--line);border-radius:3px;
  color:var(--dim);transition:border-color .18s var(--ease),color .18s var(--ease),background .18s var(--ease);
}
.towns li:hover{border-color:var(--line-strong);color:var(--white);background:rgba(44,180,255,.05)}
.towns li a{color:inherit;text-decoration:none}
.towns li:has(a):hover{border-color:var(--neon);color:var(--neon)}
.runs{display:grid;gap:0;margin-top:clamp(30px,4vw,46px);border-top:1px solid var(--line)}
@media (min-width:900px){.runs{grid-template-columns:repeat(3,1fr);border-top:0;gap:1px;background:var(--line)}}
.runs > div{padding:26px 0;border-bottom:1px solid var(--line)}
@media (min-width:900px){.runs > div{background:var(--ink);padding:28px clamp(18px,2.4vw,30px);border-bottom:0}}
.runs h3{font-size:1.08rem;margin:0 0 10px}
.runs p{margin:0;color:var(--dim);font-size:.96rem;line-height:1.6}
.areas-note{margin-top:clamp(28px,4vw,40px);color:var(--dim);max-width:74ch}

/* ── contact ─────────────────────────────────────────────── */
.cta{background:var(--raise);border-top:1px solid var(--line)}
.cta-grid{display:grid;gap:clamp(30px,5vw,60px)}
@media (min-width:900px){.cta-grid{grid-template-columns:1.05fr .95fr;align-items:start}}
.cta h2{max-width:16ch}
.facts{list-style:none;margin:0;padding:0;border-top:1px solid var(--line)}
.facts li{display:flex;justify-content:space-between;gap:20px;padding:14px 0;border-bottom:1px solid var(--line);font-size:.97rem}
.facts .k{font-family:var(--display);font-weight:600;font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dimmer);padding-top:4px;white-space:nowrap}
.facts .v{text-align:right;color:var(--white)}
.facts .v a{color:var(--white);text-decoration:none;border-bottom:1px solid var(--line-strong)}
.facts .v a:hover{color:var(--neon);border-bottom-color:var(--neon)}
.cta-note{margin-top:24px;color:var(--dim);font-size:.96rem}

/* ── footer ──────────────────────────────────────────────── */
.ftr{background:var(--ink);border-top:1px solid var(--line);padding-block:clamp(44px,6vw,66px) 30px;font-size:.95rem}
.ftr-grid{display:grid;gap:34px}
@media (min-width:760px){.ftr-grid{grid-template-columns:1.2fr 1fr 1fr}}
.ftr img{height:40px;width:auto;margin-bottom:16px}
.ftr address{font-style:normal;color:var(--dim);line-height:1.7}
.ftr address a{color:var(--dim);text-decoration:none}
.ftr address a:hover{color:var(--neon)}
.ftr h3{font-family:var(--display);font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dimmer);margin:0 0 14px;font-weight:600}
.ftr ul{list-style:none;margin:0;padding:0}
.ftr li{margin-bottom:9px;color:var(--dim);line-height:1.5}
.ftr li a{color:var(--dim);text-decoration:none;transition:color .16s var(--ease)}
.ftr li a:hover{color:var(--neon)}
.social{display:flex;flex-wrap:wrap;gap:7px;margin-top:18px}
.social a{
  font-family:var(--display);font-size:.8rem;font-weight:600;color:var(--dimmer);
  text-decoration:none;padding:6px 11px;border:1px solid var(--line);border-radius:3px;
  transition:color .16s var(--ease),border-color .16s var(--ease);
}
.social a:hover{color:var(--neon);border-color:var(--neon)}
.ftr-base{margin-top:clamp(34px,5vw,50px);padding-top:22px;border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:12px 26px;justify-content:space-between;color:var(--dimmer);font-size:.88rem}
.ftr-base a{color:var(--dimmer);text-decoration:none}
.ftr-base a:hover{color:var(--neon)}

/* mobile call bar */
.callbar{
  position:fixed;left:0;right:0;bottom:0;z-index:120;
  display:flex;padding:10px 12px calc(10px + env(safe-area-inset-bottom));
  background:rgba(6,8,11,.94);backdrop-filter:blur(12px);border-top:1px solid var(--line);
}
.callbar .btn{flex:1;padding:15px 20px}
@media (min-width:900px){.callbar{display:none}}
@media (max-width:899px){body{padding-bottom:78px}}

/* ─────────────────────────────────────────────────────────────
   MOTION

   Nothing below hides anything until the script at the foot of this
   page adds .js-rv, so a script that never arrives leaves the whole
   page visible and readable.

   Each piece does a different job and none of them is the same
   entrance fired twice: a value counts itself up, a set of cards
   arrives as a set, a process unrolls in order, a measurement draws
   itself, a cursor picks one photograph out of a wall, a droplet runs
   down glass, an answer opens. Only the garage door loops.

   Arrival uses the independent 'translate' and 'scale' properties
   rather than 'transform', so hover — which owns 'transform' — is
   never delayed by an arrival delay or pinned by a fill-mode.
   ───────────────────────────────────────────────────────────── */

/* ── the focal moment: the garage door washes itself, on a loop ──
   2.5s wipe · 3.7s held clean · 0.7s dissolve back · 0.9s beat = 7.8s.
   Nearly half the cycle is the clean frame, because that is the half
   worth looking at. The return is a DISSOLVE, never a reverse wipe —
   a wipe running backwards reads as the door getting dirty again,
   which is the opposite of what the picture is for.
   Two animations on the clean layer, each owning one property: the
   mask and the fade need different curves and must not fight. */
html.js-rv .gd-clean{clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
html.js-rv .gd.is-washed .gd-clean{
  animation:gdWipe 7800ms cubic-bezier(.42,0,.22,1) 220ms infinite,
            gdFade 7800ms cubic-bezier(.4,0,.55,1)  220ms infinite;
}
html.js-rv .gd.is-washed .gd-wet {animation:gdWet  7800ms cubic-bezier(.42,0,.22,1) 220ms infinite}
html.js-rv .gd.is-washed .gd-edge{animation:gdEdge 7800ms cubic-bezier(.42,0,.22,1) 220ms infinite}
/* A loop in the corner of the eye is a nuisance. It runs only while it is
   actually on screen, and stops when the tab is hidden. Same duration and
   same delay on all three, so pausing and resuming keeps them in step. */
html.js-rv .gd.is-held .gd-clean,
html.js-rv .gd.is-held .gd-wet,
html.js-rv .gd.is-held .gd-edge{animation-play-state:paused}

@keyframes gdWipe{          /* 0-31.75% wipe · held · mask reset while invisible */
  0%     {clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
  31.75% {clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
  88.89% {clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
  88.9%  {clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
  100%   {clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
}
@keyframes gdFade{          /* clean holds, then dissolves off between 79.37% and 88.89% */
  0%,79.37%{opacity:1}
  88.89%   {opacity:0}
  100%     {opacity:0}
}
@keyframes gdWet{           /* the freshly-washed panel stays dark, then dries */
  0%     {opacity:0;clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
  2.9%   {opacity:.92}
  31.75% {opacity:.85;clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
  46.03% {opacity:0;clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
  88.89% {opacity:0;clip-path:polygon(0 0,125% 0,100% 100%,0 100%)}
  88.9%  {opacity:0;clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
  100%   {opacity:0;clip-path:polygon(0 0,0 0,-25% 100%,0 100%)}
}
@keyframes gdEdge{          /* the wet line riding the jet path */
  0%     {opacity:0;transform:translateX(-140%) skewX(-9deg)}
  2.54%  {opacity:1}
  27.94% {opacity:1}
  31.75% {opacity:0;transform:translateX(465%) skewX(-9deg)}
  100%   {opacity:0;transform:translateX(465%) skewX(-9deg)}
}

/* ── services: six cards arriving as one set ─────────────── */
html.js-rv .svc article{opacity:0;translate:0 18px}
html.js-rv .svc.in article{
  opacity:1;translate:none;
  transition:opacity .78s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 95ms),
             translate .78s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 95ms);
}

/* ── method: five steps unrolling as a process ───────────── */
/* Sideways, not upward, and the outlined numeral counts itself off a beat
   behind its own line — so it reads as an order of operations rather than
   as five more things fading in. */
html.js-rv .steps li{opacity:0;translate:-24px 0}
html.js-rv .steps.in li{
  opacity:1;translate:none;
  transition:opacity .72s var(--ease) calc(var(--i,0) * 150ms),
             translate .88s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 150ms);
}
html.js-rv .steps li .n{opacity:0;scale:.72}
html.js-rv .steps.in li .n{
  opacity:1;scale:1;
  transition:opacity .58s var(--ease) calc(var(--i,0) * 150ms + 200ms),
             scale .74s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 150ms + 200ms);
}

/* ── pressure table: the measurement draws itself ────────── */
/* The bar length IS the data, so the row settling in is the small half and
   the bar filling is the point: you watch each pressure scale itself
   against the biggest one on the trailer. */
html.js-rv .ptab-shell tbody tr{opacity:0}
html.js-rv .ptab-shell.in tbody tr{
  opacity:1;transition:opacity .62s var(--ease) calc(var(--i,0) * 105ms);
}
html.js-rv .bar i{transform:scaleX(0)}
html.js-rv .ptab-shell.in .bar i{
  transform:scaleX(1);
  transition:transform 1.25s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 105ms + 220ms);
  animation:barGlow 1.7s ease-out calc(var(--i,0) * 105ms + 220ms) both;
}
@keyframes barGlow{
  0%  {filter:drop-shadow(0 0 0 rgba(44,180,255,0))}
  32% {filter:drop-shadow(0 0 6px rgba(44,180,255,.72))}
  100%{filter:drop-shadow(0 0 0 rgba(44,180,255,0))}
}

/* ── areas: twenty-one towns ticked off ──────────────────── */
html.js-rv .towns li{opacity:0;scale:.84}
html.js-rv .towns.in li{
  opacity:1;scale:1;
  transition:opacity .48s var(--ease) calc(var(--i,0) * 36ms),
             scale .6s cubic-bezier(.16,1,.3,1) calc(var(--i,0) * 36ms);
}

/* ── faq: the answer explains its own arrival ────────────── */
html.js-rv .faq details[open] .ans{animation:ansIn .46s cubic-bezier(.16,1,.3,1) both}
@keyframes ansIn{
  from{opacity:0;translate:0 -7px}
  to  {opacity:1;translate:none}
}

/* Reduced motion: js-rv is never added, so not one of the states above is
   ever applied and nothing is hidden — the clean door is simply already
   there, the cards are already up, the bars are already full. This block
   catches what is left: the hover movement, and the gallery's dimming,
   which would otherwise snap rather than ease. Colour and border feedback
   stay, because that is state, not movement. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition-duration:.001ms!important}
  .gd-wet,.gd-edge{display:none}
  .svc article:hover,.btn:hover,.gal figure:hover img{transform:none}
  .svc article:hover{box-shadow:none}
  .gal:hover figure{opacity:1}
}

/* ── reviews: their words, not ours ──────────────────────── */
/* Same hairline sheet and the same lift as the service cards, because it is
   the same system. Deliberately NO arrival animation: four sets on this page
   already have one each, and a fifth would make the page feel like it moves
   rather than like one thing moves. Hover still responds, which is the floor. */
.revs{display:grid;gap:1px;margin-top:clamp(30px,4vw,46px);background:var(--line)}
@media (min-width:700px){.revs{grid-template-columns:1fr 1fr}}
@media (min-width:1040px){.revs{grid-template-columns:repeat(3,1fr)}}
.rev{
  background:var(--raise-2);padding:clamp(24px,3vw,32px);
  display:flex;flex-direction:column;position:relative;z-index:0;
  transition:background .22s var(--ease),
             transform .28s cubic-bezier(.16,1,.3,1),
             box-shadow .28s var(--ease);
}
.rev:hover{
  background:#182430;transform:translateY(-5px);z-index:2;
  box-shadow:0 22px 46px -26px rgba(0,0,0,.95),0 0 0 1px rgba(44,180,255,.30);
}
/* Stars are neon. Never yellow: on this page yellow means "call" and nothing else. */
.rev .stars{display:flex;gap:3px;margin-bottom:15px;color:var(--neon)}
.rev .stars svg{width:14px;height:14px;fill:currentColor}
.rev blockquote{margin:0 0 18px;font-size:1.01rem;line-height:1.62;color:var(--white)}
.rev .who{margin:auto 0 0;font-family:var(--display);font-weight:600;font-size:.9rem;color:var(--dim)}
.rev .who span{display:block;font-family:var(--body);font-weight:400;font-size:.82rem;color:var(--dimmer);margin-top:4px}

/* ── one quote, sitting directly under the thing it proves ─ */
/* Hairline on all four sides. A colored stripe down one edge is banned on
   this site, and a callout box is exactly where one usually turns up. */
.pullq{
  margin:clamp(28px,4vw,40px) 0 0;max-width:74ch;
  padding:clamp(20px,2.6vw,26px);
  background:var(--raise-2);border:1px solid var(--line);border-radius:5px;
}
.pullq blockquote{margin:0 0 12px;color:var(--white);font-size:1rem;line-height:1.62}
.pullq .who{margin:0;font-family:var(--display);font-weight:600;font-size:.88rem;color:var(--dim)}
.pullq .who span{font-family:var(--body);font-weight:400;color:var(--dimmer)}

/* The roof section reuses the problem grid but hands the WIDER column to the
   text, because there the argument is longer than the picture is. */
@media (min-width:900px){#roof .prob-grid{grid-template-columns:.85fr 1.15fr;align-items:center}}
/* That roof photo is a stacked before/after pair. The 4:3 crop the problem
   figure uses would cut the top frame off, so here it keeps its own shape. */
#roof .prob-figure img{aspect-ratio:auto}

/* ---- real menu ---- */
.navtoggle{display:none;align-items:center;gap:9px;background:none;border:1px solid var(--line-strong);border-radius:4px;color:var(--white);font-family:var(--display);font-weight:600;font-size:.86rem;padding:8px 12px;cursor:pointer}
.navtoggle .bars{display:grid;gap:3.5px}
.navtoggle .bars i{display:block;width:16px;height:1.8px;background:currentColor;border-radius:2px}
.has-menu{position:relative}
.navbtn{display:inline-flex;align-items:center;gap:6px;background:none;border:0;padding:5px 0;cursor:pointer;
  font-family:var(--display);font-weight:500;font-size:.94rem;color:var(--dim);white-space:nowrap;
  border-bottom:1px solid transparent;transition:color .16s var(--ease),border-color .16s var(--ease)}
.navbtn svg{width:10px;height:6px;transition:transform .18s var(--ease)}
.has-menu:hover .navbtn,.has-menu:focus-within .navbtn,.navbtn[aria-expanded=true]{color:var(--white);border-bottom-color:var(--neon)}
.has-menu:hover .navbtn svg,.navbtn[aria-expanded=true] svg{transform:rotate(180deg)}
.menu{position:absolute;top:calc(100% + 10px);left:-14px;z-index:120;display:flex;gap:26px;
  background:var(--raise);border:1px solid var(--line-strong);border-radius:8px;padding:18px 22px;
  box-shadow:0 18px 44px rgba(0,0,0,.55);opacity:0;visibility:hidden;transform:translateY(-5px);
  transition:opacity .16s var(--ease),transform .16s var(--ease),visibility .16s}
.has-menu:hover .menu,.has-menu:focus-within .menu{opacity:1;visibility:visible;transform:none}
.menu-svc{flex-direction:column}
.menu .mcol{display:flex;flex-direction:column;gap:2px;min-width:150px}
.menu .mcol-h{font-family:var(--display);font-size:.72rem;letter-spacing:.09em;text-transform:uppercase;color:var(--dimmer);margin:0 0 7px}
.menu a{font-family:var(--body);font-size:.92rem;color:var(--dim);text-decoration:none;padding:5px 0;white-space:nowrap;border:0}
.menu a:hover{color:var(--neon);border:0}
.menu-all{margin-top:2px;align-self:flex-end;font-family:var(--display)!important;font-weight:600;color:var(--neon)!important}
@media (max-width:900px){
  .navtoggle{display:inline-flex;margin-left:auto;order:2}
  .hdr-in{flex-wrap:wrap}
  .hdr-tel{order:3}
  .nav{order:4;flex-basis:100%;display:none;flex-direction:column;align-items:flex-start;gap:0;
    overflow:visible;border-top:1px solid var(--line);margin-top:10px;padding-top:6px}
  .nav.open{display:flex}
  .nav>a,.navbtn{width:100%;padding:11px 0;font-size:1rem}
  .has-menu{width:100%}
  .menu{position:static;opacity:1;visibility:visible;transform:none;display:none;flex-direction:column;
    box-shadow:none;background:none;border:0;border-left:1px solid var(--line);border-radius:0;
    padding:2px 0 8px 14px;margin-bottom:6px;gap:12px}
  .has-menu.open .menu{display:flex}
  .has-menu:hover .menu{display:none}
  .has-menu.open:hover .menu{display:flex}
}

.ftr-towns{display:grid;grid-template-columns:1fr 1fr;gap:0 18px}
.ftr-more{margin-top:10px;font-size:.88rem;color:var(--dimmer)}
@media (max-width:600px){.ftr-towns{grid-template-columns:1fr 1fr}}

.menu-area{flex-direction:column;gap:14px}
.mcols{display:flex;gap:26px}
.menu-all{align-self:flex-end;margin-top:0}
@media (max-width:900px){.mcols{flex-direction:column;gap:12px}.menu-all{align-self:flex-start}}

/* ---- section bands for inner pages (dark tokens) ---- */
.band{background:var(--raise);border-block:1px solid var(--line)}
.band-deep{background:var(--raise-2);border-block:1px solid var(--line)}
main section{padding-block:clamp(44px,6vw,86px)}
