/* ==========================================================================
   DEVLIN DRYWALL CONTRACTORS, single file sales page
   Brand tokens lifted from the existing devlindrywall.ca logo:
     brand blue  #0A67B5   (exact sample from logo artwork)
     brand black #151314
   Theme lock: dark. Radius lock: 6px everywhere. Accent lock: brand blue only.
   ========================================================================== */

:root{
  --ink:#0B0D10;
  --ink-2:#12151A;
  --ink-3:#191D24;
  --line:rgba(255,255,255,.10);
  --line-2:rgba(255,255,255,.18);
  --paper:#EDF1F6;
  --muted:#9AA5B3;
  --muted-2:#6F7A88;
  --blue:#0A67B5;
  --blue-hi:#3D9BE9;
  --blue-deep:#074E89;
  --r:6px;
  --shell:1280px;
  /* the fixed header's height, in one place. Every scroll-margin-top offset is
     derived from it, so an anchor can never land under the bar because someone
     changed the header and forgot the offsets. */
  --nav-h:68px;
  --pad:clamp(20px,5vw,64px);
  --ease:cubic-bezier(.16,1,.3,1);
  --logo:url("/images/brand/logo-nav.png");
}

/* horizontal logo lockup, built from the existing brand artwork */
.brand{display:block;background:var(--logo) left center/contain no-repeat;
  text-indent:-200vw;overflow:hidden;white-space:nowrap;flex:none}
.nav-brand{width:220px;height:34px}
.foot-brand{width:272px;height:42px;margin-bottom:18px}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;
  background:var(--ink);
  color:var(--paper);
  font-family:"Archivo",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-variation-settings:"wdth" 100,"wght" 400;
  font-size:17px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
body.is-locked{overflow:hidden}
html.is-loading,html.is-loading body{overflow:hidden}
/* height:auto is what lets the width/height attributes act purely as a CLS hint.
   Without it the height attribute is a presentational hint that wins over CSS
   aspect-ratio, which sized the Why Devlin photo to its own pixel height. */
img{max-width:100%;display:block;height:auto}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit}

::selection{background:var(--blue);color:#fff}

:focus-visible{outline:2px solid var(--blue-hi);outline-offset:3px;border-radius:3px}

.shell{width:100%;max-width:var(--shell);margin-inline:auto;padding-inline:var(--pad)}
.sec{padding-block:clamp(72px,11vw,140px);position:relative}

/* ---------- type scale ---------- */
.eyebrow{
  font-size:12px;font-variation-settings:"wdth" 112,"wght" 700;
  letter-spacing:.16em;text-transform:uppercase;color:var(--blue-hi);
  margin:0 0 18px;
}
h1,h2,h3,h4{margin:0;font-variation-settings:"wdth" 108,"wght" 800;line-height:1.02;letter-spacing:-.022em}
/* Scales off whichever axis is tighter: 6.6vw keeps the two headline lines from
   overflowing narrow phones, 7.2vh keeps the hero off the fold on short laptops.
   Deliberately continuous, so the hero never snaps between two sizes on viewport
   heights that common laptops sit right on top of. */
h1{font-size:clamp(1.75rem,min(6.6vw,7.2vh),4.05rem)}
h2{font-size:clamp(2.05rem,4.6vw,3.5rem);line-height:1.05}
h3{font-size:clamp(1.15rem,1.9vw,1.5rem);line-height:1.15;letter-spacing:-.014em}
.lede{color:var(--muted);font-size:clamp(1.02rem,1.35vw,1.16rem);max-width:56ch;margin:20px 0 0}
.blue{color:var(--blue-hi)}
.sec-head{max-width:720px;margin-bottom:clamp(38px,5vw,64px)}
.sec-head .lede{max-width:52ch}
.sec-head .lede{margin-top:16px}

/* ==========================================================================
   1. PRELOADER
   ========================================================================== */
#preloader{
  position:fixed;inset:0;z-index:1000;background:var(--ink);
  display:grid;place-items:center;
  /* matches the 360ms removal delay in the inline loader script, so the node
     is never torn out part way through its own fade */
  transition:opacity .34s var(--ease),visibility .34s var(--ease);
}
#preloader.done{opacity:0;visibility:hidden}
.pre-inner{width:min(420px,74vw);text-align:center}
.pre-logo{width:100%;height:auto;opacity:0;transform:translateY(14px) scale(.97);
  animation:preIn 1s var(--ease) .12s forwards}
@keyframes preIn{to{opacity:1;transform:none}}
.pre-track{
  margin-top:30px;height:3px;width:100%;background:rgba(255,255,255,.09);
  border-radius:99px;overflow:hidden;
}
.pre-fill{
  height:100%;width:0%;border-radius:99px;
  background:linear-gradient(90deg,var(--blue-deep),var(--blue),var(--blue-hi));
  transition:width .35s var(--ease);
}
.pre-meta{
  margin-top:14px;font-size:12px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--muted-2);font-variation-settings:"wdth" 112,"wght" 600;
}
.pre-sweep{
  position:absolute;inset:0;pointer-events:none;opacity:.55;
  background:linear-gradient(105deg,transparent 38%,rgba(61,155,233,.09) 50%,transparent 62%);
  background-size:260% 100%;
  animation:sweep 2.6s linear infinite;
}
@keyframes sweep{from{background-position:150% 0}to{background-position:-110% 0}}

/* ==========================================================================
   1b. ICONS  Phosphor Bold, subset to the 26 glyphs this site uses
   ==========================================================================
   The CDN build is a 150KB font plus an 86KB stylesheet carrying roughly 1500
   glyphs, for 26 icons. Subset and self hosted it is 3.3KB, and it drops a
   third party origin off the critical path. Same font file, same class names,
   same codepoints, so every icon renders exactly as before.

   ⚠️ Adding a new ph-bold icon to the markup means rebuilding this subset, or
   the new one renders as a blank box. The rebuild is recorded in
   client-config.md under Standing decisions. */
