/* ============================================================
   THE GRIME GURUS
   Static HTML/CSS/JS. No framework, no build step, no CDN.
   Fonts self-hosted (Archivo + Instrument Sans, SIL Open Font License),
   latin subset only. Two files, both preloaded.
   ============================================================ */

/* --- type ------------------------------------------------- */
@font-face{
  font-family:'Archivo';
  src:url('fonts/archivo-var-latin.woff2') format('woff2-variations');
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Instrument Sans';
  src:url('fonts/instrumentsans-var-latin.woff2') format('woff2-variations');
  font-weight:400 700; font-stretch:75% 100%; font-style:normal; font-display:swap;
}

:root{
  /* warm near-black, bone, stone. Green is for the logo and the call button. */
  --void:#0B0A09;
  --pitch:#060505;
  --shale:#141210;
  --bone:#F5F1EA;
  --bone-2:rgba(245,241,234,.64);
  /* .38 measured 3.25:1 on the page background — under the 4.5 WCAG AA needs
     for text this size. Every photo caption and footer heading uses it. */
  --bone-3:rgba(245,241,234,.56);
  --stone:#C9B79B;
  --green:#2EFD53;
  --flag:#BC9A4E;            /* dull gold = not yet verified */
  --rule:rgba(245,241,234,.11);
  --rule-2:rgba(245,241,234,.22);

  --pad:clamp(18px,4.6vw,68px);
  --w:1560px;

  --disp:'Archivo','Arial Narrow',Helvetica,sans-serif;
  --text:'Instrument Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;

  --ease:cubic-bezier(.16,.84,.24,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;overflow-x:clip}
body{
  margin:0; background:var(--void); color:var(--bone);
  font-family:var(--text); font-size:16.5px; font-weight:420; line-height:1.62;
  letter-spacing:.002em; -webkit-font-smoothing:antialiased;
  overflow-x:clip;
}
/* Elements are meant to run off the edges. They get a real clipping ancestor
   rather than leaning on viewport overflow propagation — and it is clip, not
   hidden, on the x axis only, so vertical overhangs still escape. */
.hero,.svc-1,.svc-4{overflow-x:clip;overflow-y:visible}
img,video{display:block;max-width:100%}
/* kills the 300ms tap delay on phones — matters on a page whose whole job is
   getting a thumb onto a call button. Pinch-zoom is unaffected. */
a,button{color:inherit;text-decoration:none;touch-action:manipulation}
h1,h2,h3,h4{margin:0;font-weight:400}
p,figure,figcaption{margin:0}
::selection{background:var(--stone);color:var(--void)}
:focus-visible{outline:2px solid var(--stone);outline-offset:4px}

/* --- three registers of one system ------------------------ */
/* 1. condensed heavy display */
.d{
  font-family:var(--disp); font-weight:870; font-stretch:66%;
  line-height:.87; letter-spacing:-.018em;
}
/* 2. expanded, tracked label — the width axis doing signage work */
.lab{
  font-family:var(--disp); font-weight:620; font-stretch:118%;
  font-size:clamp(9.5px,.75vw,11px); letter-spacing:.26em; text-transform:uppercase;
  color:var(--stone); display:block;
}

.lede{color:var(--bone-2);font-size:clamp(15.5px,1.15vw,17.5px);line-height:1.66;max-width:56ch}
.small{font-size:13.5px;line-height:1.55;color:var(--bone-3)}

/* placeholders: unverified facts, dull gold, dashed */
.tbc{
  color:var(--flag);
  border-bottom:1px dashed rgba(188,154,78,.6);
  white-space:nowrap;
}

/* --- layout shells (deliberately more than one) ----------- */
.shell{max-width:var(--w);margin:0 auto;padding-inline:var(--pad)}
.shell-mid{max-width:1180px;margin:0 auto;padding-inline:var(--pad)}
/* asymmetric: more room on the left than the right */
.shell-off{max-width:var(--w);margin:0 auto;padding-left:var(--pad);padding-right:clamp(18px,2.6vw,36px)}

/* --- buttons ---------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:var(--disp);font-weight:700;font-stretch:92%;font-size:15px;
  /* a <button>.btn falls through to the UA's buttonface grey without this,
     which put cream text on near-white — same trap as the hidden rule below */
  background:transparent;
  letter-spacing:.005em;padding:16px 28px;border:1px solid transparent;border-radius:999px;
  transition:background .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease),transform .25s var(--ease);
}
.btn svg{width:17px;height:17px;flex:none}
.btn-call{background:var(--green);color:#06180B;font-weight:800}
.btn-call:hover{background:#48FF6A}
.btn-ghost{border-color:var(--rule-2);color:var(--bone)}
.btn-ghost:hover{border-color:var(--bone);background:rgba(245,241,234,.05)}
.btn:active{transform:translateY(1px)}
/* an author display rule beats the hidden attribute's UA rule, so a hidden
   button would otherwise render as an empty pill */
.btn[hidden]{display:none}
.btn-lg{padding:20px 34px;font-size:17px}

/* --- header ----------------------------------------------- */
.hdr{
  /* above .menu (98), because the button that closes the panel lives in here.
     At 90 the full-screen panel painted straight over it, and with no Escape
     key on a phone the only ways out were tapping a nav link or reloading.
     The sticky call bar (95) stays below the panel, the lightbox (130) above. */
  position:fixed;inset:0 0 auto;z-index:99;
  transition:background .4s var(--ease),border-color .4s var(--ease);
  border-bottom:1px solid transparent;
}
.hdr.stuck{background:rgba(11,10,9,.86);backdrop-filter:blur(14px);border-bottom-color:var(--rule)}
.hdr-in{display:flex;align-items:center;justify-content:space-between;gap:28px;height:70px}
.mark{
  font-family:var(--disp);font-weight:900;font-stretch:88%;font-size:clamp(15px,1.35vw,19px);
  letter-spacing:.008em;line-height:1;display:block;
  padding-block:8px;   /* padding, not flex — flex would make each word a row */
}
.mark i{font-style:normal;color:var(--green)}
.mark small{
  display:block;font-family:var(--text);font-weight:520;font-size:8px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--bone-3);margin-top:6px;
}
.hdr-nav{display:flex;gap:30px;align-items:center;font-size:14px;color:var(--bone-2)}
.hdr-nav a{position:relative;padding-block:11px;transition:color .2s}
.hdr-nav a:hover{color:var(--bone)}
.hdr-nav a::after{
  content:'';position:absolute;left:0;bottom:0;height:1px;width:100%;background:var(--stone);
  transform:scaleX(0);transform-origin:left;transition:transform .35s var(--ease);
}
.hdr-nav a:hover::after{transform:scaleX(1)}
.hdr-tel{
  font-family:var(--disp);font-weight:780;font-stretch:80%;font-size:17px;letter-spacing:.01em;
  display:flex;align-items:center;justify-content:flex-end;gap:9px;
  min-height:44px;                 /* 44px minimum, even when it is icon-only */
}
.hdr-tel svg{width:16px;height:16px;color:var(--stone)}
.hdr-cta{display:flex;align-items:center;gap:clamp(8px,1.2vw,16px)}
.btn-sm{padding:11px 18px;font-size:13.5px}
@media(max-width:1000px){.hdr-nav{display:none}}

/* --- menu button + panel (phones and tablets) ------------- */
.menu-btn{
  display:none;width:44px;height:44px;padding:0;background:none;border:0;cursor:pointer;
  flex-direction:column;align-items:center;justify-content:center;gap:5px;
}
.menu-btn span{
  display:block;width:21px;height:2px;background:var(--bone);
  transition:transform .3s var(--ease),opacity .2s var(--ease);
}
.menu-btn[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.menu-btn[aria-expanded="true"] span:nth-child(2){opacity:0}
.menu-btn[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
@media(max-width:1000px){.menu-btn{display:flex}}

.menu{
  position:fixed;inset:0;z-index:98;background:var(--void);
  display:flex;flex-direction:column;justify-content:center;
  padding:96px var(--pad) calc(28px + env(safe-area-inset-bottom));
  overflow-y:auto;
}
.menu[hidden]{display:none}
.menu nav{display:flex;flex-direction:column}
.menu nav a{
  font-family:var(--disp);font-weight:830;font-stretch:70%;
  font-size:clamp(30px,8.4vw,52px);line-height:1;letter-spacing:-.02em;
  padding:clamp(11px,2.2vw,16px) 0;border-bottom:1px solid var(--rule);
}
.menu nav a:first-child{border-top:1px solid var(--rule)}
.menu-cta{display:flex;flex-wrap:wrap;gap:11px;margin-top:clamp(24px,5vw,36px)}
.menu-cta .btn{flex:1 1 100%}
.menu-fine{margin-top:16px;font-size:13px;color:var(--bone-3)}
body.menu-open{overflow:hidden}
@media(max-width:760px){.hdr-quote{display:none}}
@media(max-width:520px){
  /* the number stays — it is the whole point of the header. The strapline goes
     instead, since the logo above it already says who this is. */
  .mark small{display:none}
  .mark{padding-block:15px}
  .hdr-tel{font-size:15.5px;gap:7px}
  .hdr-tel svg{width:15px;height:15px}
  .hdr-in{gap:12px}
}

/* ============================================================
   1. HERO — one full-bleed photograph, type on top.
   ============================================================ */
.hero{
  position:relative;min-height:100svh;overflow:hidden;
  display:flex;align-items:flex-end;
  padding-top:118px;padding-bottom:clamp(34px,6vh,74px);
}
.hero-bg{position:absolute;inset:0;z-index:0}
.hero-bg img{
  width:100%;height:100%;object-fit:cover;object-position:50% 46%;
  filter:saturate(.98) contrast(1.04) brightness(.95);
}
/* Warm near-black gradients, not a coloured wash — the photograph gets darkened
   so the type can sit on it, and nothing gets tinted. */
.hero-bg::after{
  content:'';position:absolute;inset:0;
  background:
    linear-gradient(180deg,rgba(11,10,9,.62) 0%,rgba(11,10,9,0) 26%,rgba(11,10,9,.45) 70%,var(--void) 100%),
    linear-gradient(96deg,rgba(11,10,9,.9) 0%,rgba(11,10,9,.34) 44%,rgba(11,10,9,0) 78%);
}
/* grain over the photograph: it is a 1158px file doing a 1440px job, and the
   grain is what stops that reading as softness */
.hero-bg::before{
  content:'';position:absolute;inset:0;z-index:1;pointer-events:none;opacity:.4;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.hero-in{
  position:relative;z-index:2;width:100%;
  max-width:var(--w);margin:0 auto;padding-inline:var(--pad);
}
.hero h1{margin:clamp(16px,2.2vw,30px) 0 0}
.hero h1 .l1,.hero h1 .l2{display:block;font-size:clamp(50px,7.6vw,112px)}
/* Both lines in the same heavy condensed face, green on the second — the way it
   reads on the flyer and the logo. */
.hero h1 .l2{color:var(--green)}
.hero .lede{margin-top:clamp(18px,1.8vw,28px);max-width:44ch;color:rgba(245,241,234,.78)}
.hero .lede .narrow{display:none}
.hero-cta{display:flex;flex-wrap:wrap;gap:11px;margin-top:clamp(22px,2.4vw,32px)}
.hero-fine{margin-top:14px;font-size:13px;color:rgba(245,241,234,.56);letter-spacing:.01em}
.hero-rule{
  position:absolute;left:var(--pad);bottom:0;z-index:2;width:1px;height:clamp(30px,7vh,74px);
  background:linear-gradient(180deg,transparent,var(--stone));transform-origin:bottom;
}
@media(min-width:1800px){
  .hero-bg img{filter:saturate(.96) contrast(1.05) brightness(.88)}
  .hero-bg::before{opacity:.5}
}
@media(max-width:900px){
  .hero{padding-top:104px}
  .hero h1 .l1,.hero h1 .l2{font-size:clamp(46px,13.2vw,86px)}
  /* shorter lede on a phone — every line above the call button costs a tap */
  .hero .lede .wide{display:none}
  .hero .lede .narrow{display:inline}
  .hero-cta .btn{flex:1 1 100%}
  .hero-bg img{object-position:56% 66%}
  .hero-bg::after{
    background:
      linear-gradient(180deg,rgba(11,10,9,.6) 0%,rgba(11,10,9,.3) 22%,rgba(11,10,9,.8) 62%,var(--void) 100%);
  }
}

/* --- before/after comparison ------------------------------ */
.cmp{
  position:relative;overflow:hidden;background:var(--pitch);
  cursor:ew-resize;touch-action:pan-y;user-select:none;-webkit-user-select:none;
  /* iOS pops its Save Image / Copy sheet on a press-and-hold and cancels the
     drag underneath it. Cheap insurance — desktop never shows this. */
  -webkit-touch-callout:none;
  aspect-ratio:470/626;
}
.cmp img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  pointer-events:none;filter:saturate(.92) contrast(1.03);
}
.cmp-b{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--p,52%)) 0 0);will-change:clip-path}
/* the scroll-driven one opens on the dirty photograph, with or without JS */
.cmp[data-scroll-driven]{--p:96%}
/* grain over the plates: makes compressed phone photos read as film, not as low-res */
.cmp::after{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:4;opacity:.5;
  mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.cmp-line{
  position:absolute;top:0;bottom:0;left:var(--p,52%);width:1px;z-index:5;
  background:rgba(245,241,234,.95);transform:translateX(-.5px);pointer-events:none;
}
.cmp-grip{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:38px;height:38px;border-radius:50%;background:var(--bone);
  display:grid;place-items:center;box-shadow:0 4px 20px rgba(0,0,0,.55);
  transition:box-shadow .3s var(--ease);
}
.cmp:hover .cmp-grip,.cmp.dragging .cmp-grip{box-shadow:0 4px 20px rgba(0,0,0,.55),0 0 0 7px rgba(245,241,234,.14)}
.cmp-grip svg{width:17px;height:17px;color:var(--void)}
.cmp-tag{
  position:absolute;top:0;z-index:6;
  font-family:var(--disp);font-weight:700;font-stretch:112%;font-size:9.5px;
  letter-spacing:.22em;text-transform:uppercase;padding:7px 11px;
}
.cmp-tag-b{left:0;background:rgba(6,5,5,.72);color:var(--bone)}
.cmp-tag-a{right:0;background:rgba(245,241,234,.9);color:var(--void)}

/* ============================================================
   2. PROOF — heading top-left so the hero plate can hang into
   the empty top-right. Pairs at three different sizes, offset.
   ============================================================ */
/* the hairline is the point: the hero plate crosses it, so the two
   sections visibly interlock instead of merely sitting next to each other */
.proof{
  border-top:1px solid var(--rule);
  padding-top:clamp(30px,3.8vw,54px);padding-bottom:clamp(26px,2.8vw,42px);
}
.proof-head h2{margin-top:16px;font-size:clamp(30px,4.3vw,62px);max-width:20ch}

.proof-set{
  display:grid;gap:clamp(14px,2.2vw,34px);
  grid-template-columns:repeat(12,1fr);
  margin-top:clamp(20px,2.2vw,34px);
  align-items:start;
}
.pair figcaption{margin-top:13px;display:flex;justify-content:space-between;gap:14px;align-items:baseline}
.pair h3{font-family:var(--disp);font-weight:780;font-stretch:82%;font-size:clamp(15px,1.3vw,19px);letter-spacing:-.005em}
.pair p{font-size:12.5px;color:var(--bone-3);margin-top:4px}
.pair .hint{
  font-family:var(--disp);font-weight:620;font-stretch:112%;font-size:9px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--stone);white-space:nowrap;opacity:.75;
}
/* max-widths are the source widths plus a little. These photographs came off a
   phone via Facebook — 470px per half is all there is, so nothing is asked to
   stretch much beyond that. */
