/* ==========================================================================
   Mountain Power Wash, site stylesheet.

   Extracted and organised from website/direction-demo/index.html, which is the
   approved direction. Palette, type and geometry are law and come from
   website/DESIGN-BRIEF.md. Only Besley and Archivo render. Corners are 0px
   everywhere. No hex ships outside the token block below.

   Sections, in order:
     1  Fonts
     2  Tokens
     3  Base
     4  Type roles
     5  Detail marks: icons, stitch, geese, seams, wipes, reveals
     6  Actions: buttons, CTA clusters, forms
     7  Chrome: nav, sticky mobile bar, footer
     8  Home sections: hero, trust, proof, relief, quilt, claim, heights, estimate
     9  Inner page shell: page hero, prose, pair grid, cross links
    10  Mobile
    11  Reduced motion
   ========================================================================== */


/* 1. FONTS ================================================================= */

@font-face{
  font-family:"Besley";
  src:url("../assets/fonts/Besley.woff2") format("woff2");
  font-weight:400 900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Archivo";
  src:url("../assets/fonts/Archivo.woff2") format("woff2");
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
}


/* 2. TOKENS ================================================================ */

:root{
  /* ground */
  --ink:#0E141B;            --ink-rgb:14,20,27;
  --ridge-navy:#1C2B3A;     --navy-rgb:28,43,58;
  /* relief */
  --cream:#F2EDE3;          --cream-rgb:242,237,227;
  --paper:#FAF7F0;          --paper-rgb:250,247,240;
  /* landscape */
  --haze:#3E5A6F;           --haze-rgb:62,90,111;
  --mist:#E6EBEF;           --mist-rgb:230,235,239;
  --forest:#435431;         --forest-rgb:67,84,49;
  /* the one action colour */
  --clay:#9E4528;
  --clay-press:#83381F;

  --display:"Besley","Bitstream Vera Serif",Georgia,serif;
  --body:"Archivo","Helvetica Neue",Helvetica,Arial,sans-serif;
  /* structure easing: ridgeline draws, hero resolve, generic reveals */
  --ease:cubic-bezier(.23,1,.32,1);
  /* water easing: the before and after sheet only, so the two devices read apart */
  --ease-water:cubic-bezier(.65,0,.35,1);

  /* two deliberate heading steps for section heads, nothing between them */
  --h-a:clamp(36px,5.2vw,72px);
  /* ceiling is 50 not 58: at 58 the heights headline runs 4 lines even with a
     789px column, and 3 lines would need the photo column under 450px */
  --h-b:clamp(34px,4.2vw,50px);

  /* letterspaced caps ramp. Tracking is indexed to optical size and nothing
     else: the smaller the caps run, the looser it is set. 12/13/15/16px are the
     only cap sizes on the site, so these are the only four values. */
  --ls-12:.18em;
  --ls-13:.16em;
  --ls-15:.14em;
  --ls-16:.13em;
  /* display tracking ramp, the same rule inverted: the bigger the setting, the
     tighter it closes up. Measured at the four display sizes that ship. */
  --ls-d-50:-.008em;
  --ls-d-72:-.010em;
  --ls-d-104:-.013em;
  --ls-d-154:-.016em;
  /* the phone number is set, not typed: tabular figures on one fixed track */
  --ls-tel:.04em;

  --container:1400px;
  --gutter:40px;
  --nav-h:72px;
  --pad-y:112px;
  --sbar-h:0px;   /* raised to the bar height only where the bar shows */

  /* the real transition on assets/img/roof-half-washed.jpg, measured off the
     file: the wash line crosses the frame at 36.7 percent up top and 69.4
     percent at the bottom, 26.1 degrees off vertical, centred at 53.05 percent */
  --wash-mid:53.05%;
  --wash-tilt:-26.1deg;
}


/* 3. BASE ================================================================== */

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--ink); color:var(--cream);
  font-family:var(--body); font-size:17px; font-weight:400; line-height:1.65;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
  padding-bottom:var(--sbar-h);
}
img,svg{display:block; max-width:100%}
a{color:inherit; text-decoration:none}
/* Form controls do not inherit type from the page, they take the UA font. The
   buttons that carry a label set their own, but the two icon buttons in the
   chrome, the burger and the drawer close, were left sitting on the system
   font. Nothing ever showed, because neither one contains text, but the brief
   says only Besley and Archivo render and a check that walks every element was
   right to say otherwise. */
button,input,textarea,select{font-family:inherit}
h1,h2,h3,p,figure,blockquote{margin:0}
ul{margin:0; padding:0; list-style:none}

.wrap{max-width:var(--container); margin:0 auto; padding:0 var(--gutter)}
.sec{padding:var(--pad-y) 0}

/* keyboard first stop, parked off screen until it is focused */
.skip{
  position:absolute; left:-9999px; top:0; z-index:60;
  background:var(--clay); color:var(--cream); padding:14px 20px;
  font-weight:600; font-size:13px; text-transform:uppercase;
  letter-spacing:var(--ls-13);
}
.skip:focus{left:0}


/* 4. TYPE ROLES ============================================================ */

.display{
  font-family:var(--display); font-weight:800; text-transform:uppercase;
  line-height:.90; letter-spacing:var(--ls-d-72); color:var(--paper);
}
.h2{font-size:var(--h-a); line-height:.94}
.h3{font-family:var(--display); font-weight:700; font-size:22px; line-height:1.1;
    text-transform:uppercase; letter-spacing:.005em}
.lede{font-size:18px; line-height:1.62; max-width:58ch; color:rgba(var(--cream-rgb),.80)}
.body{font-size:17px; line-height:1.65; max-width:62ch; color:rgba(var(--cream-rgb),.74)}
.label{
  font-family:var(--body); font-weight:600; font-size:12px; line-height:1;
  text-transform:uppercase; letter-spacing:var(--ls-12);
}
/* Rationed: at most one per three sections on any page. Count before adding. */
.eyebrow{
  display:block; margin-bottom:26px;
  font-family:var(--body); font-weight:600; font-size:12px; line-height:1;
  text-transform:uppercase; letter-spacing:var(--ls-12); color:rgba(var(--mist-rgb),.62);
}
/* the phone number, wherever it appears: tabular figures so the digits sit on
   one rhythm, and a fixed track so it reads as set type not a data field */
.tel{font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
     letter-spacing:var(--ls-tel)}


/* 5. DETAIL MARKS ========================================================== */

/* quilt-cut icon set. Squares, half square triangles, quarter circles and 45
   degree cuts only, two flat tones, one 48 unit art box. Fills are custom
   properties so they cross the <use> shadow boundary and no literal hex ships
   in the markup. Masters: ~/.claude/brands/mountain-power-wash/elements/icons/ */
.icdefs{position:absolute; width:0; height:0; overflow:hidden}
.ic{width:36px; height:36px; flex:0 0 auto}
.ic--sm{width:24px; height:24px; margin-top:1px}

/* quilting stitch, 2px stitch and 4px gap. Five per page, no more. */
.stitch{
  width:64px; height:2px; margin:20px 0 0;
  background-image:repeating-linear-gradient(90deg,
    rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px);
}

/* quilt binding: a flying geese strip at the block's true 2:1 base to height.
   Two deployments per page only, one per edge that earns a binding. */
.geese{display:block; width:100%; height:16px}

/* the ridgeline seam: his traced ridge contour as the divider between major
   sections. Two per page minimum, drawn by the wipe below. */
.seam{position:relative; height:92px; overflow:hidden}
.seam--air{margin-bottom:36px}   /* clear air above the clay band */
.ridge{background-repeat:repeat-x; background-position:center center;
  background-size:auto 100%; opacity:.85}