@font-face{
  font-family:"Phosphor-Bold";
  src:url("/assets/fonts/phosphor-bold-subset.woff2?v=1") format("woff2");
  font-weight:normal;
  font-style:normal;
  font-display:block;
}
.ph-bold {
  /* use !important to prevent issues with browser extensions that change fonts */
  font-family: "Phosphor-Bold" !important;
  speak: never;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;

  /* Enable Ligatures ================ */
  letter-spacing: 0;
  -webkit-font-feature-settings: "liga";
  -moz-font-feature-settings: "liga=1";
  -moz-font-feature-settings: "liga";
  -ms-font-feature-settings: "liga" 1;
  font-feature-settings: "liga";
  -webkit-font-variant-ligatures: discretionary-ligatures;
  font-variant-ligatures: discretionary-ligatures;

  /* Better Font Rendering =========== */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.ph-arrow-right:before{content:"\e06c"}
.ph-broom:before{content:"\ec54"}
.ph-caret-down:before{content:"\e136"}
.ph-caret-left:before{content:"\e138"}
.ph-caret-right:before{content:"\e13a"}
.ph-chat-teardrop-text:before{content:"\e178"}
.ph-check-circle:before{content:"\e184"}
.ph-circle-notch:before{content:"\eb44"}
.ph-envelope:before{content:"\e214"}
.ph-grid-four:before{content:"\e296"}
.ph-hammer:before{content:"\e80e"}
.ph-house:before{content:"\e2c2"}
.ph-list:before{content:"\e2f0"}
.ph-magnifying-glass-plus:before{content:"\e310"}
.ph-map-pin:before{content:"\e316"}
.ph-medal:before{content:"\e320"}
.ph-paint-roller:before{content:"\e6f4"}
.ph-phone:before{content:"\e3b8"}
.ph-play:before{content:"\e3d0"}
.ph-shield-check:before{content:"\e40c"}
.ph-sparkle:before{content:"\e6a2"}
.ph-speaker-simple-high:before{content:"\e450"}
.ph-speaker-simple-slash:before{content:"\e456"}
.ph-trophy:before{content:"\e67e"}
.ph-waveform:before{content:"\e802"}
.ph-x:before{content:"\e4f6"}

/* ==========================================================================
   2. TARGET CURSOR (dot + ring). Pointer devices only.
   ========================================================================== */
.cur{position:fixed;top:0;left:0;z-index:1200;pointer-events:none;opacity:0;
  transition:opacity .3s ease}
.cur.on{opacity:1}
.cur-dot{width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:99px;background:var(--blue-hi)}
.cur-ring{
  width:32px;height:32px;margin:-16px 0 0 -16px;border-radius:99px;
  border:1.5px solid rgba(61,155,233,.6);
  transition:width .28s var(--ease),height .28s var(--ease),margin .28s var(--ease),
             border-color .28s var(--ease),background-color .28s var(--ease);
}
.cur-ring::before,.cur-ring::after{
  content:"";position:absolute;background:rgba(61,155,233,.55);
}
.cur-ring::before{top:50%;left:-7px;width:5px;height:1.5px;margin-top:-.75px}
.cur-ring::after{top:50%;right:-7px;width:5px;height:1.5px;margin-top:-.75px}
body.cur-hot .cur-ring{width:52px;height:52px;margin:-26px 0 0 -26px;
  border-color:var(--blue-hi);background:rgba(61,155,233,.09)}
body.cur-hot .cur-dot{background:#fff}
@media (hover:none),(pointer:coarse){.cur{display:none!important}}

/* ==========================================================================
   3. NAV
   ========================================================================== */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:900;height:var(--nav-h);
  display:flex;align-items:center;
  background:transparent;border-bottom:1px solid transparent;
  transition:background .4s var(--ease),border-color .4s var(--ease),backdrop-filter .4s var(--ease);
}
.nav.stuck{
  background:rgba(11,13,16,.86);backdrop-filter:blur(16px) saturate(150%);
  -webkit-backdrop-filter:blur(16px) saturate(150%);
  border-bottom-color:var(--line);
}
.nav-in{display:flex;align-items:center;gap:28px;width:100%}
.nav-links{display:flex;align-items:center;gap:26px;margin-left:auto;white-space:nowrap}
.nav-links a{
  font-size:14px;color:var(--muted);font-variation-settings:"wdth" 104,"wght" 600;
  transition:color .25s ease;
}
.nav-links a:hover{color:var(--paper)}
.nav-tel{
  display:inline-flex;align-items:center;gap:8px;font-size:15px;
  font-variation-settings:"wdth" 108,"wght" 700;color:var(--paper)!important;
}
.nav-tel i{color:var(--blue-hi);font-size:17px}
.nav-cta{flex:none}
.nav-burger{display:none;margin-left:auto;background:none;border:0;font-size:26px;padding:6px;cursor:pointer}

@media (max-width:1000px){
  .nav-links{display:none}
  .nav-burger{display:block}
}
@media (max-width:820px){
  /* sticky bottom call bar carries the CTA here.
     .nav-in prefix so this beats the later .btn display rule */
  .nav-in .nav-cta{display:none}
}

/* mobile sheet */
.sheet{
  position:fixed;inset:0;z-index:950;background:var(--ink-2);
  display:flex;flex-direction:column;justify-content:center;gap:6px;
  padding:0 var(--pad);
  transform:translateY(-100%);visibility:hidden;pointer-events:none;
  transition:transform .5s var(--ease),visibility .5s var(--ease);
}
.sheet.open{transform:none;visibility:visible;pointer-events:auto}
/* nav rows only. Same :not(.btn) as the narrow-screen block further down: this
   rule out-specifies .btn-lg, so without it the Call Alex Now button in the
   drawer took 8px 0 padding, a divider line and a 2.4rem label. */
.sheet a:not(.btn){font-size:clamp(1.7rem,7vw,2.4rem);font-variation-settings:"wdth" 108,"wght" 800;
  letter-spacing:-.02em;padding:8px 0;border-bottom:1px solid var(--line)}
.sheet .btn{margin-top:26px;align-self:flex-start}
.sheet-x{position:absolute;top:18px;right:var(--pad);background:none;border:0;font-size:30px;cursor:pointer}

/* ==========================================================================
   4. BUTTONS
   ========================================================================== */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  padding:15px 26px;border-radius:var(--r);border:1px solid transparent;
  font-size:15px;font-variation-settings:"wdth" 108,"wght" 700;
  letter-spacing:.01em;white-space:nowrap;cursor:pointer;
  transition:transform .2s var(--ease),background-color .25s ease,border-color .25s ease,color .25s ease;
}
.btn i{font-size:18px}
.btn-p{background:var(--blue);color:#fff;box-shadow:0 10px 30px -12px rgba(10,103,181,.85)}
.btn-p:hover{background:#0d76cc}
.btn-s{background:transparent;color:var(--paper);border-color:var(--line-2)}
.btn-s:hover{border-color:var(--blue-hi);color:#fff}
.btn-w{background:var(--paper);color:#0B0D10}
.btn-w:hover{background:#fff}
.btn:active{transform:translateY(1px) scale(.985)}
.btn-lg{padding:18px 32px;font-size:16px}
.btn-sm{padding:11px 18px;font-size:13.5px}

/* ==========================================================================
   5. HERO
   ========================================================================== */
.hero{position:relative;padding-top:104px;padding-bottom:clamp(44px,5vw,72px);overflow:hidden}
.hero-bg{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(90% 60% at 78% 8%,rgba(10,103,181,.20),transparent 62%),
    radial-gradient(70% 50% at 0% 40%,rgba(10,103,181,.10),transparent 60%);
}
.hero-grid{
  position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.5;
  background-image:linear-gradient(rgba(255,255,255,.028) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.028) 1px,transparent 1px);
  background-size:74px 74px;
  mask-image:radial-gradient(120% 80% at 50% 0%,#000 20%,transparent 78%);
  -webkit-mask-image:radial-gradient(120% 80% at 50% 0%,#000 20%,transparent 78%);
}
/* headline, subhead, video sales letter, then one CTA below */
/* one full width column, not an auto one. An auto grid column takes the max
   content width of its widest item, which is the h1 at 24ch, and that was
   quietly capping the film at 792px however big the formula asked for. */
.hero-in{position:relative;z-index:1;display:grid;grid-template-columns:minmax(0,1fr);
  justify-items:center;text-align:center;
  gap:clamp(18px,2.2vw,26px);max-width:1000px;margin-inline:auto}
.hero-in h1{max-width:min(24ch,100%)}
.hero-in .lede{margin:0 auto;max-width:56ch}

/* short laptop screens: pull the whole hero in so the CTA still clears the fold.
   The h1 is deliberately NOT overridden here, it scales continuously on 7.2vh above.
   Anything added here must not reintroduce a hard size snap. */
@media (max-height:850px){
  .hero{padding-top:92px;padding-bottom:clamp(26px,3vw,38px)}
  .hero-in{gap:clamp(14px,1.8vw,20px)}
  .hero-in .lede{font-size:1rem}
}
/* The film needs real air under it before the CTA on any narrow screen, or the
   caption reads as the button's own label. Equal margins on both sides so this
   still centres cleanly inside the leftover row below. */
@media (max-width:900px){
  .hero-in > .btn-lg{margin-block:clamp(12px,2.2vh,26px)}
}
/* On a phone the hero fills the small viewport, so the reviews strip below it
   starts at the bottom edge instead of peeking a hundred pixels into the fold.
   svh is the height with the browser bars showing, which is the state the page
   loads in. vh first for anything without svh.

   The surplus used to land entirely under the Call Now button, which left the
   caption jammed 14px above it and 246px of nothing below. The hero is a flex
   column so .hero-in inherits that full height, the last grid row takes the
   leftover, and the button centres inside it: even air above and below,
   whatever the phone. On a short screen the row collapses to the button's own
   height and this changes nothing. */
@media (max-width:760px){
  .hero{min-height:100vh;min-height:100svh;display:flex;flex-direction:column}
  .hero-in{flex:1 1 auto;width:100%;grid-template-rows:auto auto auto 1fr}
  .hero-in > .btn-lg{align-self:center}
}

h1 .l{display:block;overflow:hidden}
h1 .l span{display:block;transform:translateY(104%);
  animation:lineUp .95s var(--ease) forwards}
h1 .l:nth-child(1) span{animation-delay:.10s}
h1 .l:nth-child(2) span{animation-delay:.20s}
@keyframes lineUp{to{transform:none}}


/* video sales letter */
.vsl{
  /* Width tracks the height left over after the headline, subhead and CTA.
     Alex has now asked twice for a bigger film, so the overhead constant is
     tuned to him rather than to the fold: dropping the caption subhead and the
     duration chip gave back 37px of it, and the rest is a deliberate trade of
     fold position for size. The Call Now button now sits a little further
     under the fold than it did, which is the call he made. */
  position:relative;
  width:min(100%, max(320px, calc((100dvh - 496px) * 1.7778)), 1000px);
  /* the frame owns the border, the background and the shadow now. Leaving them
     here painted a full width slab under the film, which is the "bar" Alex
     wanted shrunk: the caption alone should be all that shows below it. */
}

/* under 851px tall the hero's own padding and gaps shrink, which frees 62px,
   so the film can be that much wider before the button is pushed down */
@media (max-height:850px){
  .vsl{width:min(100%, max(300px, calc((100dvh - 442px) * 1.7778)), 1000px)}
}
/* the film is 16:9 and so is this box, but contain rather than cover is what
   guarantees Alex's "not cropped in any way" whatever gets re-encoded later */
.vsl-frame{
  position:relative;aspect-ratio:16/9;width:100%;
  border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line-2);background:#000;
  box-shadow:0 44px 90px -44px rgba(0,0,0,.95);
}
.vsl-frame img{width:100%;height:100%;object-fit:cover;filter:saturate(.85) brightness(.62)}
.vsl-frame::after{content:"";position:absolute;inset:0;pointer-events:none;
  transition:opacity .4s ease;
  background:linear-gradient(to top,rgba(11,13,16,.55),rgba(11,13,16,0) 42%)}
.vsl-play{
  position:absolute;z-index:2;top:50%;left:50%;transform:translate(-50%,-50%);
  width:82px;height:82px;border-radius:99px;border:0;cursor:pointer;
  background:var(--blue);color:#fff;font-size:30px;
  display:grid;place-items:center;
  box-shadow:0 18px 44px -14px rgba(10,103,181,.9);
  transition:transform .3s var(--ease),background-color .3s ease;
}
.vsl-play i{margin-left:4px}
.vsl-play:hover{transform:translate(-50%,-50%) scale(1.08);background:#0d76cc}
.vsl-play::before{
  content:"";position:absolute;inset:-1px;border-radius:99px;
  border:2px solid rgba(61,155,233,.5);
  animation:ping 2.6s var(--ease) infinite;
}
@keyframes ping{0%{transform:scale(1);opacity:.75}70%,100%{transform:scale(1.9);opacity:0}}
/* Alex: shrink the bar, it is too wide, and push the label all the way left.
   So it hugs its own text instead of spanning the film, and the strip it used
   to be is now just the container showing through. */
.vsl-cap{
  /* fit-content, not inline-flex: .hero-in centres its text, and an inline
     level box would inherit that and sit in the middle of the film */
  display:flex;align-items:center;width:fit-content;text-align:left;
  margin:10px 0 0;padding:5px 12px;
  border:1px solid var(--line);border-radius:var(--r);background:var(--ink-3);
}
.vsl-cap b{font-size:13.5px;font-variation-settings:"wdth" 106,"wght" 700}

/* ==========================================================================
   6. PROOF STRIP
   ========================================================================== */
.proof{border-block:1px solid var(--line);background:var(--ink-2)}
.proof-in{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line)}
@media (max-width:820px){.proof-in{grid-template-columns:repeat(2,1fr)}}
.proof-cell{background:var(--ink-2);padding:clamp(26px,3.4vw,40px) clamp(16px,2.4vw,30px);text-align:center}
.proof-n{
  font-size:clamp(2.1rem,3.6vw,2.9rem);line-height:1;
  font-variation-settings:"wdth" 118,"wght" 800;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.proof-stars{margin-top:9px;color:#F5B301;font-size:13px;letter-spacing:2px}
.proof-src{margin-top:8px;font-size:12.5px;color:var(--muted);
  font-variation-settings:"wdth" 106,"wght" 600}
.proof-src b{color:var(--paper);font-variation-settings:"wdth" 106,"wght" 700}

/* award row */
.awards{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:clamp(18px,3.4vw,44px);padding:34px 0 4px}
.awards img{
  height:clamp(96px,10vw,136px);width:auto;border-radius:var(--r);
  background:#fff;padding:6px;opacity:.88;
  transition:opacity .45s var(--ease),transform .45s var(--ease),box-shadow .45s var(--ease);
}
.awards img:hover{opacity:1;transform:translateY(-5px);
  box-shadow:0 18px 40px -18px rgba(0,0,0,.9)}
.awards-cap{text-align:center;font-size:12.5px;color:var(--muted-2);margin-top:22px;
  letter-spacing:.1em;text-transform:uppercase;font-variation-settings:"wdth" 108,"wght" 600}

/* ==========================================================================
   7. SERVICES BENTO
   ========================================================================== */
/* Six services on a six column grid: two wide cards across the top, three
   across the middle, and the painting card alone across the bottom. Everything
   below spans that grid rather than reflowing it, so the shape holds. */
.bento{display:grid;gap:14px;grid-template-columns:repeat(6,minmax(0,1fr))}
.tile.s-half{grid-column:span 3}
.tile.s-third{grid-column:span 2}
.tile.s-full{grid-column:span 6;min-height:clamp(340px,30vw,440px)}
@media (max-width:940px){
  /* two up, and the odd one out of the middle row takes the full width so the
     row never ends on an empty cell */
  .bento{grid-template-columns:repeat(2,minmax(0,1fr))}
  .tile.s-half,.tile.s-third{grid-column:span 1}
  .tile.s-mid-last,.tile.s-full{grid-column:span 2}
}
@media (max-width:600px){
  .bento{grid-template-columns:1fr}
  .tile.s-half,.tile.s-third,.tile.s-mid-last,.tile.s-full{grid-column:span 1}
}
/* the bottom card is the only one wide enough for two photos, so it carries a
   pair side by side instead of one stretched crop */
/* grid-template-rows has to be a definite fraction, not the implicit auto row.
   On an auto row the images' height:100% has nothing to resolve against, falls
   back to their intrinsic height, and a tall photo renders at full height and
   is simply clipped by the tile's overflow:hidden. object-fit and
   object-position then do nothing at all, because the box already matches the
   photo's own aspect and there is no crop for them to move. */
.tile-pics{position:absolute;inset:0;display:grid;grid-template-columns:1fr 1fr;
  grid-template-rows:minmax(0,1fr);gap:2px}
/* .tile img below is absolutely positioned at inset:0 and is declared later at
   the same specificity, so this override has to outweigh it, not just follow it */
.tile .tile-pics img{position:relative;inset:auto;height:100%;min-height:0}
/* the second photo on the painting card is a tall shot of a finished room, so
   a centred crop lands on empty ceiling. Bias it down onto the feature wall
   and the furniture, which is the part with any shape to it under the scrim. */
/* it is also a bright, very low contrast white-on-white wall, so the tile's
   standard darkening flattens it to a blank panel. Give this one photo a
   lighter hand so the battens still read through the scrim. */
.tile-pics img:last-child{object-position:50% 62%;filter:saturate(.9) brightness(.84) contrast(1.14)}
.tile:hover .tile-pics img:last-child{filter:saturate(1) brightness(.94) contrast(1.14)}
.tile.s-full h3{font-size:clamp(1.3rem,2.5vw,1.8rem)}
.tile.s-full .tile-body p{max-width:64ch}
.tile{
  position:relative;border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line);background:var(--ink-2);
  min-height:400px;display:flex;align-items:flex-end;
  transition:border-color .4s var(--ease),transform .5s var(--ease);
}
.tile:hover{border-color:var(--line-2);transform:translateY(-4px)}
.tile img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(.85) brightness(.66);
  transition:transform 1.1s var(--ease),filter .6s var(--ease);
}
.tile:hover img{transform:scale(1.06);filter:saturate(1) brightness(.76)}
.tile::after{content:"";position:absolute;inset:0;
  background:linear-gradient(to top,rgba(11,13,16,.96) 4%,rgba(11,13,16,.55) 54%,rgba(11,13,16,.12))}
.tile-body{position:relative;z-index:2;padding:clamp(20px,2.4vw,28px);width:100%}
.tile-ico{
  width:40px;height:40px;border-radius:var(--r);display:grid;place-items:center;
  background:rgba(10,103,181,.9);color:#fff;font-size:20px;margin-bottom:14px;
}
.tile h3{font-size:clamp(1.1rem,1.9vw,1.32rem);line-height:1.14;letter-spacing:-.02em;
  font-variation-settings:"wdth" 110,"wght" 800}
.tile p{margin:10px 0 0;color:#C3CCD8;font-size:14.5px;line-height:1.55}

/* ==========================================================================
   8. BEFORE / AFTER
   ========================================================================== */
/* The wipe slider that used to live here is gone. Alex supplies his before and
   after shots already composed as single branded boards, so they are a plain
   figure grid now, styled further down beside .ba-grid. The old .ba rules also
   carried touch-action:none, which on a full bleed image is a dead zone a thumb
   cannot scroll past, so they are not worth keeping around for reuse. */

/* ==========================================================================
   9. VIDEO TESTIMONIAL RAIL
   ========================================================================== */
.rail{
  display:grid;grid-auto-flow:column;grid-auto-columns:minmax(290px,335px);
  gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;
  padding-bottom:20px;margin-inline:calc(var(--pad)*-1);padding-inline:var(--pad);
  scrollbar-width:thin;scrollbar-color:var(--line-2) transparent;
}
.rail::-webkit-scrollbar{height:5px}
.rail::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:99px}
.vcard{
  scroll-snap-align:start;position:relative;border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line);background:var(--ink-2);aspect-ratio:9/13;
  cursor:pointer;transition:border-color .4s var(--ease),transform .5s var(--ease);
}
.vcard:hover{border-color:var(--blue);transform:translateY(-5px)}
.vcard img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(.62) brightness(.44);transition:transform 1s var(--ease)}
.vcard:hover img{transform:scale(1.05)}
.vcard::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgba(11,13,16,.95) 8%,rgba(11,13,16,.3) 62%,transparent)}
.vcard-in{position:relative;z-index:2;height:100%;display:flex;flex-direction:column;
  justify-content:space-between;padding:20px}
.vcard-play{
  width:56px;height:56px;border-radius:99px;background:rgba(255,255,255,.12);
  border:1px solid rgba(255,255,255,.3);backdrop-filter:blur(10px);
  display:grid;place-items:center;font-size:21px;color:#fff;
  transition:background-color .35s ease,transform .35s var(--ease),opacity .3s ease;
}
.vcard:hover .vcard-play{background:var(--blue);border-color:var(--blue);transform:scale(1.08)}
.vcard h4{font-size:16.5px;line-height:1.25;font-variation-settings:"wdth" 106,"wght" 700}
.vcard p{margin:8px 0 0;font-size:13.5px;color:#B6C0CC;line-height:1.5}
.vcard-meta{display:flex;align-items:center;gap:10px;margin-top:14px;
  font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);
  font-variation-settings:"wdth" 110,"wght" 600}
.vcard-meta span{display:inline-flex;align-items:center;gap:5px}
.vcard-meta i{color:var(--blue-hi);font-size:13px}

/* ==========================================================================
   10. WRITTEN REVIEWS
   ========================================================================== */
.quotes{columns:3;column-gap:16px}
@media (max-width:1000px){.quotes{columns:2}}
@media (max-width:640px){.quotes{columns:1}}
.q{
  break-inside:avoid;margin:0 0 16px;padding:26px;
  border:1px solid var(--line);border-radius:var(--r);background:var(--ink-2);
  transition:border-color .4s var(--ease),transform .5s var(--ease);
}
.q:hover{border-color:var(--line-2);transform:translateY(-3px)}
.q-stars{color:#F5B301;font-size:13px;letter-spacing:2px;margin-bottom:14px}
.q blockquote{margin:0;font-size:16px;line-height:1.62;color:#DCE3EB}
.q figcaption{margin-top:18px;padding-top:16px;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:12px}
.q-av{
  width:38px;height:38px;border-radius:99px;flex:none;display:grid;place-items:center;
  background:linear-gradient(150deg,var(--blue),var(--blue-deep));color:#fff;
  font-size:14px;font-variation-settings:"wdth" 110,"wght" 800;letter-spacing:.02em;
}
.q-who b{display:block;font-size:14.5px;font-variation-settings:"wdth" 104,"wght" 700}
.q-who small{display:block;font-size:12.5px;color:var(--muted);margin-top:1px}
.q.q-big blockquote{font-size:19px;line-height:1.5}
.q.q-big{background:linear-gradient(165deg,rgba(10,103,181,.16),var(--ink-2) 62%);
  border-color:rgba(61,155,233,.28)}

/* ==========================================================================
   11. PROMISE (sticky split)
   ========================================================================== */
.promise{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(36px,6vw,90px);align-items:start}
@media (max-width:940px){.promise{grid-template-columns:1fr}}
.promise-sticky{position:sticky;top:104px}
@media (max-width:940px){.promise-sticky{position:static}}
.promise-photo{
  border-radius:var(--r);overflow:hidden;border:1px solid var(--line-2);margin-top:30px;
  box-shadow:0 40px 80px -44px rgba(0,0,0,.95);
}
.promise-photo img{aspect-ratio:16/10;object-fit:cover;width:100%;
  filter:saturate(1.25) contrast(1.14) brightness(1.06)}
.pill-list{display:grid;gap:0}
.pill{padding:30px 0;border-top:1px solid var(--line);display:grid;
  grid-template-columns:52px minmax(0,1fr);gap:20px;align-items:start}
/* no border-bottom on the last pill. The section already carries its own
   border-block, so a rule here drew a second line a few pixels above it. */
.pill-ico{
  width:52px;height:52px;border-radius:var(--r);display:grid;place-items:center;
  border:1px solid var(--line-2);color:var(--blue-hi);font-size:23px;
  background:rgba(10,103,181,.10);
}
.pill p{margin:10px 0 0;color:var(--muted);font-size:15.5px;line-height:1.62;max-width:52ch}
@media (max-width:520px){.pill{grid-template-columns:1fr}}

/* ==========================================================================
   12. GUARANTEE BAND
   ========================================================================== */
.guarantee{
  border-block:1px solid var(--line);
  background:
    linear-gradient(rgba(11,13,16,.9),rgba(11,13,16,.94)),
    radial-gradient(70% 130% at 50% 0%,rgba(10,103,181,.32),transparent 66%),
    var(--ink-2);
  text-align:center;
}
.guarantee .shell{max-width:840px}
.guar-badge{
  display:inline-flex;align-items:center;gap:10px;margin-bottom:26px;
  border:1px solid rgba(61,155,233,.4);border-radius:var(--r);
  padding:9px 16px;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--blue-hi);font-variation-settings:"wdth" 110,"wght" 700;
  background:rgba(10,103,181,.12);
}
.guarantee blockquote{margin:0;font-size:clamp(1.2rem,2.1vw,1.55rem);line-height:1.5;
  color:#DFE6ED;font-variation-settings:"wdth" 102,"wght" 400}
.guar-sign{margin-top:28px;font-size:14px;color:var(--muted)}
.guar-sign b{display:block;color:var(--paper);font-size:16px;
  font-variation-settings:"wdth" 106,"wght" 700}

/* ==========================================================================
   13. AREAS MARQUEE
   ========================================================================== */
.marq{overflow:hidden;mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent)}
.marq-track{display:flex;gap:14px;width:max-content;animation:marq 46s linear infinite}
/* Pause on hover only where a real pointer exists. A touch leaves :hover stuck
   on whatever was last tapped, and near the marquee that means the cities stop
   dead and never start again for the rest of the visit. */
@media (hover:hover) and (pointer:fine){
  .marq:hover .marq-track{animation-play-state:paused}
}
@keyframes marq{to{transform:translateX(-50%)}}
.city{
  display:inline-flex;align-items:center;gap:9px;flex:none;
  border:1px solid var(--line);border-radius:var(--r);padding:13px 20px;
  background:var(--ink-2);font-size:15.5px;
  font-variation-settings:"wdth" 106,"wght" 600;
}
.city i{color:var(--blue-hi);font-size:16px}

/* ==========================================================================
   14. CLOSING CTA + FORM
   ========================================================================== */
.close{position:relative;overflow:hidden;border-top:1px solid var(--line)}
.close-bg{position:absolute;inset:0;z-index:0}
.close-bg img{width:100%;height:100%;object-fit:cover;filter:saturate(.4) brightness(.28)}
.close-bg::after{content:"";position:absolute;inset:0;
  background:linear-gradient(to right,rgba(11,13,16,.97) 22%,rgba(11,13,16,.72))}
.close-in{position:relative;z-index:1;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,.92fr);gap:clamp(38px,5.5vw,72px);align-items:center}
@media (max-width:940px){.close-in{grid-template-columns:1fr}}
.close-tel{
  display:inline-flex;align-items:center;gap:14px;margin-top:30px;
  font-size:clamp(1.5rem,3.2vw,2.1rem);font-variation-settings:"wdth" 112,"wght" 800;
  letter-spacing:-.02em;
}
.close-tel i{color:var(--blue-hi);font-size:.8em}
.close-tel:hover{color:var(--blue-hi)}
.close-list{list-style:none;margin:28px 0 0;padding:0;display:grid;gap:11px}
.close-list li{display:flex;align-items:flex-start;gap:11px;font-size:15.5px;color:var(--muted)}
.close-list i{color:var(--blue-hi);font-size:18px;margin-top:1px;flex:none}

/* Every in page target, not only the form. A nav or drawer link pointing at
   #services or #work lands the section heading underneath the fixed header
   otherwise, which reads as the link having overshot. */
#top,#services,#work,#reviews,#promise,#areas,#contact,#quote{
  scroll-margin-top:calc(var(--nav-h) + 20px);
}
/* the anchor sits on the <form> itself, so a "Book A Free Quote" tap lands on
   the first field and not on the text column above it on mobile */