.pair-a{grid-column:1/7;grid-row:1;max-width:620px}
.pair-a .cmp{aspect-ratio:760/618}
.pair-b{grid-column:6/10;grid-row:1;margin-top:clamp(30px,5vw,96px);max-width:470px}
.pair-b .cmp{aspect-ratio:4/5}
/* The offsets are all downward. A negative margin-top on a card that sits
   directly under another one lands on its caption. */
.pair-c{grid-column:2/6;grid-row:2;margin-top:clamp(14px,2vw,34px);max-width:470px}
.pair-c .cmp{aspect-ratio:1/1}
.pair-d{grid-column:7/12;grid-row:2;margin-top:clamp(44px,6vw,112px);max-width:470px}
.pair-d .cmp{aspect-ratio:470/626}
.pair-e{grid-column:10/13;grid-row:1;margin-top:clamp(120px,15vw,250px);max-width:470px}
/* One column on a phone. The pairs were a sideways swipe row for a while, which
   saved scroll but meant the slider could only be dragged by its handle — and
   the handle sits inside .cmp-line, which is pointer-events:none, so on a real
   phone it could not be touched at all. Predictable beats clever. */
@media(max-width:900px){
  .proof-set{grid-template-columns:1fr;gap:clamp(22px,5vw,34px)}
  .pair-a,.pair-b,.pair-c,.pair-d,.pair-e{
    grid-column:1/-1;grid-row:auto;margin:0;max-width:none;
  }
  .pair-b .cmp,.pair-c .cmp,.pair-d .cmp,.pair-e .cmp{aspect-ratio:470/626}
}