.ridge--light{background-image:url("../assets/svg/ridge-rule-light.svg")}
.ridge--dark{background-image:url("../assets/svg/ridge-rule.svg")}

/* the wipe: a sheet of water crossing the element left to right, with a bright
   leading edge that fades once the sheet has landed */
.wipe{position:relative; overflow:hidden}
.wipe__clip{position:absolute; inset:0; overflow:hidden; display:block}
.wipe__inner{position:absolute; inset:0; display:block}
.wipe__edge{
  position:absolute; top:0; right:0; width:130px; height:100%; display:block;
  background:linear-gradient(90deg, rgba(var(--mist-rgb),0) 0%,
    rgba(var(--mist-rgb),.14) 62%, rgba(var(--paper-rgb),.82) 96%,
    rgba(var(--paper-rgb),.95) 100%);
  pointer-events:none;
}
.js .wipe__clip{transform:translateX(-100%)}
.js .wipe__inner{transform:translateX(100%)}
.js .wipe.is-in .wipe__clip{transform:translateX(0);
  transition:transform 760ms var(--ease)}
.js .wipe.is-in .wipe__inner{transform:translateX(0);
  transition:transform 760ms var(--ease)}
.js .wipe.is-in .wipe__edge{opacity:0; transition:opacity 300ms linear 640ms}

/* generic reveal. Stagger with style="--d:140ms". */
.js .rise{opacity:0; transform:translateY(24px)}
.js .is-in.rise,.js .is-in .rise{opacity:1; transform:none;
  transition:opacity 620ms var(--ease) var(--d,0ms), transform 620ms var(--ease) var(--d,0ms)}


/* 6. ACTIONS =============================================================== */

/* clay is the only action colour on the site */
.cta{
  position:relative; z-index:0; overflow:hidden;
  display:inline-block; background:var(--clay); color:var(--cream);
  font-family:var(--body); font-weight:600; font-size:15px; line-height:1;
  text-transform:uppercase; letter-spacing:var(--ls-15); white-space:nowrap;
  padding:20px 34px; border:0; cursor:pointer;
  transition:background 140ms var(--ease), transform 140ms var(--ease);
}
/* the darker press colour arrives as a fill wiping in from the left rather
   than as a flat swap, because a wipe from one edge is the gesture the whole
   site is built on. It is one scaleX on a pseudo element behind the label, so
   the hover costs a composite and nothing else. */
.cta::before{
  content:""; position:absolute; inset:0; z-index:-1; background:var(--clay-press);
  transform:scaleX(0); transform-origin:left center;
  transition:transform 220ms var(--ease);
}
.cta:hover::before,.cta:focus-visible::before{transform:scaleX(1)}
/* pressed, the button seats into the page rather than nudging down it */
.cta:active{transform:translateY(2px)}
.cta:active::before{transition-duration:90ms}
.cta:focus-visible{outline:2px solid var(--cream); outline-offset:3px}
.cta--lg{font-size:16px; padding:23px 42px; letter-spacing:var(--ls-16)}
/* the second lane. Calling is not a lesser intent, it is a quieter one, so it
   takes the same box drawn in line weight instead of fill. The fill wipes the
   same way, in its own weight of light rather than in the action colour. */
.cta--ghost{
  background:transparent; color:var(--cream);
  box-shadow:inset 0 0 0 2px rgba(var(--mist-rgb),.42);
  transition:box-shadow 140ms var(--ease), background 140ms var(--ease);
}
.cta--ghost::before{background:rgba(var(--mist-rgb),.10)}
.cta--ghost:hover{box-shadow:inset 0 0 0 2px rgba(var(--mist-rgb),.72)}
/* on the one cream band, the ghost inverts to navy line on cream */
.relief .cta--ghost{color:var(--ridge-navy);
  box-shadow:inset 0 0 0 2px rgba(var(--navy-rgb),.40)}
.relief .cta--ghost::before{background:rgba(var(--navy-rgb),.08)}
.relief .cta--ghost:hover{box-shadow:inset 0 0 0 2px rgba(var(--navy-rgb),.70)}

/* the conversion lane set. Text first, call second, and one line of plain
   language under it so the primary button never has to explain itself. */
.cluster{display:flex; flex-wrap:wrap; align-items:center; gap:16px}
.cluster__note{
  margin-top:18px; font-size:14px; line-height:1.55;
  color:rgba(var(--cream-rgb),.62); max-width:46ch;
}
.cluster__note a{
  color:var(--cream); border-bottom:1px solid rgba(var(--mist-rgb),.34);
}
.cluster__note a:hover{border-bottom-color:var(--cream)}

/* form fields. Square, ink ground, mist hairline, no radius, no shadow. */
.field{display:block}
.field span{
  display:block; margin-bottom:9px;
  font-family:var(--body); font-weight:600; font-size:12px; line-height:1;
  text-transform:uppercase; letter-spacing:var(--ls-12);
  color:rgba(var(--mist-rgb),.66);
}
.field input,.field textarea,.field select{
  width:100%; display:block; background:var(--ink); color:var(--cream);
  font-family:var(--body); font-size:16px; line-height:1.4;
  padding:15px 16px; border:1px solid rgba(var(--mist-rgb),.26); border-radius:0;
  transition:border-color 140ms var(--ease);
}
.field input::placeholder,.field textarea::placeholder{color:rgba(var(--cream-rgb),.34)}
.field input:focus,.field textarea:focus{
  outline:none; border-color:rgba(var(--mist-rgb),.72);
}
.field textarea{resize:vertical; min-height:96px}


/* 7. CHROME =============================================================== */

/* --- nav ----------------------------------------------------------------- */
.nav{
  position:sticky; top:0; z-index:40; height:var(--nav-h);
  background:rgba(var(--ink-rgb),.92); backdrop-filter:saturate(120%) blur(8px);
  border-bottom:1px solid rgba(var(--mist-rgb),.12);
}
.nav__in{
  max-width:var(--container); margin:0 auto; padding:0 var(--gutter);
  height:100%; display:flex; align-items:center; gap:40px;
}
.nav__mark{width:196px; flex:0 0 auto; opacity:.96}
.nav__links{display:flex; gap:30px; margin-left:auto}
.nav__links a{
  font-weight:500; font-size:13px; text-transform:uppercase; letter-spacing:var(--ls-13);
  color:rgba(var(--cream-rgb),.72); transition:color 140ms var(--ease);
}
.nav__links a:hover,.nav__links a[aria-current="page"]{color:var(--cream)}
.nav__tel{
  font-family:var(--display); font-weight:700; font-size:19px;
  color:var(--cream); white-space:nowrap;
}
.nav__cta{padding:15px 24px; font-size:13px; letter-spacing:var(--ls-13)}
/* the same ring the buttons carry, on the chrome links that had none */
.nav__links a:focus-visible,.nav__tel:focus-visible,.foot__links a:focus-visible,
.foot__more:focus-visible,.menu__links a:focus-visible,.menu__tel:focus-visible{
  outline:2px solid var(--cream); outline-offset:3px;
}

/* --- mobile menu --------------------------------------------------------- */
/* Below 900px the five links come off the bar and go behind one button. The
   drawer is the page's own ink ground at full screen, square, and carries the
   same links plus both conversion lanes. It is a sibling of .nav, never a
   child: .nav has a backdrop-filter, and that makes it the containing block
   for anything fixed inside it. */
.nav__burger{display:none}
.menu{display:none}
/* the page behind the drawer does not scroll while it is open */
body.menu-open{overflow:hidden}