.form{
  scroll-margin-top:calc(var(--nav-h) + 20px);
  background:rgba(18,21,26,.94);backdrop-filter:blur(14px);
  border:1px solid var(--line-2);border-radius:var(--r);padding:clamp(26px,3.4vw,38px);
  box-shadow:0 44px 90px -44px rgba(0,0,0,.95);
}
/* no .form h3 rule here. One used to sit at this specificity and quietly beat
   .form-title's own margin, which is why the heading stayed jammed against the
   first field no matter what that margin said. .form-ok h3 sets its own. */
.form>p{margin:0 0 24px;font-size:14px;color:var(--muted)}
/* The form heading is one line and carries the whole invitation, so it is set
   larger than anything else inside the form. */
/* Alex set the three lines himself with <br>, so they have to survive every
   width rather than wrapping into a fourth. Below 380px the longest line no
   longer fits, so Archivo's width axis narrows the heading instead of the size
   axis shrinking it, see the query further down. */
.form-title{
  /* the heading is three tight lines, so it needs a clear gap under it or the
     third line reads as a label sitting on top of the first field */
  margin:0 0 clamp(30px,3.4vw,44px);font-size:clamp(1rem,3.6vw,1.5rem);line-height:1.28;
  letter-spacing:-.02em;color:var(--paper);
  font-variation-settings:"wdth" 108,"wght" 800;
}
/* full sentence labels stay sentence case, the uppercase treatment only
   works on the short ones */
