/* ══════════════════════════════════════════════════════════════
   WALLY'S WALLS
   Palette:  #000000 ink · #F0F0F0 paper · #403517 olive
             #917147 limestone · #5E5241 stone
   ══════════════════════════════════════════════════════════════ */

/* ══════════════════════════════ FONTS ══════════════════════════════
   Self-hosted. Previously these came from fonts.googleapis.com, which cost
   two extra DNS and TLS handshakes to a third party before any text could
   render, and sent every visitor's IP to Google.

   Six weights, latin subset only. The old Google request also pulled
   Archivo 500 and Inter 500, which nothing on the site renders.

   Five of the six are in use today: Archivo 600/700/800 and Inter 400/600.
   Inter 700 is kept as a safety net -- a plain <strong> outside .qa falls
   to weight 700 in the body font, and without the file the browser would
   fake it. An unused @font-face costs visitors nothing; the browser only
   downloads a face when something on the page actually needs it.

   If you add a font-weight the CSS doesn't already use, add the matching
   woff2 too. A missing weight gets synthesised by the browser and looks
   noticeably wrong, especially faux-bold.                                */

@font-face{ font-family:'Archivo'; font-style:normal; font-weight:600;
  src:url('../fonts/archivo-600.woff2') format('woff2'); font-display:swap; }
@font-face{ font-family:'Archivo'; font-style:normal; font-weight:700;
  src:url('../fonts/archivo-700.woff2') format('woff2'); font-display:swap; }
@font-face{ font-family:'Archivo'; font-style:normal; font-weight:800;
  src:url('../fonts/archivo-800.woff2') format('woff2'); font-display:swap; }

@font-face{ font-family:'Inter'; font-style:normal; font-weight:400;
  src:url('../fonts/inter-400.woff2') format('woff2'); font-display:swap; }
@font-face{ font-family:'Inter'; font-style:normal; font-weight:600;
  src:url('../fonts/inter-600.woff2') format('woff2'); font-display:swap; }
@font-face{ font-family:'Inter'; font-style:normal; font-weight:700;
  src:url('../fonts/inter-700.woff2') format('woff2'); font-display:swap; }

:root{
  --ink:        #000000;
  --paper:      #F0F0F0;
  --olive:      #403517;
  --limestone:  #917147;
  --stone:      #5E5241;

  --paper-warm: #E7E3DC;
  --olive-deep: #2C2410;
  --tan-light:  #B99A6F;
  --muted:      #5a544c;
  --line:       rgba(0,0,0,.12);
  --line-light: rgba(240,240,240,.16);

  --font-head: 'Archivo', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --wrap: 1240px;
  --gut: clamp(20px, 5vw, 64px);
  --sec: clamp(72px, 10vw, 140px);

  --r-sm: 6px;
  --r:    12px;
  --r-lg: 20px;

  --shadow: 0 2px 4px rgba(0,0,0,.04), 0 12px 32px rgba(0,0,0,.08);
  --shadow-lg: 0 4px 8px rgba(0,0,0,.06), 0 24px 64px rgba(0,0,0,.16);

  --ease: cubic-bezier(.22,.61,.36,1);
}

/* ───────────────────────── reset ───────────────────────── */
*, *::before, *::after{ box-sizing: border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; scroll-padding-top:84px; }
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-body);
  font-size:17px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img,video,iframe{ max-width:100%; display:block; }
img{ height:auto; }
a{ color:inherit; }
h1,h2,h3,h4{ font-family:var(--font-head); font-weight:700; line-height:1.05; letter-spacing:-.025em; margin:0; }
p{ margin:0 0 1.1em; }
ul,ol{ margin:0; padding:0; list-style:none; }
button{ font:inherit; }

@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{ animation-duration:.01ms !important; transition-duration:.01ms !important; }
}

.wrap{ width:100%; max-width:var(--wrap); margin-inline:auto; padding-inline:var(--gut); }

.skip-link{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--ink); color:var(--paper); padding:12px 20px; border-radius:0 0 var(--r-sm) 0;
  font-weight:600; text-decoration:none;
}
.skip-link:focus{ left:0; }

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

/* ───────────────────────── type helpers ───────────────────────── */
.eyebrow{
  font-family:var(--font-head);
  font-size:.74rem; font-weight:700; letter-spacing:.18em; text-transform:uppercase;
  color:var(--tan-light); margin:0 0 18px;
}
.eyebrow--dark{ color:var(--limestone); }

h1{ font-size:clamp(2.7rem, 7.2vw, 5.4rem); font-weight:800; letter-spacing:-.04em; }
h2{ font-size:clamp(1.9rem, 4.2vw, 3.1rem); }
h3{ font-size:1.2rem; letter-spacing:-.015em; }

.section{ padding-block:var(--sec); position:relative; }
.section--dark{ background:var(--olive); color:var(--paper); }
.section--stone{ background:var(--paper-warm); }