/* --- dense strip: eight more photographs in ~300px of scroll.
   Static 50/50 splits, not sliders — at 160px a half, dragging is fiddly and
   the split reads instantly. The big pairs above stay draggable; this is the
   volume, and the hierarchy is deliberate. */
.strip{margin-top:clamp(22px,2.4vw,36px);padding-top:clamp(18px,2vw,28px);border-top:1px solid var(--rule)}
.strip-head{display:flex;flex-wrap:wrap;gap:14px 24px;align-items:center;justify-content:space-between}
.strip-head p{margin:0;font-size:13.5px;color:var(--bone-3)}
/* capped: the composite-decking crop is only 151px per half, and three columns
   of a full-width grid would ask it for 209 */
.strip-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(10px,1.6vw,26px);margin-top:clamp(16px,1.8vw,28px);
  max-width:1180px;
}
@media(max-width:820px){.strip-grid{grid-template-columns:repeat(2,1fr)}}
.mini-imgs{position:relative;display:grid;grid-template-columns:1fr 1fr;gap:2px;background:var(--pitch)}
/* height:auto matters — the height attribute is a presentational hint and beats
   aspect-ratio outright, so without it each image renders at its own natural
   height and the grid rows go ragged */
.mini-imgs img{width:100%;height:auto;aspect-ratio:5/7;object-fit:cover;filter:saturate(.93) contrast(1.03)}
.mini-imgs::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:.42;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
.mini figcaption{margin-top:9px}
.mini h3{font-family:var(--disp);font-weight:760;font-stretch:84%;font-size:clamp(13px,1.05vw,15.5px);letter-spacing:-.005em}
.mini p{font-size:11.5px;color:var(--bone-3);margin-top:3px}