.f.f-long label{text-transform:none;letter-spacing:.01em;font-size:14px;
  font-variation-settings:"wdth" 104,"wght" 600;color:#C6CFDA;line-height:1.45}
/* the message prompt is the one Alex wrote himself, so it reads larger than
   every other label on the form */
.f.f-msg label{font-size:16px;line-height:1.5;color:var(--paper);
  font-variation-settings:"wdth" 104,"wght" 700;
  /* the sign off sits on its own line and is close to a phone's full measure,
     so keep it off a one word last line */
  text-wrap:pretty}
.f.f-msg textarea{min-height:132px;font-size:16px}
.fgrid{display:grid;gap:16px;grid-template-columns:1fr 1fr;align-items:end}
.f{display:grid;gap:7px}
.f.wide{grid-column:span 2}
@media (max-width:520px){.fgrid{grid-template-columns:1fr}.f.wide{grid-column:span 1}}
.f label{font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;
  color:#BAC4D0;font-variation-settings:"wdth" 106,"wght" 700}
.f input,.f select,.f textarea{
  width:100%;background:#0B0D10;border:1px solid var(--line-2);border-radius:var(--r);
  padding:13px 14px;color:var(--paper);font-family:inherit;font-size:15px;
  transition:border-color .25s ease,background-color .25s ease;
}
.f textarea{resize:vertical;min-height:96px}
.f input::placeholder,.f textarea::placeholder{color:#69737F}
.f input:focus,.f select:focus,.f textarea:focus{outline:none;border-color:var(--blue-hi);background:#0E1116}
.f-err{font-size:12.5px;color:#FF8A80;min-height:0}
.f.bad input,.f.bad select,.f.bad textarea{border-color:#FF8A80}
.form .btn{width:100%;margin-top:22px}
.form-fine{margin:14px 0 0;font-size:12.5px;color:var(--muted-2);text-align:center;line-height:1.5}
.form-ok{display:none;text-align:center;padding:22px 0}
.form-ok i{font-size:46px;color:var(--blue-hi)}
.form-ok h3{margin:14px 0 8px}
.form-ok p{margin:0;color:var(--muted);font-size:15px}
/* the narrowest phones: pull the card's own padding back to the page gutter and
   condense the heading, which keeps it at a readable 16px on three lines */
@media (max-width:380px){
  .form{padding:20px}
  .form-title{font-variation-settings:"wdth" 88,"wght" 800;letter-spacing:-.025em}
}
.form.sent .fgrid,.form.sent .btn,.form.sent .form-fine,.form.sent .form-title{display:none}
.form.sent .form-ok{display:block}

/* ==========================================================================
   15. FOOTER + STICKY CALL BAR
   ========================================================================== */
.foot{border-top:1px solid var(--line);background:var(--ink-2);padding-block:clamp(44px,5vw,66px)}
.foot-in{display:grid;grid-template-columns:1.5fr 1fr 1fr;gap:clamp(28px,4vw,56px)}
@media (max-width:780px){.foot-in{grid-template-columns:1fr 1fr}}
@media (max-width:520px){.foot-in{grid-template-columns:1fr}}
.foot p{margin:0;color:var(--muted);font-size:14.5px;line-height:1.65;max-width:38ch}
.foot h5{margin:0 0 14px;font-size:12px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted-2);font-variation-settings:"wdth" 110,"wght" 700}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.foot ul a{font-size:14.5px;color:var(--muted);transition:color .25s ease}
.foot ul a:hover{color:var(--blue-hi)}
.foot-bot{margin-top:clamp(34px,4vw,52px);padding-top:22px;border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:14px;justify-content:space-between;
  font-size:13px;color:var(--muted-2)}

.callbar{
  position:fixed;left:0;right:0;bottom:0;z-index:880;display:none;
  grid-template-columns:1fr 1fr;gap:1px;background:var(--line);
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
.callbar a{display:flex;align-items:center;justify-content:center;gap:8px;
  padding:15px 10px;font-size:14.5px;font-variation-settings:"wdth" 106,"wght" 700}
.callbar .c1{background:var(--blue);color:#fff}
.callbar .c2{background:var(--ink-2);color:var(--paper)}
@media (max-width:820px){.callbar{display:grid}body{padding-bottom:60px}}

/* ==========================================================================
   16. CLIENT MEDIA
   Every asset in this section is now Alex's real material: the brand film in
   the hero, the ten award certificates, the five before and after boards, the
   five homeowner videos and the thirteen review screenshots.
   ========================================================================== */

/* ---- awards, two rows, HomeStars on top and TrustedPros beneath ---------- */
/* The certificate width is worked out from the row, not from the viewport, so
   five across always fit on one line instead of leaving the last year stranded
   underneath. --awn is how many the row is trying to fit and steps down as the
   screen narrows, rather than letting the certificates shrink to stamps. The
   row is capped to exactly that many at full size, or the wider screens fit a
   sixth slot's worth of space and wrap five into four plus one again. */
.awards-row{
  --awgap:clamp(12px,1.8vw,22px);
  --awn:5;
  --cwmax:214px;
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:var(--awgap);
  max-width:calc(var(--awn) * var(--cwmax) + (var(--awn) - 1) * var(--awgap));
  margin-inline:auto;
}
.awards-row-tp{--cwmax:152px}
@media (max-width:840px){.awards-row{--awn:3}}
@media (max-width:560px){.awards-row{--awn:2}}
.awards-row + .awards-row{margin-top:clamp(14px,2vw,24px)}
.awards-lbl{
  text-align:center;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted-2);font-variation-settings:"wdth" 110,"wght" 700;
  margin:0 0 14px;
}
.awards-row + .awards-lbl{margin-top:clamp(28px,3.4vw,44px)}
.cert{
  border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  background:#fff;flex:none;transition:transform .4s var(--ease),border-color .3s ease;
}
.cert:hover{transform:translateY(-5px);border-color:var(--blue-hi)}
.cert img{display:block;width:100%;height:auto}
.cert-hs,.cert-tp{width:min(var(--cwmax),
  calc((100% - (var(--awn) - 1) * var(--awgap)) / var(--awn)))}

/* ---- hero brand film ---------------------------------------------------- */
.vsl-frame video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  background:#000;
}
.vsl-frame.playing::after{opacity:0}
.vsl-frame.playing .vsl-play{opacity:0;pointer-events:none}
.vsl-sound{
  position:absolute;right:10px;top:10px;z-index:5;
  display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  border:1px solid rgba(255,255,255,.30);border-radius:var(--r);
  background:rgba(11,13,16,.72);backdrop-filter:blur(8px);
  color:var(--paper);padding:6px 10px;font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;font-variation-settings:"wdth" 108,"wght" 700;
  transition:background-color .3s ease,border-color .3s ease;
}
.vsl-sound:hover{background:var(--blue);border-color:var(--blue)}
.vsl-sound i{font-size:12px}

/* ---- before and after boards -------------------------------------------- */
/* Alex supplies these already composed as single before/after boards, so this
   is a plain figure grid, not a wipe slider. */
.ba-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(12px,1.6vw,18px)}
@media (max-width:760px){.ba-grid{grid-template-columns:1fr}}
.ba-card{
  margin:0;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  background:var(--ink-3);transition:transform .4s var(--ease),border-color .3s ease;
}
.ba-card:hover{transform:translateY(-4px);border-color:var(--line-2)}
.ba-card img{display:block;width:100%;height:auto;transition:transform .6s var(--ease)}
.ba-card:hover img{transform:scale(1.03)}
.ba-card:last-child:nth-child(odd){grid-column:1 / -1;justify-self:center;
  max-width:calc(50% - clamp(6px,.8vw,9px))}
@media (max-width:760px){.ba-card:last-child:nth-child(odd){max-width:100%}}
.ba-card figcaption{
  padding:15px 18px;font-size:14px;color:var(--muted);line-height:1.5;
  border-top:1px solid var(--line);
}

/* ---- homeowner video reviews -------------------------------------------- */
.vcard video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;background:#000;
}
/* Once a card is started it is a plain video player. The whole overlay goes
   pointer-events:none so every click lands on the video itself, which is what
   gives back the behaviour people expect: click the picture to play or pause,
   drag the scrub bar to skip. The browser's own controls do all of it. The one
   thing kept on top is the play badge, centred and shown whenever the film is
   paused, so a stopped card still reads as playable. There is no close button:
   pausing is what stopping a video means. */
.vcard-copy{transition:opacity .4s ease}
.vcard.started::after,
.vcard.started .vcard-copy{opacity:0}
.vcard.started .vcard-in{pointer-events:none}
.vcard.started:hover{transform:none}
.vcard.started .vcard-play{
  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  background:rgba(11,13,16,.62);border-color:rgba(255,255,255,.42);
}
.vcard.started:hover .vcard-play{background:var(--blue);border-color:var(--blue);
  transform:translate(-50%,-50%) scale(1.06)}
.vcard.playing .vcard-play{opacity:0}

/* ---- written review screenshots ----------------------------------------- */
/* Photographs of real Google, HomeStars and TrustedPros reviews, shown as
   images rather than retyped. Each screenshot already carries its own white
   card and coloured border, so the site adds no frame of its own: no border,
   no background, no caption bar. A two column grid keeps the two Google shots
   on one row, the two HomeStars shots on the next and the two TrustedPros
   shots on the last, so each platform reads as a block. They are all the same
   landscape shape, so nothing needs masonry. */
.shots{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,2.2vw,26px);align-items:start;
}
/* one column from the site's own mobile breakpoint, not lower. These are
   landscape screenshots: two of them side by side on a tablet leaves each
   about 330px wide and the review text stops being readable. */
@media (max-width:900px){.shots{grid-template-columns:minmax(0,1fr)}}
.shot{
  position:relative;margin:0;border-radius:var(--r);overflow:hidden;
  transition:transform .4s var(--ease);
}
.shot[role="button"]{cursor:pointer}
.shot:hover{transform:translateY(-4px)}
.shot:focus-visible{outline:2px solid var(--blue-hi);outline-offset:4px}
.shot img{display:block;width:100%;height:auto}
/* the whole screenshot opens the lightbox. Nothing sits over it until the
   cursor is on it, so the review reads clean at rest. */
.shot .shot-zoom{
  position:absolute;top:10px;right:10px;display:grid;place-items:center;
  width:32px;height:32px;border-radius:var(--r);
  background:rgba(11,13,16,.72);color:#fff;font-size:15px;
  opacity:0;transition:opacity .25s ease;pointer-events:none;
}
.shot:hover .shot-zoom,.shot:focus-visible .shot-zoom{opacity:1}
@media (hover:none){.shot .shot-zoom{display:none}}

/* ---- review lightbox ------------------------------------------------------
   Opened from a screenshot tile, which is where the review is readable at
   close to its own size. The sheet is capped by both width and height, so the
   picture lands inside the viewport without the page ever scrolling behind
   it, and it carries no frame either: the screenshot alone on the overlay,
   with the counter as a line of text underneath rather than a bar. */
/* [hidden] is a UA rule, so this class's own display would outrank it and a
   closed lightbox would stay a full page, invisible click blocker. It also has
   to clear the header at 900 and the sticky call bar at 880, or a modal ends
   up with the site painted over the top of it. */
.lb[hidden]{display:none}
.lb{
  position:fixed;inset:0;z-index:970;display:grid;place-items:center;
  padding:clamp(14px,4vw,44px);
  background:rgba(6,8,11,.92);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  opacity:0;transition:opacity .22s ease;
}
.lb.on{opacity:1}
.lb-fig{
  margin:0;max-width:min(1040px,100%);display:flex;flex-direction:column;gap:12px;
  transform:scale(.97);transition:transform .3s var(--ease);
}
.lb.on .lb-fig{transform:none}
.lb-fig img{
  display:block;width:auto;max-width:100%;border-radius:var(--r);
  max-height:calc(100dvh - clamp(112px,16vw,168px));height:auto;object-fit:contain;
}
.lb-fig figcaption{
  font-size:13px;color:var(--muted);display:flex;align-items:center;gap:12px;
}
.lb-fig figcaption b{color:var(--paper);font-variation-settings:"wdth" 104,"wght" 700}
.lb-fig figcaption span{color:var(--muted-2);font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;font-variation-settings:"wdth" 108,"wght" 700}
.lb-fig figcaption em{margin-left:auto;font-style:normal;color:var(--muted-2);
  font-size:12px;white-space:nowrap}
.lb-x,.lb-nav{
  position:absolute;display:grid;place-items:center;cursor:pointer;
  border:1px solid var(--line-2);border-radius:var(--r);
  background:rgba(18,21,26,.86);color:var(--paper);
  transition:background-color .25s ease,border-color .25s ease,transform .25s var(--ease);
}
.lb-x:hover,.lb-nav:hover{background:var(--blue);border-color:var(--blue-hi)}
.lb-x:focus-visible,.lb-nav:focus-visible{outline:2px solid var(--blue-hi);outline-offset:3px}
.lb-x{top:clamp(12px,2.4vw,26px);right:clamp(12px,2.4vw,26px);width:44px;height:44px;font-size:19px}
.lb-nav{top:50%;margin-top:-26px;width:52px;height:52px;font-size:22px}
.lb-prev{left:clamp(10px,2.2vw,28px)}
.lb-next{right:clamp(10px,2.2vw,28px)}
.lb-nav:hover{transform:scale(1.06)}
/* on a phone the arrows would sit on top of the screenshot, so drop them to
   the bottom of the sheet where a thumb reaches them anyway */
@media (max-width:720px){
  .lb{padding:14px}
  .lb-fig img{max-height:calc(100dvh - 210px)}
  .lb-nav{top:auto;bottom:16px;margin-top:0;width:56px;height:48px}
  .lb-prev{left:calc(50% - 66px)}
  .lb-next{right:calc(50% - 66px)}
  .lb-fig{margin-bottom:76px}
  .lb-x{width:40px;height:40px}
}
@media (prefers-reduced-motion:reduce){
  .lb,.lb-fig{transition:none}
  .lb-fig{transform:none}
}

/* ---- guarantee band, quote left and Alex right --------------------------- */
.guarantee .shell{max-width:var(--shell)}
.guar-split{
  display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr);
  gap:clamp(34px,5vw,72px);align-items:center;text-align:left;
}
@media (max-width:900px){
  .guar-split{grid-template-columns:1fr;text-align:center;justify-items:center}
}
/* Alex's portrait sits on the band unframed, as asked: no border, no card,
   no shadow. The photo's own dark backdrop does the work. */
.guar-portrait{display:block;width:100%;max-width:340px;height:auto}
.guar-split .guar-sign{margin-top:20px;text-align:center}

/* ==========================================================================
   17. SERVICE CARD LINK
   One card, one page, one slug. The whole tile is the link; the pill at the
   bottom is the visible affordance.
   ========================================================================== */
.tile{text-decoration:none;color:inherit;display:flex}
.tile-go{
  margin-top:16px;display:inline-flex;align-items:center;gap:8px;
  border:1px solid var(--line-2);border-radius:var(--r);
  padding:9px 14px;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  font-variation-settings:"wdth" 108,"wght" 700;color:var(--paper);
  transition:border-color .3s ease,background-color .3s ease;
}
.tile:hover .tile-go{border-color:var(--blue-hi);background:rgba(10,103,181,.18)}
.tile-go i{color:var(--blue-hi);font-size:14px;transition:transform .35s var(--ease)}
.tile:hover .tile-go i{transform:translateX(3px)}

/* ==========================================================================
   18. REVEAL
   ========================================================================== */
.rv{opacity:0;transform:translateY(26px);
  transition:opacity .85s var(--ease),transform .85s var(--ease)}
.rv.in{opacity:1;transform:none}
.rv-d1{transition-delay:.07s}.rv-d2{transition-delay:.14s}.rv-d3{transition-delay:.21s}
.rv-d4{transition-delay:.28s}.rv-d5{transition-delay:.35s}
img.rvi{opacity:0;transform:scale(1.035);
  transition:opacity 1.05s var(--ease),transform 1.3s var(--ease),filter .6s var(--ease)}
img.rvi.in{opacity:1;transform:none}

@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,img.rvi{opacity:1!important;transform:none!important}
  h1 .l span{transform:none!important}
  .cur{display:none!important}
  .marq-track{animation:none}
}