@media (max-width:900px){
  .nav__burger{
    display:flex; flex-direction:column; justify-content:center; gap:6px;
    flex:0 0 auto; width:48px; height:48px; padding:0 10px; margin-right:-10px;
    background:transparent; border:0; cursor:pointer;
  }
  .nav__burger span{display:block; width:28px; height:2px; background:var(--cream)}
  .nav__burger:focus-visible{outline:2px solid var(--cream); outline-offset:2px}

  .menu{
    display:block; position:fixed; inset:0; z-index:60; overflow-y:auto;
    background:var(--ink); opacity:0; transform:translateY(-10px);
    transition:opacity 200ms var(--ease), transform 200ms var(--ease);
  }
  .menu[hidden]{display:none}
  .menu.is-open{opacity:1; transform:none}
  .menu__in{
    min-height:100%; display:flex; flex-direction:column;
    padding:0 var(--gutter) 44px;
  }
  .menu__top{
    display:flex; align-items:center; justify-content:space-between;
    height:var(--nav-h); margin:0 -10px 22px 0;
  }
  /* the same mark at the same width the bar sets it at, so opening the drawer
     does not move the brand */
  .menu__mark{width:150px; opacity:.96}
  .menu__close{
    display:flex; align-items:center; justify-content:center;
    width:48px; height:48px; padding:0; background:transparent; border:0;
    cursor:pointer; color:var(--cream);
  }
  .menu__close svg{width:22px; height:22px; stroke:currentColor; stroke-width:2}
  .menu__close:focus-visible{outline:2px solid var(--cream); outline-offset:2px}
  .menu__links{display:block; border-top:1px solid rgba(var(--mist-rgb),.18)}
  .menu__links a{
    display:block; padding:20px 0;
    border-bottom:1px solid rgba(var(--mist-rgb),.18);
    font-family:var(--display); font-weight:700; font-size:clamp(28px,7.6vw,38px);
    line-height:1.06; text-transform:uppercase; letter-spacing:var(--ls-d-50);
    color:rgba(var(--paper-rgb),.86);
  }
  .menu__links a[aria-current="page"]{color:var(--paper)}
  .menu__foot{margin-top:auto; padding-top:40px}
  .menu__tel{
    display:block; padding:8px 0; font-family:var(--display); font-weight:700;
    font-size:34px; color:var(--paper);
  }
  .menu__cta{display:block; margin-top:20px; text-align:center}
}
/* above the drawer's own breakpoint it can never be open, so nothing of it
   is left in the tab order either */
@media (min-width:901px){ .menu{display:none} }

/* --- sticky mobile bar --------------------------------------------------- */
/* Two lanes, one thumb away, on phones only. Held back until the hero cluster
   has scrolled off so the same ask is never on screen twice. */
.sbar{display:none}
@media (max-width:767px){
  :root{--sbar-h:64px}
  .sbar{
    display:grid; grid-template-columns:1fr 1fr;
    position:fixed; left:0; right:0; bottom:0; z-index:50; height:var(--sbar-h);
    border-top:1px solid rgba(var(--mist-rgb),.20);
    transform:translateY(100%); transition:transform 220ms var(--ease);
  }
  .js .sbar.is-up{transform:translateY(0)}
  .sbar a{
    display:flex; align-items:center; justify-content:center; gap:9px;
    font-family:var(--body); font-weight:600; font-size:13px; line-height:1;
    text-transform:uppercase; letter-spacing:var(--ls-13);
  }
  .sbar__text{background:var(--clay); color:var(--cream)}
  .sbar__text:active{background:var(--clay-press)}
  .sbar__call{background:var(--ridge-navy); color:var(--cream)}
  .sbar svg{width:19px; height:19px; flex:0 0 auto}
}
/* no JS: the bar is simply always up rather than never present */
html:not(.js) .sbar{transform:none}

/* --- footer -------------------------------------------------------------- */
.foot{background:var(--ink); border-top:1px solid rgba(var(--mist-rgb),.14);
  padding:96px 0 64px}
.foot__grid{display:grid; grid-template-columns:repeat(12,1fr); gap:var(--gutter);
  align-items:start}
.seal{grid-column:1 / span 3; position:relative; width:176px; height:176px}
.seal img,.seal svg{position:absolute; inset:0; width:100%; height:100%}
.seal img{opacity:.92}
/* tracking measured off the roundel's own top arc: 44 unit cap height,
   59 unit pitch on a 388 unit radius */
.seal text{font-family:var(--display); font-weight:700;
  font-size:63px; letter-spacing:21px}
.foot__area{grid-column:5 / span 4}
.foot__contact{grid-column:9 / span 4}
.foot h2{margin-bottom:18px; color:rgba(var(--mist-rgb),.62)}
.foot p{font-size:15px; line-height:1.7; color:rgba(var(--cream-rgb),.72)}
.foot__more{
  display:inline-block; margin-top:16px; font-size:15px; color:var(--cream);
  border-bottom:1px solid rgba(var(--mist-rgb),.34);
}
.foot__more:hover{border-bottom-color:var(--cream)}
.foot__line{display:flex; align-items:center; gap:18px}
.foot__mono{width:72px; height:72px; flex:0 0 auto}
.foot__tel{
  display:inline-block; font-family:var(--display); font-weight:700;
  font-size:29px; color:var(--paper);
}
.foot__links{margin-top:22px; display:flex; flex-direction:column; gap:10px;
  font-size:15px; color:rgba(var(--cream-rgb),.72)}
.foot__links a{border-bottom:1px solid rgba(var(--mist-rgb),.22)}
.foot__links a:hover{color:var(--cream); border-bottom-color:var(--cream)}
/* the service area drawn the way he describes it: one surveyed line, five
   towns, nothing else. Schematic, at real relative positions. */
.famap{width:100%; max-width:342px; height:auto; margin-top:26px}
.famap__line{fill:none; stroke:rgba(var(--mist-rgb),.34); stroke-width:2}
.famap__pin{fill:var(--cream)}
.famap__t{
  fill:rgba(var(--cream-rgb),.72); font-family:var(--body); font-weight:600;
  font-size:15px; letter-spacing:1.9px;
}
.famap__s{
  fill:rgba(var(--mist-rgb),.58); font-family:var(--body); font-weight:600;
  font-size:13px; letter-spacing:2.2px;
}
.foot__base{
  padding-top:26px; border-top:0;
  display:flex; justify-content:space-between; gap:24px; flex-wrap:wrap;
  font-size:13px; color:rgba(var(--cream-rgb),.56);
}
.geese--foot{margin-top:72px; color:var(--haze)}


/* 8. HOME SECTIONS ========================================================= */

/* --- hero ---------------------------------------------------------------- */
.hero{
  position:relative; isolation:isolate; overflow:hidden;
  min-height:620px; height:calc(100svh - var(--nav-h)); max-height:960px;
  display:flex; align-items:flex-end;
}
.hero__media{position:absolute; inset:0; z-index:-3}
/* the crew and the water arc are the subject, so the grade stops well short of
   crushing them. The type side is darkened by the scrim, not by the frame. */
.hero__media img{
  width:100%; height:100%; object-fit:cover; object-position:52% 42%;
  filter:grayscale(.20) saturate(.96) contrast(1.12) brightness(.88);
}
.hero__tint{position:absolute; inset:0; z-index:-2; background:var(--haze);
  mix-blend-mode:color; opacity:.30}
/* directional scrim, not a full frame tint: a hard wedge holds the type column
   and the steeple shaft behind the headline under L60, then releases by 62
   percent so the crew, the wand and the water arc stay the bright incident */
.hero__scrim{position:absolute; inset:0; z-index:-1; background:
  linear-gradient(90deg, rgba(var(--ink-rgb),.95) 0%, rgba(var(--ink-rgb),.93) 26%,
                          rgba(var(--ink-rgb),.78) 40%, rgba(var(--ink-rgb),.46) 52%,
                          rgba(var(--ink-rgb),.12) 60%, rgba(var(--ink-rgb),0) 68%),
  linear-gradient(180deg, rgba(var(--ink-rgb),.46) 0%, rgba(var(--ink-rgb),.02) 24%,
                          rgba(var(--ink-rgb),0) 74%, rgba(var(--ink-rgb),.92) 100%);
}
/* second half of the same directional scrim: the headline band only, so the
   bright sky behind the last word is held down without touching the crew */