/* height:auto — the width/height attributes are a presentational hint and would
   otherwise beat aspect-ratio outright */
/* the caption clears the clip's overhang instead of landing on the photograph */
/* NB: not .bar — that class belongs to the fixed call bar, which is display:none
   above 900px and would take this with it */
@media(max-width:900px){
}

/* ============================================================
   4. RUNNING ORDER — one screen carried entirely by type.
   No photograph anywhere in this section.
   ============================================================ */
.order{padding-block:clamp(28px,3vw,46px);background:var(--shale);border-block:1px solid var(--rule);overflow-x:clip}
.order-head{display:block}
.order-head h2{font-size:clamp(28px,3.6vw,52px);margin-top:16px}
.order-list{margin-top:clamp(20px,2.2vw,34px)}
.order-list{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,52px)}
.step{
  padding-top:clamp(18px,2vw,30px);border-top:1px solid var(--rule);border-bottom:0;
}
.step p{padding-top:10px}
.step-n{
  font-family:var(--disp);font-weight:880;font-stretch:64%;
  font-size:clamp(52px,8.6vw,138px);line-height:.8;letter-spacing:-.03em;
  color:transparent;-webkit-text-stroke:1px rgba(201,183,155,.5);
  margin-left:calc(var(--pad) * -1);   /* runs out past the text column, to the screen edge */
}
.step h3{font-size:clamp(24px,3.2vw,48px)}
.step p{color:var(--bone-2);font-size:clamp(14.5px,1.05vw,16.5px);line-height:1.62;padding-top:clamp(4px,.7vw,12px)}
@media(max-width:820px){
  .order-list{grid-template-columns:1fr;gap:0}
  .step{padding-block:clamp(16px,4vw,26px)}
  .step-n{font-size:clamp(38px,11vw,58px)}
}

/* ============================================================
   5. SERVICES — four rows, four different layouts.
   ============================================================ */
.svcs{padding-top:clamp(28px,3vw,46px);padding-bottom:clamp(22px,2.4vw,36px)}
.svcs-head h2{font-size:clamp(30px,4.6vw,68px);margin-top:14px}
.svc-n{
  font-family:var(--disp);font-weight:700;font-stretch:118%;font-size:10px;
  letter-spacing:.26em;text-transform:uppercase;color:var(--stone);display:block;
}
.svc h3{margin:14px 0 16px;font-size:clamp(26px,3vw,44px)}
.svc p{color:var(--bone-2);max-width:50ch}
/* The price is the second question a homeowner has, so it gets set like a
   price and not filed away in the spec list. */
.price{
  display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
  margin-top:clamp(20px,2.2vw,30px);padding-top:16px;border-top:1px solid var(--rule);
}
.price em{
  font-family:var(--disp);font-weight:620;font-stretch:118%;font-size:9.5px;font-style:normal;
  letter-spacing:.26em;text-transform:uppercase;color:var(--stone);
}
.price b{
  font-family:var(--disp);font-weight:860;font-stretch:70%;
  font-size:clamp(30px,3.4vw,48px);line-height:.9;letter-spacing:-.022em;color:var(--bone);
}
.price .tbc{
  font-family:var(--text);font-weight:500;font-size:15px;letter-spacing:0;color:var(--flag);
}
.price s{text-decoration:none;font-size:12.5px;color:var(--bone-3)}

.specs{
  margin-top:22px;padding-top:16px;border-top:1px solid var(--rule);
  display:grid;gap:8px;font-size:13.5px;color:var(--bone-3);
}
.specs b{color:var(--bone-2);font-weight:600}
/* when a price sits above them the specs do not need a second rule */
.price + .specs{border-top:0;padding-top:14px;margin-top:6px}
.plate{position:relative;overflow:hidden;background:var(--pitch)}
.plate img{width:100%;height:100%;object-fit:cover;filter:saturate(.93) contrast(1.03)}
.plate::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:.42;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* 01 — text left, big photograph running off the right edge */
.svc-1{
  display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(24px,4vw,64px);align-items:start;
  margin-top:clamp(20px,2.4vw,38px);
}
.svc-1 .plate{margin-right:calc(var(--pad) * -1 - 30px);aspect-ratio:1536/994}

/* 02 — narrow photograph left, text right, dropped down */
.svc-2{
  display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(24px,4vw,72px);align-items:start;
  padding-block:clamp(26px,3vw,52px);
}
.svc-2 .plate{aspect-ratio:560/420}
.svc-media{position:relative;margin:0}
/* 10vw bottoms out at 92px, which on a 390px phone made the waste-water clip
   a thumbnail — the single most persuasive thing on the page, shown at the
   size of a postage stamp and easy to read as "not playing". It gets real
   estate on a phone now and stays an inset on a desktop, where the plate it
   sits on is already large. */
.svc-vid{position:absolute;z-index:6;right:clamp(-24px,-1.6vw,-10px);bottom:clamp(-34px,-3.4vw,-22px);width:clamp(132px,10vw,142px)}
@media(max-width:760px){
  .svc-vid{width:min(168px,42vw);right:-6px;bottom:-26px}
}
.svc-vid video{
  width:100%;height:auto;aspect-ratio:576/1024;object-fit:cover;background:var(--void);
  border:1px solid rgba(245,241,234,.28);box-shadow:0 18px 44px -16px rgba(0,0,0,.95);
}
.svc-vid img{width:100%;height:auto;border:1px solid rgba(245,241,234,.28)}
.svc-media figcaption{margin-top:clamp(44px,3.6vw,56px);max-width:66%;font-size:11.5px;line-height:1.5;color:var(--bone-3)}
.svc-2 .svc-body{padding-top:clamp(0px,4vw,72px)}

