/* ============================================================================
   Premier Plumbing
   Brand: black + copper (Grant's brief). Editorial-trade layout, warm bone
   counterweight, copper reserved for accents so it stays valuable.
   ========================================================================== */

:root{
  --ink:      #0A1730;
  --ink-2:    #0F2043;
  --ink-3:    #182C56;
  --bone:     #F4F6FA;
  --bone-2:   #DFE5EF;
  --copper:   #D2112B;
  --copper-l: #EE3F52;
  --copper-d: #9C0A1E;

  --on-dark:      #F4F6FA;
  --on-dark-mute: rgba(241,236,228,.60);
  --on-bone:      #0A1730;
  --on-bone-mute: rgba(16,18,21,.62);

  /* Display face. Archivo descends from American gothic signage — sturdy, wide,
     high legibility at size, and it reads as fabricated rather than published,
     which is the point for a heating engineer. Variable, so the same file gives
     the hero its weight and the small headings their restraint.
     (The previous editorial serif is kept at the bottom of this file, commented,
     if that direction is ever wanted back.) */
  --display: "Archivo", "Arial Narrow", Arial, sans-serif;
  --sans:  "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* The van face. Grant's van is signwritten in a heavy Clarendon-style slab
     serif, leaned right — the classic signwriter's Egyptian. Bevan Italic is
     the closest free web match, so the hero speaks in his own lettering rather
     than in a typeface we picked for him. It is ONE weight and has no width
     axis, so it is deliberately not --display: it is used for the hero
     headline, the section wordmarks and the logo lockup (rendered in the same
     face by scripts/prep.js) and nothing else. Everything that needs a weight
     range or has to stay readable at paragraph size stays on Archivo. */
  --van: "Bevan", "Bookman Old Style", Georgia, serif;

  --gut:  clamp(22px, 4vw, 56px);
  --maxw: 1360px;
  --pv:   clamp(72px, 10vw, 148px);

  --ease:  cubic-bezier(.25,1,.5,1);
  --ease-io: cubic-bezier(.4,0,.2,1);
  --hdr-h: 96px;
}

/* The editorial direction this mockup started from, kept for reference only.
   Larken was the display face and is COMMERCIAL, so it was never shipped; the
   free stand-in was Fraunces. To go back, restore --display to
   "Larken","Fraunces","Times New Roman",serif, put the licensed
   Larken-Bold.woff2 in assets/fonts/, uncomment the block below, and restore the
   Fraunces link in the page head along with the SOFT/WONK/opsz settings on the
   heading rule. Left commented so the mockup does not 404 on every load.

@font-face{
  font-family:"Larken";
  src:url("../assets/fonts/Larken-Bold.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
}
*/

*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
body{
  margin:0;
  background:var(--ink);
  color:var(--on-dark);
  font-family:var(--sans);
  font-size:17px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
body.lock{ overflow:hidden; }
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
h1,h2,h3,h4{
  margin:0; font-family:var(--display); font-weight:800; line-height:1.06;
  /* pushed wide on the width axis — signage proportions, not text proportions */
  font-variation-settings:"wdth" 112;
  letter-spacing:-.005em;
}
p{ margin:0; }
ul{ margin:0; padding:0; list-style:none; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }

.wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gut); }
.sr{ position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }
.skip{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--copper); color:#fff; padding:12px 20px; border-radius:0 0 6px 0;
}
.skip:focus{ left:0; }
:focus-visible{ outline:2px solid var(--copper-l); outline-offset:3px; }

.kicker{
  font-size:13px; font-weight:600; letter-spacing:.16em; text-transform:uppercase;
  color:var(--copper-l); margin:0 0 22px;
}
.lbl{
  font-size:11px; font-weight:700; letter-spacing:.2em; text-transform:uppercase;
  color:var(--on-dark-mute); margin:0 0 18px;
}