.section-head{ max-width:720px; margin-bottom:clamp(40px,6vw,72px); }
.section-head--split{
  max-width:none; display:grid; gap:24px 56px;
  grid-template-columns:1fr; align-items:end;
}
.section-head-note{ color:var(--muted); max-width:46ch; margin:0; font-size:1.02rem; }

@media (min-width:900px){
  .section-head--split{ grid-template-columns:1.1fr .9fr; }
}

/* ───────────────────────── buttons ───────────────────────── */
.btn{
  --btn-bg:var(--limestone);
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  font-family:var(--font-head); font-weight:700; font-size:.95rem; letter-spacing:.01em;
  padding:15px 30px; border:2px solid transparent; border-radius:var(--r-sm);
  text-decoration:none; cursor:pointer; white-space:nowrap;
  transition:transform .2s var(--ease), background-color .2s var(--ease),
             color .2s var(--ease), border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.btn--solid{ background:var(--btn-bg); color:#fff; }
.btn--solid:hover{ background:var(--olive); transform:translateY(-2px); box-shadow:0 10px 26px rgba(64,53,23,.3); }
.btn--ghost{ background:transparent; color:var(--ink); border-color:rgba(0,0,0,.25); }
.btn--ghost:hover{ border-color:var(--ink); background:var(--ink); color:var(--paper); transform:translateY(-2px); }
.btn--sm{ padding:11px 20px; font-size:.86rem; }
/* Measures dead-centre, but it's the tallest item in the row so it overhangs
   the text band top and bottom and reads low. 2px up is an optical fix.
   position/top rather than transform, so the hover lift still works. */
.header-quote{ position:relative; top:-2px; }
.btn--lg{ padding:18px 32px; font-size:1.02rem; }
.btn--block{ width:100%; }
.btn:active{ transform:translateY(0); }

/* ══════════════════════════════ HEADER ══════════════════════════════ */
.site-header{
  position:fixed; inset:0 0 auto 0; z-index:100;
  padding-block:16px;
  transition:background-color .35s var(--ease), box-shadow .35s var(--ease), padding .35s var(--ease);
}
.header-inner{ display:flex; align-items:center; gap:24px; }

.brand{
  display:flex; align-items:center; gap:11px;
  text-decoration:none; flex:0 0 auto; margin-right:auto;
}
.brand-mark{ width:34px; height:auto; }
.brand-mark--dark{ display:none; }
.brand-name{
  font-family:var(--font-head); font-weight:800; font-size:1.1rem;
  letter-spacing:-.02em; text-transform:uppercase; color:var(--paper);
  line-height:1.05;
}

.nav{ display:none; gap:30px; }
.nav a{
  font-family:var(--font-head); font-weight:600; font-size:.9rem;
  text-decoration:none; color:var(--paper); opacity:.88;
  position:relative; padding-block:4px;
  transition:opacity .2s var(--ease);
}
.nav a::after{
  content:''; position:absolute; left:0; right:0; bottom:0; height:2px;
  background:var(--limestone); transform:scaleX(0); transform-origin:left;
  transition:transform .3s var(--ease);
}
.nav a:hover{ opacity:1; }
.nav a:hover::after{ transform:scaleX(1); }

.header-actions{ display:flex; align-items:center; gap:14px; flex:0 0 auto; }

/* The number is the highest-intent tap on a phone, so the header keeps a
   tap-to-call link at every width. Below 1120px it shrinks to the handset
   icon alone: the digits next to the burger push the row past the viewport
   on a 375px screen. The link carries an aria-label so the icon-only state
   still announces the number. */
.phone-link{
  display:inline-flex; align-items:center; gap:8px;
  font-family:var(--font-head); font-weight:700; font-size:.92rem;
  text-decoration:none; color:var(--paper);
  transition:color .2s var(--ease);
}
.phone-link svg{ width:17px; height:17px; fill:currentColor; flex:0 0 auto; }
.phone-link:hover{ color:var(--tan-light); }

/* scrolled state */
.site-header.is-stuck{
  background:var(--paper);
  box-shadow:0 1px 0 var(--line), 0 8px 30px rgba(0,0,0,.07);
  padding-block:10px;
}
.site-header.is-stuck .brand-name,
.site-header.is-stuck .nav a,
.site-header.is-stuck .phone-link{ color:var(--ink); }
.site-header.is-stuck .brand-mark--light{ display:none; }
.site-header.is-stuck .brand-mark--dark{ display:block; }
.site-header.is-stuck .nav-toggle span{ background:var(--ink); }

/* While the menu is open the olive panel covers the header, so the scrolled
   (light-background) treatment above would leave a dark logo and dark text on
   a dark panel. Force everything back to white. Must come after the
   .is-stuck rules — same specificity, so source order decides. */
.site-header.nav-is-open .brand-name,
.site-header.nav-is-open .nav a,
.site-header.nav-is-open .phone-link{ color:var(--paper); }
.site-header.nav-is-open .brand-mark--light{ display:block; }
.site-header.nav-is-open .brand-mark--dark{ display:none; }
.site-header.nav-is-open .nav-toggle span{ background:var(--paper); }

/* burger */
.nav-toggle{
  display:inline-flex; flex-direction:column; justify-content:center; gap:5px;
  width:42px; height:42px; padding:0 9px;
  background:transparent; border:0; cursor:pointer;
}
.nav-toggle span{
  display:block; height:2px; width:100%; background:var(--paper); border-radius:2px;
  transition:transform .3s var(--ease), opacity .2s var(--ease), background-color .3s var(--ease);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1){ transform:translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2){ opacity:0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3){ transform:translateY(-7px) rotate(-45deg); }

@media (min-width:1120px){
  .nav{ display:flex; }
  .phone-link{ display:flex; }
  .nav-toggle{ display:none; }
}
/* On narrow screens the sticky bottom CTA does this job: keeping the header
   button as well pushes the row past the viewport width.

   The switch is at 1120px, not 1060px. Measured: with six nav links, the phone
   number and the quote button on one row, "Our Work" and "Service Area" wrap to
   a second line below 1120px and the header grows from 32px to 56px. It was set
   to 1060px, which left a broken band from 1060 to about 1090; the longer "Get a
   free quote" label widened that band, so the breakpoint moved to where the row
   actually fits. If you change a nav label or the button text, re-measure: the
   number here is a consequence of the copy, not a round figure. */
@media (max-width:1119px){
  .header-quote{ display:none; }
  /* Icon only, but padded out to a ~44px tap target. */
  .phone-digits{ display:none; }
  .phone-link{ padding:11px; margin-right:-6px; }
  .phone-link svg{ width:21px; height:21px; }
}

/* 320px, an original iPhone SE, is the one width where the wordmark plus the
   two tap targets do not fit: adding the phone icon to the header pushed the
   row 4px past the viewport there. Shrink the wordmark and close the gap
   rather than shaving either tap target. */
@media (max-width:359px){
  .brand{ gap:9px; }
  .brand-mark{ width:30px; }
  .brand-name{ font-size:1rem; }
  .header-actions{ gap:8px; }
}

/* mobile drawer */
@media (max-width:1119px){
  .nav{
    /* inset:0 — all four sides — so the panel is the full height of the
       screen. It was 'inset:0 0 auto 0', which left the bottom auto and let
       the height be set by however many links there are, so the page showed
       through underneath. */
    position:fixed; inset:0;
    padding:96px var(--gut) 40px;
    background:var(--olive); flex-direction:column; gap:0;
    transform:translateY(-100%); transition:transform .45s var(--ease);
    z-index:-1;
    overflow-y:auto;                    /* links still reachable on a short screen */
    -webkit-overflow-scrolling:touch;
  }
  .nav.is-open{ display:flex; transform:translateY(0); }
  .nav a{
    color:var(--paper) !important; font-size:1.5rem; font-weight:700;
    padding-block:16px; border-bottom:1px solid var(--line-light);
  }
  .nav a::after{ display:none; }
}

/* ══════════════════════════════ HERO ══════════════════════════════ */
.hero{
  position:relative;
  min-height:100svh;
  display:flex; flex-direction:column; justify-content:flex-end;
  /* No top padding on tall screens: the headline is centred in the space from
     the very top of the page down to the quote card, so any top padding here
     would push it below that midpoint. Short screens re-add clearance below. */
  padding-block:0 56px;
  color:var(--paper);
  overflow:hidden;
  isolation:isolate;
}
.hero-media{
  position:absolute; inset:0; z-index:-2;
  /* Shown before the Vimeo player loads, and if it's blocked or slow.
     The hero is never an empty black box. */
  background:var(--olive-deep) url('../img/hero-poster-v2.jpg') center/cover no-repeat;
  /* WebP is roughly 60% lighter on the one image that loads before anything
     else. The plain url() above stays as the fallback: a browser that does
     not understand image-set() simply uses it. */
  background-image:image-set(
    url('../img/hero-poster-v2.webp') type('image/webp'),
    url('../img/hero-poster-v2.jpg')  type('image/jpeg'));
}
/* A wide poster behind a portrait screen shows mostly sky and dirt; use a
   vertical crop on phones. */
@media (max-width:768px){
  .hero-media{
    background-image:url('../img/hero-poster-vertical-v2.jpg');
    background-image:image-set(
      url('../img/hero-poster-vertical-v2.webp') type('image/webp'),
      url('../img/hero-poster-vertical-v2.jpg')  type('image/jpeg'));
  }
}

/* Oversize and centre the Vimeo iframe so the video always covers the hero,
   the way object-fit:cover does for an image. Sized for a 16:9 video:
     177.7778vh = 100vh x (16/9)   — height-driven width
     56.25vw    = 100vw / (16/9)   — width-driven height
   For a 4:3 encode these become 133.3333vh and 75vw. */
.hero-frame{
  /* Cover maths driven by one ratio, so it always matches the video that
     actually loaded. main.js adds .is-vertical when it swaps in the phone
     cut; a mismatch here is what produces letterboxing. */
  --hero-ratio: 1.7778;                        /* 16:9 wide cut */
  position:absolute; top:50%; left:50%;
  transform:translate(-50%, -50%);
  width:calc(100vh * var(--hero-ratio));
  height:100vh;
  min-width:100vw;
  min-height:calc(100vw / var(--hero-ratio));
  border:0;
  /* The global reset sets max-width:100% on every iframe so images can never
     overflow. That rule must NOT apply here: this frame is deliberately wider
     and taller than the viewport so the video covers the hero. Without these
     two lines it gets clamped to the screen size, turns portrait on a phone,
     and Vimeo letterboxes the 16:9 video into a thin band. */
  max-width:none;
  max-height:none;
  pointer-events:none;   /* no hover controls, clicks pass through */
  filter:saturate(1.02) contrast(1.03);
  /* Hidden until the player actually loads. A blocked or failed iframe paints
     an opaque grey box, which would cover the poster underneath and leave the
     white headline unreadable — so the frame only appears once it's ready. */
  opacity:0;
  transition:opacity .9s var(--ease);
}
.hero-frame.is-ready{ opacity:1; }
.hero-frame.is-vertical{ --hero-ratio: 0.5625; }   /* 9:16 phone cut */
.hero-scrim{
  position:absolute; inset:0; z-index:-1;
  background:
    /* Vertical: the headline sits around 30-50% down, which used to be the
       thinnest part of this gradient — that was the readability problem.
       Lifted through the middle, still darkest at the bottom behind the card. */
    linear-gradient(to top,
      rgba(20,16,6,.90)   0%,
      rgba(20,16,6,.62)  30%,
      rgba(20,16,6,.47)  55%,
      rgba(20,16,6,.44)  75%,
      rgba(20,16,6,.60) 100%),
    /* Horizontal: weight sits behind the left-aligned text and clears before
       the right of the frame, so the work stays bright where it matters. */
    linear-gradient(to right,
      rgba(20,16,6,.50)  0%,
      rgba(20,16,6,.16) 34%,
      rgba(20,16,6,0)   58%);
}

/* The inner column fills the hero so the headline can centre itself in the
   space between the top of the page and the quote card, which stays pinned
   to the bottom. */
.hero-inner{
  position:relative; flex:1 1 auto;
  display:flex; flex-direction:column;
}
.hero-copy{
  max-width:840px;
  flex:1 1 auto;
  display:flex; flex-direction:column; justify-content:center;
}
.hero h1{
  margin:0; text-wrap:balance;
  /* Leading matched to the quote card's body text: that text runs 15px on a
     23.3px line, so 8.3px of whitespace between lines. Expressed as
     1em + 8.3px rather than a ratio so the gap stays the same as the
     headline scales down — a fixed ratio would shrink the gap with it. */
  line-height:calc(1em + 8.3px);
  /* Insurance for the brightest frames of the video, where even the scrim
     isn't quite enough. Wide and soft so it reads as depth, not a drop shadow. */
  text-shadow:0 2px 30px rgba(20,16,6,.55), 0 1px 4px rgba(20,16,6,.35);
}
/* The tan reads well on the solid olive sections but disappears over pale
   parts of the video. In the hero only, go near-white. */
.hero .eyebrow{
  color:rgba(240,240,240,.92);
  text-shadow:0 1px 14px rgba(20,16,6,.7), 0 1px 3px rgba(20,16,6,.45);
}

.hero-foot{
  display:grid; gap:28px;
  grid-template-columns:1fr;
  align-items:end;
}

.hero-card{
  background:rgba(28,23,10,.55);
  backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
  border:1px solid rgba(240,240,240,.18);
  border-radius:var(--r-lg);
  padding:26px;
  max-width:400px;
  box-shadow:var(--shadow-lg);
}
.hero-card-title{
  font-family:var(--font-head); font-weight:700; font-size:1.22rem;
  letter-spacing:-.02em; margin:0 0 8px;
}
.hero-card-text{ font-size:.94rem; line-height:1.55; color:rgba(240,240,240,.82); margin:0 0 20px; }
.hero-card-call{
  display:block; text-align:center; margin-top:14px;
  font-size:.88rem; color:rgba(240,240,240,.78); text-decoration:none;
  transition:color .2s var(--ease);
}
.hero-card-call:hover{ color:var(--tan-light); }


.hero-scroll{
  position:absolute; left:50%; bottom:20px; transform:translateX(-50%);
  width:38px; height:38px; display:none; place-items:center;
  border:1px solid rgba(240,240,240,.3); border-radius:50%;
  animation:bob 2.4s var(--ease) infinite;
}
.hero-scroll svg{ width:17px; height:17px; fill:none; stroke:var(--paper); stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
@keyframes bob{ 0%,100%{ transform:translate(-50%,0); } 50%{ transform:translate(-50%,7px); } }

@media (min-width:820px){
  .hero-foot{ gap:48px; }
  .hero-scroll{ display:grid; }
}

/* Lift the quote card off the bottom of the hero.
   A transform rather than margin or padding on purpose: the headline centres
   itself in the space above this card, so changing the card's place in the
   layout would drag the headline up with it. A transform moves the card
   visually and leaves the layout — and the headline — exactly where it is.
   12.3vh ~= 111px at 900px tall, and holds its relative position as the
   window resizes. Gated on height so it never crowds the headline on a
   short screen. */
@media (min-width:820px) and (min-height:700px){
  .hero-foot{ transform:translateY(-12.3vh); }
}

/* Short viewports — laptops, landscape phones. Keep the whole hero on screen
   rather than letting the headline run off the top. */
@media (max-height:820px){
  /* Too little room to centre cleanly — put the header clearance back so the
     headline can't slide under the nav. */
  .hero{ padding-block:0 44px; }
  .hero h1{ font-size:clamp(2.3rem, 5.6vw, 4rem); }
  .hero-card{ padding:22px; }
  .hero-card-text{ margin-bottom:16px; }
  .hero-scroll{ display:none; }
}
@media (max-height:680px){
  /* An iPhone SE, or any phone in landscape, lands here. The blurb used to be
     display:none, so the shortest screens lost the one line of copy that says
     what the quote actually involves. Tighten it instead of dropping it. */
  .hero{ padding-block:0 36px; }
  .hero-card{ padding:18px; }
  .hero-card-title{ font-size:1.12rem; margin-bottom:6px; }
  .hero-card-text{ font-size:.86rem; line-height:1.45; margin-bottom:12px; }
  .hero-card-call{ margin-top:10px; }
}

/* ══════════════════════════════ ABOUT ══════════════════════════════ */
.about-grid{ display:grid; gap:clamp(40px,6vw,80px); grid-template-columns:1fr; align-items:center; }

.lead-statement{
  font-size:clamp(1.5rem,3vw,2.2rem); line-height:1.22; letter-spacing:-.03em;
  margin:0 0 28px; text-wrap:balance;
}
.lead-statement em{ font-style:normal; color:var(--limestone); }

.about-copy p{ color:var(--muted); max-width:56ch; }
.about-cta{ display:flex; flex-wrap:wrap; gap:12px; margin-top:30px; }

.about-media{ position:relative; }
/* <picture> wraps these images now, so the img is no longer a direct child.
   Descendant selector, not child. */
picture{ display:block; }
.about-media img{
  width:100%; border-radius:var(--r-lg); box-shadow:var(--shadow-lg);
  aspect-ratio:4/3; object-fit:cover;
}
@media (min-width:980px){
  .about-grid{ grid-template-columns:1fr 1fr; }
}

/* ══════════════════════════════ WHY / CARDS ══════════════════════════════ */
.cards{
  display:grid; gap:20px;
  grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
}
.card{
  background:rgba(240,240,240,.055);
  border:1px solid var(--line-light);
  border-radius:var(--r-lg);
  padding:32px 28px;
  transition:transform .3s var(--ease), background-color .3s var(--ease), border-color .3s var(--ease);
}
.card:hover{
  transform:translateY(-5px);
  background:rgba(240,240,240,.09);
  border-color:rgba(185,154,111,.5);
}
.card-icon{
  width:50px; height:50px; display:grid; place-items:center;
  border-radius:var(--r); background:var(--limestone); margin-bottom:22px;
}
.card-icon svg{ width:25px; height:25px; fill:none; stroke:#fff; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
.card h3{ margin:0 0 12px; }
.card p{ margin:0; font-size:.96rem; line-height:1.6; color:rgba(240,240,240,.72); }

.services{
  margin-top:clamp(48px,7vw,80px);
  padding-top:clamp(40px,5vw,56px);
  border-top:1px solid var(--line-light);
}
.services-title{
  font-size:.76rem; font-weight:700; letter-spacing:.18em; text-transform:uppercase;
  color:var(--tan-light); margin:0 0 24px;
}
.services-list{
  display:grid; gap:10px 32px;
  grid-template-columns:repeat(auto-fit, minmax(190px,1fr));
}
.services-list li{
  font-family:var(--font-head); font-weight:600; font-size:1.02rem;
  padding-block:10px; border-bottom:1px solid var(--line-light);
  display:flex; align-items:center; gap:12px;
}
.services-list li::before{
  content:''; width:7px; height:7px; flex:0 0 auto;
  background:var(--limestone); border-radius:1px; transform:rotate(45deg);
}

/* ══════════════════════════════ GALLERY ══════════════════════════════ */
.gallery{
  display:grid; gap:14px;
  grid-template-columns:repeat(2, 1fr);
  grid-auto-rows:170px;
}
.shot{
  margin:0; position:relative; overflow:hidden;
  border-radius:var(--r); background:var(--stone);
  grid-row:span 1;
}
.shot img{
  width:100%; height:100%; object-fit:cover;
  transition:transform .7s var(--ease), filter .5s var(--ease);
}
.shot:hover img{ transform:scale(1.055); }

/* feature = 2 wide × 2 tall · wide = 2 wide × 1 tall.
   With 8 items on a 4-column grid these tile with no gaps. */
.shot--feature{ grid-column:span 2; grid-row:span 2; }
.shot--wide{ grid-column:span 2; }

@media (min-width:700px){
  .gallery{ grid-template-columns:repeat(4,1fr); grid-auto-rows:215px; gap:16px; }
}
@media (hover:none){
    }

.work-more{
  margin:clamp(32px,4vw,48px) 0 0; text-align:center;
  color:var(--muted); font-size:1rem;
}
.work-more a{
  font-family:var(--font-head); font-weight:700; color:var(--ink);
  text-decoration:none; border-bottom:2px solid var(--limestone);
  padding-bottom:1px;
  /* No white-space:nowrap here. It was fine for the original short
     label, but any longer copy then forced horizontal scroll on phones
     instead of wrapping. Layout must survive the words changing. */ 
  transition:color .2s var(--ease);
}
.work-more a:hover{ color:var(--limestone); }

/* ══════════════════════════════ PROCESS ══════════════════════════════ */
.steps{
  display:grid; gap:2px;
  grid-template-columns:repeat(auto-fit, minmax(230px,1fr));
  background:rgba(0,0,0,.1);
  border-radius:var(--r-lg); overflow:hidden;
}
.step{
  background:var(--paper-warm);
  padding:36px 30px 40px;
  position:relative;
  transition:background-color .3s var(--ease);
}
.step:hover{ background:#EFEBE3; }
.step-num{
  font-family:var(--font-head); font-weight:800; font-size:2.6rem;
  letter-spacing:-.05em; color:var(--limestone); opacity:.4;
  display:block; margin-bottom:14px; line-height:1;
}
.step h3{ margin:0 0 10px; }
.step p{ margin:0; font-size:.95rem; line-height:1.6; color:var(--muted); }

/* ══════════════════════════════ REVIEWS ══════════════════════════════ */
.quotes{
  display:grid; gap:20px;
  grid-template-columns:repeat(auto-fit, minmax(280px,1fr));
}
.quote{
  margin:0; padding:32px 30px;
  background:#fff; border-radius:var(--r-lg);
  border:1px solid var(--line);
  box-shadow:var(--shadow);
  display:flex; flex-direction:column;
  position:relative;
}
.quote::before{
  content:'"';
  font-family:var(--font-head); font-weight:800; font-size:4.2rem; line-height:.8;
  color:var(--limestone); opacity:.3;
  display:block; margin-bottom:6px;
}
.quote p{ font-size:1.02rem; line-height:1.62; margin:0 0 22px; flex:1; }
.quote footer{ display:flex; flex-direction:column; gap:2px; padding-top:18px; border-top:1px solid var(--line); }
.quote cite{ font-family:var(--font-head); font-weight:700; font-style:normal; font-size:.98rem; }
.quote footer span{ font-size:.86rem; color:var(--muted); }

.quote.is-placeholder p{ color:#8b857c; font-style:italic; }
.quote.is-placeholder cite,
.quote.is-placeholder footer span{ color:#a39d93; }

/* ══════════════════════════════ SERVICE AREA ══════════════════════════════ */
.areas-grid{ display:grid; gap:clamp(36px,5vw,64px); grid-template-columns:1fr; align-items:center; }
.areas-copy h2{ margin-bottom:20px; }
.areas-copy > p{ color:rgba(240,240,240,.78); max-width:52ch; }

.areas-list{
  display:grid; gap:8px 24px;
  grid-template-columns:repeat(auto-fit, minmax(160px,1fr));
  margin:28px 0 32px;
}
.areas-list li{
  font-size:.95rem; color:rgba(240,240,240,.85);
  display:flex; align-items:center; gap:10px;
}
.areas-list li::before{
  content:''; width:5px; height:5px; border-radius:50%;
  background:var(--tan-light); flex:0 0 auto;
}

.areas-map{
  position:relative;
  border-radius:var(--r-lg); overflow:hidden;
  border:1px solid var(--line-light);
  box-shadow:var(--shadow-lg);
  background:var(--olive-deep);
}
.areas-map iframe{
  position:relative; z-index:1;
  width:100%; height:clamp(320px, 46vw, 470px); border:0; display:block;
  filter:grayscale(.35) contrast(1.05);
}
.areas-map-fallback{
  position:absolute; inset:0; z-index:0; margin:0;
  display:grid; place-items:center; text-align:center; padding:24px;
  font-family:var(--font-head); font-weight:600; font-size:1rem;
  color:rgba(240,240,240,.45);
}

@media (min-width:940px){
  .areas-grid{ grid-template-columns:.92fr 1.08fr; }
}

/* ══════════════════════════════ CONTACT ══════════════════════════════ */
.contact-grid{ display:grid; gap:clamp(36px,5vw,72px); grid-template-columns:1fr; align-items:start; }
.contact-copy > p{ color:var(--muted); max-width:48ch; }

.contact-details{ margin:32px 0 0; display:grid; gap:0; }
.contact-details li{
  display:flex; flex-wrap:wrap; gap:4px 20px; align-items:baseline;
  padding-block:15px; border-bottom:1px solid var(--line);
}
.contact-details li:first-child{ border-top:1px solid var(--line); }
.contact-label{
  font-family:var(--font-head); font-weight:700; font-size:.73rem;
  letter-spacing:.14em; text-transform:uppercase; color:var(--limestone);
  flex:0 0 118px;
}
.contact-details a{
  font-family:var(--font-head); font-weight:600; font-size:1.06rem;
  text-decoration:none; border-bottom:1.5px solid transparent;
  overflow-wrap:anywhere;
  transition:border-color .2s var(--ease), color .2s var(--ease);
}
.contact-details a:hover{ color:var(--limestone); border-bottom-color:var(--limestone); }

.contact-shot{ margin:36px 0 0; border-radius:var(--r-lg); overflow:hidden; box-shadow:var(--shadow); }
.contact-shot img{ width:100%; aspect-ratio:16/9; object-fit:cover; }

@media (min-width:940px){
  .contact-grid{ grid-template-columns:1fr 1.02fr; }
}
@media (max-width:939px){
  /* Kept on mobile: it was hidden here, which meant phone visitors never saw
     the one photo in the contact section. 16/9 at 375px is about 190px tall,
     so it costs little scroll before the form. */
  .contact-shot{ margin-top:28px; }
}

/* ───────────────────────── form ───────────────────────── */
.form{
  background:#fff;
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  padding:clamp(26px,3.6vw,40px);
  box-shadow:var(--shadow-lg);
}
.field{ margin-bottom:18px; }
.field-row{ display:grid; gap:0 18px; grid-template-columns:1fr; }
@media (min-width:540px){ .field-row{ grid-template-columns:1fr 1fr; } }

.field label{
  display:block; margin-bottom:7px;
  font-family:var(--font-head); font-weight:600; font-size:.86rem;
  letter-spacing:.01em;
}
.field label span{ color:var(--limestone); }

.field input, .field select, .field textarea{
  width:100%; font:inherit; font-size:1rem; color:var(--ink);
  padding:13px 15px;
  background:var(--paper);
  border:1.5px solid rgba(0,0,0,.14);
  border-radius:var(--r-sm);
  transition:border-color .2s var(--ease), background-color .2s var(--ease), box-shadow .2s var(--ease);
  -webkit-appearance:none; appearance:none;
}
.field textarea{ resize:vertical; min-height:110px; line-height:1.55; }
.field select{
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235E5241' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 13px center; background-size:18px;
  padding-right:42px; cursor:pointer;
}
.field input:focus, .field select:focus, .field textarea:focus{
  outline:none; background:#fff;
  border-color:var(--limestone);
  box-shadow:0 0 0 3px rgba(145,113,71,.16);
}
.field.has-error input,
.field.has-error select,
.field.has-error textarea{ border-color:#b23c2e; background:#fdf5f4; }

.err{
  margin:6px 0 0; font-size:.82rem; color:#b23c2e; min-height:0;
  display:none;
}
.field.has-error .err{ display:block; }

.hp{ position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

.form-note{
  margin:14px 0 0; font-size:.82rem; color:var(--muted); text-align:center;
}
.form-status{
  margin:16px 0 0; padding:0; font-size:.94rem; line-height:1.5;
  border-radius:var(--r-sm);
}
.form-status.is-shown{ padding:14px 16px; }
.form-status.is-ok{ background:#eaf4ec; color:#1f5c33; border:1px solid #bfdcc7; }
.form-status.is-bad{ background:#fdf1ef; color:#8f2e22; border:1px solid #f0cdc7; }
.form-status.is-info{ background:#f4efe6; color:var(--olive); border:1px solid #ddd0ba; }

/* ══════════════════════════════ FOOTER ══════════════════════════════ */
.site-footer{
  background:var(--olive-deep); color:var(--paper);
  padding-block:clamp(56px,7vw,88px) 0;
}
.footer-inner{
  display:grid; gap:44px; grid-template-columns:1fr;
  padding-bottom:48px;
}
.footer-brand img{ width:118px; height:auto; margin-bottom:18px; }
.footer-brand p{ color:rgba(240,240,240,.6); font-size:.95rem; max-width:32ch; margin:0; }

.footer-nav{ display:grid; gap:36px; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
/* Two columns leave about 150px each on a phone — not enough for
   wallyswalls@fastmail.com, so it broke mid-word as "...fastmail.co / m".
   One full-width column below this and the address sits on a single line. */
@media (max-width:560px){
  .footer-nav{ grid-template-columns:1fr; gap:28px; }
}
.footer-nav h4{
  font-size:.74rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:var(--tan-light); margin:0 0 16px;
}
.footer-nav a{
  display:block; text-decoration:none; font-size:.95rem;
  color:rgba(240,240,240,.72); padding-block:6px;
  overflow-wrap:anywhere;           /* long email addresses must not push the page wide */
  transition:color .2s var(--ease), transform .2s var(--ease);
}
.footer-nav a:hover{ color:var(--paper); transform:translateX(3px); }

.footer-base{
  display:flex; flex-wrap:wrap; gap:8px 24px; justify-content:space-between;
  padding-block:22px;
  border-top:1px solid rgba(240,240,240,.1);
  font-size:.85rem; color:rgba(240,240,240,.5);
}
.footer-base p{ margin:0; }

@media (min-width:820px){
  .footer-inner{ grid-template-columns:1fr 1.3fr; gap:64px; }
}

/* ══════════════════════════════ STICKY MOBILE CTA ══════════════════════════════ */
.sticky-cta{
  position:fixed; left:16px; right:16px; bottom:16px; z-index:90;
  display:none; align-items:center; justify-content:center;
  padding:16px; border-radius:var(--r-sm);
  background:var(--limestone); color:#fff; text-decoration:none;
  font-family:var(--font-head); font-weight:700; font-size:1rem;
  box-shadow:0 8px 28px rgba(0,0,0,.3);
  transform:translateY(120%); transition:transform .4s var(--ease);
}
.sticky-cta.is-shown{ transform:translateY(0); }
@media (max-width:1119px){ .sticky-cta{ display:flex; } }

/* ══════════════════════════════ REVEAL ON SCROLL ══════════════════════════════ */
.reveal{ opacity:0; transform:translateY(22px); }
.reveal.is-in{
  opacity:1; transform:none;
  transition:opacity .7s var(--ease), transform .7s var(--ease);
}
@media (prefers-reduced-motion: reduce){
  .reveal{ opacity:1; transform:none; }
}

/* ══════════════════════════════ FAQ PAGE ══════════════════════════════ */
/* This page has no hero, so the header needs its own solid background
   instead of relying on the scroll state. */
.site-header.is-solid{
  position:sticky;
  background:var(--paper);
  box-shadow:0 1px 0 var(--line);
}
.site-header.is-solid .brand-name,
.site-header.is-solid .nav a,
.site-header.is-solid .phone-link{ color:var(--ink); }
.site-header.is-solid .brand-mark--light{ display:none; }
.site-header.is-solid .brand-mark--dark{ display:block; }
.site-header.is-solid .nav-toggle span{ background:var(--ink); }
.site-header.is-solid .nav a[aria-current="page"]{ color:var(--limestone); }

.faq-head{ padding-block:clamp(48px,7vw,88px) clamp(32px,4vw,48px); }
.faq-title{
  font-size:clamp(2rem,4.6vw,3.4rem); letter-spacing:-.03em;
  max-width:16ch; margin:0 0 22px; text-wrap:balance;
}
.faq-intro{ color:var(--muted); max-width:56ch; font-size:1.05rem; margin:0; }
.faq-intro a{ color:var(--ink); border-bottom:2px solid var(--limestone); text-decoration:none; }
.faq-intro a:hover{ color:var(--limestone); }

.faq-body{ padding-block:clamp(48px,6vw,80px); }
.faq-group{ margin-bottom:clamp(44px,6vw,72px); }
.faq-group-label{
  font-family:var(--font-head); font-size:.74rem; font-weight:700;
  letter-spacing:.18em; text-transform:uppercase; color:var(--limestone);
  padding-bottom:14px; margin:0 0 28px; border-bottom:1px solid rgba(0,0,0,.14);
}

/* Answers are plain, always-visible text — no accordion. Content behind a
   click is retrieved less reliably by search and AI answer engines, and
   being quotable is the whole point of this page. */
.qa{ margin-bottom:clamp(30px,3.4vw,44px); max-width:70ch; }
.qa:last-child{ margin-bottom:0; }
.qa h2{
  font-size:clamp(1.12rem,1.7vw,1.35rem); line-height:1.3;
  letter-spacing:-.015em; margin:0 0 12px;
}
.qa p{ color:var(--muted); font-size:1rem; line-height:1.68; margin:0 0 .9em; }
.qa p:last-child{ margin-bottom:0; }
.qa strong{ color:var(--ink); font-weight:600; }

/* Deliberately loud. These must be filled in or deleted before launch, and
   they should be impossible to publish by accident. */
.needs-input{
  background:#FFF4D6;
  border:1px dashed #C08A2E;
  border-left:4px solid #C08A2E;
  border-radius:var(--r-sm);
  padding:13px 16px;
  color:#6B4E12 !important;
  font-size:.92rem !important;
}
.needs-input strong{ color:#6B4E12; letter-spacing:.04em; }

.faq-cta{
  margin-top:clamp(40px,5vw,64px);
  padding:clamp(28px,4vw,44px);
  background:var(--olive); color:var(--paper);
  border-radius:var(--r-lg);
}
.faq-cta h2{ margin:0 0 10px; font-size:clamp(1.4rem,2.4vw,1.9rem); }
.faq-cta p{ color:rgba(240,240,240,.78); margin:0 0 22px; }
.faq-cta-actions{ display:flex; flex-wrap:wrap; gap:12px; }
.faq-cta .btn--ghost{ color:var(--paper); border-color:rgba(240,240,240,.4); }
.faq-cta .btn--ghost:hover{ background:var(--paper); color:var(--ink); border-color:var(--paper); }