/* 03 — no big photograph: the oven pair, live and draggable, at native size */
.svc-3{
  display:grid;grid-template-columns:minmax(0,1fr) clamp(240px,32vw,470px);
  gap:clamp(24px,4vw,64px);align-items:start;
  padding-bottom:clamp(28px,3.4vw,58px);
}

/* 04 — photograph right, copy left, and an outsized outline numeral riding
   across the edge of the photograph. Outline type over a picture is legible
   where body copy would not be. */
.svc-4{position:relative;padding-bottom:clamp(26px,3vw,50px)}
.svc-4 .plate{width:min(620px,100%);margin-left:auto;aspect-ratio:560/420}
.svc-4 .plate::before{
  content:'';position:absolute;inset:0;z-index:2;
  background:
    linear-gradient(90deg,rgba(6,5,5,.78) 0%,rgba(6,5,5,.3) 24%,rgba(6,5,5,0) 50%),
    linear-gradient(180deg,rgba(6,5,5,.3) 0%,rgba(6,5,5,0) 40%,rgba(6,5,5,.55) 100%);
}
.svc-4-n{
  position:absolute;z-index:3;pointer-events:none;
  left:clamp(90px,40vw,620px);top:clamp(6px,2.4vw,42px);
  font-family:var(--disp);font-weight:880;font-stretch:64%;
  font-size:clamp(112px,23vw,336px);line-height:.76;letter-spacing:-.045em;
  color:transparent;-webkit-text-stroke:1.5px rgba(240,235,225,.44);
}
.svc-4 .svc-body{
  position:relative;z-index:4;
  margin-left:clamp(0px,14vw,200px);
  max-width:min(1000px,100%);
  margin-top:clamp(-150px,-9vw,-88px);
}
/* specs beside the paragraph rather than under it — fills the row, and gives
   this one a different internal structure to 01–03 */
.svc-4-cols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.8fr);gap:clamp(20px,3vw,54px);align-items:start}
/* the specs sit alongside the paragraph rather than under it, with just enough
   top padding to clear the bottom edge of the photograph above */
/* This has to clear the photograph, and clamp(4px,.9vw,16px) never came close:
   the specs printed over the picture by 36px at 1280 rising to 66px from 1728
   up. Nobody saw it because the photograph itself was clipped to nothing until
   the reveal deadlock was fixed. Measured overlap plus a 16px gap. */
.svc-4-cols .specs{margin-top:0;padding-top:clamp(64px,5.7vw,98px);border-top:0}
@media(max-width:900px){
  .svc-1,.svc-2,.svc-3{grid-template-columns:1fr;gap:26px}
  .svc-1 .plate{margin-right:calc(var(--pad) * -1);margin-left:calc(var(--pad) * -1)}
  .svc-2 .svc-body{padding-top:0}
  .svc-2{padding-block:clamp(32px,7vw,54px)}
  .svc-3{padding-bottom:clamp(32px,7vw,54px)}
  .svc-3 .plate-wrap{max-width:246px}
  .svc-4 .plate{width:auto;margin-inline:calc(var(--pad) * -1);aspect-ratio:5/4}
  .svc-4 .plate::before{
    background:linear-gradient(180deg,rgba(6,5,5,.2) 0%,rgba(6,5,5,.5) 32%,rgba(6,5,5,.94) 60%,rgba(6,5,5,.98) 100%);
  }
  .svc-4 .svc-body{max-width:none;margin-left:0;margin-top:clamp(-116px,-19vw,-80px)}
  .svc-4-cols{grid-template-columns:1fr;gap:0}
  .svc-4-cols .specs{margin-top:6px;padding-top:14px;border-top:0}
  .svc-4-n{left:auto;right:clamp(-14px,-1vw,-4px);top:clamp(-16px,-2vw,-6px);font-size:clamp(104px,30vw,150px)}
}

/* ============================================================
   6. MARQUEE — bleeds off both edges.
   ============================================================ */

/* ============================================================
   6. GALLERY — a grid that takes photographs and video alike.
   Eight show; the rest sit behind one button so twenty pictures
   do not turn into five screens of scrolling.
   ============================================================ */
.gallery{padding-block:clamp(26px,2.8vw,42px);background:var(--pitch);border-bottom:1px solid var(--rule)}
.g-head{display:flex;flex-wrap:wrap;gap:8px 24px;align-items:baseline;justify-content:space-between}
.g-head p{font-size:13.5px;color:var(--bone-3)}
.g-grid{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(8px,1vw,14px);margin-top:clamp(16px,1.8vw,26px);
}
@media(max-width:900px){.g-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:560px){.g-grid{grid-template-columns:repeat(3,1fr);gap:6px}}
.g-item{margin:0}
.g-item[hidden]{display:none}
.g-open{
  display:block;width:100%;padding:0;border:0;background:var(--void);
  position:relative;overflow:hidden;cursor:pointer;
}
.g-open img{
  width:100%;height:auto;aspect-ratio:3/4;object-fit:cover;
  filter:saturate(.93) contrast(1.03);
  transition:transform .5s var(--ease),filter .4s var(--ease);
}
.g-open:hover img,.g-open:focus-visible img{transform:scale(1.04);filter:saturate(1) contrast(1.05)}
.g-play{
  position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;
  background:linear-gradient(180deg,rgba(6,5,5,.1),rgba(6,5,5,.45));
}
.g-play svg{
  width:44px;height:44px;color:var(--void);background:rgba(245,241,234,.94);
  border-radius:50%;padding:11px 9px 11px 13px;
}
.g-more{margin-top:clamp(14px,1.6vw,22px)}