/* ── buttons ─────────────────────────────────────────────────────────────── */
.btn{
  --bg:transparent; --fg:var(--on-dark); --bd:rgba(241,236,228,.34);
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  padding:17px 38px; border:1px solid var(--bd); border-radius:60px;
  background:var(--bg); color:var(--fg);
  font-size:15px; font-weight:600; letter-spacing:.01em;
  overflow:hidden; isolation:isolate;
  transition:color .4s var(--ease), border-color .4s var(--ease);
}
.btn::after{
  content:""; position:absolute; inset:0; z-index:-1; border-radius:inherit;
  background:linear-gradient(120deg,var(--copper-l),var(--copper));
  transform:translateY(101%); transition:transform .5s var(--ease);
}
.btn:hover{ color:#14100C; border-color:transparent; }
.btn:hover::after{ transform:translateY(0); }
/* Same fault the mobile call bar was fixed for, and for the same reason: the
   template's near-black on copper was tuned for SGS's light copper. On this
   red, #14100C runs 4.95:1 at the light end of the gradient but only 3.51:1 at
   the dark end, so the site's primary CTA — the enquiry submit — was failing AA
   across half of itself. White is no better on --copper-l (3.89:1), so the
   gradient is re-anchored to copper -> copper-d, exactly like the call bar:
   5.5:1 rising to 8.5:1, white throughout. */
.btn--copper{ background:linear-gradient(120deg,var(--copper),var(--copper-d)); border-color:transparent; color:#fff; }
.btn--copper::after{ background:var(--bone); }
.btn--ghost{ --bd:rgba(241,236,228,.3); }

.lnk{
  display:inline-flex; align-items:center; gap:10px;
  font-size:15px; font-weight:600; color:var(--on-dark-mute);
  transition:color .3s var(--ease);
}
.lnk svg{ width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:1.7; transition:transform .35s var(--ease); }
.lnk:hover{ color:var(--copper-l); }
.lnk:hover svg{ transform:translateX(6px); }

/* ── header ──────────────────────────────────────────────────────────────── */
.hdr{
  /* above the menu overlay: the bar stays visible while the menu is open, and
     the burger's own z-index cannot escape this stacking context otherwise */
  position:fixed; inset:0 0 auto; z-index:105;
  transition:background .45s var(--ease), backdrop-filter .45s var(--ease), transform .45s var(--ease);
}
.hdr.is-stuck{ background:rgba(10,23,48,.86); backdrop-filter:blur(14px); }
.hdr.is-hidden{ transform:translateY(-100%); }
.hdr__in{
  max-width:var(--maxw); margin-inline:auto; padding:20px var(--gut);
  display:flex; align-items:center; justify-content:space-between; gap:20px;
}
.hdr__logo img{ width:clamp(120px,13vw,168px); height:auto; }
.hdr__tel{
  display:inline-flex; align-items:center; gap:10px;
  font-size:17px; font-weight:700; letter-spacing:.01em;
  /* the hero is light edge to edge, so the bar starts dark and flips once the
     header picks up its own dark backing on scroll */
  color:var(--ink);
  transition:color .3s var(--ease);
}
.hdr.is-stuck .hdr__tel{ color:var(--on-dark); }
.hdr__tel svg{ width:20px; height:20px; fill:none; stroke:currentColor; stroke-width:1.8; transition:transform .35s var(--ease); }
.hdr__tel:hover{ color:var(--copper-l); }
.hdr__tel:hover svg{ transform:translateX(5px); }

/* pill hamburger, centred — the reference's signature control */
.burger{
  position:fixed; left:50%; top:20px; transform:translateX(-50%); z-index:110;
  width:140px; height:56px; border-radius:60px;
  background:var(--bone); color:var(--ink);
  display:grid; place-items:center;
  box-shadow:0 8px 30px rgba(0,0,0,.28);
  transition:background .35s var(--ease), width .4s var(--ease);
}
.burger:hover{ background:#fff; }
/* the pill collapses to a circle once it becomes a close button */
.burger[aria-expanded="true"]{ width:56px; }
.burger__box{ position:relative; display:block; width:34px; height:12px; }
.burger__box i{
  position:absolute; left:0; width:100%; height:2px; background:currentColor; border-radius:2px;
  transition:transform .4s var(--ease), opacity .25s var(--ease);
}
.burger__box i:nth-child(1){ top:0; }
.burger__box i:nth-child(2){ bottom:0; }
.burger[aria-expanded="true"] .burger__box i:nth-child(1){ transform:translateY(5px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__box i:nth-child(2){ transform:translateY(-5px) rotate(-45deg); }

/* ── menu overlay ────────────────────────────────────────────────────────── */
.menu{
  position:fixed; inset:0; z-index:100;
  background:var(--ink); color:var(--on-dark);
  padding:calc(var(--hdr-h) + clamp(30px,6vw,80px)) 0 60px;
  overflow-y:auto;
  clip-path:inset(0 0 100% 0);
  transition:clip-path .7s var(--ease);
}
.menu[hidden]{ display:none; }
.menu.is-open{ clip-path:inset(0 0 0 0); }
.menu__grid{
  max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gut);
  display:grid; grid-template-columns:1.15fr .85fr .7fr .5fr;
  column-gap:clamp(24px,4vw,60px); row-gap:clamp(40px,7vw,110px);
  align-items:start;
}

/* primary links — tight uppercase serif stack, no rules, no numbers */
.menu__links{ grid-column:1; grid-row:1 / span 2; }
.menu__links a{
  display:block; line-height:.98;
  font-family:var(--display); font-weight:700; text-transform:uppercase;
  font-size:clamp(38px,5.6vw,72px); letter-spacing:-.005em;
  opacity:0; transform:translateY(26px);
  transition:color .3s var(--ease), transform .6s var(--ease), opacity .6s var(--ease);
}
.menu.is-open .menu__links a{ opacity:1; transform:none; transition-delay:calc(.16s + var(--i,0) * .055s); }
.menu__links a:hover{ color:var(--copper-l); }

/* secondary link columns */
.menu__col{ grid-row:1; }
.menu__h{
  font-size:clamp(15px,1.3vw,19px); font-weight:400; text-transform:uppercase;
  letter-spacing:.02em; color:var(--copper-l); margin-bottom:26px;
}
.menu__col ul li{ margin-bottom:12px; }
.menu__col a{ font-size:clamp(15px,1.25vw,19px); color:var(--on-dark); transition:color .3s var(--ease); }
.menu__col a:hover{ color:var(--copper-l); }

/* contact block sits under the first link column, as on the reference */
.menu__contact{ grid-column:2; grid-row:2; display:grid; gap:12px; align-content:start; }
.menu__contact a,.menu__contact span{ font-size:clamp(15px,1.25vw,19px); color:var(--on-dark); }
.menu__contact a{ transition:color .3s var(--ease); }
.menu__contact a:hover{ color:var(--copper-l); }
.menu__contact span{ color:var(--on-dark-mute); }

.menu__col,.menu__contact{
  opacity:0; transform:translateY(18px);
  transition:opacity .6s var(--ease) .4s, transform .6s var(--ease) .4s;
}
.menu.is-open .menu__col,.menu.is-open .menu__contact{ opacity:1; transform:none; }

/* header sits on the dark overlay while it is open */
body.lock .hdr__tel{ color:var(--on-dark); }

/* ── hero ────────────────────────────────────────────────────────────────── */
.hero{
  position:relative; min-height:100svh;
  display:grid; grid-template-columns:1fr 1fr; align-items:center;
  background:var(--bone); color:var(--on-bone);
  overflow:hidden;
}
.hero__copy{
  padding:calc(var(--hdr-h) + 40px) clamp(30px,4vw,70px) 90px var(--gut);
  max-width:760px; margin-left:auto; width:100%;
}
.hero__copy .kicker{ color:var(--copper-d); }
/* The van face, and sized down from the Archivo setting it replaces: Bevan
   runs a much larger cap height per em and carries far more ink, so the old
   104px cap put "PLUMBING" through the right-hand edge of the column. The
   letter-spacing goes positive because signwriting is spaced open — pulled
   tight, the slabs of adjacent letters touch and it turns into a hedge. */
.hero__h1{
  font-family:var(--van); font-style:italic; font-weight:400;
  font-variation-settings:normal;
  font-size:clamp(38px,5.2vw,82px);
  text-transform:uppercase; letter-spacing:.005em; line-height:1.0;
  margin-bottom:30px;
}
.hero__lead{ font-size:clamp(16px,1.25vw,19px); color:var(--on-bone-mute); max-width:32em; margin-bottom:42px; }
.hero__cta{ display:flex; align-items:center; gap:26px; flex-wrap:wrap; }
.hero__cta .btn{ --fg:var(--on-bone); --bd:rgba(16,18,21,.28); }
.hero__cta .lnk{ color:var(--on-bone-mute); }

.hero__media{ position:relative; align-self:stretch; overflow:hidden; background:var(--bone); }
/* -22%, not the template's -6%. At -6% the photo covered the panel with only
   60px of vertical slack and none at all horizontally, so object-position had
   almost nothing to move and the crop was whatever the aspect ratios decided.
   Overhanging the panel properly makes BOTH axes live, which is what lets the
   crop below pick a region rather than describe one. It also gives the parallax
   more to travel through. */
.hero__photo{ position:absolute; inset:-22% 0; }
.hero__photo img{
  width:100%; height:100%; object-fit:cover;
  /* The letterforms are narrow windows onto this shot against a LIGHT panel, so
     what shows through them has to stay dark.
     This photo (the blue-lit ensuite, Grant's pick) is a BRIGHTER shot than the
     slate wet room it replaced — luminance grid mean 119 against 89 — because
     of all the white marble. Two things make it work anyway:
       · the crop centres the one genuinely dark region, the black vanity and
         the LED mirror above it, which the grid puts at x=50-80%, y=60-80%
         (cells as low as 23 against 190 for the marble top-left);
       · brightness is pulled to .62 rather than .86, which lands the same
         ~75 mean the old hero had. Darkening also deepens the blue LED
         instead of washing it, so the glow is what reads inside the letters.
     If this is swapped again, grid the replacement — do not eyeball it. */
     Note the filter INVERTS the template's contrast boost. Mean luminance was
     never the problem here — at brightness(.62) the letterforms measured a mean
     of 78, the same as the old hero. The problem was the tail: 7% of the
     letterform pixels sat above 190, near enough bone that those parts of
     "PLUMBING" vanished into the panel and the word read as half-there.
     contrast(.82) BEFORE brightness pulls the highlights down without touching
     the blacks — white marble at 244 lands near 134, split-face stone at 30
     stays at 29 — so the knockout reads evenly across its whole width. */
  object-position:64% 72%;
  filter:contrast(.82) saturate(1.3) brightness(.6);
  animation:photoSettle 1.8s var(--ease) both;
}
@keyframes photoSettle{ from{ transform:scale(1.42); } to{ transform:scale(1.3); } }

/* Bone panel with the client lockup knocked out of it — the photo behind is
   visible only through the letterforms and the lion, everything else is
   whitespace. Two mask layers composited: the full panel MINUS the lockup.
   NOTE: the panel is LIGHT, so the photo behind must be a dark one or the
   knockout has nothing to read against. See .hero__photo img below. */
.hero__cut{
  position:absolute; inset:0; z-index:2;
  background:var(--bone);

  -webkit-mask-image:url(../assets/monogram-mask.png), linear-gradient(#000,#000);
          mask-image:url(../assets/monogram-mask.png), linear-gradient(#000,#000);
  -webkit-mask-size:96% auto, 100% 100%;
          mask-size:96% auto, 100% 100%;
  -webkit-mask-position:center, center;
          mask-position:center, center;
  -webkit-mask-repeat:no-repeat, no-repeat;
          mask-repeat:no-repeat, no-repeat;
  -webkit-mask-composite:xor;      /* older WebKit spelling */
          mask-composite:exclude;
}

.hero__scroll{
  position:absolute; left:var(--gut); bottom:34px; z-index:3;
  display:flex; align-items:center; gap:14px;
  font-size:11px; font-weight:700; letter-spacing:.22em; text-transform:uppercase;
  color:var(--on-bone-mute);
}
.hero__scroll i{ display:block; width:64px; height:1px; background:currentColor; opacity:.5; position:relative; overflow:hidden; }
.hero__scroll i::after{
  content:""; position:absolute; inset:0; background:var(--copper);
  animation:scrollLine 2.4s var(--ease-io) infinite;
}
@keyframes scrollLine{ 0%{ transform:translateX(-100%);} 55%,100%{ transform:translateX(100%);} }

/* ── intro ───────────────────────────────────────────────────────────────── */
.intro{ background:var(--ink); padding:var(--pv) 0; }
.intro__in{ display:grid; grid-template-columns:200px 1fr; gap:40px; align-items:start; }
.vlbl{
  writing-mode:vertical-rl; transform:rotate(180deg);
  font-size:12px; font-weight:700; letter-spacing:.26em; text-transform:uppercase;
  color:var(--copper-l); justify-self:end; margin:0;
}
/* Weight and width do the work the serif used to: at 400 on the default width
   axis this large statement was landing indistinguishable from Satoshi body. */
.intro__txt{
  font-family:var(--display); font-size:clamp(23px,2.5vw,40px);
  font-weight:500; font-variation-settings:"wdth" 108;
  line-height:1.32; letter-spacing:-.012em; max-width:19em;
}

/* ── teaser cards ────────────────────────────────────────────────────────── */
.teaser{ background:var(--ink); padding:0 0 var(--pv); }
.teaser__grid{ display:grid; grid-template-columns:1.35fr 1fr; gap:clamp(24px,4vw,70px); align-items:end; }
.card__media{ display:block; overflow:hidden; background:var(--ink-3); }
.card__media img{ width:100%; height:100%; object-fit:cover; transition:transform 1s var(--ease); }
.card--tall .card__media{ aspect-ratio:4/3; }
.card:not(.card--tall) .card__media{ aspect-ratio:3/2; }
.card:hover .card__media img{ transform:scale(1.055); }
.card__t{ font-family:var(--sans); font-size:19px; font-weight:600; margin:22px 0 4px; }
.card__t a{ display:inline-flex; align-items:center; gap:10px; }
.card__t svg{ width:19px; height:19px; fill:none; stroke:var(--copper-l); stroke-width:1.8; transition:transform .35s var(--ease); }
.card:hover .card__t svg{ transform:translateX(6px); }
.card__s{ color:var(--on-dark-mute); font-size:16px; }

/* ── giant wordmark ──────────────────────────────────────────────────────── */
.giant{
  margin:0; text-align:center; line-height:.82;
  font-size:clamp(70px,19vw,272px); text-transform:uppercase; letter-spacing:-.02em;
  color:var(--bone);
  user-select:none; pointer-events:none;
  padding:clamp(20px,4vw,60px) 0;
}
.giant span{ display:block; will-change:transform; }
.giant--out{
  color:transparent;
  -webkit-text-stroke:1px rgba(241,236,228,.5);
}

/* ── work ────────────────────────────────────────────────────────────────── */
/* overflow:clip, NOT hidden — hidden would make this a scrollport and kill the
   position:sticky the panels below depend on. clip still contains the drifting
   wordmark without creating one. */
.work{ background:var(--ink); padding:0 0 var(--pv); overflow:clip; }

/* ── work: stacking panels ───────────────────────────────────────────────── */
/* Each item pins at the top of the viewport and the following one scrolls up
   over it. The one being covered shrinks a little and dims, which is what
   reads as depth — without it the panels just slide and look flat.
   --stack-p is 0..1 cover progress, written per item by main.js. */
:root{ --job-gap: clamp(14px,2.6vw,40px); }

.stack__item{ position:sticky; top:0; pointer-events:none; }

/* The wordmark sits behind the panels; once the first one arrives it would
   otherwise show through the inset frame around them. */
.stack__item--title{
  top:clamp(56px,8vw,120px); padding-bottom:clamp(28px,4vw,60px);
  transition:opacity .45s var(--ease);
}
.stack__item--title.is-covered{ opacity:0; }

.job{ height:100lvh; padding:var(--job-gap); }
.stack__item + .stack__item .job{ margin-top:calc(-1 * var(--job-gap)); }
.stack__item:last-child .job{ margin-bottom:calc(-1 * var(--job-gap)); }

.job__in{
  height:100%;
  position:relative; overflow:hidden; background:var(--ink-3);
  display:grid; grid-template-areas:"job"; grid-template-columns:100%; grid-template-rows:100%;
}
/* The dimming veil. Sits above the caption too, so a covered card goes down as
   one piece rather than leaving its title floating bright on top. */
.job__in::before{
  content:""; position:absolute; inset:0; z-index:4; pointer-events:none;
  background:rgba(0,0,0,.5); opacity:0;
}
.job__img,.job__cap{ grid-area:job; }
.job__img{ position:relative; height:100%; margin:0; }
.job__img img{ width:100%; height:100%; display:block; }

/* Grant's photos are portrait phone shots on a landscape card, so the two ways
   to fill it are both bad: cover crops most of the picture away (that is the
   "zoomed in" look), and contain leaves dead bars either side of a full-bleed
   card. So the card is filled twice — a blurred, darkened copy of the same
   shot underneath to carry the full width, and the real photo whole and sharp
   on top of it. Same file both times, so it costs one download, and the sides
   always tone with the photo they belong to. */
.job__fill{
  position:absolute; inset:0; object-fit:cover;
  filter:blur(34px) brightness(.42) saturate(.75);
  /* scaled up so the blur's soft edge never reaches the card's own edge */
  transform:scale(1.15);
}
.job__shot{ position:relative; z-index:1; object-fit:contain; }
/* The clip is portrait phone footage, so it gets the same treatment as the
   photos: contained whole, blurred poster carrying the width behind it. */
.job__video{
  position:relative; z-index:1;
  width:100%; height:100%; object-fit:contain; display:block;
}

/* Wash so the bone type holds up over a bright photo. Deeper across the top
   band where the caption sits — the flat 20% was fine under the big title but
   left the copper sub-line sinking into white boiler casing and light tile —
   then settling to a constant 20% over the rest of the shot. */
.job__img::before{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(180deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,.2) 34%, rgba(0,0,0,.2) 100%);
}
.job__cap{ position:relative; z-index:3; align-self:start; padding:clamp(24px,4.6vw,80px); }
.job__cap h3{ font-size:clamp(30px,4.6vw,72px); text-transform:uppercase; color:var(--bone); }
.job__cap p{
  font-size:12px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--copper-l); margin-top:8px; font-weight:600;
}

/* Mouse only — on touch the panels still stack, they just do not scale.
   The [style] guard means these only bite once main.js has written --stack-p,
   so reduced-motion (where it never does) falls back to plain panels. */
@media (hover:hover) and (pointer:fine){
  .stack__item:not(:last-child)[style] .job{ transform:scale(max(1 - var(--stack-p) * .15, 0)); }
  .stack__item:not(:last-child)[style] .job__in::before{ opacity:var(--stack-p); }
}

/* A full-height panel on a phone is a lot of scrolling for three images, and
   portrait photos crop badly into it — square keeps the stack but shortens it. */
@media (max-width:700px){
  .job{ height:auto; aspect-ratio:1/1; }
}

.work__cta{ text-align:center; margin-top:clamp(40px,6vw,80px); }

/* ── services ────────────────────────────────────────────────────────────── */
.svc{ background:var(--bone); color:var(--on-bone); padding:var(--pv) 0; overflow:hidden; }
.svc__h{ font-size:clamp(30px,4.9vw,92px); font-weight:400; line-height:1.1; text-align:center; margin-bottom:clamp(44px,6vw,84px); }

.car{ position:relative; padding-left:var(--gut); }
.car__track{ display:flex; gap:clamp(20px,2.6vw,40px); will-change:transform; transition:transform .75s var(--ease); }
.car.is-dragging .car__track{ transition:none; }
.svc__item{ flex:0 0 clamp(260px,29vw,420px); }
.svc__media{ overflow:hidden; aspect-ratio:4/3; background:var(--bone-2); margin-bottom:24px; }
.svc__media img{ width:100%; height:100%; object-fit:cover; transition:transform 1s var(--ease); }
.svc__item:hover .svc__media img{ transform:scale(1.055); }
.svc__item h3{
  font-family:var(--sans); font-size:19px; font-weight:600;
  display:flex; align-items:center; gap:10px; margin-bottom:10px;
}
.svc__item h3 svg{ width:19px; height:19px; fill:none; stroke:var(--copper); stroke-width:1.8; transition:transform .35s var(--ease); }
.svc__item:hover h3 svg{ transform:translateX(6px); }
.svc__item p{ color:var(--on-bone-mute); font-size:16px; }

/* Arrows sit level with the top of the cards and float over the peeking next
   card — same read as the reference, without riding up into the heading. */
.car__nav{ position:absolute; right:var(--gut); top:6px; z-index:3; display:flex; flex-direction:column; gap:14px; }
.car__btn{
  width:70px; height:70px; border-radius:50%;
  border:1px solid rgba(16,18,21,.24);
  /* the arrows float over the peeking card, so they carry their own backing
     rather than relying on whatever photo happens to sit behind them */
  background:rgba(241,236,228,.88); backdrop-filter:blur(6px);
  display:grid; place-items:center;
  transition:background .35s var(--ease), border-color .35s var(--ease), color .35s var(--ease), transform .35s var(--ease);
}
.car__btn svg{ width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:1.6; }
.car__btn:hover{ background:var(--ink); border-color:var(--ink); color:var(--bone); }
.car__btn[disabled]{ opacity:.28; pointer-events:none; }

.car__bar{ height:2px; background:rgba(16,18,21,.14); margin:clamp(36px,5vw,64px) var(--gut) 0 0; }
.car__bar i{ display:block; height:100%; width:20%; background:var(--copper); transition:width .5s var(--ease), transform .5s var(--ease); }

/* ── figures ─────────────────────────────────────────────────────────────── */
.figs{ background:var(--bone); color:var(--on-bone); padding:0 0 var(--pv); }
.figs__grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:clamp(20px,3vw,40px); border-top:1px solid rgba(16,18,21,.14); padding-top:clamp(34px,4vw,56px); }
.fig__n{ display:block; font-family:var(--display); font-size:clamp(44px,5.4vw,84px); font-weight:600; line-height:1; }
.fig__n--word{ font-size:clamp(34px,4vw,64px); color:var(--copper-d); }
.fig p{ font-size:14px; color:var(--on-bone-mute); margin-top:12px; max-width:14em; }

/* ── full bleed ──────────────────────────────────────────────────────────── */
.bleed{ position:relative; min-height:min(88svh,760px); display:grid; align-items:end; overflow:hidden; background:var(--ink); }
.bleed__media{ position:absolute; inset:-8% 0; }
/* 58%, not 32%: this is the van, and the caption has to land on something it
   can be read against. Sitting the crop low puts the signwriting in the top
   half — where it is legible and nothing overlaps it — and brings the road up
   under the heading, so white type falls on tarmac rather than on the white
   van body and its own red lettering. */
.bleed__media img{ width:100%; height:100%; object-fit:cover; object-position:center 58%; }
/* Deeper and taller than the template's wash. That one was tuned for a dark
   bathroom photo; this is a white van in daylight, which is the brightest and
   busiest image on the site, and at .25 through the middle the body copy was
   sitting on white panel and losing. Held under 45% at the top so the
   signwriting still reads as signwriting. */
.bleed::after{ content:""; position:absolute; inset:0; background:linear-gradient(0deg, rgba(16,18,21,.96) 6%, rgba(16,18,21,.78) 34%, rgba(16,18,21,.42) 62%, rgba(16,18,21,.45)); }
.bleed__cap{ position:relative; z-index:2; padding-bottom:clamp(46px,7vw,90px); }
.bleed__cap h2{ font-size:clamp(34px,5vw,84px); text-transform:uppercase; letter-spacing:-.01em; }
.bleed__p{ margin-top:20px; max-width:30em; color:var(--on-dark-mute); font-size:17px; }

/* ── reviews / quotes ──────────────────────────────────────────────────────── */
.reviews{ background:var(--ink); padding:0 0 var(--pv); overflow:hidden; }
/* REVIEWS is seven characters where WORK is four, so at the shared .giant size
   it came out around 1.4x the viewport width at every screen size and the
   section's overflow quietly clipped the end off it. Sized to its own length so
   the whole word lands, while still filling the width like the other wordmark. */
.reviews .giant{ font-size:clamp(40px,12.4vw,188px); }
.car--quote{ padding-left:var(--gut); }
/* Solid, not translucent: these float over the peeking next quote, and at 72%
   the text behind bled through and made them read as ghosted rather than as
   controls. */
.car--quote .car__btn{ border-color:rgba(241,236,228,.26); background:var(--ink-2); }
.car--quote .car__btn:hover{ background:var(--bone); border-color:var(--bone); color:var(--ink); }
.quote{
  flex:0 0 min(1100px, calc(100vw - var(--gut) * 2));
  display:grid; grid-template-columns:1.1fr .9fr; gap:clamp(26px,4vw,64px); align-items:center;
}
.quote__txt{ min-width:0; }
.quote blockquote{
  margin:0; font-family:var(--display); font-weight:500;
  font-variation-settings:"wdth" 108; letter-spacing:-.012em;
  font-size:clamp(24px,2.9vw,50px); line-height:1.22;
}
.quote__by{ margin-top:26px; font-size:13px; letter-spacing:.16em; text-transform:uppercase; color:var(--copper-l); font-weight:600; }
.quote__media{ overflow:hidden; aspect-ratio:3/4; background:var(--ink-3); }
.quote__media img{ width:100%; height:100%; object-fit:cover; }

/* ── accreditations ──────────────────────────────────────────────────────── */
.accr{ background:var(--ink); padding:0 0 var(--pv); }
.accr__row{
  display:flex; flex-wrap:wrap; justify-content:space-between; gap:24px 40px;
  border-block:1px solid rgba(241,236,228,.13); padding:clamp(26px,3.4vw,44px) var(--gut);
}
.accr__row span{
  font-size:clamp(13px,1.15vw,16px); font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--on-dark-mute);
}

/* ── cta ─────────────────────────────────────────────────────────────────── */
.cta{ background:var(--ink); padding:0 0 var(--pv); text-align:center; }
.cta__tel{
  display:block; font-family:var(--display); font-weight:700;
  font-size:clamp(38px,7.4vw,112px); letter-spacing:-.01em; line-height:1;
  transition:color .4s var(--ease);
}
.cta__tel:hover{ color:var(--copper-l); }
.cta__btns{ display:flex; gap:16px; justify-content:center; flex-wrap:wrap; margin-top:clamp(34px,4vw,52px); }

/* ── footer ──────────────────────────────────────────────────────────────── */
.ft{ background:var(--ink-2); padding:clamp(52px,6vw,84px) 0 30px; }
.ft__grid{ display:grid; grid-template-columns:1.6fr 1fr 1fr 1.3fr; gap:clamp(30px,4vw,56px); }
.ft__brand img{ width:180px; height:auto; margin-bottom:22px; }
.ft__brand p{ color:var(--on-dark-mute); font-size:15px; }
.ft__col ul li{ margin-bottom:11px; }
.ft__col a{ color:var(--on-dark-mute); font-size:15px; transition:color .3s var(--ease); }
.ft__col a:hover{ color:var(--copper-l); }
.ft__area{ margin-top:20px; font-size:13px; color:rgba(241,236,228,.4); line-height:1.7; }
.ft__bot{
  display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px;
  margin-top:clamp(40px,5vw,70px); padding-top:26px;
  border-top:1px solid rgba(241,236,228,.12);
  font-size:13px; color:rgba(241,236,228,.42);
}

/* ── mobile call bar ─────────────────────────────────────────────────────── */
.callbar{ display:none; }

/* ── scroll reveal ───────────────────────────────────────────────────────── */
.rv{ opacity:0; transform:translateY(30px); transition:opacity .7s var(--ease), transform .7s var(--ease); }
.rv.in{ opacity:1; transform:none; }
.rv--d1{ transition-delay:.09s; } .rv--d2{ transition-delay:.18s; }
.rv--d3{ transition-delay:.27s; } .rv--d4{ transition-delay:.36s; }

.rv-lines .ln{ display:block; overflow:hidden; }
.rv-lines .ln > span{ display:block; transform:translateY(105%); transition:transform .85s var(--ease); }
.rv-lines.in .ln > span{ transform:none; }

/* Curtain wipe — the reveal used on the reference site's work grid. A panel
   painted the same colour as the section sits over the image, then collapses
   toward its own top edge, so the photo is UNCOVERED from the bottom up rather
   than faded in. The 1px bleed on every side kills the subpixel seam you get
   when a scaled panel lands on a fractional device pixel.
   --wipe-bg lets the same class sit on a bone section without editing it. */
.wipe{ position:relative; }
.wipe::after{
  content:""; position:absolute; bottom:-1px; left:-1px;
  width:calc(100% + 2px); height:calc(100% + 2px);
  background:var(--wipe-bg, var(--ink));
  transform:scaleY(1); transform-origin:50% 0;
  pointer-events:none; z-index:2;
  transition:transform 0s, opacity .6s ease;
}
.wipe.in::after{
  transform:scaleY(0); opacity:0;
  /* opacity snaps out only once the panel has finished retracting */
  transition:transform .6s cubic-bezier(.165,.84,.44,1), opacity 0s ease .6s;
}
/* Staggered variants, for when wipes sit side by side rather than stacked. */
.wipe--d1.in::after{ transition-delay:.2s, .8s; }
.wipe--d2.in::after{ transition-delay:.4s, 1s; }

/* ══ responsive ═══════════════════════════════════════════════════════════ */
@media (max-width:1100px){
  .menu__grid{ grid-template-columns:1fr 1fr; row-gap:44px; }
  .menu__links{ grid-column:1 / -1; grid-row:1; }
  .menu__col{ grid-row:2; }
  .menu__contact{ grid-column:1 / -1; grid-row:3; }
  .ft__grid{ grid-template-columns:1fr 1fr; }
  .figs__grid{ grid-template-columns:repeat(2,1fr); row-gap:36px; }
  .quote{ grid-template-columns:1fr; }
  .quote__media{ aspect-ratio:16/10; order:-1; }
}

@media (max-width:900px){
  :root{ --hdr-h:80px; }
  .hero{ grid-template-columns:1fr; min-height:auto; }
  .hero__copy{ order:2; padding:clamp(26px,6vw,48px) var(--gut) 84px; max-width:none; margin:0; }
  /* The monogram is a wide, short lockup — at 96% of a phone's width it is only
     ~160px tall, so a 520px block left ~180px of dead bone above AND below it
     before the copy even started. Height is set close to the letterforms so the
     panel hugs the logo instead of framing it in whitespace. The photo behind is
     inset/cover, so it fills whatever height this ends up being. */
  /* Sized off the lockup, which now carries the water droplet and so renders
     ~196px tall here rather than ~159px. The mark in the header gained the
     droplet too, which pushed the bar from 77px to 86px — hence 376 and 61%
     rather than 365 and 59%. Both numbers exist to hold the same two gaps:
     ~24px clear of the header, ~96px down to the copy. */
  /* 376px was sized for SGS's ~196px-tall lockup. This one is a wide 2.89:1
     lockup that renders ~129px tall at 96% of a phone's width, so the old height
     left a band of dead bone above and below it. */
  .hero__media{ order:1; min-height:min(40svh,320px); }
  .hero__cut{
    /* 88%, not 96% — this lockup is a wide 2.89:1 and at 96% the final G sat
       hard against the screen edge. */
    -webkit-mask-size:88% auto, 100% 100%; mask-size:88% auto, 100% 100%;
    /* Nudged below centre so the lockup clears the fixed header — centred, it
       tucked ~7px UNDER the bar. The extra height above is taken by the panel
       growing, and the mask sitting low in it keeps the gap to the copy at the
       same 96px it already was. */
    -webkit-mask-position:center 61%, center;
            mask-position:center 61%, center;
  }
  .hero__scroll{ display:none; }
  .intro__in{ grid-template-columns:1fr; }
  .vlbl{ writing-mode:horizontal-tb; transform:none; justify-self:start; }
  .teaser__grid{ grid-template-columns:1fr; gap:44px; }
  .car__nav{ position:static; flex-direction:row; justify-content:flex-end; padding-right:var(--gut); margin-bottom:26px; }
  .car--quote .car__nav,.svc .car__nav{ top:auto; }
  .car__btn{ width:58px; height:58px; }
}

@media (max-width:700px){
  body{ font-size:16px; }
  :root{ --pv:clamp(58px,13vw,86px); --hdr-h:88px; }
  .hdr__tel span{ display:none; }
  .hdr__tel svg{ width:24px; height:24px; }
  .burger{ width:112px; height:50px; top:16px; }
  /* Sized by HEIGHT, not width. The template sized this by width (88px), which
     suited SGS's wide, short lockup; this client's mark is the lion alone and is
     PORTRAIT (678x840), so 88px wide rendered it 109px tall inside an 80px bar
     and it spilled over the top of the hero. Height-based sizing is the only
     thing that holds for a square or upright mark — same fix the Shower Surgeon
     stacked logo needed. 20px padding + 48px mark + 20px = the 88px bar below. */
  .hdr__logo img{ width:auto; height:48px; }
  .hero__copy{ padding-bottom:56px; }
  .hero__h1{ font-size:clamp(40px,12.5vw,60px); }
  .hero__cta{ gap:16px; }
  .hero__cta .btn{ width:100%; }
  .giant{ font-size:clamp(64px,26vw,150px); }
  .shot figcaption h3{ font-size:clamp(28px,9vw,44px); }
  .figs__grid{ grid-template-columns:1fr 1fr; }
  .ft__grid{ grid-template-columns:1fr; }
  .ft__bot{ flex-direction:column; gap:6px; }
  .accr__row{ justify-content:flex-start; gap:16px 26px; }
  .accr__row span{ font-size:12px; }
  .menu__grid{ grid-template-columns:1fr; row-gap:34px; }
  .menu__col{ grid-row:auto; }
  .menu__contact{ grid-column:1; grid-row:auto; }
  .menu__links a{ font-size:clamp(34px,11vw,46px); }
  .menu__h{ margin-bottom:16px; }

  /* The van is a landscape photo, and on a phone this panel is portrait and
     tall, so cover threw away more than half the width — the signwriting came
     out cut off at both ends, which is the one thing the picture is here for.
     On phones it gets its own 4:3 box with the caption underneath instead of
     behind it, which shows the whole van. The parallax is switched off with it:
     main.js writes an inline transform, and once the box is no longer
     overhanging the section, 16px of travel is just a 16px gap. */
  .bleed{ min-height:0; display:block; }
  .bleed::after{ display:none; }
  .bleed__media{ position:relative; inset:auto; aspect-ratio:4/3; transform:none !important; }
  .bleed__cap{ padding:clamp(28px,6vw,44px) 0 clamp(34px,7vw,56px); }

  /* sticky call bar clears the footer on touch */
  .callbar{
    position:fixed; left:0; right:0; bottom:0; z-index:80;
    /* gap:0 — a 1px gap here let the container's own background show through as
       a hairline between the two buttons, which read as a seam rather than a
       divider. The copper/ink colour change is separation enough. */
    display:grid; grid-template-columns:1fr auto; gap:0;
    background:rgba(241,236,228,.14);
    padding-bottom:env(safe-area-inset-bottom);
  }
  .callbar a{
    background:var(--ink-2); color:var(--bone);
    padding:16px 18px; text-align:center; font-weight:700; font-size:15px;
  }
  /* Was near-black on light copper. Against this client's red that lands at
     3.4:1 and fails AA, so the bar runs red->dark-red with white text instead
     (5.5:1 at the light end, 8.5:1 at the dark). */
  .callbar a:first-child{ background:linear-gradient(120deg,var(--copper),var(--copper-d)); color:#fff; }
  .callbar__alt{ padding-inline:26px; }
  body{ padding-bottom:56px; }
  .menu{ padding-bottom:80px; }
}

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .rv,.rv-lines .ln > span{ opacity:1; transform:none; }
  .wipe::after{ display:none; }
  .hero__photo img{ animation:none; transform:none; }
}
