/* ==========================================================================
   Aspen Contracting — homepage
   Built from Figma: UX-James, node 690:1561 (1600 × 2927)
   Type:   Playfair Display (display, 400/700) · Inter (UI/body)
           Figma specified Antic Didone; swapped for Playfair to get a real bold.
   ========================================================================== */

:root{
  --ink:#333333;          /* headings, dark pill, card titles */
  --body:#404040;         /* body copy, nav links */
  --muted:#6e6e6e;        /* card body copy */
  --eyebrow:#797979;
  --line:#e4e4e4;         /* card border, testimonial pill */
  --line-strong:#c2c2c2;  /* card button border */

  /* DISPLAY FONT — swap point 2 of 2 (the other is the Google Fonts <link> in
     <head>). Drives H1, "Our Services", card titles and the pull-quote.
     Playfair Display ships a real 700, so the headline bolds are genuine. */
  --display:"Playfair Display",Georgia,"Times New Roman",serif;
  --ui:"Inter",-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;

  /* Content sits in an 85% column; the nav bar and the hero photo stay full-bleed.
     The column stops growing at the Figma frame's own content width, so past
     ~1704px the card grid is exactly the 468.85 it was drawn at. */
  --content-w:85%;
  --content-max:1448.76px;
  --gutter:clamp(20px,2.5vw,40px);   /* nav bar inset from the screen edges */

  /* Figma had 21 / 25. Widening the gutters narrows the cards, since the 85%
     column is fixed and the three cards divide whatever is left. */
  --card-gap-x:32px;
  --card-gap-y:34px;
  --card-pad:33.07px;
  --media-h:313.24px;
  --radius:20px;
}

*,*::before,*::after{box-sizing:border-box}

html{-webkit-text-size-adjust:100%}

/* ------------------------------------------------------------ preloader -- */
/* Shown until the page (images, fonts, video) finishes loading — see the
   `load`-event listener in site.js. Sits above everything, including the
   fixed header. */