/* --- lightbox ---------------------------------------------- */
.lb{
  position:fixed;inset:0;z-index:130;background:var(--pitch);
  display:grid;grid-template-rows:1fr auto;
  padding:clamp(52px,7vh,74px) clamp(12px,4vw,64px) clamp(14px,3vh,26px);
}
.lb[hidden]{display:none}
/* The stage is a 1fr grid row, and that height is indefinite as far as
   percentage resolution goes — so max-height:100% on the media resolved
   against nothing and never constrained anything. A 900x1200 photograph
   rendered at its full size inside a 775px stage and hung 363px below the
   viewport with no way to scroll to it; at 1440x800 it was 456px. An absolute
   containing block is definite, so the percentages finally bite, and
   margin:auto centres what is left. */
.lb-stage{position:relative;min-height:0}
.lb-stage img,.lb-stage video{
  position:absolute;inset:0;margin:auto;
  max-width:100%;max-height:100%;width:auto;height:auto;display:block;
  background:var(--void);
}
.lb-btn{
  position:absolute;z-index:2;width:48px;height:48px;padding:0;cursor:pointer;
  background:rgba(245,241,234,.08);border:1px solid var(--rule-2);border-radius:50%;
  color:var(--bone);display:grid;place-items:center;
  transition:background .2s var(--ease),border-color .2s var(--ease);
}
.lb-btn:hover{background:rgba(245,241,234,.16);border-color:var(--bone)}
.lb-btn svg{width:21px;height:21px}
.lb-close{top:clamp(10px,2vh,20px);right:clamp(10px,2vw,24px)}
.lb-prev{left:clamp(8px,1.6vw,22px);top:50%;transform:translateY(-50%)}
.lb-next{right:clamp(8px,1.6vw,22px);top:50%;transform:translateY(-50%)}
.lb-bar{
  display:flex;flex-wrap:wrap;gap:6px 20px;align-items:baseline;justify-content:space-between;
  padding-top:clamp(10px,1.6vh,16px);
}
.lb-cap{font-size:13.5px;color:var(--bone-2)}
.lb-count{
  font-family:var(--disp);font-weight:620;font-stretch:112%;font-size:10px;
  letter-spacing:.2em;color:var(--stone);white-space:nowrap;
}
@media(max-width:560px){
  .lb-prev,.lb-next{top:auto;bottom:clamp(56px,9vh,76px);transform:none}
}
body.lb-open{overflow:hidden}
/* the fixed call bar sits under the viewer and shows through at the caption */
body.lb-open .bar{display:none}

/* ============================================================
   7. AREAS — type-led list, asymmetric split.
   ============================================================ */
.areas{padding-block:clamp(28px,3vw,46px);background:var(--shale);border-block:1px solid var(--rule)}
.areas-in{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,.65fr);gap:clamp(30px,5vw,88px);align-items:start}
.areas h2{margin-top:20px;font-size:clamp(28px,3.4vw,50px)}
.towns{
  margin-top:clamp(24px,3vw,42px);
  font-family:var(--disp);font-weight:830;font-stretch:68%;
  font-size:clamp(30px,4.8vw,74px);line-height:1.02;letter-spacing:-.02em;
}
.towns span{color:var(--bone)}
.towns i{font-style:normal;color:var(--stone);opacity:.42;padding-inline:.12em}
.areas .lede{margin-top:clamp(22px,2.6vw,34px);max-width:52ch}
.card{border:1px solid var(--rule);background:var(--shale)}
.card-rows{padding:clamp(20px,2vw,28px)}
.row{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:12px 0;border-bottom:1px solid var(--rule);font-size:14px;min-height:46px;
}
.row a{display:inline-flex;align-items:center;min-height:44px}
.row:last-child{border-bottom:0}
.row b{font-weight:600;color:var(--bone-2);white-space:nowrap}
.row span{text-align:right;color:var(--bone)}
.card .btn{margin:0 clamp(20px,2vw,28px) clamp(20px,2vw,28px);width:calc(100% - clamp(40px,4vw,56px))}
@media(max-width:900px){.areas-in{grid-template-columns:1fr;gap:38px}}

/* reviews — deliberately empty */
.revs{padding-bottom:clamp(38px,4.6vw,70px)}
.revs-in{display:grid;grid-template-columns:minmax(0,.4fr) minmax(0,1fr);gap:clamp(20px,4vw,64px);align-items:start}
@media(max-width:820px){.revs-in{grid-template-columns:1fr;gap:18px}}

/* ============================================================
   8. CALL — the number, as big as it goes.
   ============================================================ */
.close{
  padding-block:clamp(34px,5vh,62px);text-align:center;
}
.close h2{font-size:clamp(34px,5.6vw,88px);margin-top:20px}
.close h2 em{font-style:normal;color:var(--stone)}
.close .lede{margin:22px auto 0;text-align:center}
.tel-big{
  display:block;padding-block:6px;margin-top:clamp(24px,3.4vw,44px);
  font-family:var(--disp);font-weight:900;font-stretch:63%;
  font-size:clamp(46px,12.4vw,178px);line-height:.86;letter-spacing:-.035em;
  transition:color .3s var(--ease);
}
.tel-big:hover{color:var(--stone)}
.close-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:clamp(28px,3.4vw,44px)}
@media(max-width:520px){.close-row .btn{flex:1 1 100%}}

/* --- footer ----------------------------------------------- */
footer{padding-top:clamp(24px,2.6vw,38px);background:var(--shale);border-top:1px solid var(--rule)}
.f-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr;gap:clamp(28px,4vw,60px)}
.f-grid p{margin-top:16px;color:var(--bone-3);font-size:14px;max-width:36ch}
.f-col h3{
  font-family:var(--disp);font-weight:700;font-stretch:118%;font-size:9.5px;
  letter-spacing:.26em;text-transform:uppercase;color:var(--bone-3);
}
.f-col ul{list-style:none;margin:18px 0 0;padding:0}
.f-col li{font-size:14px;color:var(--bone-2)}
/* footer links are the fallback contact route — give them a real target */
/* :not(.tbc) matters. "8am – 8pm, <span class="tbc">days to confirm</span>"
   puts the placeholder directly inside the li, so it was picking up the 46px
   tap-target box meant for the links — and its dashed underline, being a
   border-bottom, sank to the foot of that box and floated well clear of the
   words it is supposed to mark. A placeholder is not a tap target. */