/* ==========================================================================
   19. SERVICES DROPDOWN IN THE HEADER
   Desktop opens on hover, bound to the wrapper that holds both the label and
   the panel so the cursor never crosses dead space. Spacing is padding on the
   wrapper, never margin, or the hover state drops mid travel and it flickers.
   ========================================================================== */
.nav-drop{position:relative;display:inline-flex;align-items:center;padding-block:22px}
.nav-drop > a{display:inline-flex;align-items:center;gap:6px}
.nav-drop > a i{font-size:13px;color:var(--blue-hi);transition:transform .3s var(--ease)}
.nav-drop:hover > a{color:var(--paper)}
.nav-drop:hover > a i{transform:rotate(180deg)}
.nav-panel{
  position:absolute;top:100%;left:50%;transform:translate(-50%,8px);
  min-width:270px;padding:8px;border-radius:var(--r);
  background:rgba(14,17,22,.97);backdrop-filter:blur(16px) saturate(150%);
  -webkit-backdrop-filter:blur(16px) saturate(150%);
  border:1px solid var(--line-2);box-shadow:0 30px 60px -30px rgba(0,0,0,.95);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .24s var(--ease),transform .24s var(--ease),visibility .24s;
}
.nav-drop:hover .nav-panel,
.nav-drop:focus-within .nav-panel{opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%,0)}
.nav-panel a{
  display:flex;align-items:center;gap:11px;padding:11px 13px;border-radius:var(--r);
  font-size:14.5px;color:var(--muted);white-space:nowrap;
  transition:background-color .22s ease,color .22s ease;
}
.nav-panel a:hover{background:rgba(10,103,181,.16);color:var(--paper)}
.nav-panel a[aria-current="page"]{color:var(--paper);background:rgba(10,103,181,.12)}