.hero__band{position:absolute; inset:0; z-index:-1;
  background:linear-gradient(90deg, rgba(var(--ink-rgb),0) 30%,
                          rgba(var(--ink-rgb),.42) 56%, rgba(var(--ink-rgb),.42) 100%);
  -webkit-mask-image:linear-gradient(180deg, rgba(var(--ink-rgb),0) 17%, rgba(var(--ink-rgb),1) 30%, rgba(var(--ink-rgb),1) 61%, rgba(var(--ink-rgb),0) 70%);
  mask-image:linear-gradient(180deg, rgba(var(--ink-rgb),0) 17%, rgba(var(--ink-rgb),1) 30%, rgba(var(--ink-rgb),1) 61%, rgba(var(--ink-rgb),0) 70%);
}
.hero__in{width:100%; padding-bottom:96px; padding-top:64px}
.hero__h1{font-size:clamp(48px,10.5vw,154px); line-height:.88; max-width:1020px;
  letter-spacing:var(--ls-d-154)}
/* hand set. Besley 800 caps leave four loose joints at hero size, measured off
   the 1440 render: L before T, T before the word space, T before Y, and Y before
   the full stop. Each is pulled in on its own, nothing else is touched. */
.kn-a{letter-spacing:-.045em}
.kn-b{letter-spacing:-.030em}
.kn-c{letter-spacing:-.055em}
.kn-d{letter-spacing:-.075em}
/* the full stop is the whole promise: it gets the action colour, once */
.hero__stop{color:var(--clay)}
.hero__sub{margin-top:30px; max-width:44ch; font-size:19px; line-height:1.55;
  color:rgba(var(--cream-rgb),.88)}
.hero__act{margin-top:40px}

/* set piece 1: the headline resolves out of the ground, and the frame behind it
   settles. The photograph comes in a few percent over size and eases back to
   rest across the whole of the headline's arrival, so the hero reads as a shot
   being landed rather than a picture being parked. Once, on load, on the one
   element that is already its own layer, and nowhere else on the site. */
.js .hero__media img{transform:scale(1.055)}
.js .hero.is-in .hero__media img{transform:none;
  transition:transform 1500ms var(--ease)}
.js .hero__h1{opacity:.05; transform:translateY(26px)}
.js .hero__sub,.js .hero__act{opacity:0; transform:translateY(18px)}
.js .hero.is-in .hero__h1{opacity:1; transform:none;
  transition:opacity 600ms var(--ease), transform 600ms var(--ease)}
.js .hero.is-in .hero__sub{opacity:1; transform:none;
  transition:opacity 600ms var(--ease) 240ms, transform 600ms var(--ease) 240ms}
.js .hero.is-in .hero__act{opacity:1; transform:none;
  transition:opacity 600ms var(--ease) 360ms, transform 600ms var(--ease) 360ms}

/* --- trust row ----------------------------------------------------------- */
/* the three promises the estimate button is standing on */
.trust{background:var(--ink); padding:40px 0 36px}
/* three even columns that start on the same gutter line as the headline above,
   so the row sits on the page's left edge and not in its own centred box */
.trust__in{display:grid; grid-template-columns:repeat(3,minmax(0,340px));
  justify-content:start; gap:0 40px; align-items:center}
.trust__i{display:flex; align-items:center; gap:16px}
.trust__i .label{color:rgba(var(--cream-rgb),.84)}

/* --- proof --------------------------------------------------------------- */
.proof{background:var(--ink); position:relative; overflow:hidden}
/* the seal sits under the reviews the way a maker's stamp sits under the work,
   run off the corner so it reads as ground, not as a badge */
.proof__ghost{
  position:absolute; z-index:0; pointer-events:none;
  width:900px; height:900px; right:-322px; bottom:-336px; opacity:.04;
}
.proof>.wrap{position:relative; z-index:1}
.proof__head{max-width:900px}
.proof__head .stitch{margin-top:20px}
.proof__head .body{margin-top:22px}
.quotes{margin-top:88px; display:grid; grid-template-columns:repeat(12,1fr);
  gap:0 var(--gutter); align-items:start}
.q p{font-family:var(--display); font-weight:400; color:var(--paper)}
.q figcaption{margin-top:20px; display:flex; align-items:baseline; gap:14px}
/* the name is bright, the place under it is quiet */
.q figcaption span:last-child{
  font-size:13px; letter-spacing:.02em; color:rgba(var(--cream-rgb),.52);
}
/* and the name stays bright even when it is the only span in the caption.
   :last-child counts as a class in the cascade, so the quiet rule above was out
   specifying the bright one on every caption that carries a name and nothing
   else, which is all three of the ones set on ridge navy. Measured there, cream
   at 52 percent over ridge navy resolves to a mid grey that reads 4.46:1, under
   the 4.5 the brief asks for. Pinned here by matching that specificity and
   coming after it. */
.q figcaption span.label{color:var(--cream)}
/* one review carries the message (progress photos), the other two support it */
.q--hero{
  grid-column:1 / span 7; position:relative;
  border-top:2px solid rgba(var(--mist-rgb),.38); padding-top:34px;
}
/* the opening mark set at display scale and dropped behind the line it opens */
.q--hero::before{
  content:"\201C"; position:absolute; left:-30px; top:2px; z-index:0;
  font-family:var(--display); font-weight:800; font-size:236px; line-height:1;
  color:rgba(var(--haze-rgb),.40); pointer-events:none;
}
.q--hero>*{position:relative; z-index:1}
.q--hero p{font-size:clamp(27px,3.5vw,46px); line-height:1.16}
.q-side{
  grid-column:9 / span 4; padding-top:10px;
  display:flex; flex-direction:column; gap:46px;
}
.q--sm{border-top:1px solid rgba(var(--mist-rgb),.18); padding-top:22px}
.q--sm p{font-size:19px; line-height:1.38; color:rgba(var(--paper-rgb),.86)}
.q--sm figcaption{margin-top:16px}

/* --- the one cream relief band ------------------------------------------- */
/* Exactly one per page. Adding a second breaks the brief. */
.relief{
  background:var(--cream); color:var(--ink);
  border-top:2px solid var(--ridge-navy); border-bottom:2px solid var(--ridge-navy);
}
.relief .display{color:var(--ridge-navy)}
.relief__head{display:block; max-width:820px}
/* the crest stamps the one light section, the way a seal stamps the one page
   that carries the guarantee */
.relief__seal{width:126px; height:auto; margin-bottom:26px}
.relief__body{margin-top:28px; max-width:56ch; font-size:17px; line-height:1.62;
  color:rgba(var(--navy-rgb),.82)}

/* --- set piece 2: the water sheet on one real roof ------------------------ */
/* 1120px against a 900px source is a 1.24x upscale on roof-half-washed.jpg,
   and it is accepted: that file is the approved direction-demo crop, the wash
   line in it is what --wash-mid and --wash-tilt are measured from, and there
   is no larger original of that frame. Nothing else on the site is upscaled. */
.ba{margin-top:60px; max-width:1120px}
/* trade card double rule: the 2px plate rule, a breath of cream, then the 1px
   hairline set outside it */
.ba__frame{
  position:relative; aspect-ratio:3 / 2; overflow:hidden;
  background:var(--ridge-navy); border:2px solid var(--ridge-navy);
  outline:1px solid var(--ridge-navy); outline-offset:3px;
}
.ba__frame>img{
  width:100%; height:100%; object-fit:cover; object-position:50% 50%;
  filter:contrast(1.06) saturate(1.06);
}
.ba__tag{position:absolute; z-index:3; background:var(--ink); color:var(--cream);
  padding:9px 13px}