.f-col li a,.f-col li>span:not(.tbc){display:inline-flex;align-items:center;min-height:46px;padding-block:2px}
.f-col a:hover{color:var(--stone)}
.f-bot{
  margin-top:clamp(16px,1.8vw,26px);border-top:1px solid var(--rule);
  padding:22px 0 calc(22px + env(safe-area-inset-bottom));
  display:flex;flex-wrap:wrap;gap:10px 24px;justify-content:space-between;
  font-size:12.5px;color:var(--bone-3);
}
@media(max-width:820px){
  .f-grid{grid-template-columns:1fr 1fr;gap:20px clamp(20px,4vw,40px)}
  .f-brand{grid-column:1/-1}
}

/* --- tablet tier -----------------------------------------
   Between 561 and 900px the layout has already collapsed to one column, which
   would otherwise blow a 470px photograph up to the full 900. Cap and centre. */
@media(min-width:561px) and (max-width:900px){
  /* pair-e joined the set later — without it the carpet pair was asking a
     470px file to fill 697px on a tablet */
  .pair-a,.pair-b,.pair-e{max-width:520px}
  .pair-b{margin-inline-start:auto;margin-inline-end:0}
  .pair-e{margin-inline-end:auto;margin-inline-start:0}
  .svc-1 .plate,.svc-4 .plate{margin-inline:0;max-width:640px;margin-left:auto}
  .svc-2 .plate{max-width:620px}
}

/* --- sticky call bar (phones) ----------------------------- */
.bar{
  position:fixed;left:0;right:0;bottom:0;z-index:95;display:none;gap:9px;
  padding:10px 12px calc(10px + env(safe-area-inset-bottom));
  background:rgba(6,5,5,.92);backdrop-filter:blur(16px);border-top:1px solid var(--rule);
  transform:translateY(105%);transition:transform .45s var(--ease);
}
.bar.on{transform:none}
.bar .btn{flex:1;padding:15px 10px;font-size:15px}
@media(max-width:900px){.bar{display:flex}body{padding-bottom:72px}}

/* --- reveal on entry: masked, per-element, varied ---------- */
.up{opacity:0;transform:translateY(var(--up-y,20px));transition:opacity .8s var(--ease),transform .8s var(--ease)}
.up.in{opacity:1;transform:none}
.wipe{clip-path:inset(0 100% 0 0);transition:clip-path 1.15s var(--ease)}
.wipe.in{clip-path:inset(0 0 0 0)}
.lines>*{opacity:0;transform:translateY(.32em);transition:opacity .7s var(--ease),transform .8s var(--ease)}
.lines.in>*{opacity:1;transform:none}
.draw{transform:scaleY(0);transition:transform 1s var(--ease)}
.draw.in{transform:scaleY(1)}

@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  html{scroll-behavior:auto}
  .up,.lines>*{opacity:1;transform:none}
  .wipe{clip-path:none}
  .draw{transform:none}
}

/* ============================================================
   9. SERVICE PAGES
   One page per service, sharing this stylesheet — which is why it
   was lifted out of index.html in the first place. Five copies of
   38KB of inline CSS is not a stylesheet, it is five stylesheets.

   These are deliberately quieter than the home page. That one gets
   the full-viewport photograph because it has to stop a stranger;
   a service page is already the answer to a question somebody
   typed, so it leads with what the job is and what it costs.
   ============================================================ */
.sub{position:relative;overflow-x:clip;overflow-y:visible;isolation:isolate}
/* Photo beside the type rather than behind it. Only hero-drive and svc-jetwash
   are wide enough to fill a 1440 hero without being enlarged; the carpet, oven
   and gutter shots are 560-900px and would have to be stretched to do the same
   job. Rather than give one service a full-bleed header and the other three a
   plainer one — which reads as three unfinished pages — every service page
   gets the photograph at a size its file can actually carry. The full-bleed
   hero stays unique to the home page, where it belongs. */
.sub-side{
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(24px,4vw,68px);align-items:center;
}
/* One shape on every service page. g-oven.jpg is 900x1200 — left to its own
   proportions it made the oven hero a ~1000px slab, and under 860px, where the
   photo moves above the type, it filled the whole first screen and pushed the
   H1, the price and both buttons below the fold. */
.sub-shot{position:relative;overflow:hidden;background:var(--pitch);max-width:620px;margin-left:auto;width:100%;aspect-ratio:4/3}
.sub-shot img{width:100%;height:100%;object-fit:cover;display:block;filter:saturate(.93) contrast(1.03)}
.sub-shot::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:.42;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
@media(max-width:860px){
  .sub-side{grid-template-columns:1fr;gap:26px}
  /* Photograph below the type, not above it. Ordering it first pushed the
     headline, the price and both buttons off the bottom of the screen — at
     860x900 the call button landed 167px below the fold. The brief is that
     the number should be in front of somebody, not something they scroll for. */
  .sub-shot{max-width:none;margin-left:0;aspect-ratio:16/10}
}
.sub-bg{position:absolute;inset:0;z-index:0}
.sub-bg img{width:100%;height:100%;object-fit:cover;filter:saturate(.9) contrast(1.03)}
.sub-bg::after{
  content:'';position:absolute;inset:0;
  background:
    linear-gradient(90deg,rgba(11,10,9,.94) 0%,rgba(11,10,9,.72) 46%,rgba(11,10,9,.3) 100%),
    linear-gradient(180deg,rgba(11,10,9,.86) 0%,rgba(11,10,9,.18) 40%,rgba(11,10,9,.92) 100%);
}
.sub-in{
  position:relative;z-index:2;
  padding-top:clamp(104px,15vh,168px);padding-bottom:clamp(34px,5vh,64px);
}
.sub h1{font-size:clamp(34px,5.6vw,86px);margin-top:12px;max-width:16ch}
.sub-lede{margin-top:18px;max-width:52ch;font-size:clamp(15px,1.15vw,18.5px);color:var(--bone-2)}
.sub-cta{display:flex;flex-wrap:wrap;gap:11px;margin-top:clamp(20px,2.6vw,32px)}
.sub-from{
  margin-top:18px;font-size:13.5px;color:var(--bone-3);
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
}
.sub-from b{font-family:var(--disp);font-weight:870;font-stretch:70%;font-size:27px;color:var(--bone)}
/* An unconfirmed price must not be dressed as a confirmed one. .sub-from b set
   the price cream, which outranked .tbc on specificity, so the one from-price
   nobody has set — gutters — rendered exactly like the three that are settled.
   The dashed underline was there; the colour that carries the meaning was not. */