/* mobile header: logo, then phone, then the burger, and nothing else */
.nav-tel-m{display:none}
@media (max-width:900px){
  .nav-links{display:none}
  .nav-burger{display:block;margin-left:10px;font-size:24px}
  .nav-in{gap:10px}
  .nav-in .nav-cta{display:none}
  /* the logo has to give way, or the phone number wraps onto three lines
     between it and the burger on a 390px screen */
  .nav-brand{width:152px;height:24px}
  .nav-tel-m{
    display:inline-flex;align-items:center;gap:6px;margin-left:auto;
    white-space:nowrap;
    font-size:14.5px;font-variation-settings:"wdth" 104,"wght" 700;color:var(--paper);
  }
  .nav-tel-m i{color:var(--blue-hi);font-size:17px}
}
@media (max-width:400px){
  .nav-brand{width:124px;height:20px}
  .nav-tel-m{font-size:13.5px}
  .nav-tel-m i{font-size:15px}
}
/* iPhone SE. Even with the smaller logo and phone above, the header row still
   wanted 336px inside a 320px screen, so the last 6px of the burger's tap box
   sat off the right edge. The header buys the space back from its own padding
   and gaps; every other section keeps the full --pad. */
@media (max-width:360px){
  .nav-in{padding-inline:14px;gap:6px}
  .nav-burger{margin-left:4px}
}
@media (min-width:901px){.nav-burger{display:none}}