/* the two chips are the only labels sewn onto a photograph, so they get sewn */
.ba__tag::after{
  content:""; position:absolute; inset:-6px; pointer-events:none;
  background:
    repeating-linear-gradient(90deg, rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px) left top/100% 2px repeat-x,
    repeating-linear-gradient(90deg, rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px) left bottom/100% 2px repeat-x,
    repeating-linear-gradient(180deg, rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px) left top/2px 100% repeat-y,
    repeating-linear-gradient(180deg, rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px) right top/2px 100% repeat-y;
}
.ba__tag--before{left:15%; top:6%}
.ba__tag--after{right:6%; bottom:8%}
/* the sheet is a rotated band that rests on the photo's own wash line */
.ba__water{
  position:absolute; z-index:2; pointer-events:none;
  top:-24%; height:148%; left:var(--wash-mid); width:13%; margin-left:-6.5%;
  transform:translateX(0) rotate(var(--wash-tilt));
}
.ba__water span{position:absolute; top:0; bottom:0; display:block}
.ba__water-body{
  left:0; right:0;
  background:linear-gradient(90deg,
    rgba(var(--mist-rgb),0) 0%, rgba(var(--mist-rgb),.05) 14%,
    rgba(var(--paper-rgb),.19) 31%, rgba(var(--paper-rgb),.09) 44%,
    rgba(var(--paper-rgb),.36) 61%, rgba(var(--paper-rgb),.72) 78%,
    rgba(var(--paper-rgb),.93) 88%, rgba(var(--paper-rgb),.34) 96%,
    rgba(var(--paper-rgb),0) 100%);
  -webkit-mask-image:linear-gradient(180deg, rgba(var(--ink-rgb),.52) 0%, rgba(var(--ink-rgb),1) 9%,
    rgba(var(--ink-rgb),.68) 20%, rgba(var(--ink-rgb),.97) 33%, rgba(var(--ink-rgb),.62) 45%, rgba(var(--ink-rgb),1) 57%,
    rgba(var(--ink-rgb),.74) 70%, rgba(var(--ink-rgb),.96) 82%, rgba(var(--ink-rgb),.55) 100%);
  mask-image:linear-gradient(180deg, rgba(var(--ink-rgb),.52) 0%, rgba(var(--ink-rgb),1) 9%,
    rgba(var(--ink-rgb),.68) 20%, rgba(var(--ink-rgb),.97) 33%, rgba(var(--ink-rgb),.62) 45%, rgba(var(--ink-rgb),1) 57%,
    rgba(var(--ink-rgb),.74) 70%, rgba(var(--ink-rgb),.96) 82%, rgba(var(--ink-rgb),.55) 100%);
}
.ba__water-lead{
  right:4%; width:11%;
  -webkit-backdrop-filter:blur(1.5px); backdrop-filter:blur(1.5px);
}
.js .ba__water{transform:translateX(-600%) rotate(var(--wash-tilt))}
.js .ba__frame.is-in .ba__water{
  transform:translateX(0) rotate(var(--wash-tilt));
  transition:transform 780ms var(--ease-water);
}
.ba__cap{margin-top:18px; font-size:14px; letter-spacing:.01em;
  color:rgba(var(--navy-rgb),.72)}
.ba__act{margin-top:34px}
.relief .cluster__note{color:rgba(var(--navy-rgb),.66)}
.relief .cluster__note a{color:var(--ridge-navy);
  border-bottom-color:rgba(var(--navy-rgb),.34)}

/* --- services: the quilt block IS the tile -------------------------------- */
.services{background:var(--ink)}
.services__head{max-width:760px}
/* one seam weight everywhere: 1px of mist between every cell and around the
   whole wall, so the six blocks read as pieced together rather than parked */
.quilt{
  margin-top:50px; display:grid; grid-template-columns:repeat(12,1fr);
  gap:1px; background:rgba(var(--mist-rgb),.24);
  border:1px solid rgba(var(--mist-rgb),.24);
}
/* Every tile is the link to its service page, edge to edge, and the ground of
   it is a photograph of that service. The quilt block the tile used to be
   drawn as is now the 44px chip in the corner: the vernacular survives at
   seasoning scale and the real job carries the block. The colour classes stay
   because they are what shows while a lazy photograph is still on the wire. */
.tile{
  position:relative; display:flex; flex-direction:column; justify-content:flex-end;
  min-height:400px; overflow:hidden;
}
.tile--navy{background:var(--ridge-navy)}
.tile--forest{background:var(--forest)}
.tile--ink{background:var(--ink)}
.tile--haze{background:var(--haze)}
.tile:nth-child(1){grid-column:span 5}
.tile:nth-child(2){grid-column:span 4}
.tile:nth-child(3){grid-column:span 3}
.tile:nth-child(4){grid-column:span 3}
.tile:nth-child(5){grid-column:span 5}
.tile:nth-child(6){grid-column:span 4}
.tile__ph{
  position:absolute; inset:0; z-index:0; width:100%; height:100%;
  object-fit:cover; object-position:var(--op,50% 50%);
  filter:contrast(1.06) saturate(.98) brightness(.94);
  transition:transform 420ms var(--ease);
}
.tile:hover .tile__ph{transform:scale(1.035)}
/* the top scrim only, so the corner chip has a field to sit on whatever the
   sky in that frame is doing. The bar carries its own scrim below. */
.tile__scrim{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg, rgba(var(--ink-rgb),.44) 0%,
    rgba(var(--ink-rgb),.08) 30%, rgba(var(--ink-rgb),0) 56%);
}
/* the quilt block, kept as a sewn-on patch rather than as the whole tile */
.tile__chip{
  position:absolute; z-index:2; top:20px; left:20px; width:44px; height:44px;
  box-shadow:0 0 0 1px rgba(var(--mist-rgb),.46);
}
/* the patch is sewn on as the tile arrives: it settles into its corner out of
   the corner, which is the direction a patch would be laid from. Small, once,
   and it is the only thing on the wall that moves, so the eye is told which
   part of the tile is the maker's mark. */
.js .tile .tile__chip{opacity:0; transform:translate(-7px,-7px) scale(.9)}
.js .tile.is-in .tile__chip{opacity:1; transform:none;
  transition:opacity 380ms var(--ease) 140ms, transform 520ms var(--ease) 140ms}
.tile__bar{
  position:relative; z-index:2; padding:22px 26px 26px;
  border-top:2px solid rgba(var(--mist-rgb),.24);
  background:linear-gradient(180deg, rgba(var(--ink-rgb),.93) 0%,
    rgba(var(--ink-rgb),.985) 44%, var(--ink) 100%);
  transition:border-top-color 140ms var(--ease);
}
/* the scrim is anchored to the bar, not to the tile, so it lands in the same
   place whatever height the tile is at that breakpoint */
.tile__bar::before{
  content:""; position:absolute; left:0; right:0; bottom:100%; height:132px;
  pointer-events:none;
  background:linear-gradient(180deg, rgba(var(--ink-rgb),0) 0%,
    rgba(var(--ink-rgb),.32) 46%, rgba(var(--ink-rgb),.80) 100%);
}
.tile__name{display:flex; align-items:flex-start; gap:12px}
/* two heading lines and two body lines reserved, so every label bar in a row
   starts at the same y and the quilt reads as one wall */
.tile__bar .h3{color:var(--paper); min-height:2.2em}
.tile__bar p{margin-top:10px; font-size:14px; line-height:1.5; min-height:3em;
  color:rgba(var(--cream-rgb),.68); max-width:38ch}