.sub-from b.tbc{color:var(--flag)}

/* breadcrumb doubles as the way back to the home page on a phone,
   where the logo is the only other route */
.crumb{font-size:12.5px;color:var(--bone-3);display:flex;gap:8px;align-items:center}
.crumb a{color:var(--bone-2);border-bottom:1px solid var(--rule-2);padding-bottom:1px;position:relative}
/* The visible link is 35x22 — the right size for a breadcrumb and the wrong
   size for a thumb. This is the only route back to the home page on a phone
   other than the logo, so the hit area is pushed out to 44px without touching
   the underline or the type. */
.crumb a::after{
  content:'';position:absolute;left:-6px;right:-6px;top:50%;
  transform:translateY(-50%);height:44px;
}
.crumb a:hover{color:var(--bone);border-bottom-color:var(--bone)}
.crumb span{opacity:.5}

/* --- generic band, alternating so nothing reads as one long slab --- */
.blk{padding-block:clamp(38px,5.4vw,84px)}
.blk-alt{background:var(--shale);border-block:1px solid var(--rule)}
.blk h2{font-size:clamp(26px,3.4vw,50px);margin-top:14px}
.blk-lede{margin-top:16px;max-width:62ch;color:var(--bone-2)}
.duo{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));gap:clamp(18px,2.6vw,40px);margin-top:clamp(22px,3vw,44px)}
.duo .pair{margin:0}

/* --- what the job actually involves --- */
.does{margin-top:clamp(22px,3vw,40px);display:grid;gap:0}
.does li{
  list-style:none;display:grid;grid-template-columns:clamp(38px,4vw,64px) 1fr;
  gap:clamp(12px,1.6vw,24px);align-items:baseline;
  padding-block:clamp(14px,1.6vw,22px);border-top:1px solid var(--rule);
}
.does li:last-child{border-bottom:1px solid var(--rule)}
/* Solid, not outlined. An outline needs display sizes to read — at 20px the
   1px stroke closes up the counters and the numeral turns to a smudge. The
   big 04 watermark on the home page can carry it; this cannot. */
.does .n{
  font-family:var(--disp);font-weight:870;font-stretch:64%;font-size:clamp(15px,1.5vw,20px);
  color:var(--stone);
}
.does h3{font-size:clamp(16px,1.35vw,20px);font-family:var(--text);font-weight:600;margin-bottom:5px}
.does p{color:var(--bone-2);font-size:14.5px;max-width:58ch}

/* --- FAQ ------------------------------------------------------
   <details> rather than JS: it works with the keyboard, it works
   with the page printed, and it works before app.js has loaded.
   An answer that is still a placeholder stays out of the FAQPage
   markup entirely — see the note in each page's JSON-LD. */
.faq{margin-top:clamp(22px,3vw,40px);border-top:1px solid var(--rule)}
.faq details{border-bottom:1px solid var(--rule)}
.faq summary{
  list-style:none;cursor:pointer;display:flex;gap:16px;align-items:baseline;
  padding-block:clamp(15px,1.7vw,22px);
  font-family:var(--text);font-weight:600;font-size:clamp(15.5px,1.3vw,19px);
  min-height:44px;
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:'+';margin-left:auto;flex:none;
  font-family:var(--disp);font-weight:700;font-size:21px;line-height:1;color:var(--stone);
  transition:transform .3s var(--ease);
}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq summary:hover{color:var(--bone)}
.faq-a{padding-bottom:clamp(16px,2vw,26px);max-width:64ch;color:var(--bone-2);font-size:15px}
.faq-a p + p{margin-top:11px}

/* --- the other three services, at the foot of each page --- */
.rel{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(240px,100%),1fr));gap:1px;background:var(--rule);margin-top:clamp(22px,3vw,40px);border:1px solid var(--rule)}
.rel a{background:var(--void);padding:clamp(18px,2vw,28px);display:block;transition:background .3s var(--ease)}
.rel a:hover{background:var(--shale)}
.rel b{display:block;font-family:var(--disp);font-weight:800;font-stretch:74%;font-size:clamp(17px,1.5vw,22px)}
.rel span{display:block;margin-top:6px;font-size:13px;color:var(--bone-3)}

@media(max-width:760px){
  .sub h1{max-width:none}
  .sub-cta .btn{flex:1 1 100%}
}

/* .pair-a..e carry grid placement for the home page's overlapping collage, so
   a service page must not borrow them — it wants a plain two-up. Aspect comes
   from the file's own proportions so nothing is stretched or letterboxed. */
/* One aspect for every slider in a two-up, whatever the source file is.
   Pairing a landscape driveway with a portrait staircase left one column far
   taller than the other and the two captions on different lines, which reads
   as a mistake rather than a layout. .cmp img is object-fit:cover, so a square
   crops both honestly. */
.duo .cmp{aspect-ratio:1/1}
/* one slider and no second photograph: keep it to a column rather than letting
   a 1:1 slider stretch the full width of the shell */
.duo-1{grid-template-columns:minmax(0,min(520px,100%))}

/* a still photograph sitting in a two-up next to a slider */
.duo .plate{aspect-ratio:1/1}

/* the way through from a service block on the home page to its own page */
.more{margin-top:18px}
.more a{
  font-family:var(--disp);font-weight:700;font-stretch:92%;font-size:14px;
  letter-spacing:.005em;color:var(--bone);
  border-bottom:1px solid var(--rule-2);padding-bottom:3px;
  display:inline-flex;align-items:center;gap:9px;min-height:44px;
}
.more a:hover{border-bottom-color:var(--green);color:var(--green)}
.more span{transition:transform .3s var(--ease)}
.more a:hover span{transform:translateX(4px)}