/* The burger stays put and becomes the X, and it is the only close control.
   The header owns a stacking context, so a z-index on the button alone cannot
   lift it above the drawer. The header itself has to outrank the drawer. */
body.nav-open .nav{z-index:960;background:transparent;backdrop-filter:none;
  -webkit-backdrop-filter:none;border-bottom-color:transparent}
.nav-burger{position:relative}

/* drawer: services stay nested as a tap to expand sub list */
.sheet{justify-content:flex-start;padding-top:96px;overflow-y:auto;gap:0}
.sheet-in{width:100%;display:flex;flex-direction:column;gap:0}
/* the nav rows only. Without the :not(.btn) this also claimed the Call Alex Now
   button at the foot of the drawer, and it out-specifies .btn and .btn-p, so the
   button lost its blue fill and its 32px side padding, took a divider line across
   it, and space-between threw the icon to one edge and the label hard against the
   other. Everything else in this rule is what an L1 row needs. */
.sheet a:not(.btn),.sheet-sub-btn{
  font-size:clamp(1.35rem,5.6vw,1.9rem);font-variation-settings:"wdth" 108,"wght" 800;
  letter-spacing:-.02em;padding:14px 0;border-bottom:1px solid var(--line);
  background:none;border-left:0;border-right:0;border-top:0;color:var(--paper);
  text-align:left;width:100%;cursor:pointer;font-family:inherit;
  display:flex;align-items:center;justify-content:space-between;gap:12px;
}
.sheet-sub-btn i{font-size:.7em;color:var(--blue-hi);transition:transform .3s var(--ease)}
.sheet-sub-btn[aria-expanded="true"] i{transform:rotate(180deg)}
.sheet-sub{display:grid;gap:0;padding-left:16px}
.sheet-sub[hidden]{display:none}
.sheet-sub a{
  font-size:clamp(1rem,4vw,1.15rem);font-variation-settings:"wdth" 104,"wght" 600;
  color:var(--muted);padding:12px 0;
}
/* the drawer CTA is an ordinary .btn-p.btn-lg now, so it needs no font-size of
   its own; the 15px here was only ever undoing the row rule's 1.9rem. */