/* the affordance is the binding turning to the action colour, not a lift */
.tile:hover .tile__bar,.tile:focus-visible .tile__bar{border-top-color:var(--clay)}
.tile:focus-visible{outline:2px solid var(--cream); outline-offset:-4px}

/* --- the one full bleed clay band ---------------------------------------- */
/* Exactly one per page. Clay appears here and on buttons, nowhere else. */
.claim{background:var(--clay); color:var(--cream); padding:112px 0;
  position:relative; overflow:hidden}
/* the binding goes on the hot edge, where a quilt takes its wear */
.geese--claim{position:absolute; top:0; left:0; z-index:2; color:var(--ink)}
/* type as texture: the word the whole band is about, run off two edges so it
   reads as a watermark in the cloth and never as a second headline */
.claim::before{
  content:"WASHED"; position:absolute; right:-34%; bottom:-15%; z-index:0;
  font-family:var(--display); font-weight:800; line-height:.82;
  font-size:clamp(140px,19vw,280px); letter-spacing:var(--ls-d-104);
  color:rgba(var(--cream-rgb),.06); white-space:nowrap; pointer-events:none;
}
.claim>.wrap{position:relative; z-index:1}
.claim .display{color:var(--cream); font-size:clamp(36px,7.3vw,104px); line-height:.90;
  letter-spacing:var(--ls-d-104)}
.claim__sub{
  margin-top:36px; max-width:54ch; font-size:20px; line-height:1.55;
  color:rgba(var(--cream-rgb),.92);
}

/* --- heights ------------------------------------------------------------- */
.heights{position:relative; background:var(--ink); padding:0}
.heights__grid{display:grid; grid-template-columns:1.45fr 1fr; align-items:center}
.heights__copy{
  padding:var(--pad-y) 4vw var(--pad-y)
          max(var(--gutter), calc((100vw - var(--container)) / 2 + var(--gutter)));
}
.heights__copy .display{font-size:var(--h-b); line-height:.96;
  letter-spacing:var(--ls-d-50)}
.heights__copy .body{margin-top:30px}
.heights__media{position:relative; align-self:stretch;
  min-height:760px; overflow:hidden}
/* framed on the steeple and the crew. The source file is pre-cropped above the
   trailer board, so no scale compensation is needed here. */
.heights__media img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 50%;
  filter:contrast(1.08) brightness(.9);
}
.heights__media::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(90deg, var(--ink) 0%, rgba(var(--ink-rgb),.30) 22%,
    rgba(var(--ink-rgb),0) 52%);
}
/* the plain hairline over the signature line is stitched instead: the line it
   carries is the one Keith would say out loud */
.sign{display:flex; align-items:center; gap:16px; margin-top:48px;
  position:relative; padding-top:28px}
.sign::before{
  content:""; position:absolute; left:0; right:0; top:0; height:2px;
  background-image:repeating-linear-gradient(90deg,
    rgba(var(--mist-rgb),.58) 0 2px, transparent 2px 6px);
}
.sign svg{width:27px; height:28px; flex:0 0 auto; fill:rgba(var(--cream-rgb),.70)}
.sign span{font-family:var(--display); font-weight:400; font-size:19px;
  color:rgba(var(--cream-rgb),.86)}

/* --- estimate strip ------------------------------------------------------ */
/* The compact form. The full four field version lives on /estimate/. Raised
   navy so it reads as a counter set into the dark page, not another band. */
.est{background:var(--ridge-navy)}
.est__grid{display:grid; grid-template-columns:1fr 1fr; gap:var(--gutter);
  align-items:start}
.est__copy .display{font-size:var(--h-b); line-height:.96;
  letter-spacing:var(--ls-d-50)}
.est__copy .body{margin-top:26px}
/* the matched proof beside the ask: the one thing a stranger filling in a form
   wants to know is whether the person on the other end answers */
.est__proof{margin-top:46px; max-width:46ch}
.est__form{display:grid; gap:20px}
.est__row{display:grid; grid-template-columns:1fr 1fr; gap:20px}
.est__send{justify-self:start; margin-top:4px}
/* the graceful failure path: no backend yet, so the form always hands the
   visitor the lane that does work */
.est__note{
  margin-top:4px; font-size:14px; line-height:1.55; max-width:46ch;
  color:rgba(var(--cream-rgb),.62);
}
.est__note a{color:var(--cream); border-bottom:1px solid rgba(var(--mist-rgb),.34)}
.est__note a:hover{border-bottom-color:var(--cream)}
.est__status{
  display:none; margin-top:4px; padding:16px 18px; font-size:15px; line-height:1.55;
  background:var(--ink); border-left:3px solid var(--clay);
  color:rgba(var(--cream-rgb),.86);
}
.est__status.is-shown{display:block}
.est__status a{color:var(--cream); border-bottom:1px solid rgba(var(--mist-rgb),.44)}


/* 9. INNER PAGE SHELL ====================================================== */
/* Every page that is not the home page opens on this block. Inner page workers
   fill it from their content file: h1, lede, one CTA cluster. */

.phero{
  position:relative; background:var(--ridge-navy); overflow:hidden;
  padding:104px 0 88px;
}
.phero__media{position:absolute; inset:0; z-index:0}
/* the band a hero photo is cut down to is short and wide, so a tall frame gets
   most of itself cropped away. --op says which part of it survives, set on the
   page when the middle of the file is not the subject. */
.phero__media img{width:100%; height:100%; object-fit:cover;
  object-position:var(--op,50% 50%);
  filter:grayscale(.18) contrast(1.10) brightness(.72)}
.phero__media::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(90deg, rgba(var(--ink-rgb),.94) 0%,
    rgba(var(--ink-rgb),.80) 42%, rgba(var(--ink-rgb),.34) 78%,
    rgba(var(--ink-rgb),.20) 100%);
}
.phero>.wrap{position:relative; z-index:1}
.phero h1{font-size:clamp(38px,6.4vw,86px); line-height:.92;
  letter-spacing:var(--ls-d-72); max-width:19ch}
.phero .lede{margin-top:28px}
.phero__act{margin-top:38px}

/* plain running copy on a dark section */
.prose{max-width:62ch}
.prose p{margin-top:22px; font-size:17px; line-height:1.65;
  color:rgba(var(--cream-rgb),.74)}
.prose p:first-child{margin-top:0}
.prose a{color:var(--cream); border-bottom:1px solid rgba(var(--mist-rgb),.34)}
.prose a:hover{border-bottom-color:var(--cream)}
.prose h2{margin-top:52px; font-family:var(--display); font-weight:700;
  font-size:var(--h-b); line-height:1.04; text-transform:uppercase;
  color:var(--paper); letter-spacing:var(--ls-d-50)}
.prose ul{margin-top:22px; display:grid; gap:12px}
.prose li{position:relative; padding-left:26px; font-size:17px; line-height:1.6;
  color:rgba(var(--cream-rgb),.74)}
/* the bullet is a quilt-cut square, set on the cap line */
.prose li::before{
  content:""; position:absolute; left:0; top:.58em; width:9px; height:9px;
  background:var(--clay);
}

/* a matched before and after pair, side by side */
.pair{display:grid; grid-template-columns:1fr 1fr; gap:var(--gutter);
  margin-top:50px}
.pair figure{position:relative}
.pair img{width:100%; height:auto; border:2px solid var(--ridge-navy)}
.pair figcaption{margin-top:14px; font-size:13px; text-transform:uppercase;
  letter-spacing:var(--ls-13); color:rgba(var(--mist-rgb),.62)}
/* Two photographs of the same surface are never the same shape on disk, so a
   pair left at its intrinsic ratios lands two ragged bottom edges and two
   captions on different lines. .pair--box puts both halves in one box: the
   ratio is set once on the pair in --ar, the framing once in --op, and both
   halves take the identical crop. Every value is picked off the two files it
   applies to, and set close enough to their own ratios that the subject never
   leaves the frame. */