.preloader{
  position:fixed;inset:0;
  z-index:1000;
  display:flex;
  align-items:center;
  justify-content:center;
  background:#fff;
  transition:opacity .5s ease,visibility 0s linear .5s;
}
.preloader.is-hidden{
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity .5s ease;
}
.preloader__leaf{
  width:56px;
  height:auto;
  animation:preloader-pulse 1.4s ease-in-out infinite;
}
@keyframes preloader-pulse{
  0%,100%{transform:scale(1);opacity:.5}
  50%{transform:scale(1.14);opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .preloader__leaf{animation:none}
}

body{
  margin:0;
  background:#fff;
  color:var(--body);
  font-family:var(--ui);
  font-weight:500;
  font-size:18px;          /* design was 20 */
  line-height:29.5px;      /* leading scaled to match, from 32.739 */
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

h1,h2,h3,p{margin:0}
ul{margin:0;padding:0;list-style:none}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}

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

.wrap{
  width:var(--content-w);
  max-width:var(--content-max);
  margin:0 auto;
}

.skip{
  position:absolute;left:-9999px;top:0;z-index:100;
  background:var(--ink);color:#fff;padding:12px 20px;font-size:15px;
}
.skip:focus{left:0}

/* --------------------------------------------------------------- header -- */

.head{
  position:fixed;              /* stays with the reader instead of scrolling away */
  top:0;left:0;right:0;
  z-index:30;
  /* Figma had 157.7, already trimmed to 137.7 to lift the bar. Trimmed again to
     124 for a slightly shorter bar — nav text, nav gap, and the CTA pill are
     unchanged, just recentred within the shorter box. */
  height:99px;
  display:flex;
  align-items:center;
  /* transparent border reserves the 1px so the bar doesn't jump when it solidifies */
  border-bottom:1px solid transparent;
  transition:height .3s ease,background-color .3s ease,border-color .3s ease,box-shadow .3s ease;
}
/* Applied once the hero has scrolled past — over the hero it stays transparent. */
.head.is-stuck{
  height:69px;
  background:#fff;
  border-bottom-color:var(--line);
  box-shadow:0 1px 20px rgba(0,0,0,.06);
}
/* Full-bleed bar: its contents run out to the screen edges, wider than the
   85% column below it. */
.head__in{display:flex;align-items:center;width:100%;padding:0 var(--gutter)}

.logo{flex:none}
.logo img{width:166px;height:53.7px}   /* Figma had 204 × 66; was 184 × 59.5 */

.navwrap{display:flex;align-items:center;margin-left:auto}
.nav{display:flex;align-items:center;gap:42px}
.nav__link{
  display:flex;align-items:center;gap:9px;
  font-size:15px;font-weight:500;line-height:32.739px;
  color:var(--body);
  transition:color .2s ease;
}
.nav__link:hover{color:var(--ink)}
.nav__caret{width:12px;height:7px;flex:none;transition:transform .2s ease}

/* "Our Services" dropdown — a disclosure, not a link: its only job is exposing
   the six service pages, so it's a <button>, opened by hover on desktop (pure
   CSS) and by click/tap everywhere (JS toggles .is-open — see site.js). */
.nav__item{position:relative}
.nav__trigger{background:none;border:0;padding:0;cursor:pointer}
.nav__item:hover .nav__caret,
.nav__trigger[aria-expanded="true"] .nav__caret{transform:rotate(180deg)}

/* The wrapper starts flush against the trigger (no gap) so the padding below
   is still part of the hoverable subtree — the mouse can cross from trigger
   into panel without the gap closing it early. */
.nav__dropdown{
  position:absolute;
  top:100%;
  left:50%;
  transform:translateX(-50%);
  padding-top:18px;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity .18s ease,visibility .18s;
  z-index:40;
}
.nav__item:hover .nav__dropdown,
.nav__dropdown.is-open{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
}
.nav__dropdown-panel{
  display:flex;
  flex-direction:column;
  min-width:230px;
  background:#fff;
  border:1px solid var(--line);
  border-radius:14px;
  padding:10px;
  box-shadow:0 24px 48px -20px rgba(15,15,15,.18);
}
.nav__dropdown-link{
  padding:10px 14px;
  border-radius:8px;
  font-size:14px;
  font-weight:500;
  color:var(--body);
  white-space:nowrap;
  transition:background .15s ease,color .15s ease;
}
.nav__dropdown-link:hover{background:#f5f4f2;color:var(--ink)}
.nav__dropdown-divider{height:1px;background:var(--line);margin:6px 4px}

/* Dark pill — header */
.btn-dark{
  display:inline-flex;
  align-items:center;
  justify-content:space-between;
  gap:10px;
  background:var(--ink);
  color:#fff;
  border-radius:50px;
  white-space:nowrap;
  transition:background .2s ease,transform .2s ease;
}
.btn-dark:hover{background:#222}
.btn-dark img{flex:none}

.head .btn-dark{
  width:255.371px;
  height:46.344px;
  margin-left:72px;
  padding:0 8.6px 0 21.7px;
  font-size:15px;
}
/* The exported asset is an UP arrow; Figma rotates it 90° so it reads right. */
.head .btn-dark img{width:34.1px;height:34.1px;transform:rotate(90deg)}

.burger{display:none;width:28px;height:17px;flex-direction:column;justify-content:space-between;margin-left:auto}
.burger span{display:block;height:2px;background:var(--ink);border-radius:2px;transition:transform .3s ease,opacity .2s ease}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(7.5px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-7.5px) rotate(-45deg)}

/* ----------------------------------------------------------------- hero -- */

.hero{
  position:relative;
  height:clamp(620px,60.07vw,820px);
  overflow:hidden;
}

.hero__bg{position:absolute;inset:0;overflow:hidden;background:#fff}
/* The static poster photo carried its own white falloff on the left and
   bottom baked into the pixels. The video render doesn't have that same
   fade, so `.hero__veil` recreates it in CSS — same left/bottom blend into
   the page, now working for both the poster and the live video.
   `contain` (not cover) is deliberate: the hero's height is capped at 961, so a
   width-driven image outgrows it on wide screens and gets clipped through the
   house. Contain fits the whole frame and lets it shrink instead; the space it
   leaves is white, which is what the veil now blends into. */
.hero__bg img,.hero__bg video{
  position:absolute;
  /* Plain top offset instead of transform:translateY — a transform promotes
     the video to its own GPU compositing layer, and Chrome renders a visible
     seam at that layer's clipped edge, right where `contain`'s letterbox
     boundary sits on wide screens (blank on the left, frame starting
     partway across). A positional offset moves the frame the same 40px
     without ever creating that layer. */
  top:-40px;
  right:0;
  left:0;
  width:100%;
  /* 100% + the top offset above, so shifting the frame up doesn't uncover a
     bare strip of unveiled background at the bottom — the element is that
     much taller than the box first, then pulled up to match, so its bottom
     edge still lands flush with the box's own bottom. */
  height:calc(100% + 40px);
  max-width:none;
  object-fit:contain;
  /* Anchored top, not bottom: at mid desktop widths `contain`'s letterbox
     gap lands vertically (width, not height, is the fitting dimension), and
     a bottom anchor put that gap above the frame — a blank strip behind the
     transparent header instead of the photo/video showing through flush
     with the page top. Only wide/ultrawide screens have no vertical gap to
     move (height is the fitting dimension there), so this is a no-op there. */
  object-position:right top;
}

/* Past this width, `.hero`'s capped height (820px) makes height, not width,
   the constraining dimension for `contain` — the frame stops spanning full
   width and a blank letterboxed gap opens up on the left, edge sitting
   wherever the video happens to start. That edge is a persistent rendering
   seam in Chrome (a stray vertical strip), so `cover` here removes the
   letterbox — and the seam with it — by filling the box completely instead
   of leaving a boundary for one to sit on. Trades a sliver of the frame's
   left/right edges for that; the veil already hides the left side. */
@media (min-width:1366px){
  .hero__bg img,.hero__bg video{object-fit:cover}
}

/* Left and bottom fade to white, blending the frame into the page — the
   same falloff the static poster used to carry baked into its own pixels. */
.hero__veil{
  position:absolute;inset:0;
  pointer-events:none;
  /* Stays fully opaque through roughly the text column's own width
     (hero__lead caps at 41.25vw), then fades out faster than before —
     covers less of the house on the right without touching legibility
     where the copy actually sits. The bottom fade reaches all the way to
     100% (not a shorter stop like the horizontal one) — the video's own
     footage doesn't fade to white near its bottom edge the way the static
     poster photo did, so anything short of full height left a hard seam
     between video content and the page's white background below the hero. */
  background:
    linear-gradient(90deg,#fff 0%,rgba(255,255,255,.95) 18%,rgba(255,255,255,.65) 36%,rgba(255,255,255,0) 46%),
    linear-gradient(0deg,#fff 0%,rgba(255,255,255,.9) 6%,rgba(255,255,255,.3) 16%,rgba(255,255,255,0) 26%);
  /* Thins the veil out specifically in the strip between the nav and where
     the copy actually starts (padding-top on .hero__copy, just below) — the
     horizontal fade above is full-height, so that empty strip was just as
     opaque as the text behind it needs to be. A mask only dials back
     *strength* there; it can't introduce a hard edge the way changing the
     gradient's own stops could, and it doesn't touch the bottom gradient's
     fade-to-100% at all, so the seam fix there is untouched. */
  -webkit-mask-image:linear-gradient(180deg,rgba(0,0,0,.4) 0px,rgba(0,0,0,1) 200px);
  mask-image:linear-gradient(180deg,rgba(0,0,0,.4) 0px,rgba(0,0,0,1) 200px);
}

.hero__in{position:relative;height:100%}
.hero__copy{padding-top:209.73px}   /* Figma had 224.73; +25, -40 */

/* Load sequence: kicker, headline, paragraph, phone, CTA rise in on a stagger.
   Wrapped in no-preference so a reduced-motion user (or a browser where the JS/CSS
   fails) sees the content at full opacity from the first paint — never opacity:0. */
@media (prefers-reduced-motion:no-preference){
  .hero__eyebrow,.hero h1,.hero__lead,.hero__phone,.hero .btn-dark{
    opacity:0;
    transform:translateY(16px);
    animation:hero-rise .8s cubic-bezier(.22,.68,.32,1) forwards;
  }
  .hero__eyebrow{animation-delay:.05s}
  .hero h1{animation-delay:.16s}
  .hero__lead{animation-delay:.29s}
  .hero__phone{animation-delay:.4s}
  .hero .btn-dark{animation-delay:.5s}
}
@keyframes hero-rise{to{opacity:1;transform:none}}

.hero__eyebrow{
  font-size:18px;
  font-weight:600;
  line-height:32.739px;
  letter-spacing:.18px;
  text-transform:uppercase;
  color:var(--eyebrow);
}

/* Margin is tuned so the CAP HEIGHT lands where Figma's does (y=272). Figma
   forces a 32.739px line-height on 60px display type, so its box top sits well
   below its glyphs — matching the box would drop the headline ~11px too low. */
.hero h1{
  /* 1 lands ~19px of visual gap below the kicker. The value looks small because
     it fights the kicker's 32.7px leading on 18px type — measure the cap height,
     not the box. */
  margin-top:1px;
  font-family:var(--display);
  font-weight:700;
  /* 57 at the 1600 reference width. Clears the roofline comfortably — 65
     overshot it by 19px, 61 cleared by 46. */
  font-size:clamp(34px,3.5625vw,57px);
  line-height:1;
  letter-spacing:-.02em;                 /* tightened for Playfair at display size */
  color:var(--ink);
  max-width:none;     /* Playfair Bold at 70px is wide; let the column bound it */
}

/* 660 / 1600 = 41.25vw — sampled to nearly match the H1 text's own width
   (681.7px at the 1600 reference) so the paragraph block lines up with the
   headline above it rather than reading as a narrower column. Checked against
   the roofline at the paragraph's lower position (877px) — 660 clears it by
   ~140px, well short of the overlap the narrower 592 measure was fixed for. */
.hero__eyebrow,.hero__lead,.hero__phone{max-width:min(660px,41.25vw)}
/* 20.9 gives ~33px from the H1 baseline to the paragraph's cap — pulled back
   from 45 (32.9), which read as too loose once the wider measure was in. */
.hero__lead{margin-top:20.9px}
.hero__phone{margin-top:17.1px;font-weight:800}

/* Dark pill — hero */
/* Reduced from the design's 340.5 × 61.8 / 20px / 53px arrow. Width hugs the
   label now rather than being pinned, so the pill stays snug at any size. */
.hero .btn-dark{
  margin-top:18px;
  height:54px;
  border-radius:66.667px;
  padding:0 5px 0 26px;
  font-size:17px;
}
.hero .btn-dark img{width:44px;height:44px;transform:rotate(90deg)}

/* ------------------------------------------------------------- services -- */

.services{padding-top:0}

.services h2{
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(30px,3.25vw,52px);   /* 52 at the 1600 reference width; was 60 */
  line-height:1;
  letter-spacing:-.02em;
  color:var(--ink);
}
.services__sub{margin-top:5px}

/* Same rise as the hero's load sequence, but this section is below the fold, so
   it can't play on page load — JS toggles .is-visible via IntersectionObserver
   (see script at the foot of the page) once the heading scrolls into view. */
@media (prefers-reduced-motion:no-preference){
  .services h2,.services__sub{
    opacity:0;
    transform:translateY(16px);
    transition:opacity .8s cubic-bezier(.22,.68,.32,1),transform .8s cubic-bezier(.22,.68,.32,1);
  }
  .services__sub{transition-delay:.1s}
  .services h2.is-visible,.services__sub.is-visible{opacity:1;transform:none}
}

.grid{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:var(--card-gap-y) var(--card-gap-x);
  margin-top:36px;
}

.card{
  display:flex;
  flex-direction:column;
  min-height:626.464px;
  background:#fff;
  border:2px solid var(--line);
  border-radius:var(--radius);
  overflow:hidden;
  /* Same curve as the hero's load-in, so the page's motion reads as one language. */
  transition:transform .35s cubic-bezier(.22,.68,.32,1),box-shadow .35s ease,border-color .35s ease;
}
/* Hover lift: no card is announced as clickable elsewhere, so the whole surface
   answers on hover — small rise, soft shadow, border fades against it. */
.card:hover{
  transform:translateY(-6px);
  border-color:transparent;
  box-shadow:0 24px 48px -20px rgba(15,15,15,.22);
}

.card__media{flex:none;height:var(--media-h);overflow:hidden}
.card__media a{display:block;width:100%;height:100%}
.card__media img{
  width:100%;
  height:100%;
  object-fit:cover;
  /* every frame is anchored to the left and bottom of its crop in the design */
  object-position:left bottom;
  transition:transform .5s ease;
}
.card:hover .card__media img{transform:scale(1.06)}
/* Mirrored in the source design */
.card__media--flip img{transform:scaleX(-1)}
.card:hover .card__media--flip img{transform:scaleX(-1) scale(1.06)}

/* Scroll reveal: fade + rise as each card enters the viewport. JS toggles
   .is-visible and staggers by column — see the script at the foot of the page.
   Wrapped in no-preference for the same reason as the hero sequence above. */
@media (prefers-reduced-motion:no-preference){
  .card{opacity:0;transform:translateY(24px)}
  .card.is-visible{opacity:1;transform:none}
  /* Higher specificity than .card.is-visible, not just later in source — so
     hover wins over the revealed state regardless of how these rules get
     reordered later. */
  .card.is-visible:hover{transform:translateY(-6px)}
}

.card__body{
  display:flex;
  flex-direction:column;
  flex:1;
  padding:29.57px var(--card-pad) 40px;
}

.card h3{
  font-family:var(--display);
  font-weight:700;
  font-size:30px;
  line-height:32.739px;
  letter-spacing:-.01em;   /* less negative than the big sizes — optical tracking */
  color:var(--ink);
}

.card p{
  margin-top:16.76px;
  /* Minimum breathing room before the button. The button also has margin-top:auto,
     which pins it to the card foot — but that collapses to zero on the cards with
     the longest copy, which is where they were crowding. */
  margin-bottom:28px;
  font-size:15px;
  line-height:25px;
  color:var(--muted);
}

/* Outlined pill — sits at a fixed distance from the card foot, as designed */
.btn-outline{
  align-self:flex-start;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:5px 22px;
  border:1.5px solid var(--line-strong);
  border-radius:48.042px;
  background:#fff;
  font-size:14.413px;
  font-weight:500;
  line-height:31.457px;
  color:var(--ink);
  white-space:nowrap;
  transition:border-color .2s ease,background .2s ease;
}
.btn-outline:hover{border-color:var(--ink);background:#fafafa}
.card .btn-outline{margin-top:auto}

/* ---------------------------------------------------------- testimonial -- */

.quote{
  padding-top:72.5px;
  padding-bottom:128px;
  text-align:center;
}
/* line-height:0 keeps the inline pill off a text baseline; children set their own */
.quote figure{margin:0;line-height:0}
.quote .btn-outline{align-self:auto;border-color:var(--line)}

.quote blockquote{
  margin:25.9px auto 0;
  max-width:1166.06px;
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(24px,2.75vw,44px);   /* 44 at the 1600 reference width */
  line-height:1.5;
  letter-spacing:-.02em;
  color:var(--ink);
}
.quote figcaption{
  margin-top:33px;
  font-size:21px;
  font-weight:500;
  line-height:18px;
  color:var(--ink);
}

/* ---------------------------------------------------------------- foot -- */
/* Light, not a dark band — the logo asset's fills (#333 text, a white backing
   shape) are fixed colors baked into the SVG, so it only reads correctly on a
   light ground. Keeping the footer light also matches the rest of the page,
   which never goes to a full-bleed dark section. */

.foot{border-top:1px solid var(--line)}

.foot__top{
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:48px;
  padding:48px 0 28px;
}

.foot__brand{display:flex;flex-direction:column;gap:14px;max-width:400px}
.foot__logo{width:184px;height:59.5px}
.foot__blurb{font-size:16px;line-height:1.6;color:var(--muted)}

.foot__menu,.foot__services,.foot__contact{flex:none;padding-top:10px}   /* optically aligns the header with the logo mark */
/* Shared by the menu and contact columns — sits above each column's content
   like a section heading, heavier than .foot__label so the hierarchy reads:
   column header, then each item's own small label. */
.foot__title{
  font-size:13px;
  font-weight:700;
  letter-spacing:.02em;
  color:var(--ink);
  margin-bottom:14px;
  white-space:nowrap;
}

.foot__links{display:flex;flex-direction:column;align-items:flex-start;gap:10px}
.foot__navlink{font-size:15px;font-weight:500;color:var(--body);transition:color .2s ease}
.foot__navlink:hover{color:var(--ink)}

/* Right-aligned text of very different lengths shared no edge and read as
   loose, unlabelled text — left-aligning under the "Contact" header already
   groups them; the type now matches .foot__navlink so both columns read as
   one consistent list style rather than two different treatments. */
.foot__items{display:flex;flex-direction:column;align-items:flex-start;gap:10px}
.foot__link{font-size:15px;font-weight:500;color:var(--body);transition:color .2s ease}
.foot__link:hover{color:var(--ink)}

.foot__bottom{
  padding:16px 0 32px;
  border-top:1px solid var(--line);
}
.foot__copy{font-size:13px;color:var(--muted)}

/* ------------------------------------------------------------ page hero -- */
/* Interior-page banner (service pages, etc). Unlike the homepage hero, this
   photo has no baked-in white falloff — any card photo needs to work here — so
   contrast comes from a dark scrim instead of a light one, standard treatment. */

.page-hero{
  position:relative;
  min-height:420px;
  display:flex;
  align-items:center;   /* was flex-end — content now centers in the image row */
  overflow:hidden;
  margin-top:99px;   /* clears the fixed header at its unstuck height */
}
.page-hero__bg{position:absolute;inset:0}
.page-hero__bg img{width:100%;height:100%;object-fit:cover}
.page-hero__scrim{
  position:absolute;inset:0;
  /* Darkened further for legibility — six different photos share this one
     scrim, so it has to hold up against the lightest of them (the kitchen). */
  background:linear-gradient(180deg,rgba(15,15,15,.46) 0%,rgba(15,15,15,.58) 45%,rgba(15,15,15,.88) 100%);
}

.page-hero__in{position:relative;padding:56px 0 48px;color:#fff}

/* Its own row below the image, on the page background — dark-on-light instead
   of the white-on-photo it used when it lived inside the hero. */
.breadcrumb-bar{padding:22px 0;border-bottom:1px solid var(--line)}
.breadcrumb{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:13px}
.breadcrumb a{color:var(--body);transition:color .2s ease}
.breadcrumb a:hover{color:var(--ink)}
.breadcrumb__sep{color:var(--line-strong)}
.breadcrumb__current{color:var(--ink);font-weight:500}

/* Title and blurb center on the image. */
.page-hero h1.page-hero__title--sm{font-size:clamp(26px,5.6vw,60px)}   /* 12px down from the default clamp, contact page only */

.page-hero h1{
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(38px,5.6vw,72px);
  letter-spacing:-.02em;
  line-height:1.05;
  max-width:960px;
  margin-left:auto;
  margin-right:auto;
  text-align:center;
}
.page-hero__intro{
  margin-top:18px;
  margin-left:auto;
  margin-right:auto;
  font-size:17px;
  line-height:1.6;
  color:rgba(255,255,255,.9);
  max-width:820px;
  text-align:center;
}

/* Placeholder content well — real copy lands here page by page. */
.page-content{padding:72px 0 24px}
.page-content__placeholder{
  padding:48px;
  border:1px dashed var(--line-strong);
  border-radius:var(--radius);
  font-size:15px;
  color:var(--muted);
  text-align:center;
}
/* ------------------------------------------------------- before/after --- */
/* Drag-to-reveal comparison. --pos (0-100, set inline + updated by JS) is
   the % of the frame width the "before" layer covers, measured from the left. */

.ba-slider-heading__eyebrow{text-align:left}
.ba-slider-heading__title{margin-bottom:40px;max-width:none;text-align:left}

.ba-slider{width:100%;margin:0 auto}
.ba-slider + .ba-slider{margin-top:32px}
.ba-slider__frame{
  position:relative;
  aspect-ratio:16/10;
  overflow:hidden;
  border-radius:var(--radius);
  background:#111;
  box-shadow:0 24px 48px -20px rgba(15,15,15,.28);
  cursor:ew-resize;
  user-select:none;
  -webkit-user-select:none;
  /* Without this, touch drags fight the browser's own scroll-gesture
     recognizer — it has to wait a beat to decide "is this a page scroll or
     a drag?" before committing, which reads as jerky/delayed input on
     phones specifically (desktop mouse drag was never affected). */
  touch-action:none;
}
.ba-slider__img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  display:block;
  pointer-events:none;
}
.ba-slider__img--before{
  clip-path:inset(0 calc(100% - var(--pos) * 1%) 0 0);
}
/* Scrim so the labels/hint hold up against any photo, light sky included. */
.ba-slider__scrim{
  position:absolute;top:0;left:0;right:0;height:88px;
  background:linear-gradient(180deg,rgba(15,15,15,.78) 0%,rgba(15,15,15,0) 100%);
  pointer-events:none;
}
.ba-slider__tag{
  position:absolute;top:20px;
  font-family:var(--ui);
  font-weight:600;
  font-size:13px;
  letter-spacing:.02em;
  color:#fff;
  text-transform:uppercase;
  text-shadow:0 1px 3px rgba(0,0,0,.5);
  pointer-events:none;
}
.ba-slider__tag--before{left:24px}
.ba-slider__tag--after{right:24px}
.ba-slider__hint{
  position:absolute;top:20px;left:50%;
  transform:translateX(-50%);
  margin:0;
  font-family:var(--ui);
  font-size:13px;
  color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,.5);
  white-space:nowrap;
  pointer-events:none;
}
.ba-slider__handle{
  position:absolute;top:0;bottom:0;
  left:calc(var(--pos) * 1%);
  transform:translateX(-50%);
  display:flex;
  align-items:center;
  justify-content:center;
  width:2px;
}
.ba-slider__handle-line{
  position:absolute;inset:0;
  background:#fff;
  box-shadow:0 0 0 1px rgba(0,0,0,.08);
}
.ba-slider__handle-grip{
  position:relative;
  display:flex;
  align-items:center;
  justify-content:center;
  width:46px;height:130px;
  border-radius:999px;
  background:#fff;
  color:var(--ink);
  box-shadow:0 6px 16px rgba(15,15,15,.25);
}
.ba-slider__handle-grip svg{width:20px;height:17px}
.ba-slider__handle:focus-visible .ba-slider__handle-grip{
  outline:2px solid var(--ink);
  outline-offset:3px;
}

@media (max-width:760px){
  .ba-slider__tag{font-size:11px;top:14px}
  .ba-slider__tag--before{left:16px}
  .ba-slider__tag--after{right:16px}
  .ba-slider__hint{font-size:11px;top:14px}
  .ba-slider__handle-grip{width:36px;height:96px}
  .ba-slider__handle-grip svg{width:16px;height:14px}
}

/* ---------------------------------------------------------- gallery grid -- */

.gallery-grid__label{
  margin-top:48px;
  font-size:14px;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--eyebrow);
}
.gallery-grid{
  display:grid;
  grid-template-columns:repeat(5,1fr);
  gap:20px;
  margin-top:20px;
}
.gallery-grid--4col{grid-template-columns:repeat(4,1fr)}
.gallery-grid--3col{grid-template-columns:repeat(3,1fr)}
.gallery-grid__item{
  display:block;
  padding:0;
  border:0;
  border-radius:var(--radius);
  overflow:hidden;
  aspect-ratio:4/3;
  background:none;
  cursor:pointer;
}
.gallery-grid__item img{
  width:100%;height:100%;
  object-fit:cover;
  display:block;
  transition:transform .5s ease;
}
.gallery-grid__item:hover img{transform:scale(1.06)}
.gallery-grid__item:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

@media (max-width:900px){
  .gallery-grid{grid-template-columns:repeat(2,1fr);gap:16px}
}

/* -------------------------------------------------------------- lightbox -- */

.lightbox{
  position:fixed;inset:0;
  z-index:200;
  display:flex;
  align-items:center;
  justify-content:center;
  background:rgba(10,10,10,.92);
  padding:24px;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity .25s ease,visibility 0s linear .25s;
}
.lightbox.is-open{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
  transition:opacity .25s ease;
}
.lightbox__img{
  max-width:min(1100px,90vw);
  max-height:88vh;
  transform:scale(.96);
  transition:transform .25s ease;
  object-fit:contain;
  border-radius:8px;
}
.lightbox.is-open .lightbox__img{transform:scale(1)}
.lightbox__close{
  position:absolute;top:24px;right:24px;
  display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;
  border:0;border-radius:50%;
  background:rgba(255,255,255,.1);
  color:#fff;
  transition:background .2s ease;
}
.lightbox__close:hover{background:rgba(255,255,255,.2)}
.lightbox__nav{
  position:absolute;top:50%;
  transform:translateY(-50%);
  display:flex;align-items:center;justify-content:center;
  width:52px;height:52px;
  border:0;border-radius:50%;
  background:rgba(255,255,255,.1);
  color:#fff;
  transition:background .2s ease;
}
.lightbox__nav:hover{background:rgba(255,255,255,.2)}
.lightbox__nav--prev{left:24px}
.lightbox__nav--next{right:24px}

@media (max-width:600px){
  .lightbox__close{top:14px;right:14px;width:38px;height:38px}
  .lightbox__nav{width:42px;height:42px}
  .lightbox__nav--prev{left:10px}
  .lightbox__nav--next{right:10px}
}

/* ------------------------------------------------------------- process -- */

.process{padding:24px 0 96px}
.process__eyebrow{
  font-size:14px;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--eyebrow);
}
.process__title{
  margin-top:6px;
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(28px,3.25vw,42px);
  letter-spacing:-.02em;
  color:var(--ink);
  max-width:none;
}
.process__grid{
  display:grid;
  grid-template-columns:repeat(4,1fr);
  gap:28px;
  margin-top:56px;
}
.process__step{
  position:relative;
  padding:32px 26px 30px;
  background:#fff;
  border:2px solid var(--line);
  border-radius:var(--radius);
  transition:transform .35s cubic-bezier(.22,.68,.32,1),box-shadow .35s ease,border-color .35s ease;
}
.process__step:hover{
  transform:translateY(-6px);
  border-color:transparent;
  box-shadow:0 24px 48px -20px rgba(15,15,15,.18);
}
.process__icon{
  display:flex;
  align-items:center;
  justify-content:center;
  width:52px;height:52px;
  border-radius:50%;
  background:#f4f2ee;
  color:var(--ink);
  margin-bottom:22px;
}
.process__num{
  display:block;
  font-family:var(--display);
  font-weight:700;
  font-size:13px;
  letter-spacing:.04em;
  color:var(--line-strong);
  margin-bottom:8px;
}
.process__step h3{
  font-family:var(--display);
  font-weight:700;
  font-size:21px;
  letter-spacing:-.01em;
  color:var(--ink);
}
.process__step p{
  margin-top:10px;
  font-size:15px;
  line-height:1.6;
  color:var(--muted);
}
/* Chevron in the gap between cards, pointing to the next step. Skipped on the
   last card (no markup) and hidden once cards wrap to their own rows. */
.process__arrow{
  display:block;
  position:absolute;
  top:50%;right:-38px;
  transform:translate(50%,-50%);
  color:var(--line-strong);
}

@media (max-width:900px){
  .process{padding:20px 0 64px}
  .process__grid{grid-template-columns:repeat(2,1fr);gap:32px 24px}
  .process__arrow{display:none}
}
@media (max-width:520px){
  .process__grid{grid-template-columns:1fr;gap:24px}
}

/* ------------------------------------------------------------- cta band -- */

.cta-band{
  padding:80px 0;
  margin-top:16px;
  background:var(--ink);
  color:#fff;
  text-align:center;
}
.cta-band__in h2{
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(28px,3.25vw,42px);
  letter-spacing:-.02em;
}
.cta-band__in p{
  margin-top:14px;
  font-size:17px;
  color:rgba(255,255,255,.78);
  max-width:480px;
  margin-left:auto;
  margin-right:auto;
}
.cta-band__actions{
  margin-top:32px;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:18px;
}
.btn-light{
  display:inline-flex;
  align-items:center;
  gap:12px;
  padding:14px 24px;
  background:#fff;
  color:var(--ink);
  font-family:var(--ui);
  font-weight:600;
  font-size:15px;
  border-radius:50px;
  white-space:nowrap;
  transition:background .2s ease,transform .2s ease;
}
.btn-light:hover{background:#f0f0f0}
.btn-light svg{flex:none}

@media (max-width:520px){
  .cta-band{padding:56px 0}
}

/* -------------------------------------------------------------- contact -- */

.contact{
  padding:72px 0 96px;
  display:grid;
  grid-template-columns:1.6fr 1fr;
  gap:56px;
  align-items:start;
}

.contact-form__title{
  font-family:var(--display);
  font-weight:700;
  font-size:clamp(26px,2.8vw,34px);
  letter-spacing:-.02em;
  color:var(--ink);
}
.contact-form__sub{margin-top:8px;font-size:15px;color:var(--muted)}

.field-row{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.field{margin-top:20px;display:flex;flex-direction:column}
.field label{
  margin-bottom:8px;
  font-size:13px;
  font-weight:600;
  color:var(--body);
}
.field input,.field select,.field textarea{
  width:100%;
  padding:13px 16px;
  border:1.5px solid var(--line);
  border-radius:10px;
  background:#fff;
  font-family:var(--ui);
  font-size:15px;
  color:var(--ink);
  transition:border-color .2s ease;
}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;
  border-color:var(--ink);
}
.field textarea{resize:vertical}

/* Spam honeypot — off-screen, not display:none, so bots that skip hidden
   fields still fill it in and get caught. */
.field--hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.field--captcha input[type="text"]{max-width:120px}

.contact-form__submit{margin-top:28px;padding:0 8px 0 26px;height:52px}
.contact-form__submit img{width:34.1px;height:34.1px;transform:rotate(90deg)}
.contact-form__submit[disabled]{opacity:.6;cursor:default}

.contact-form__error{
  margin-top:20px;
  padding:14px 16px;
  border:1px solid #e3b3ac;
  border-radius:10px;
  background:#fdf3f2;
  font-size:14px;
  color:#8a3b34;
}
.contact-form__error a{color:inherit;text-decoration:underline}

.contact-form__success{
  padding:48px;
  background:#faf9f7;
  border:1px solid var(--line);
  border-radius:var(--radius);
}

.contact-info{
  display:flex;
  flex-direction:column;
  gap:32px;
  padding:36px;
  background:#faf9f7;
  border:1px solid var(--line);
  border-radius:var(--radius);
}
.contact-info__title{
  font-size:13px;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--eyebrow);
  margin-bottom:12px;
}
.contact-info__link{
  display:block;
  font-size:16px;
  color:var(--ink);
  margin-top:4px;
  transition:color .2s ease;
}
.contact-info__link:hover{color:var(--body)}
.contact-info__text{font-size:15px;color:var(--body)}
.contact-info__links{display:flex;flex-direction:column;gap:8px}
.contact-info__links a{font-size:15px;color:var(--body);transition:color .2s ease}
.contact-info__links a:hover{color:var(--ink)}

@media (max-width:900px){
  .contact{grid-template-columns:1fr;gap:40px;padding:56px 0 72px}
  .field-row{grid-template-columns:1fr}
}

@media (max-width:900px){
  .page-hero{margin-top:83px}
}
@media (max-width:760px){
  .page-hero{min-height:340px}
  .page-hero__in{padding:40px 0 36px}
  .breadcrumb-bar{padding:16px 0}
  .page-content{padding:48px 0 20px}
  .page-content__placeholder{padding:32px 20px}
}

/* --------------------------------------------------------- responsive --- */

@media (max-width:1240px){
  .head .btn-dark{margin-left:40px}
  .nav{gap:30px}
}

@media (max-width:1080px){
  :root{--media-h:270px}
  .card{min-height:0}
  .grid{grid-template-columns:repeat(2,1fr)}
  .hero__copy{padding-top:200px}
}

@media (max-width:900px){
  .head{height:83px}
  .logo img{width:144px;height:46.6px}
  /* The whole cluster — links and the CTA — becomes the drawer, so the primary
     action stays reachable on phones instead of being hidden. */
  .navwrap{
    position:absolute;
    top:100%;left:0;right:0;
    flex-direction:column;
    align-items:stretch;
    margin:0;
    padding:0 var(--gutter);   /* drawer links line up with the logo above */
    background:#fff;
    border-bottom:1px solid var(--line);
    max-height:0;
    overflow:hidden;
    transition:max-height .35s ease;
  }
  /* Generous headroom for the drawer's max-height transition — the submenu can
     be open too, so this only needs to exceed the tallest real combination;
     max-height never forces empty space when content is shorter. */
  .navwrap.is-open{max-height:720px}
  .nav{flex-direction:column;align-items:stretch;gap:0}
  .nav__link{padding:18px 0;border-bottom:1px solid var(--line);font-size:17px}
  .nav__link:last-child{border-bottom:0}
  .head .btn-dark{width:100%;height:56px;margin:18px 0 24px;padding:0 8px 0 22px}
  .burger{display:flex}

  /* Dropdown becomes an inline accordion: no floating panel, no hover-open
     (there's no hover on touch), expand/collapse driven by .is-open only. */
  .nav__item{width:100%}
  .nav__trigger{width:100%;justify-content:space-between}
  .nav__item:hover .nav__dropdown{max-height:0}
  .nav__dropdown{
    position:static;
    transform:none;
    padding-top:0;
    max-height:0;
    overflow:hidden;
    opacity:1;
    visibility:visible;
    pointer-events:auto;
    transition:max-height .3s ease;
  }
  /* max-height itself is set inline by JS (site.js), sized to the panel's
     actual content — a fixed guessed value here kept drifting out of sync
     with content changes, and caused a flaky nested-transition bug where
     the outer drawer's own max-height animation (see .navwrap below) would
     sometimes stop this inner one from repainting until something else
     forced a reflow. An inline style always wins the cascade over this
     class, so this rule only matters as a no-JS fallback. */
  .nav__dropdown.is-open{max-height:440px}
  .nav__dropdown-panel{
    background:none;
    border:0;
    border-radius:0;
    box-shadow:none;
    padding:2px 0 12px 14px;
  }
  .nav__dropdown-link{padding:11px 0;font-size:15px;border-radius:0}
}

@media (max-width:760px){
  /* 85% would leave too little room on a phone; give the column more of the screen */
  :root{--media-h:240px;--content-w:90%}
  body{font-size:17px;line-height:28px}
  /* Photo band up top on phones, copy below it — the reverse of desktop's
     text-over-image treatment, which doesn't read well once the hero
     stacks. Anchoring the contain-fit image to the top and reserving that
     same height as padding on the copy flips which one comes first. */
  /* The header floats transparently over the hero on desktop by design, but
     with the image now starting at the very top there's nothing behind the
     header but photo — clear it the same way page-hero does on subpages. */
  .hero{height:auto;min-height:0;margin-top:83px}
  /* The copy sits below the photo on phones, not over it, so the desktop
     veil's left-side fade (there to keep text legible over the image) has
     nothing to do here — just washes the photo out. Bottom fade stays, thin,
     to blend into the white copy section instead of cutting hard. */
  .hero__veil{
    -webkit-mask-image:none;
    mask-image:none;
    background:linear-gradient(0deg,#fff 0%,rgba(255,255,255,.7) 4%,rgba(255,255,255,.2) 10%,rgba(255,255,255,0) 16%);
  }
  /* `cover` (not `contain`) here on purpose: the source photo carries a wide
     built-in white fade on its left third, so fitting the whole frame left
     the house itself small. A taller-than-native band forces `cover` to
     crop that fade off the left edge instead of shrinking the house.
     `.hero__bg` needs its own explicit height for this — it's absolutely
     positioned, and `.hero`'s height is auto (driven by the copy below),
     so `inset:0`'s 100%-of-parent height would otherwise chase the copy's
     own height and let `cover` stretch the photo down over the text. */
  .hero__bg{height:calc(118vw + 20px);bottom:auto}
  .hero__bg img,.hero__bg video{top:0;height:100%;object-fit:cover;object-position:right top}
  .hero__copy{padding-top:calc(118vw - 30px)}
  /* full-width copy on phones; the proportional cap is a desktop concern only */
  .hero__eyebrow,.hero__lead,.hero__phone{max-width:none}
  .hero h1{max-width:none}
  .hero__eyebrow{line-height:1.3}
  .hero h1{
    margin-top:6px;
    font-size:clamp(26px,7.2vw,34px);
    white-space:nowrap;
  }
  .hero .btn-dark{
    width:100%;
    max-width:290px;
    height:50px;
    padding:0 5px 0 22px;
    font-size:16px;
  }
  .hero .btn-dark img{width:40px;height:40px}
  .grid{grid-template-columns:1fr;margin-top:28px}
  .services{padding-top:88px}
  .services__sub{margin-top:8px}
  .quote{padding-top:56px;padding-bottom:72px}
  .quote blockquote{margin-top:36px;line-height:1.35}
  .quote figcaption{margin-top:24px;font-size:17px}
  .card__body{padding:24px 22px 30px}
  .card h3{font-size:26px}

  .foot__top{flex-direction:column;align-items:flex-start;gap:32px;padding:48px 0 32px}
  .foot__brand{max-width:none}
  .foot__menu,.foot__services,.foot__contact{padding-top:0}
  .foot__bottom{padding:20px 0 32px}
}

@media (prefers-reduced-motion:reduce){
  *{transition-duration:.01ms !important}
}