.sheet .btn{margin-top:26px;align-self:flex-start}

/* ==========================================================================
   20. SERVICE PAGES
   Same header, footer, tokens and rhythm as the approved home page. The only
   new pieces are the page hero and the long form copy column.
   ========================================================================== */
.svc-hero{position:relative;overflow:hidden;border-bottom:1px solid var(--line);
  padding-top:calc(68px + clamp(48px,7vw,96px));padding-bottom:clamp(48px,7vw,88px)}
.svc-hero-bg{position:absolute;inset:0;z-index:0}
.svc-hero-bg img{width:100%;height:100%;object-fit:cover;filter:saturate(.5) brightness(.3)}
.svc-hero-bg::after{content:"";position:absolute;inset:0;
  background:linear-gradient(to right,rgba(11,13,16,.95) 26%,rgba(11,13,16,.62))}
.svc-hero .shell{position:relative;z-index:1}
.svc-hero h1{font-size:clamp(2.2rem,6vw,4rem);line-height:.98;letter-spacing:-.035em;
  font-variation-settings:"wdth" 114,"wght" 800;max-width:16ch}
.svc-hero .lede{margin-top:22px;max-width:60ch}
.svc-hero .btn{margin-top:30px}
.crumb{display:flex;align-items:center;gap:9px;margin-bottom:22px;
  font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted-2);
  font-variation-settings:"wdth" 108,"wght" 700}
.crumb a{color:var(--muted-2)}
.crumb a:hover{color:var(--blue-hi)}
.crumb i{font-size:12px}
/* the service hero button reads "Call Us 604-309-8317": the number is the same
   one in the header, set a touch lighter so the label still leads */
.btn-tel{font-variation-settings:"wdth" 104,"wght" 600;opacity:.92;margin-left:2px}

/* Alex pulled the three side cards: "just give it to me straight with the text
   on the screen". One centred reading column now. The column itself carries the
   measure so the headings, the paragraphs and the lists all share one left edge
   and one right edge, instead of the paragraphs alone being held to 68ch inside
   a shell twice that wide. */
/* this div also carries .shell, so its own padding-inline sits inside the
   max-width: add it back or the reading column comes out 128px narrower */
.svc-body{display:block;max-width:calc(720px + var(--pad) * 2);margin-inline:auto}
.svc-copy h2{font-size:clamp(1.35rem,2.7vw,1.95rem);line-height:1.15;letter-spacing:-.025em;
  margin:clamp(38px,4.6vw,58px) 0 16px;font-variation-settings:"wdth" 110,"wght" 800}
.svc-copy h2:first-child{margin-top:0}
.svc-copy h3{font-size:clamp(1.05rem,1.9vw,1.3rem);margin:30px 0 12px;
  font-variation-settings:"wdth" 108,"wght" 700}
.svc-copy p{margin:0 0 14px;font-size:16.5px;line-height:1.7;color:#C7D0DB}
.svc-copy ul{margin:0 0 18px;padding-left:22px;color:#C7D0DB;font-size:16.5px;line-height:1.7}
.svc-copy li{margin-bottom:7px}
.svc-copy .pull{
  margin:26px 0;padding:22px 24px;border-left:3px solid var(--blue);
  background:var(--ink-2);border-radius:0 var(--r) var(--r) 0;
  font-size:17.5px;line-height:1.55;color:var(--paper);
  font-variation-settings:"wdth" 104,"wght" 600;
}
.svc-copy .pull p{margin:0;color:inherit;font-size:inherit}
.svc-copy hr{border:0;border-top:1px solid var(--line);margin:clamp(34px,4vw,50px) 0}

.svc-box{border:1px solid var(--line-2);border-radius:var(--r);background:var(--ink-2);
  padding:24px}
.svc-box h4{font-size:16px;margin:0 0 12px;font-variation-settings:"wdth" 106,"wght" 700}
.svc-box p{margin:0 0 16px;font-size:14.5px;line-height:1.6;color:var(--muted)}
.svc-box .btn{width:100%}
.svc-box ul{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.svc-box ul a{display:flex;align-items:center;gap:10px;font-size:14.5px;color:var(--muted);
  transition:color .22s ease}
.svc-box ul a:hover{color:var(--blue-hi)}
.svc-box ul i{color:var(--blue-hi);font-size:15px;flex:none}