.pair--box img{aspect-ratio:var(--ar); height:auto; object-fit:cover;
  object-position:var(--op,50% 50%)}
.pair--box .shot{position:relative; display:block}

/* the before and after slider ---------------------------------------------
   Progressive enhancement, and the order matters: what ships in the markup is
   the static pair above. js/site.js finds any pair carrying data-ba-slider,
   builds this block, and moves the two <img> elements it already has into it.
   With JavaScript off nothing here ever renders and the visitor still gets
   both photographs side by side with their captions.

   Both halves are stacked in one box at the pair's own --ar and --op, so they
   take the identical crop the boxed pair was already giving them, and the
   reveal is a clip-path on the before layer driven by one custom property. */
/* left edge, not centred: the water sheet device and the boxed pairs both sit
   on the page's own gutter line, and a centred block breaks that column */
.baslide{margin:50px 0 0}
.baslide__frame{
  position:relative; overflow:hidden; aspect-ratio:var(--ar,3 / 2);
  background:var(--ridge-navy);
  /* the trade card double rule, the same one the water sheet device carries */
  border:2px solid var(--ridge-navy);
  outline:1px solid var(--ridge-navy); outline-offset:3px;
  cursor:ew-resize;
  /* the browser keeps vertical panning, this element keeps the horizontal
     drag, so a thumb can still scroll the page over a full width photograph */
  touch-action:pan-y;
  -webkit-user-select:none; user-select:none;
}
.baslide__layer{position:absolute; inset:0}
.baslide__layer--before{clip-path:inset(0 calc(100% - var(--pos,50%)) 0 0)}
.baslide__layer img{
  position:absolute; inset:0; width:100%; height:100%; border:0;
  object-fit:cover; object-position:var(--op,50% 50%);
  -webkit-user-drag:none; -webkit-user-select:none; user-select:none;
  pointer-events:none;
}
/* the two chips are the same sewn labels the water sheet device wears, moved
   onto the top corners and dropped when their own side is nearly gone */
.baslide .ba__tag{top:20px; bottom:auto; transition:opacity 200ms var(--ease)}
.baslide .ba__tag--before{left:20px; right:auto}
.baslide .ba__tag--after{right:20px; left:auto}
.baslide.is-min .ba__tag--before,.baslide.is-max .ba__tag--after{opacity:0}
/* the handle is a real button so it takes focus and keys without any help */
.baslide__handle{
  position:absolute; z-index:4; top:0; bottom:0; left:var(--pos,50%);
  width:56px; margin-left:-28px; padding:0; border:0; background:transparent;
  display:flex; align-items:center; justify-content:center;
  cursor:ew-resize; touch-action:none; -webkit-appearance:none;
}
.baslide__handle::before{
  content:""; position:absolute; top:0; bottom:0; left:50%; width:2px;
  margin-left:-1px; background:rgba(var(--paper-rgb),.94);
}
/* the one round corner on the site, and it is round because it is a grip:
   the thing the hand goes to is shaped like the thing the hand goes to */
.baslide__grip{
  position:relative; display:flex; align-items:center; justify-content:center;
  width:54px; height:54px; border-radius:50%;
  background:var(--paper); color:var(--ink);
  box-shadow:0 0 0 2px rgba(var(--navy-rgb),.38);
  transition:background 140ms var(--ease);
}
.baslide__handle:hover .baslide__grip{background:var(--cream)}
.baslide__handle:focus-visible{outline:none}
.baslide__handle:focus-visible .baslide__grip{
  box-shadow:0 0 0 3px var(--ink), 0 0 0 6px var(--cream);
}
.baslide__grip svg{width:26px; height:14px; fill:currentColor}
.baslide__cap{margin-top:18px; font-size:14px; line-height:1.55;
  letter-spacing:.01em; color:rgba(var(--mist-rgb),.62)}

/* running copy with a supporting frame beside it. The reading measure does not
   move: the copy column is the same 62ch the .prose block was already set to,
   and the photograph takes the width the sentence was never going to use. */
.duo{display:grid; grid-template-columns:minmax(0,62ch) minmax(0,1fr);
  gap:44px 56px; align-items:start}
.duo .prose{max-width:none}
.duo__fig img{width:100%; height:auto; aspect-ratio:var(--ar,4/3);
  object-fit:cover; object-position:var(--op,50% 50%);
  border:2px solid var(--ridge-navy)}
.duo__fig figcaption{margin-top:14px; font-size:13px; text-transform:uppercase;
  letter-spacing:var(--ls-13); color:rgba(var(--mist-rgb),.62)}
/* a pair or a slider sitting in the duo's second column is already placed by
   the grid, so it drops the top margin it carries when it stands on its own */
.duo>.pair,.duo>.baslide{margin-top:0}

/* the questions people actually text in, answered the way he answers them.
   Pieced the same way the services wall and the cross links are: one 1px seam
   of mist between every cell and around the block, cells sitting on the page's
   own ground. Question left, answer right, so the block reads as a ledger
   rather than as a stack of headings. Every answer is visible: the FAQPage
   schema build.py writes is read out of this markup, so there is nothing in the
   structured data that a visitor cannot see on the page. */
.faq{
  margin-top:44px; display:grid; gap:1px;
  background:rgba(var(--mist-rgb),.24); border:1px solid rgba(var(--mist-rgb),.24);
}
.faq__item{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.5fr);
  gap:22px 48px; background:var(--ink); padding:32px 34px;
}
.faq__q{
  position:relative; padding-left:26px;
  font-family:var(--display); font-weight:700; font-size:21px; line-height:1.18;
  text-transform:uppercase; letter-spacing:.005em; color:var(--paper);
}
/* the quilt-cut square, the same mark the prose bullets are set with */
.faq__q::before{
  content:""; position:absolute; left:0; top:.44em; width:9px; height:9px;
  background:var(--clay);
}
.faq__a{font-size:16px; line-height:1.62; color:rgba(var(--cream-rgb),.74);
  max-width:60ch}
.faq__a a{color:var(--cream); border-bottom:1px solid rgba(var(--mist-rgb),.34)}
.faq__a a:hover{border-bottom-color:var(--cream)}

/* cross links to the other service pages, at the foot of every inner page */
.xlinks{display:grid; grid-template-columns:repeat(3,1fr); gap:1px;
  margin-top:56px; background:rgba(var(--mist-rgb),.24);
  border:1px solid rgba(var(--mist-rgb),.24)}
.xlinks a{display:block; background:var(--ink); padding:26px 24px;
  transition:background 140ms var(--ease)}
.xlinks a:hover{background:var(--ridge-navy)}
.xlinks .h3{display:block; font-size:18px; color:var(--paper)}
.xlinks span{display:block; margin-top:8px; font-size:14px;
  color:rgba(var(--cream-rgb),.62)}


/* 10. MOBILE =============================================================== */

@media (max-width:900px){
  :root{--gutter:22px; --pad-y:80px}
  body{font-size:16px}
  .nav__links,.nav__tel{display:none}
  .nav__in{gap:16px; justify-content:space-between}
  .nav__mark{width:150px}
  /* the ramp says 11px caps want the loosest track on the page, but the nav has
     346px to hold a mark and a button, so this one role is set to fit */
  .nav__cta{padding:13px 18px; font-size:11px; letter-spacing:.11em}
  .hero{height:calc(100svh - var(--nav-h)); max-height:none}
  /* 72 not 56: the sticky bar owns the bottom 64px of a phone screen, so the
     hero copy is set clear of it rather than tucked under it */
  .hero__in{padding-bottom:72px}
  .hero__sub{font-size:17px; margin-top:22px}
  .hero__act{margin-top:30px}
  .cluster{gap:12px}
  .cluster .cta{width:100%; text-align:center}
  .trust{padding:32px 0 30px}
  .trust__in{grid-template-columns:1fr; gap:18px 0}
  .proof__ghost{width:470px; height:470px; right:-160px; bottom:-150px}
  .q--hero::before{left:-12px; font-size:148px}
  .hero__scrim{background:
    linear-gradient(180deg, rgba(var(--ink-rgb),.62) 0%, rgba(var(--ink-rgb),.10) 30%,
      rgba(var(--ink-rgb),.66) 62%, rgba(var(--ink-rgb),.96) 100%)}
  .seam{height:56px}
  .seam--air{margin-bottom:28px}
  .quotes{grid-template-columns:1fr; gap:0; margin-top:52px}
  .q--hero,.q-side{grid-column:1 / -1}
  .q-side{margin-top:46px; gap:40px; padding-top:0}
  .ba{margin-top:40px}
  .ba__tag--before{left:12%; top:5%}
  .relief__seal{width:104px; margin-bottom:22px}
  .quilt{grid-template-columns:1fr; margin-top:36px}
  /* one column, and the photograph is what sets the height now: 320px leaves
     roughly 180px of picture above a bar that is 140px tall on a phone */
  .tile,.tile:nth-child(n){grid-column:1 / -1; min-height:320px}
  .tile__chip{top:16px; left:16px; width:40px; height:40px}
  .tile__bar{padding:20px 22px 24px}
  /* the two reserved lines come off here. They exist so that every bar in a
     ROW starts at the same y, and at this width there is no row: the wall is
     one column, so a bar with a one line name was holding an empty line under
     it, six times over, for an alignment that has nothing to align to. */
  .tile__bar .h3,.tile__bar p{min-height:0}
  .tile__bar::before{height:104px}
  .claim{padding:84px 0}
  .heights__grid{grid-template-columns:1fr}
  .heights__copy{padding:var(--pad-y) var(--gutter) 56px; order:2}
  .heights__media{min-height:440px; order:1}
  .heights__media::after{
    background:linear-gradient(180deg, rgba(var(--ink-rgb),.55) 0%,
      rgba(var(--ink-rgb),0) 34%, rgba(var(--ink-rgb),.55) 100%)}
  .est__grid{grid-template-columns:1fr; gap:44px}
  .est__row{grid-template-columns:1fr}
  .est__send{justify-self:stretch; text-align:center}
  .phero{padding:72px 0 64px}
  .phero__media::after{
    background:linear-gradient(180deg, rgba(var(--ink-rgb),.86) 0%,
      rgba(var(--ink-rgb),.78) 60%, rgba(var(--ink-rgb),.90) 100%)}
  .pair{grid-template-columns:1fr; gap:28px}
  /* the slider is already one box, so it only loses its margins here. The grip
     stays at 48px, which is still over the 40px a thumb needs. */
  .baslide{margin-top:36px}
  .baslide .ba__tag{top:14px}
  .baslide .ba__tag--before{left:14px}
  .baslide .ba__tag--after{right:14px}
  .baslide__grip{width:48px; height:48px}
  .duo{grid-template-columns:1fr; gap:36px}
  /* the ledger becomes a list: question, then its answer under it */
  .faq{margin-top:34px}
  .faq__item{grid-template-columns:1fr; gap:14px; padding:26px 22px}
  .faq__q{font-size:19px}
  .xlinks{grid-template-columns:1fr}
  .foot__grid{grid-template-columns:1fr; gap:44px}
  .seal,.foot__area,.foot__contact{grid-column:1 / -1}
  .famap{max-width:308px}
  .geese--foot{margin-top:52px}
}

/* Phones. The bar holds a 150px mark, a button and the menu key at 390px only
   by dropping one of them, and the button is the one the sticky bar already
   carries twice at the bottom of the screen. */
@media (max-width:559px){
  .nav__cta{display:none}
}

/* The sticky bar splits the screen in two, so its right hand cell is half the
   viewport. "Call 276-235-1442" is set at 169px and that cell is 160px at
   320px wide, which put the last digit on the edge of the screen. The track is
   the lever, the way it is on the nav button: measured, 12px on a .05em track
   sets the same label at 144px and leaves 8px of air either side. The number
   is not shortened, because the number is the point of the lane. */
@media (max-width:359px){
  .sbar a{font-size:12px; letter-spacing:.05em; padding:0 8px; gap:7px}
  .sbar svg{width:17px; height:17px}
}

/* The full bar wants 940px to hold the mark, the five links, the number and
   the button on one line. The links come back at 901px, which leaves a 40px
   band where the row is over subscribed and the button, being the one item
   set to nowrap, is the one that runs off the right edge. It comes off the
   bar in that band only. The number beside it is still a live lane, and every
   page carries the estimate button in its own copy. */
@media (min-width:901px) and (max-width:940px){
  .nav__cta{display:none}
}

/* Narrow phones. The primary label is 272px of nowrap type, and under 375px
   the gutters plus the button's own padding leave it less than that, so the
   text ran past the right edge of the document. Below that width the label is
   allowed to take a second line inside the button instead of shrinking under
   a readable size. The buttons in a cluster are already full width and
   centred here, so a wrapped label stays centred with them. */
@media (max-width:374px){
  .cta{white-space:normal; line-height:1.28; text-align:center}
  .cta--lg{padding:19px 24px}
}

/* Text links on phones. A 15px link in a column is a 25px target, which is
   under the 40px a thumb needs. The padding is added to the link box only:
   these are inline boxes, so vertical padding grows the hit area without
   moving a single line of the copy around it. The underline moves from a
   border, which the padding would push away from the words, to a text
   decoration, which stays on them. */
@media (max-width:767px){
  .foot__links{gap:0}
  .prose a,.cluster__note a,.est__note a,.foot__links a,.foot__more{
    padding:9px 0; border-bottom:0;
    text-decoration:underline; text-decoration-thickness:1px;
    text-underline-offset:4px; text-decoration-color:rgba(var(--mist-rgb),.34);
  }
  .prose a:hover,.cluster__note a:hover,.est__note a:hover,
  .foot__links a:hover,.foot__more:hover{
    color:var(--cream); text-decoration-color:var(--cream);
  }
  .relief .cluster__note a{text-decoration-color:rgba(var(--navy-rgb),.34)}
  .relief .cluster__note a:hover{color:var(--ridge-navy);
    text-decoration-color:var(--ridge-navy)}
}


/* 11. MOTION OFF =========================================================== */

@media (prefers-reduced-motion:reduce){
  .js .hero__h1,.js .hero__sub,.js .hero__act,.js .rise{opacity:1 !important; transform:none !important}
  .js .wipe__clip,.js .wipe__inner{transform:none !important}
  .wipe__edge{display:none}
  /* the hero frame is simply at rest, and the corner patch is simply sewn on.
     js/site.js reads the same query and never fires the slider hint. */
  .js .hero__media img{transform:none !important}
  .js .tile .tile__chip{opacity:1 !important; transform:none !important}
  /* the button fill is kept, not killed: the blanket rule below takes the
     transition off it, so the press colour still arrives on hover, instantly */
  /* the water sheet still rests on the wash line, it just arrives without moving */
  .js .ba__water{transform:translateX(0) rotate(var(--wash-tilt)) !important}
  /* the sticky bar keeps its hide and show, it just arrives without sliding:
     the blanket transition kill below is what takes the motion out of it */
  *,*::before,*::after{transition:none !important; animation:none !important}
}

@media (max-width:374px){ .phero h1{font-size:clamp(30px,9.9vw,37px);overflow-wrap:break-word} }
