/* ==========================================================================
   Mobile Floor Coverings — shared stylesheet
   Every page uses this file and only this file. No page-level stylesheet,
   no new custom properties outside :root. If a page needs something this
   file lacks, it goes in this file.
   ========================================================================== */

/* --- 1. Fonts. Self-hosted. No Google Fonts request, no CDN. -------------- */
@font-face{
  font-family:'Archivo';
  src:url('../fonts/archivo-wdth-wght.woff2') format('woff2-variations'),
      url('../fonts/archivo-wdth-wght.woff2') format('woff2');
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url('../fonts/inter-latin-var.woff2') format('woff2-variations'),
      url('../fonts/inter-latin-var.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
}

/* --- 2. Tokens. Every contrast ratio below was computed, not estimated. --- */
:root{
  /* Ink — #184888 is 74.9% of every non-white pixel in the client's logo */
  --blue:#184888;          /*  8.47:1 on paper  */
  --blue-deep:#0F3163;     /* 11.98:1           */
  --blue-mid:#2F6199;      /*  5.97:1           */
  --blue-wash:#E8EDF5;

  /* Ground. This was a warm cream lifted off the sunlit side of the van;
     across whole pages it read as a yellow cast, so the site chrome is now
     white with cool neutral greys and the warmth stays in the photograph.
     Changed 2026-08-26. */
  --paper:#FFFFFF;
  --sand:#F1F4F8;
  --hair:#DDE3EB;

  /* Accent — black. Buttons, rules, eyebrows, the seam motif.
     The build first used an amber lifted from the sunlit van; it read as brown
     and was replaced 2026-08-26. Blue stays the brand, black does the work. */
  --accent:#111317;           /* 18.59:1 on white, and white on it is 18.59:1 */
  --accent-hover:#184888;     /* a black button turns brand blue on hover */
  --on-dark-accent:#FFFFFF;   /* the same role on --blue-deep grounds     */

  --ink:#15181D;           /* 17.79:1 on white */
  --muted:#58616F;         /*  6.26:1 */
  --on-dark:#C9D5E6;       /*  8.63:1 on --blue-deep */

  --ease:cubic-bezier(.22,1,.36,1);
  --dur-micro:200ms;
  --dur-reveal:400ms;
  --dur-hero:800ms;

  --hdr-h:66px;      /* fixed. the hero subtracts exactly this */
  --gap:1.25rem;
  --pad:clamp(1.25rem,5vw,2.5rem);
  --max:1180px;
  --max-prose:66ch;
  --section:clamp(3.5rem,8vw,6.5rem);

  --fs-h1:clamp(2.35rem,6.6vw,4.3rem);
  --fs-h2:clamp(1.85rem,4.1vw,3.05rem);
  --fs-h3:clamp(1.05rem,1.6vw,1.22rem);

  /* Vault R8 — a phone invents a dark version of any page that has not
     declared one, at paint time, after computed style. `only` is the keyword
     that disables it; `light` alone does not. Also drives scrollbars, the
     list a <select> opens, checkboxes and the autofill panel — none of which
     any background we set can reach. */
  color-scheme:only light;
  accent-color:var(--blue);
}

/* --- 3. Reset ------------------------------------------------------------ */
*,*::before,*::after{ box-sizing:border-box }
html{ -webkit-text-size-adjust:100% }
body,h1,h2,h3,h4,p,ul,ol,li,figure,blockquote,dl,dd{ margin:0 }
ul[class],ol[class]{ list-style:none; padding:0 }
img,svg,video{ max-width:100%; height:auto; display:block }
button,input,select,textarea{ font:inherit; color:inherit }

html{ scroll-behavior:smooth }              /* anchor jumps only — never a scroll library */
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto } }

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:1.0625rem;
  line-height:1.62;
  font-synthesis-weight:none;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}

/* --- 4. Type ------------------------------------------------------------- */
h1,h2,h3,.h-display{
  font-family:'Archivo',Impact,system-ui,sans-serif;
  text-transform:uppercase;
  color:var(--blue);
  text-wrap:balance;
  overflow-wrap:break-word;      /* nothing may overflow its box at 360px */
  hyphens:none;                  /* and nothing may be hyphen-broken either */
}
h1{ font-size:var(--fs-h1); font-weight:800; font-stretch:88%;
    letter-spacing:-.005em; line-height:.92 }
h2{ font-size:var(--fs-h2); font-weight:800; font-stretch:92%; line-height:.95 }
h3{ font-size:var(--fs-h3); font-weight:700; font-stretch:100%;
    letter-spacing:.02em; line-height:1.2 }

p{ max-width:var(--max-prose) }
p + p{ margin-top:.9em }

.lede{ font-size:clamp(1.08rem,2vw,1.28rem); line-height:1.5; color:var(--ink) }
.muted{ color:var(--muted) }
.small{ font-size:.875rem; line-height:1.5 }

/* The eyebrow. Always --accent. */
.eyebrow{
  font-family:'Inter',sans-serif;
  font-size:.78rem; font-weight:600; letter-spacing:.13em;
  text-transform:uppercase; color:var(--accent);
  margin:0 0 .85rem;
}

a{ color:var(--blue); text-underline-offset:.18em; text-decoration-thickness:1px }
a:hover{ color:var(--blue-deep) }
:focus-visible{ outline:3px solid var(--blue); outline-offset:3px; border-radius:2px }

/* Tabular figures wherever a price or a phone number is set */
.num{ font-family:'Archivo',sans-serif; font-weight:700;
      font-variant-numeric:tabular-nums; font-stretch:100% }

/* --- 5. Layout ----------------------------------------------------------- */
.wrap{
  width:100%; max-width:var(--max); margin-inline:auto;
  /* max(), not a sum: the inset can raise the gutter, never stack onto it */
  padding-left:max(var(--pad), env(safe-area-inset-left, 0px));
  padding-right:max(var(--pad), env(safe-area-inset-right, 0px));
}
.section{ padding-block:var(--section) }
.section--sand{ background:var(--sand) }
.section--wash{ background:var(--blue-wash) }
.section--deep{ background:var(--blue-deep); color:var(--on-dark) }
.section--deep h2,.section--deep h3{ color:#fff }
.section--deep .eyebrow{ color:var(--on-dark-accent) }
/* :not(.btn) matters — a bare `.section--deep a` is (0,1,1) and outranks
   .btn--onDark (0,1,0), which paints white text on a white button. */
.section--deep a:not(.btn){ color:var(--on-dark-accent) }

.skip{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--blue); color:#fff; padding:.85rem 1.25rem; font-weight:600;
}
.skip:focus{ left:.5rem; top:.5rem }

/* The seam rule — the motif that ties every page together. A 3px black rule
   under each section heading that draws in from the left. scaleX only, so it
   never touches layout. */
.seam{
  display:block; width:88px; height:3px; background:var(--accent);
  margin:1.05rem 0 1.6rem;
  transform:scaleX(0); transform-origin:left center;
  transition:transform var(--dur-reveal) var(--ease);
}
.is-in .seam,.seam.is-in{ transform:scaleX(1) }
.section--deep .seam{ background:var(--on-dark-accent) }

/* --- 6. Buttons ---------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:.55rem;
  min-height:48px; padding:.8rem 1.5rem;
  font-family:'Archivo',sans-serif; font-weight:700; font-stretch:100%;
  font-size:.95rem; letter-spacing:.045em; text-transform:uppercase;
  text-decoration:none; border:2px solid transparent; border-radius:2px;
  cursor:pointer;
  transition:background-color var(--dur-micro) var(--ease),
             border-color var(--dur-micro) var(--ease),
             color var(--dur-micro) var(--ease),
             transform var(--dur-micro) var(--ease);
}
.btn:active{ transform:translateY(1px) }
.btn--primary{ background:var(--accent); color:#fff }        /* white on black = 18.6:1 */
.btn--primary:hover{ background:var(--accent-hover); color:#fff }
.btn--blue{ background:var(--blue); color:#fff }             /* 9.05:1 */
.btn--blue:hover{ background:var(--blue-deep); color:#fff }
.btn--ghost{ background:transparent; color:var(--blue); border-color:var(--blue) }
.btn--ghost:hover{ background:var(--blue); color:#fff }
.section--deep .btn--ghost{ color:#fff; border-color:var(--on-dark) }
.section--deep .btn--ghost:hover{ background:#fff; color:var(--blue-deep) }
/* On the dark hero and the blue header, white is the primary. */
.btn--onDark{ background:#fff; color:var(--blue-deep) }
.btn--onDark:hover{ background:var(--sand); color:var(--blue-deep) }
.btn--onDarkGhost{ background:transparent; color:#fff; border-color:rgba(255,255,255,.5) }
.btn--onDarkGhost:hover{ background:#fff; color:var(--blue-deep); border-color:#fff }
.btn-row{ display:flex; flex-wrap:wrap; gap:.75rem; margin-top:1.75rem }

/* --- 7. Header ----------------------------------------------------------- */
/* Fixed, and transparent while it sits over the hero photograph; it turns to
   translucent brand blue with a blur once the page scrolls, so it stays
   readable over the light sections below. Inner pages have no dark hero, so
   they carry .hdr--solid and start in the scrolled state.

   The height is FIXED and never animates. It used to shrink on scroll, and
   because a sticky header is in flow, every padding / height / font-size
   change altered the document height as the page scrolled (measured: 7554px
   down to 7544px), which shoved everything below it up and down and read as
   the whole site shaking. Only colour changes now. */
.hdr{
  position:fixed; top:0; left:0; right:0; z-index:100;
  background:transparent;
  border-bottom:1px solid transparent;
  transition:background-color var(--dur-micro) var(--ease),
             border-color var(--dur-micro) var(--ease);
}
.hdr.is-stuck,
.hdr--solid{
  background:rgba(15,49,99,.93);
  -webkit-backdrop-filter:blur(18px);
          backdrop-filter:blur(18px);
  border-bottom-color:rgba(255,255,255,.12);
}
.hdr__in{
  max-width:var(--max); margin-inline:auto;
  min-height:calc(var(--hdr-h) - 1px);
  padding:0 max(var(--pad), env(safe-area-inset-right, 0px))
         0 max(var(--pad), env(safe-area-inset-left, 0px));
  display:grid; grid-template-columns:1fr auto 1fr;
  align-items:center; gap:1rem;
}

/* The supplied logo is a 990x178 PNG on an opaque white background, so dropped
   straight in it reads as a white box with an illegible second line. The
   lockup is rebuilt from the logo's own two parts: his actual badge, and his
   name set in the site's display face. The original PNG still ships as the
   favicon and the share card. */
.hdr__logo{
  justify-self:center; display:flex; align-items:center; gap:.6rem;
  text-decoration:none; flex:0 0 auto;
}
.hdr__mark{ height:38px; width:auto; flex:0 0 auto; display:block }
.hdr__name{
  font-family:'Archivo',sans-serif; color:#fff;
  text-transform:uppercase; line-height:.98;
  display:flex; flex-direction:column;
}
.hdr__name b{ font-weight:800; font-stretch:88%; font-size:1.12rem; letter-spacing:-.005em }
.hdr__name span{
  font-weight:600; font-stretch:100%; font-size:.55rem; letter-spacing:.19em;
  color:rgba(255,255,255,.72);
}

.nav{ display:flex; align-items:center; gap:.1rem }
.nav--left{ justify-self:start }
.nav--right{ justify-self:end }
.nav > a{
  display:inline-block; padding:.5rem .62rem; border-radius:2px;
  font-family:'Archivo',sans-serif; font-weight:600; font-stretch:86%;
  font-size:.82rem; letter-spacing:.09em; text-transform:uppercase;
  color:rgba(255,255,255,.82); text-decoration:none; position:relative;
  white-space:nowrap;
  transition:color var(--dur-micro) var(--ease);
}
.nav > a::after{
  content:''; position:absolute; left:.62rem; right:.62rem; bottom:.22rem;
  height:2px; background:#fff;
  transform:scaleX(0); transform-origin:left center;
  transition:transform var(--dur-micro) var(--ease);
}
.nav > a:hover,.nav > a[aria-current="page"]{ color:#fff }
.nav > a:hover::after,.nav > a[aria-current="page"]::after{ transform:scaleX(1) }

/* Facebook / Instagram, the way the reference site carries them */
.hdr__socials{ display:flex; align-items:center; gap:.15rem; margin-left:.3rem }
.social{
  width:34px; height:34px; flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
  border-radius:50%; color:rgba(255,255,255,.72);
  transition:color var(--dur-micro) var(--ease),
             background-color var(--dur-micro) var(--ease);
}
.social svg{ width:17px; height:17px; fill:currentColor; display:block }
.social:hover{ color:#fff; background:rgba(255,255,255,.15) }

.hdr__call{ margin-left:.5rem; flex:0 0 auto }
.hdr__burger{
  display:none; justify-self:end;
  width:46px; height:46px; align-items:center; justify-content:center;
  background:transparent; border:1px solid rgba(255,255,255,.35);
  border-radius:2px; cursor:pointer;
}
.hdr__burger span{ display:block; width:20px; height:2px; background:#fff; position:relative }
.hdr__burger span::before,.hdr__burger span::after{
  content:''; position:absolute; left:0; width:20px; height:2px; background:#fff;
  transition:transform var(--dur-micro) var(--ease);
}
.hdr__burger span::before{ top:-6px } .hdr__burger span::after{ top:6px }
.hdr__burger[aria-expanded="true"] span{ background:transparent }
.hdr__burger[aria-expanded="true"] span::before{ transform:translateY(6px) rotate(45deg) }
.hdr__burger[aria-expanded="true"] span::after{ transform:translateY(-6px) rotate(-45deg) }

/* --- The phone menu ------------------------------------------------------ */
.mobmenu{ display:none }
@media (max-width:1259px){
  .hdr__in{ grid-template-columns:1fr auto }
  .hdr__logo{ justify-self:start }
  .hdr__burger{ display:flex }
  .nav--left,.nav--right{ display:none }

  .mobmenu{
    position:absolute; left:0; right:0; top:100%;
    flex-direction:column;
    background:rgba(15,49,99,.98);
    -webkit-backdrop-filter:blur(18px); backdrop-filter:blur(18px);
    border-bottom:1px solid rgba(255,255,255,.14);
    box-shadow:0 18px 30px -18px rgba(0,0,0,.55);
    padding:.35rem max(var(--pad), env(safe-area-inset-right, 0px)) 1.15rem
            max(var(--pad), env(safe-area-inset-left, 0px));
    max-height:calc(100svh - var(--hdr-h)); overflow-y:auto;
    /* In landscape the six links exceed the cap, so the menu really scrolls;
       without this a flick that hits either end carries into the page behind
       and drags the header through its is-stuck flip mid-gesture. */
    overscroll-behavior:contain;
  }
  .mobmenu.is-open{ display:flex }
  .mobmenu nav{ display:flex; flex-direction:column }
  /* Scoped to the nav, not to every <a> in the menu — the unscoped version
     also repainted the Call button's own text white on white. */
  .mobmenu nav a{
    padding:.9rem .15rem; border-bottom:1px solid rgba(255,255,255,.13);
    font-family:'Archivo',sans-serif; font-weight:600; font-stretch:88%;
    font-size:1.02rem; letter-spacing:.07em; text-transform:uppercase;
    color:rgba(255,255,255,.9); text-decoration:none;
  }
  .mobmenu nav a:hover{ color:#fff }
  .mobmenu__foot{
    display:flex; align-items:center; justify-content:space-between;
    gap:1rem; margin-top:1.15rem;
  }
  .mobmenu__foot .btn{ flex:1 1 auto }
  .mobmenu__foot .btn{ padding:.8rem 1rem }
  .mobmenu .hdr__socials{ margin:0; gap:.4rem }
  .mobmenu .social{ border:1px solid rgba(255,255,255,.25) }
  .mobmenu .social:hover{ border-color:transparent }
}
@media (max-width:560px){
  .hdr__mark{ height:32px }
  .hdr__name b{ font-size:1rem }
  .hdr__name span{ font-size:.5rem; letter-spacing:.14em }
}

/* Below 700px the phone moves to a fixed bar at the bottom of the screen,
   where it is bigger, always visible, and the largest tappable thing on the
   page — which is what it should have been all along. */
.callbar{ display:none }
@media (max-width:1060px){
  .callbar{
    position:fixed; left:0; right:0; bottom:0; z-index:99;
    display:flex; align-items:center; justify-content:center; gap:.6rem;
    min-height:58px; padding:.6rem 1rem;
    background:var(--accent); color:#fff; text-decoration:none;
    font-family:'Archivo',sans-serif; font-weight:700; font-size:1.05rem;
    letter-spacing:.03em; text-transform:uppercase;
    font-variant-numeric:tabular-nums;
    box-shadow:0 -4px 18px rgba(17,19,23,.22);
    padding-bottom:calc(.6rem + env(safe-area-inset-bottom, 0px));
  }
  .callbar:hover,.callbar:active{ background:var(--accent-hover); color:#fff }
  .callbar svg{ width:19px; height:19px; fill:currentColor; flex:0 0 auto }
  /* the bar grows by the home-indicator inset now that cover is set */
  body{ padding-bottom:calc(64px + env(safe-area-inset-bottom, 0px)) }
}
@media print{ .callbar,.hdr{ display:none } }

/* --- 8. Hero ------------------------------------------------------------- */
/* Structure taken from the reference site kodyn picked: full-bleed media, a
   dark gradient over it, centred content, the CTAs, and a scroll cue. The
   header is fixed and sits on top, so this is a full 100svh.

   This is a deliberate departure from vault R5 ("light, welcoming heros,
   dark text") — he asked for this specific treatment by name, twice. It also
   removes the problem that treatment was fighting: with the photograph full
   bleed there is no panel edge to blend, so there is no seam to get wrong. */
.hero{
  position:relative; overflow:hidden; isolation:isolate;
  min-height:100vh;      /* fallback: a browser that does not know svh drops */
  min-height:100svh;     /* the next line and would otherwise have NO hero height */
  display:flex; align-items:center; justify-content:center;
  background:var(--blue-deep); text-align:center;
}
.hero__media{ position:absolute; inset:0; z-index:0; overflow:hidden }
.hero__img{
  /* Deliberately oversized and offset: the entrance slides it and the scrub
     translates it, and at exactly 100% either one would pull a bare edge into
     frame. */
  position:absolute; top:-7%; left:-7%; width:114%; height:114%;
  max-width:none;   /* the global img{max-width:100%} reset was capping this at
                       100% while left:-7% still applied, leaving a bare strip
                       of hero background down the right-hand edge */
  object-fit:cover; object-position:55% 88%;
  display:block;
}
/* Dark enough at the top for white header links, dark enough at the bottom
   for the scroll cue, lighter through the middle so the van still reads.
   Every text-over-photo contrast ratio here was sampled from the composited
   pixels, not estimated. */
.hero__overlay{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  /* Carries the whole scrim now that the radial is gone. The middle band is
     where the headline and lede sit, so that is where it is strongest. */
  background:linear-gradient(180deg,
    rgba(8,24,48,.66) 0%, rgba(8,24,48,.52) 20%,
    rgba(8,24,48,.60) 44%, rgba(8,24,48,.60) 66%,
    rgba(8,24,48,.78) 100%);
}
.hero__canvas{
  position:absolute; inset:0; z-index:2; width:100%; height:100%;
  display:block; pointer-events:none;
  opacity:0; transition:opacity var(--dur-hero) var(--ease);
}
.hero__canvas.is-live{ opacity:1 }

/* The van carries its own large painted lettering, and with the photograph
   full bleed that lettering sits directly behind the headline whatever crop
   is used — the two compete. A soft pool of shade behind the copy only,
   rather than more darkness over the whole picture, gives the type a clean
   field and leaves the van bright at the edges. */
.hero__content{
  position:relative; z-index:3;
  width:100%; max-width:54rem; margin-inline:auto;
  padding:calc(var(--hdr-h) + clamp(1.5rem,5vw,3rem)) var(--pad) clamp(5rem,11vw,7rem);
}
/* The copy used to sit on a second scrim here: a ::before at z-index:-1, 150%
   wide and 135% tall, holding a dark radial gradient. It was removed
   2026-08-27 after it rendered on one machine as a large light-grey blob with
   soft edges, covering the lower two thirds of the hero — its exact geometry.
   A negative-z-index pseudo-element that overhangs its parent on all four
   sides is the fragile part; it did not reproduce here, and rather than chase
   one GPU, the darkening it provided has been folded into .hero__overlay,
   which is a plain full-bleed layer at z-index 1 and cannot misplace itself.
   Contrast over the photograph is unchanged. */
.hero .eyebrow{ color:rgba(255,255,255,.78) }
.hero h1{ color:#fff }
.hero h1 .l2{ display:block; color:#fff }
.hero__sub{
  margin:1.35rem auto 0; max-width:48ch;
  color:rgba(255,255,255,.9);
}
/* The hero's fact line: the address and the hours. kodyn cut the hero sub
   paragraph on 2026-09-13, so this is now their ONLY appearance above the fold
   and it shows at every size. Do not hide it again without moving those two
   facts somewhere else above the fold — the client called them out by name.
   SUPERSEDED 1 Oct 2026: John took the line out himself ("I don't need the
   location added a second time on the front page"), so the markup is gone and
   this rule styles nothing. See the 1 Oct block at the end of this file. */
.hero__facts{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:.5rem .95rem; margin-top:1.6rem;
  font-size:.78rem; font-weight:600; letter-spacing:.055em; text-transform:uppercase;
  color:rgba(255,255,255,.9);
}
.hero__facts li{ display:flex; align-items:center; gap:.55rem }
/* No dot between the two facts. On a phone they wrap onto separate lines and
   the separator ended up orphaned at the start of the second one. The gap does
   the job on a single line. */
/* The headline is the mark (kodyn, 2026-09-13) — the sunburst and mountains
   only, no wordmark and NO box behind it. `logo-mark-white.png` is derived from
   logo-mark.png by mapping luminance to alpha, so the blue square is gone and
   the artwork is white on transparent. It sits straight on the photograph. */
.hero__h1--logo{ margin:0 0 .35rem }
.hero__h1--logo img{
  display:block; margin:0 auto; height:auto;
  width:min(310px, 46vw);
  filter:drop-shadow(0 3px 20px rgba(6,20,42,.6));
}

.hero .btn-row{ justify-content:center; margin-top:2rem }

/* Scroll cue */
.hero__scroll{
  position:absolute; left:50%; bottom:1.6rem; transform:translateX(-50%);
  z-index:3; display:flex; flex-direction:column; align-items:center; gap:.45rem;
  color:rgba(255,255,255,.6); text-decoration:none;
  font-family:'Inter',sans-serif; font-size:.66rem; font-weight:500;
  letter-spacing:.24em; text-transform:uppercase;
  transition:color var(--dur-micro) var(--ease);
}
.hero__scroll:hover{ color:#fff }
.hero__scroll svg{
  width:20px; height:20px; stroke:currentColor; fill:none;
  stroke-width:2; stroke-linecap:round; stroke-linejoin:round;
}
@media (max-width:1060px){ .hero__scroll{ bottom:4.6rem } }

/* On a phone the hero box is portrait and the photograph landscape, so cover
   crops horizontally: object-position X is the only lever, and this one moves
   the van's painted lettering out from directly behind the copy. */
@media (max-width:859px){
  /* Portrait box, landscape photo: cover crops horizontally, so X is the only
     lever. At 74% the window sat on the cab and cropped the logo badge clean
     out of frame on a phone. 44% centres the window on the badge and the
     painted lettering, which is the whole point of the picture. */
  .hero__img{ object-position:44% 56% }
}

/* Y is tuned so the van's painted logo sits clear ABOVE the headline rather
   than behind it. The wider the screen the shorter the hero is relative to its
   width, so cover crops more vertically and the logo climbs — these two walk Y
   back to hold it in the same place. */
@media (min-width:1500px){ .hero__img{ object-position:55% 80% } }
@media (min-width:1800px){ .hero__img{ object-position:55% 74% } }

/* An anchor jump must clear the fixed header rather than hide under it. */
[id]{ scroll-margin-top:calc(var(--hdr-h) + 14px) }

/* --- 8b. Trust bar ------------------------------------------------------- */
/* A strip of at-a-glance facts directly under the hero. Same idea as the
   reference site's: light band, centred row, hairline dividers, small bold
   uppercase labels with a brand-coloured icon. */
.trustbar{
  /* White, not the band grey: the section immediately below it is --sand, and
     matching that just merged the two into one flat block. */
  background:var(--paper);
  border-bottom:1px solid var(--hair);
  padding:1.1rem 0;
}
.trustbar__in{
  max-width:var(--max); margin-inline:auto; padding-inline:var(--pad);
  display:flex; align-items:center; justify-content:center;
  gap:clamp(.85rem,2.2vw,1.6rem); flex-wrap:nowrap;
}
.trustbar__item{
  display:flex; align-items:center; gap:.55rem;
  font-family:'Inter',sans-serif; font-size:.78rem; font-weight:700;
  letter-spacing:.07em; text-transform:uppercase; color:var(--muted);
  white-space:nowrap;
}
.trustbar__icon{ width:20px; height:20px; flex:0 0 auto; color:var(--blue) }
.trustbar__div{ width:1px; height:24px; background:var(--hair); flex:0 0 auto }

/* The reference row is nowrap, which overflows. Ours becomes a two-up grid
   and drops the dividers rather than scrolling sideways. It has to switch at
   1180, not 1040: four labels plus three dividers measured 1145px wide and
   pushed the whole page into a horizontal scroll at 1100. */
@media (max-width:1180px){
  .trustbar__in{
    display:grid; grid-template-columns:repeat(2,auto);
    justify-content:center; gap:.85rem 1.75rem;
  }
  .trustbar__div{ display:none }
  .trustbar__item{ font-size:.76rem; letter-spacing:.07em; white-space:normal }
}
/* Phones: a wrapping, centred cluster rather than a left-aligned column of
   four icon rows, which read as a checklist and ate most of a screen. This is
   the sb-professional-painting treatment — same markup, flex-wrap, dividers
   already gone, type tightened. */
@media (max-width:520px){
  .trustbar{ padding:.85rem 0 }
  .trustbar__in{
    display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
    gap:.4rem 1rem;
  }
  .trustbar__item{ font-size:.72rem; gap:.45rem; letter-spacing:.05em }
  .trustbar__icon{ width:15px; height:15px }
  /* Our labels are far longer than the reference site's, so they cannot pair up
     at this width and the bar became a four-row checklist. Three of the four
     are already said within a screen of here — the hero eyebrow directly above
     carries "Whatcom & Skagit County · Since 1986", and Our Services directly
     below IS the material list. Only the free-measuring line is not a repeat,
     so on phones that is the one that stays. */
  .trustbar__item--echo{ display:none }
}

/* --- 10. Cards, steps, quotes ------------------------------------------- */
.grid{ display:grid; gap:1.5rem; margin-top:2.25rem }
.grid--3{ grid-template-columns:repeat(auto-fit,minmax(250px,1fr)) }
.grid--2{ grid-template-columns:repeat(auto-fit,minmax(300px,1fr)) }

.card{
  background:#fff; border:1px solid var(--hair); border-radius:2px;
  padding:1.6rem 1.5rem;
}
.section--sand .card{ background:var(--paper) }
.card h3{ margin-bottom:.55rem }
.card p{ max-width:none }

/* The three steps. His own words, numbered. */
.step{ position:relative; padding-left:3.4rem }
.step__n{
  position:absolute; left:0; top:-.15rem;
  font-family:'Archivo',sans-serif; font-weight:800; font-stretch:88%;
  font-size:2.1rem; line-height:1; color:var(--accent);
  font-variant-numeric:tabular-nums;
}
.step h3{ color:var(--blue) }

blockquote{ margin:0 }
.quote{
  background:#fff; border:1px solid var(--hair); border-left:4px solid var(--accent);
  border-radius:2px; padding:1.5rem 1.4rem;
}
.section--sand .quote{ background:var(--paper) }
.quote p{ max-width:none; font-size:1.02rem }
.quote footer{
  margin-top:.9rem; font-size:.82rem; color:var(--muted);
  font-weight:600; letter-spacing:.03em;
}
.quote cite{ font-style:normal; color:var(--blue); text-transform:uppercase;
             font-family:'Archivo',sans-serif; font-weight:700; letter-spacing:.03em }

/* Figures */
figure{ margin:0 }
figure img{ width:100%; border-radius:2px; border:1px solid var(--hair) }
figcaption{ margin-top:.6rem; font-size:.8rem; color:var(--muted) }

/* Plain lists inside prose */
.list{ list-style:none; padding:0; margin:1.1rem 0 0; max-width:var(--max-prose) }
.list li{ position:relative; padding-left:1.5rem; margin-bottom:.55rem }
.list li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:8px; height:2px; background:var(--accent);
}

/* Price table */
.rates{ width:100%; border-collapse:collapse; margin-top:2rem; font-size:.98rem }
.rates caption{ text-align:left; color:var(--muted); font-size:.85rem; padding-bottom:.75rem }
.rates th,.rates td{ text-align:left; padding:.85rem .75rem; border-bottom:1px solid var(--hair) }
.rates thead th{
  font-family:'Archivo',sans-serif; font-weight:700; text-transform:uppercase;
  font-size:.78rem; letter-spacing:.06em; color:var(--blue);
  border-bottom:2px solid var(--blue);
}
.rates td:last-child,.rates th:last-child{ text-align:right }
.rates .num{ color:var(--accent); white-space:nowrap }
.table-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch }

/* Town list */
.towns{ display:flex; flex-wrap:wrap; gap:.6rem; margin-top:1.75rem; padding:0; list-style:none }
.towns li{
  border:1px solid var(--hair); background:#fff; border-radius:999px;
  padding:.45rem 1rem; font-size:.9rem; font-weight:500;
}
.section--sand .towns li{ background:var(--paper) }

/* --- 11. Forms ----------------------------------------------------------- */
.form{ display:grid; gap:1.1rem; max-width:640px }
.field{ display:flex; flex-direction:column; gap:.4rem }
.field label{ font-size:.86rem; font-weight:600; color:var(--ink) }
.field .req{ color:var(--accent) }
.field input,.field select,.field textarea{
  width:100%; padding:.75rem .85rem;
  background:#fff; color:var(--ink);
  border:1px solid var(--hair); border-radius:2px;
  transition:border-color var(--dur-micro) var(--ease),
             box-shadow var(--dur-micro) var(--ease);
}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none; border-color:var(--blue);
  box-shadow:0 0 0 3px rgba(24,72,136,.16);
}
.field textarea{ min-height:120px; resize:vertical }
.field-row{ display:grid; gap:1.1rem; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)) }

.checks{ display:grid; gap:.5rem; grid-template-columns:repeat(auto-fit,minmax(160px,1fr));
         padding:0; margin:0; list-style:none }
.checks li{ display:flex; align-items:center; gap:.55rem }
.checks input{ width:20px; height:20px; flex:0 0 auto }
fieldset{ border:0; padding:0; margin:0 }
legend{ font-size:.86rem; font-weight:600; padding:0 0 .55rem }

/* Honeypot. Never display:none — some bots skip hidden fields. */
.hp{ position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden }

.form-status{ font-size:.95rem; padding:.85rem 1rem; border-radius:2px; margin-top:.25rem }
.form-status:empty{ display:none }
.form-status--ok{ background:var(--blue-wash); color:var(--blue-deep);
                  border-left:4px solid var(--blue) }
.form-status--err{ background:var(--sand); color:var(--ink); border-left:4px solid var(--accent) }
.form-status a{ color:inherit; font-weight:700 }

/* Vault R8, the part CSS backgrounds cannot reach: the browser paints the
   autofill panel itself and offers no property for it. */
input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus,
textarea:-webkit-autofill,select:-webkit-autofill{
  -webkit-box-shadow:0 0 0 100px #fff inset !important;
  box-shadow:0 0 0 100px #fff inset !important;
  -webkit-text-fill-color:var(--ink) !important;
  caret-color:var(--ink);
}
::placeholder{ color:#767E8C; opacity:1 }

/* --- 12. Footer ---------------------------------------------------------- */
.ftr{ background:var(--blue-deep); color:var(--on-dark); padding-block:clamp(2.75rem,6vw,4rem) }
.ftr a{ color:var(--on-dark); text-decoration:none }
.ftr a:hover{ color:#fff; text-decoration:underline }
.ftr__grid{
  display:grid; gap:2.25rem;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
}
.ftr__logo img{ height:44px; width:auto; background:#fff; padding:.4rem .6rem; border-radius:2px }
.ftr h2,.ftr h3{
  color:#fff; font-size:.78rem; letter-spacing:.13em; font-stretch:100%;
  font-weight:700; margin-bottom:.9rem;
}
.ftr ul{ list-style:none; padding:0; margin:0 }
.ftr li{ margin-bottom:.5rem; font-size:.92rem }
.ftr__nap{ font-size:.92rem; font-style:normal; line-height:1.7 }
.ftr__nap strong{ color:#fff }
.ftr__phone{
  display:inline-block; margin-top:.35rem;
  font-family:'Archivo',sans-serif; font-weight:700; font-size:1.35rem;
  color:var(--on-dark-accent) !important; font-variant-numeric:tabular-nums;
}
.ftr__base{
  margin-top:2.5rem; padding-top:1.4rem; border-top:1px solid rgba(217,205,182,.26);
  display:flex; flex-wrap:wrap; gap:.65rem 1.4rem;
  align-items:center; font-size:.82rem;
}
.ftr__base ul{ display:flex; flex-wrap:wrap; gap:.65rem 1.25rem }
.ftr__base li{ margin:0 }

/* --- 13. Motion ---------------------------------------------------------- */
/* opacity and transform only, everywhere, so the compositor never has to
   touch layout or paint. The only @keyframes in this file is the review
   marquee, which translates and is disabled under reduced motion. */
.reveal{
  opacity:0; transform:translateY(14px);
  transition:opacity var(--dur-reveal) var(--ease),
             transform var(--dur-reveal) var(--ease);
}
.reveal.is-in{ opacity:1; transform:none }
.reveal[data-delay="1"]{ transition-delay:.08s }
.reveal[data-delay="2"]{ transition-delay:.16s }
.reveal[data-delay="3"]{ transition-delay:.24s }

/* Backstop: if the JS never runs at all, nothing stays invisible. */
@media (prefers-reduced-motion:reduce){
  .reveal{ opacity:1; transform:none; transition:none }
  .seam{ transform:scaleX(1); transition:none }
  .hero__canvas{ display:none }
  .btn,.nav a::after,.hdr{ transition:none }
}
/* .reveal starts at opacity:0, so a page printed before its JS ran would
   otherwise come out as blank sheets. */
@media print{
  .reveal{ opacity:1 !important; transform:none !important }
  .hdr,.ftr__base,.hero__canvas,.btn{ display:none }
  body{ background:#fff }
}

/* --- 14. Utilities ------------------------------------------------------- */
.vh{
  position:absolute !important; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.center{ text-align:center }
.center p,.center .seam{ margin-inline:auto }
.center p{ max-width:60ch }
.stack > * + *{ margin-top:1.1rem }
.pt0{ padding-top:0 }

/* --- 15. 404 ------------------------------------------------------------- */
.oops{ padding-block:clamp(4rem,12vw,8rem); text-align:center }
.oops h1{ font-size:clamp(3rem,12vw,7rem) }

/* --- 16. Defensive dark-mode restatement -------------------------------- */
/* For extensions and reader modes that key off the media query rather than
   honouring color-scheme. Vault R8. */
@media (prefers-color-scheme:dark){
  :root{ color-scheme:only light }
  body{ background:var(--paper); color:var(--ink) }
  .card,.rev,.quote,.towns li,.field input,.field select,.field textarea{ background:#fff }
}

/* --- 17. Centred sections ------------------------------------------------
   The reference site centres every section header — small eyebrow, big
   heading, rule, intro line — and it reads better on a phone, where a centred
   block needs no left gutter to feel deliberate.

   Note `p { max-width: 66ch }` from the type rules applies to the eyebrow too,
   so text-align alone was not enough: without margin-inline the paragraph box
   stays hard left inside a centred parent and only its text centres, which is
   why the hero eyebrow sat off to the left.
   ------------------------------------------------------------------------ */
.hero__content .eyebrow{ margin-inline:auto }

.section > .wrap{ text-align:center }
.section > .wrap p{ margin-inline:auto }
.section > .wrap .seam{ margin-inline:auto }
.section > .wrap .btn-row{ justify-content:center }
.section > .wrap .towns{ justify-content:center }

/* The three steps read better with the numeral centred above the heading
   than hanging off the left edge. */
.section > .wrap .step{ padding-left:0 }
.section > .wrap .step__n{
  position:static; display:block; margin:0 auto .35rem; line-height:1;
}

/* Blocks where centring genuinely hurts readability keep their own alignment:
   form fields need their labels on the left of the control, a rate table has
   columns, and a bulleted list has a hanging marker. */
.section > .wrap .form,
.section > .wrap .field,
.section > .wrap .checks,
.section > .wrap .rates,
.section > .wrap .list,
.section > .wrap .quote,
.section > .wrap .card{ text-align:left }
.section > .wrap .form p,
.section > .wrap .card p,
.section > .wrap .quote p,
.section > .wrap .list{ margin-inline:0 }
.section > .wrap .form{ margin-inline:auto }
.section > .wrap .quote footer{ text-align:left }
/* A centred rule should draw from the middle outwards; drawing from its left
   edge was right when the whole block was left-aligned. */
.section > .wrap .seam{ transform-origin:center }

/* When a section header has no intro line under it, the rule is the last thing
   before the content and 36px reads cramped. Only targets that case — headers
   that do have a paragraph keep the tighter spacing. */
.section > .wrap .reveal > .seam:last-child{ margin-bottom:3rem }

/* --- 18. Review marquee --------------------------------------------------
   Same construction as the reference site: an overflow-hidden viewport, a
   flex track, and one linear keyframe that translates the track -50%. The
   card set is duplicated once in JS, so at -50% the second copy sits exactly
   where the first started and the loop is seamless.

   translate only, so it stays on the compositor. It pauses on hover, on
   focus-within, when the tab is hidden and when it scrolls out of view —
   nothing on this site runs on a timer nobody is watching.
   ------------------------------------------------------------------------ */
.marquee{
  overflow:hidden; margin-top:2.5rem;
  -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
          mask-image:linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
}
.marquee__track{
  display:flex; gap:1.5rem; width:max-content;
  animation:marquee-scroll 66s linear infinite;
}
/* NOT -50%. The track is 14 cards and therefore 13 gaps, so half its width is
   7 cards + 6.5 gaps — but a seamless wrap needs 7 cards + 7 gaps. -50% was
   half a gap (12px) short and the row snapped backwards once a cycle. The JS
   measures the real distance and sets --marquee-shift; this is the fallback. */
@keyframes marquee-scroll{
  from{ transform:translateX(0) }
  to{ transform:translateX(calc(-50% - var(--marquee-half-gap, 0px))) }
}
.marquee__track:hover,
.marquee__track:focus-within,
.marquee.is-paused .marquee__track{ animation-play-state:paused }

.rev{
  flex:0 0 340px; display:flex; flex-direction:column; gap:.9rem;
  background:var(--paper); border:1px solid var(--hair); border-radius:2px;
  padding:1.6rem 1.5rem; text-align:left;
  transition:box-shadow var(--dur-micro) var(--ease);
}
.rev:hover{ box-shadow:0 10px 28px -16px rgba(15,49,99,.4) }
.rev p{ max-width:none; margin:0; font-size:.97rem; line-height:1.55 }
.rev__top{ display:flex; align-items:center; justify-content:space-between; gap:.75rem }
.rev__src{
  font-family:'Inter',sans-serif; font-size:.66rem; font-weight:700;
  letter-spacing:.13em; text-transform:uppercase; color:var(--muted);
}
.stars{ display:inline-flex; gap:2px }
.stars svg{ width:16px; height:16px; fill:#B4780C; display:block }
.rev footer{
  margin-top:auto; padding-top:.35rem;
  font-size:.82rem; color:var(--muted); font-weight:600; letter-spacing:.02em;
}
.rev cite{
  font-style:normal; color:var(--blue); text-transform:uppercase;
  font-family:'Archivo',sans-serif; font-weight:700; letter-spacing:.03em;
}
@media (max-width:560px){ .rev{ flex:0 0 78vw; padding:1.35rem 1.25rem } }

/* Reduced motion: no auto-scroll. It becomes an ordinary horizontal scroller
   the visitor drives themselves, which is what the animation was standing in
   for anyway. */
@media (prefers-reduced-motion:reduce){
  .marquee{ overflow-x:auto; -webkit-overflow-scrolling:touch }
  .marquee__track{ animation:none }
}

/* --- 19. Platform buttons ------------------------------------------------
   White button carrying the platform's own logo. Google's and Yelp's brand
   guidelines both permit their mark on a link that points at your own
   listing, which is exactly what these do — so the logos are drawn in their
   official colours rather than tinted to our palette.
   ------------------------------------------------------------------------ */
.btn--brand{
  background:var(--paper); color:var(--ink);
  border:1px solid var(--hair);
  gap:.7rem; padding-inline:1.35rem;
}
.btn--brand:hover{
  background:var(--paper); color:var(--ink);
  border-color:var(--blue-mid);
  box-shadow:0 6px 18px -10px rgba(15,49,99,.45);
}
.brandmark{ width:20px; height:20px; flex:0 0 auto; display:block }

/* --- 20. Material tiles --------------------------------------------------
   Replaces the "Sample Board" card. That component existed because every
   material photograph we had was 300px — a chip on a card is honest at that
   size and a full-bleed image is not. Now that most of these are 1536px
   photographs of real installed floors, the constraint is gone, and the card
   chrome it justified (border, drop shadow, punched hole, label in a strip
   underneath) was what made the grid read as a 2010 product catalogue.

   These are full-bleed instead: the photograph IS the tile, the label sits on
   it, and nothing is framed. Transform and opacity only.
   ------------------------------------------------------------------------ */
.tiles{
  display:grid; gap:1rem; margin-top:2.5rem;
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  padding:0; list-style:none;
}
@media (min-width:900px){ .tiles{ grid-template-columns:repeat(3,1fr) } }

.tile{ margin:0 }
.tile a{
  position:relative; display:block; overflow:hidden;
  aspect-ratio:5/6; border-radius:2px; text-decoration:none;
  background:var(--sand);
}
.tile img{
  width:100%; height:100%; object-fit:cover; display:block;
  transition:transform 700ms var(--ease);
}
/* The scrim only covers the lower half, so the material still reads at the
   top of the tile. Contrast of the label against the darkest part of each
   photograph was measured, not assumed. */
.tile a::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg,
    rgba(8,20,38,0) 38%, rgba(8,20,38,.30) 62%, rgba(8,20,38,.82) 100%);
  transition:opacity var(--dur-micro) var(--ease);
}
.tile__label{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  display:flex; align-items:center; justify-content:space-between; gap:.75rem;
  padding:1.1rem 1.15rem;
  font-family:'Archivo',sans-serif; font-weight:800; font-stretch:92%;
  font-size:1.02rem; letter-spacing:.02em; text-transform:uppercase;
  color:#fff;
}
.tile__label svg{
  width:20px; height:20px; flex:0 0 auto;
  stroke:currentColor; fill:none; stroke-width:2.2;
  stroke-linecap:round; stroke-linejoin:round;
  transition:transform var(--dur-micro) var(--ease);
}
.tile a:hover img,
.tile a:focus-visible img{ transform:scale(1.055) }
.tile a:hover .tile__label svg,
.tile a:focus-visible .tile__label svg{ transform:translateX(5px) }
.tile a:focus-visible{ outline:3px solid var(--blue); outline-offset:3px }

@media (prefers-reduced-motion:reduce){
  .tile img,.tile__label svg{ transition:none }
  .tile a:hover img{ transform:none }
}
@media (max-width:560px){
  .tile a{ aspect-ratio:4/3 }
  .tile__label{ font-size:.95rem; padding:.95rem 1rem }
}

/* The header row is a 1fr auto 1fr grid, so BOTH side columns take the width
   of the wider one. With "Call (360) 441-0976" spelled out, that pushed the
   header's minimum to 1180px — it overflowed at 1041, the first width where
   the desktop nav returns. Dropping the word and adding a handset glyph
   brings the requirement under 1040 so the breakpoint holds. */
.hdr__tel{
  font-size:.86rem; letter-spacing:.01em; padding:.62rem .9rem;
  gap:.45rem; font-variant-numeric:tabular-nums; min-height:42px;
}
.hdr__tel svg{ width:15px; height:15px; fill:currentColor; flex:0 0 auto }

/* --- 21. Audit fixes -----------------------------------------------------
   Found by the pre-deploy critique panel and each confirmed by measurement.
   ------------------------------------------------------------------------ */

/* .field label sets --ink explicitly, which beat the inherited --on-dark on
   the dark Book section: labels rendered at 1.39:1 and the required asterisks
   at 1.45:1. Five effectively unlabelled boxes. */
.section--deep .field label{ color:var(--on-dark) }
.section--deep .field .req{ color:var(--sun-glow, #F0C46A) }
.section--deep legend{ color:var(--on-dark) }

/* The focus ring was --blue everywhere, which is 1.42:1 on --blue-deep — so
   every link in the header, the Book section, the phone menu and the whole
   footer had an invisible focus indicator. WCAG 2.4.7 / 1.4.11. */
.hdr :focus-visible,
.mobmenu :focus-visible,
.section--deep :focus-visible,
.ftr :focus-visible,
.hero :focus-visible,
.pagehead :focus-visible{ outline-color:#fff }

/* 4.09:1 on white, and the textarea hint is normal-size text. */
::placeholder{ color:#5F6773; opacity:1 }      /* 5.72:1 */

/* 34px was the only tap target on the page under 44px; everything else is
   46-58px. Desktop keeps the compact size, where a pointer is precise. */
@media (max-width:1259px){
  .mobmenu .social{ width:44px; height:44px }
}

/* The marquee auto-scrolls for longer than five seconds, and its only pause
   hooks were :hover and :focus-within — neither of which a touch user has,
   and nothing inside a review card is focusable. WCAG 2.2.2 (Level A) needs a
   real control, so there is a button. */
.marquee__ctrl{
  display:inline-flex; align-items:center; gap:.5rem;
  margin:1.1rem auto 0; padding:.5rem .95rem;
  background:var(--paper); color:var(--ink);
  border:1px solid var(--hair); border-radius:999px;
  font-family:'Inter',sans-serif; font-size:.72rem; font-weight:700;
  letter-spacing:.1em; text-transform:uppercase; cursor:pointer;
  transition:border-color var(--dur-micro) var(--ease);
}
.marquee__ctrl:hover{ border-color:var(--blue-mid) }
.marquee__ctrl svg{ width:13px; height:13px; fill:currentColor; display:block }
.marquee__ctrl .ico-play{ display:none }
.marquee.is-stopped .marquee__ctrl .ico-play{ display:block }
.marquee.is-stopped .marquee__ctrl .ico-pause{ display:none }
/* A user-requested stop must beat the visibility/intersection pauses. */
.marquee.is-stopped .marquee__track{ animation-play-state:paused !important }
@media (prefers-reduced-motion:reduce){ .marquee__ctrl{ display:none } }

/* --- 22. Phone pass ------------------------------------------------------
   Found by testing real handset viewports — width AND height. The width-only
   sweep could never see any of this.
   ------------------------------------------------------------------------ */

/* The fixed call bar covers the bottom 58px of the SCREEN, not of the hero.
   On a short viewport the hero is taller than the screen, so its own bottom
   padding does not help and both hero CTAs landed underneath the bar —
   measured on iPhone SE landscape (667x375) and a 640x360 Android. */
@media (max-width:1060px){
  .hero__content{
    padding-bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 1.5rem);
  }
}

/* The scroll cue sat on top of the CTAs on every short viewport — both
   landscape phones and a 568px-tall portrait. It is decoration; the CTAs are
   the page's job. */
@media (max-height:700px){
  .hero__scroll{ display:none }
}

/* Landscape phones. A 100svh hero with a 4.3rem headline does not fit in
   375px of height, so the whole block is compacted rather than letting it
   push the CTAs off the screen. */
@media (max-height:520px) and (orientation:landscape){
  .hero{ min-height:auto }
  .hero__content{
    padding-top:calc(var(--hdr-h) + .85rem);
    padding-bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 1rem);
  }
  .hero h1{ font-size:clamp(1.9rem,5.2vw,2.6rem) }
  .hero__sub{ font-size:1rem; margin-top:.85rem }
  .hero .btn-row{ margin-top:1.15rem }
}

/* Tap targets. 32-38px on the logo and 37px on the marquee control were the
   only two controls left under 44px once footer text links are excluded. */
@media (max-width:1259px){
  .hdr__logo{ min-height:44px }
  .marquee__ctrl{ min-height:44px; padding-inline:1.15rem }
}



/* Very short viewports — a 640x360 budget Android in landscape, or a 320x568
   first-generation SE. At these sizes the hero content is taller than the
   screen no matter how the padding is arranged, so the CTAs fall into the
   bottom 58px that the fixed call bar owns. The lede is the one part that can
   go: the headline says the same thing and the buttons are the point. */
@media (max-height:430px) and (orientation:landscape){
  .hero__sub{ display:none }
  /* ...and the fact line tightens up rather than disappearing. */
  .hero__facts{ margin-top:.7rem; font-size:.7rem }
  /* The mark has to give back the vertical budget here or the CTAs land behind
     the fixed call bar — measured at -25px before this cap. */
  .hero__h1--logo img{ width:min(74px,14vw) }
  .hero h1{ font-size:clamp(1.7rem,4.6vw,2.2rem) }
  .hero__content{ padding-top:calc(var(--hdr-h) + .5rem) }
  .hero .btn-row{ margin-top:1rem }
}
@media (max-height:600px) and (orientation:portrait){
  /* Same reason as the landscape tier: a 320x568 phone had 2px of clearance
     above the call bar with the full-size mark. */
  .hero__h1--logo img{ width:min(112px,34vw) }
  .hero__content{ padding-top:calc(var(--hdr-h) + .75rem) }
  .hero h1{ font-size:clamp(2rem,8vw,2.6rem) }
  .hero__sub{ margin-top:.85rem; font-size:1rem }
  .hero .btn-row{ margin-top:1.15rem }
}

/* --- 23. Touch and mid-range phone pass ----------------------------------
   From a phone-only critique panel. Every rule here is an OVERRIDE placed at
   the end of the file on purpose: reordering the originals would have moved
   them out from under the reduced-motion blocks that depend on source order.
   ------------------------------------------------------------------------ */

/* backdrop-filter:blur(18px) on a full-width fixed header makes the compositor
   re-capture and blur the backdrop on every scrolled frame, for the whole
   page — the single most expensive thing on the page during a scroll on a
   mid-range Android GPU, competing with the hero's WebGL loop. It was painted
   under a 93% fill, so at most 7% of it was ever visible. Above 1060px the
   desktop nav is showing and the machine can afford it; below, it cannot. */
@media (max-width:1259px){
  .hdr.is-stuck,
  .hdr--solid{
    -webkit-backdrop-filter:none; backdrop-filter:none;
    background:rgba(15,49,99,.97);
  }
  .mobmenu{
    -webkit-backdrop-filter:none; backdrop-filter:none;
    background:#0F3163;
  }
}

/* will-change:transform was removed from .hero__img 2026-08-27. It pinned a
   full-screen composited layer, and on two of the client's own devices the
   hero's layer stack stopped compositing partway down the page — everything
   below the cut painted white, slicing the headline mid-glyph. Nothing on this
   site is worth a hero that does not render. */

/* Sticky hover on touch. iOS applies :hover on tap and holds it until you tap
   elsewhere, so a hover rule becomes a latched state. The marquee one is the
   real problem — touching the reviews row stopped the auto-scroll for good.
   Specificity is deliberate in each case: .marquee:not(.is-paused) outranks
   the pause button's own rule so the button still works, and :not(:active)
   leaves the press state on the call bar intact. */
@media (hover:none){
  .marquee:not(.is-paused) .marquee__track:hover{ animation-play-state:running }
  .callbar:hover:not(:active){ background:var(--accent); color:#fff }
  .tile a:hover img{ transform:none }
  .social:hover{ background:rgba(255,255,255,.08) }
  .rev:hover{ box-shadow:none }
}

/* Section 17 centres running copy along with the heading. That reads well
   while the 66ch cap sets the measure, but below ~560px the viewport sets it
   and the John paragraph lands as eight double-ragged lines with no fixed left
   edge to return to. margin-inline:0 is load-bearing — without it the 66ch box
   stays auto-centred and the flush-left text sits indented from the wrap.
   `:not(.step) >` matters: a step's numeral and heading are centred by design,
   so its paragraph has to stay centred with them. */
/* STRUCK 25 Sep 2026. kodyn: "words not centered". The rule below used to flip
   running copy to flush-left under 560px; the measured effect was 21 places
   across 7 pages where a centred heading sat over left-aligned text, which is
   what he was seeing. He has now asked for centred phone copy on three sites
   running, so the client preference settles it and the readability argument is
   noted and overruled. Prose pages, cards, forms and tables keep their own
   left alignment — those rules live outside this block and are untouched.
@media (max-width:560px){
  .section > .wrap :not(.step) > p:not(.eyebrow):not(.lede){
    text-align:left;
    margin-inline:0;
  }
}
*/

/* --- 24. The van on a phone ----------------------------------------------
   Full-bleed background, the same idea as the desktop hero: photograph edge to
   edge, copy over the top of it.

   The photograph is 1415x1200 — wider per unit of height than any phone. With
   object-fit:cover in a 390x844 box it scales to the box's HEIGHT, so only
   about 39% of its width survives and object-position Y does nothing at all.
   Everything below is about making that narrow vertical slice a good picture:
   pick the right slice, then take just enough light out of it for white type
   without turning a white truck navy.
   ------------------------------------------------------------------------ */
@media (max-width:859px) and (orientation:portrait){
  /* X frames the slice on the logo badge and the painted wordmark, which is
     the part of this photograph worth showing. */
  .hero__img{ object-position:46% 50% }

  /* The desktop scrim is three stacked layers — a linear overlay, a radial
     behind the copy, and the WebGL floor. On desktop they fall on a van that
     fills a wide frame; on a phone all three land on the same flat white
     flank at once and it read as dark navy rather than as a truck. Measured
     over the composited pixels: the van's white came out at #2A3E5E.

     So on a phone the linear scrim does the work alone: strong at the very top
     where the header sits, strong again at the foot behind the call bar, and
     backed right off through the middle where the logo is. */
  .hero__overlay{
    background:linear-gradient(180deg,
      rgba(8,24,48,.80) 0%,
      rgba(8,24,48,.66) 14%,
      rgba(8,24,48,.72) 40%,
      rgba(8,24,48,.72) 70%,
      rgba(8,24,48,.86) 100%);
  }

  /* The plank floor is the third layer on the same flat white. Halving it
     keeps the motion without adding to the haze. */
  .hero__canvas{ opacity:.5 }
}

/* Keep the hero to roughly one screen on a short phone. At 375x667 it measured
   746px tall — 79px past the viewport — which pushed the Call button down
   behind the fixed call bar. These are the smallest reductions that fit it. */
@media (max-width:859px) and (orientation:portrait) and (max-height:745px){
  .hero h1{ font-size:clamp(1.95rem,7.4vw,2.45rem) }
  .hero__sub{ font-size:.98rem; margin-top:.8rem }
  .hero .btn-row{ margin-top:1.05rem; gap:.6rem }
  .hero__content{ padding-top:calc(var(--hdr-h) + .8rem) }
  .hero .eyebrow{ margin-bottom:.5rem }
}

/* --- 25. Inner-page header ------------------------------------------------
   The home page reserves the fixed header's height inside .hero__content.
   Nothing else did, so an inner page's first section slid underneath it.

   This is that reservation, and it doubles as the page's title block. It sits
   on --blue-deep so the solid blue header merges into it rather than ruling a
   line across the top of every page, which is the same relationship the header
   has with the dark hero photograph on the home page.
   ------------------------------------------------------------------------ */
.pagehead{
  background:var(--blue-deep); color:var(--on-dark);
  padding-top:calc(var(--hdr-h) + clamp(2.25rem,6vw,3.75rem));
  padding-bottom:clamp(2.25rem,6vw,3.75rem);
  text-align:center;
}
.pagehead h1{ color:#fff; margin:0 }
.pagehead .eyebrow{ color:var(--on-dark-accent) }
.pagehead .seam{ background:var(--on-dark-accent) }
.pagehead .lede{ color:var(--on-dark); margin-inline:auto }
/* .pagehead is not a .section, so the section-17 centring cascade never reaches
   it — the seam and the button row have to be centred here explicitly. */
.pagehead .seam{ margin-inline:auto }
.pagehead .btn-row{ justify-content:center }
.pagehead a:not(.btn){ color:var(--on-dark-accent) }
.pagehead .btn--ghost{ color:#fff; border-color:var(--on-dark) }
.pagehead .btn--ghost:hover{ background:#fff; color:var(--blue-deep) }

/* A material block on the flooring page: photograph one side, copy the other,
   alternating down the page. Reuses .grid--2's track sizing rather than
   inventing a second grid. */
.matrow{ align-items:center }
/* Section 17 centres everything inside a .section > .wrap. That is right for a
   single centred column and wrong beside a left-aligned photograph, where the
   ragged-both-edges column reads as a mistake. */
/* These have to out-specify section 17's own `.section > .wrap .seam` and
   `.section > .wrap p` (0,3,0), so they are written from .section down rather
   than from .matrow. A (0,2,1) selector loses to those and the column silently
   stays centred. */
.section > .wrap .matrow > div{ text-align:left }
.section > .wrap .matrow > div .seam{ margin-inline:0; transform-origin:left center }
.section > .wrap .matrow > div p{ margin-inline:0 }
.section > .wrap .matrow > div .btn-row{ justify-content:flex-start }
.matrow img{
  width:100%; height:auto; display:block; border-radius:2px;
  aspect-ratio:5/4; object-fit:cover;
}
.matrow figure{ margin:0 }
@media (min-width:720px){
  /* Even rows put the photograph on the right. order, not direction, so the
     DOM stays heading-then-image for a screen reader on every row. */
  .matrow--flip figure{ order:2 }
}
@media (prefers-reduced-motion:reduce){
  .matrow img{ transition:none }
}

/* --- 26. Long-form prose (privacy, terms, accessibility) -----------------
   Section 17 centres everything inside a .section > .wrap, which is right for
   a short marketing block and wrong for two thousand words of policy. These
   have to be written from .section down to out-specify it (0,3,0), and the
   display headings drop out of all-caps because a legal page set in Archivo
   800 uppercase reads as shouting rather than as a document.
   ------------------------------------------------------------------------ */
.section > .wrap .prose{ max-width:var(--max-prose); margin-inline:auto; text-align:left }
.section > .wrap .prose p,
.section > .wrap .prose ul,
.section > .wrap .prose ol{ margin-inline:0; text-align:left; max-width:none }
.section > .wrap .prose .seam{ margin-inline:0; transform-origin:left center }
.section > .wrap .prose h2,
.section > .wrap .prose h3{ text-align:left }
.prose h2{
  text-transform:none; font-size:1.4rem; font-weight:700; font-stretch:100%;
  line-height:1.25; letter-spacing:0; margin-top:2.6rem;
}
.prose h3{ text-transform:none; font-size:1.1rem; margin-top:1.8rem }
.prose h2 + p,.prose h3 + p{ margin-top:.7rem }
.prose ul{ margin:.9rem 0 0; padding-left:1.25rem }
.prose li{ margin-bottom:.45rem }
.prose .updated{ color:var(--muted); font-size:.9rem; margin-top:.35rem }

/* --- 27. Long single-word page titles at 320px ---------------------------
   --fs-h1 bottoms out at 2.35rem, and "Accessibility" is one 13-character
   word: at 320px it measured wider than the 280px text column and h1's
   overflow-wrap:break-word snapped it to "Accessibilit / y". A word cannot be
   re-broken, so the type has to give. This is in the shell because any future
   page with a long one-word title hits the same wall.
   ------------------------------------------------------------------------ */
@media (max-width:360px){
  .pagehead h1{ font-size:clamp(1.95rem,9vw,2.35rem) }
}

/* --- 28. A photograph shown at a controlled aspect ------------------------
   The recent-work photographs are square crops of taller originals. Dropped
   into a 1180px wrap at their own aspect they render as a 1180px square slab
   that fills the whole screen and reads as a mistake. .shot caps the width and
   crops to a landscape frame, which is what a photograph of a floor wants to
   be anyway.
   ------------------------------------------------------------------------ */
.shot{ max-width:860px; margin-inline:auto }
.shot img{ aspect-ratio:16/10; object-fit:cover; object-position:50% 55% }
@media (max-width:560px){
  .shot img{ aspect-ratio:4/3 }
}

/* --- 29. Inner-page header on short viewports -----------------------------
   Same defect the home hero had, in a different component: on a landscape
   phone (measured 844x390 and 667x375) and on a 320x568, the .pagehead is
   taller than the screen, so its CTAs land in the bottom 58px that the fixed
   call bar owns and are invisible on first paint. Compact the block rather
   than pad it — padding would only push the buttons further down.
   ------------------------------------------------------------------------ */
@media (max-height:540px) and (orientation:landscape){
  .pagehead{
    padding-top:calc(var(--hdr-h) + .9rem);
    padding-bottom:calc(58px + env(safe-area-inset-bottom, 0px) + .5rem);
  }
  .pagehead h1{ font-size:clamp(1.85rem,5vw,2.5rem) }
  .pagehead .lede{ font-size:1rem; margin-top:.75rem }
  .pagehead .seam{ margin-block:.75rem 1rem }
  .pagehead .btn-row{ margin-top:1rem }
}
@media (max-height:600px) and (orientation:portrait){
  .pagehead{
    padding-top:calc(var(--hdr-h) + 1rem);
    padding-bottom:calc(58px + env(safe-area-inset-bottom, 0px) + .5rem);
  }
  .pagehead h1{ font-size:clamp(1.95rem,7.6vw,2.5rem) }
  .pagehead .lede{ font-size:1rem; margin-top:.75rem }
  .pagehead .btn-row{ margin-top:1rem }
}

/* --- 30. A card that is entirely a link -----------------------------------
   A heading link inside a card is a 19px-tall tap target, which fails WCAG
   2.5.8's 24x24 minimum outright. Stretching the link over the whole card
   makes the target the card, and keeps one link per card for a screen reader
   rather than wrapping the whole thing in an anchor.
   ------------------------------------------------------------------------ */
.card--link{ position:relative; transition:border-color var(--dur-micro) var(--ease) }
.card--link h3 a::after{ content:''; position:absolute; inset:0 }
.card--link:hover{ border-color:var(--blue) }
.card--link h3 a{ text-decoration:none }
.card--link h3 a:focus-visible{ outline:none }
.card--link:focus-within{ outline:3px solid var(--blue); outline-offset:3px }

/* Measured on mobile-showroom at 844x390: the .pagehead came to 403px in a
   390px viewport and the CTAs overlapped the call bar by 5px (8px at 667x375).
   The lede is the only part that can go — the headline says the same thing and
   the buttons are the point. Same trade the hero makes at this height. */
@media (max-height:440px) and (orientation:landscape){
  .pagehead .lede{ display:none }
  .pagehead .seam{ margin-block:.6rem .85rem }
}

/* --- 31. Contrast corrections --------------------------------------------
   --hair (#DDE3EB) on white is 1.29:1. WCAG 1.4.11 wants 3:1 for the boundary
   of a user-interface component, and a form control whose edge you cannot see
   is a real problem on the one page whose whole job is the form. --hair stays
   as the hairline for decorative rules and card edges; controls get an edge
   you can actually see.
   ------------------------------------------------------------------------ */
.field input,.field select,.field textarea{ border-color:#8A93A3 }   /* 3.10:1 on #fff */
.field input::placeholder,.field textarea::placeholder{ color:#5C6675 }
.checks input{ accent-color:var(--blue) }

/* The desktop nav shows the current page with an underline (site.css:271). The
   phone menu is the ONLY nav below 1060px and had no equivalent, so
   aria-current was announced but never shown. */
@media (max-width:1259px){
  .mobmenu nav a[aria-current="page"]{
    color:#fff; font-weight:700;
    box-shadow:inset 3px 0 0 var(--on-dark-accent); padding-left:.75rem;
  }
}

/* ===========================================================================
   25 Sep 2026 — hero photograph swap, and the animation removed.
   kodyn: "he wants a different hero image", "the animation can go", and "the
   scroll animation on the bottom of the hero does not animate properly".

   The old object-position values were framing the VAN photograph (the tail
   light, the lettering). They are wrong for any other picture, so they are
   overridden here rather than hunted down in six media queries — this block is
   last in the file, and media queries carry no extra specificity.
   =========================================================================== */
.hero__img{ object-position:50% 50% }

/* The cue used to be bobbed by anime.js from home.js, which is gone. CSS does
   it with no JavaScript at all, and honours reduced motion, which the old
   library never checked. */
@media (prefers-reduced-motion:no-preference){
  .hero__scroll svg{ animation:cue-bob 1.9s var(--ease, ease-in-out) infinite }
}
@keyframes cue-bob{
  0%,100% { transform:translateY(0) }
  50%     { transform:translateY(6px) }
}

/* The scrim was built for the old night photograph of the van and buried this
   one. Measured against the new picture: at 38% the palest text on it (the
   eyebrow) still reads 5.67:1 on white, so the middle can be much lighter and
   stay past 4.5:1. It stays heavier at the very top and bottom, where the
   header and the call bar sit over it. Vault rule R5: lighter heroes. */
.hero__overlay{
  background:linear-gradient(180deg,
    rgba(8,24,48,.50) 0%,
    rgba(8,24,48,.34) 22%,
    rgba(8,24,48,.38) 46%,
    rgba(8,24,48,.42) 70%,
    rgba(8,24,48,.64) 100%);
}
@media (max-width:760px){
  /* A phone crops this 16:9 picture hard into its bright middle, so the phone
     keeps a little more scrim than the desktop does. */
  .hero__overlay{
    background:linear-gradient(180deg,
      rgba(8,24,48,.56) 0%,
      rgba(8,24,48,.42) 20%,
      rgba(8,24,48,.46) 48%,
      rgba(8,24,48,.50) 72%,
      rgba(8,24,48,.72) 100%);
  }
}

/* ===========================================================================
   Before / after slider - ported from the pool-cage build, 25 Sep 2026, for
   John's stairway pair. Same component, so a fix in one is a fix in both.
   =========================================================================== */
   and the CTAs overlapped the call bar by 79-94px across 844x390, 667x375 and
   640x360. Roughly 100px has to come out. The logo gives up 48 of it, the
   headline ~20, and the eyebrow goes entirely — the logo already says who this
   is, so the eyebrow is the redundant line at this height.
   ------------------------------------------------------------------------ */
@media (max-height:520px) and (orientation:landscape){
  .hero__content{ padding-top:calc(var(--hdr-h) + .5rem) }
  .hero__logo{ width:clamp(78px,10vw,112px); margin-bottom:.55rem }
  .hero .eyebrow{ display:none }
  .hero h1{ font-size:clamp(1.5rem,4vw,2rem) }
  .hero .seam{ margin-block:.6rem .8rem }
  .hero .btn-row{ margin-top:.85rem; gap:.55rem }
  .hero .btn{ min-height:44px; padding:.6rem 1.15rem; font-size:.88rem }
}

/* --- 20. THE BEFORE / AFTER — the signature component --------------------
   Two photographs of one job, registered to the same framing, with a draggable
   seam between them. It is deliberately NOT a plugin: no card frame, no drop
   shadow, no circular puck, no "drag me" badge. Those five things are what
   make a before/after read as a widget bolted on, and this client already
   rejected framed-card chrome once.

   Three traps are handled here rather than discovered later:
   1. draggable="false" + -webkit-user-drag:none on both images. A native image
      drag fires pointercancel and destroys the pointer capture, which leaves
      click-to-jump working and DRAG BROKEN ON DESKTOP ONLY. user-select:none
      does not cover it.
   2. touch-action:pan-y — horizontal gestures come to us, vertical page scroll
      still belongs to the phone. touch-action:none would trap the reader.
   3. A real <input type="range"> underneath does the keyboard and screen-reader
      work, so this is operable with no pointer at all and degrades to two
      stacked photographs if the JS never runs.
   ------------------------------------------------------------------------ */
.ba{
  position:relative; margin:2.5rem 0 0; border-radius:2px; overflow:hidden;
  touch-action:pan-y; user-select:none; -webkit-user-select:none;
  background:var(--sand, #f4f4f2); line-height:0;
}
.ba__img{
  display:block; width:100%; height:auto;
  -webkit-user-drag:none; user-drag:none; pointer-events:none;
}
/* The clipped layer sits on top and is inset from the RIGHT, so it paints on
   the LEFT — which is why it holds the BEFORE, matching the Before label in
   that corner. Getting this pair the wrong way round shows the finished house
   under the word "Before", and it is not obvious from the code alone.
   clip-path is cheaper than animating width and it never reflows. */
.ba__clip{
  position:absolute; inset:0;
  clip-path:inset(0 calc(100% - var(--x,50%)) 0 0);
}
.ba__clip .ba__img{ height:100%; object-fit:cover }

.ba__line{
  position:absolute; top:0; bottom:0; left:var(--x,50%);
  width:2px; margin-left:-1px; background:#fff; z-index:3; pointer-events:none;
  box-shadow:0 0 0 1px rgba(0,0,0,.22);
}
.ba__grip{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  display:flex; align-items:center; gap:34px; color:#fff;
  filter:drop-shadow(0 1px 3px rgba(0,0,0,.55));
}
.ba__grip svg{ width:15px; height:15px }

.ba__tag{
  position:absolute; bottom:0; z-index:2; padding:.9rem 1.05rem;
  font-family:'Archivo',sans-serif; font-weight:800; font-stretch:92%;
  font-size:.95rem; letter-spacing:.08em; text-transform:uppercase; color:#fff;
  line-height:1; text-shadow:0 1px 6px rgba(0,0,0,.6); pointer-events:none;
}
.ba__tag--before{ left:0 }
.ba__tag--after{ right:0 }

/* The operable control. Transparent, stretched over the whole component, with
   a thumb the width of a thumb. */
.ba__range{
  position:absolute; inset:0; width:100%; height:100%; margin:0; z-index:4;
  -webkit-appearance:none; appearance:none; background:transparent; cursor:ew-resize;
}
.ba__range::-webkit-slider-thumb{
  -webkit-appearance:none; width:44px; height:100vh; background:transparent; cursor:ew-resize;
}
.ba__range::-moz-range-thumb{
  width:44px; height:100vh; border:0; background:transparent; cursor:ew-resize;
}
.ba__range::-moz-range-track{ background:transparent }
.ba__range:focus-visible{ outline:3px solid var(--brand); outline-offset:3px }

@media (max-width:560px){
  .ba__tag{ font-size:.82rem; padding:.7rem .8rem }
  .ba__grip{ gap:26px }
}
@media (prefers-reduced-motion:reduce){
  .ba{ scroll-behavior:auto }
}


/* ===========================================================================
   Recent-work gallery, 25 Sep 2026.
   The ten photographs are a mix of 4:3 and 3:4, so every cell is a square and
   the picture is cropped to it — a grid of one shape reads as a gallery, and a
   CSS grid avoids the iPhone column-gap bug that multi-column albums hit.
   =========================================================================== */
.work-grid{
  display:grid;
  gap:1rem;
  grid-template-columns:1fr;
  margin-top:2.2rem;
}
.work-card{ margin:0 }
.work-card img{
  display:block; width:100%; aspect-ratio:1/1; object-fit:cover;
  border-radius:2px; background:var(--sand, #f1f0ec);
}
.work-card figcaption{
  margin-top:.55rem;
  font-size:.86rem; line-height:1.45; color:var(--muted, #5a5f66);
}
@media (min-width:620px){ .work-grid{ grid-template-columns:repeat(2,1fr); gap:1.15rem } }
@media (min-width:1000px){ .work-grid{ grid-template-columns:repeat(3,1fr); gap:1.25rem } }

/* This pair already has BEFORE and AFTER written across the photographs, so the
   component's own corner labels are switched off. Remove the modifier once
   clean originals arrive and they come back. */
.ba--captioned .ba__tag{ display:none }

/* ===========================================================================
   25 Sep 2026, second round.
   =========================================================================== */

/* The hero headline is now the wordmark kodyn sent, knocked out white. It is
   2.22:1 where the old mark was square, so every width here is set to land on
   the SAME height the square mark had at that tier — the two short-screen caps
   exist because the CTAs were measured landing behind the fixed call bar. */
.hero__h1--logo img{ width:min(420px, 78vw) }
@media (max-height:430px) and (orientation:landscape){
  .hero__h1--logo img{ width:min(165px, 32vw) }
}
@media (max-height:600px) and (orientation:portrait){
  .hero__h1--logo img{ width:min(250px, 66vw) }
}

/* Rounded photographs, to match the newer builds (kodyn, 25 Sep). Those sites
   carry --radius:14px; this one had square corners everywhere. Anything that
   crops or overlays needs overflow:hidden as well, or the corner shows through
   at the child's square edge. */
:root{ --radius:14px }
.work-card img,
.shot img,
.grid figure img,
.ba,
.rev,
.card{ border-radius:var(--radius) }
.tile,
.tile a{ border-radius:var(--radius); overflow:hidden }

/* ===========================================================================
   1 Oct 2026 — John's email: the front page first.
   "I don't want it to be the full page", "I want the Whatcom and Skagit part
   below the business name and all the writing much larger", "I don't need the
   location added a second time on the front page", and "myself and my old
   customers need aggressively large font". kodyn: "dont make the wording to
   big just up it to where it still looks good" — so every size below is a
   step up, not a jump, and the page keeps its proportions.
   This block is last in the file on purpose; media queries carry no extra
   specificity, so it wins over every earlier hero tier it restates.
   =========================================================================== */

/* --- The hero: about three quarters of a screen, not all of it ----------- */
.hero{
  min-height:clamp(500px, 74vh, 820px);
  min-height:clamp(500px, 74svh, 820px);
}
/* The photograph was oversized by 14% so the entrance and the scroll scrub
   could move it without showing an edge. Both are gone (25 Sep), so the crop
   was throwing away a seventh of the picture for nothing. */
.hero__img{ top:0; left:0; width:100%; height:100% }
.hero__content{
  padding-top:calc(var(--hdr-h) + clamp(1.5rem,4vw,2.75rem));
  padding-bottom:clamp(2.75rem,6vw,4.25rem);
}
.hero__h1--logo{ margin:0 }
.hero__h1--logo img{ width:min(500px, 82vw) }

/* The county line, now under the name. Full white and a size up from the old
   eyebrow, which was 12px at 78% white over a busy photograph — the line John
   called hard to read. The shadow is tight, so it reads as the type being
   legible rather than as a glow. */
.hero__where{
  max-width:none; margin:1rem auto 0;
  font-family:'Archivo',sans-serif; font-weight:700; font-stretch:100%;
  font-size:clamp(1.05rem, 1.7vw, 1.35rem); line-height:1.3;
  letter-spacing:.1em; text-transform:uppercase; color:#fff;
  text-shadow:0 1px 2px rgba(6,20,42,.55), 0 2px 14px rgba(6,20,42,.6);
}
.hero__dot{ margin-inline:.35em }

.hero .btn-row{ margin-top:1.9rem; gap:.85rem }
.hero .btn{ font-size:1.06rem; min-height:56px; padding:.9rem 1.7rem }

/* Phones: the two halves of the county line on their own lines (a wrapped
   middle dot ends up orphaned at the start of the second), and the two
   buttons stacked at one width rather than ragged. Portrait only: a 568x320
   landscape phone is under 600px wide too, and stacking there put the Call
   button under the fixed call bar. */
@media (max-width:600px) and (orientation:portrait){
  .hero__where span{ display:block }
  .hero__where .hero__dot{ display:none }
  .hero__where{ font-size:clamp(1rem, 4.9vw, 1.2rem); letter-spacing:.08em }
  .hero .btn-row{
    flex-direction:column; align-items:stretch;
    max-width:21rem; margin-inline:auto; margin-top:1.6rem;
  }
}

/* The short-screen tiers, restated for the new pieces. A landscape phone
   still needs the hero to be only as tall as its content, and the CTAs still
   have to clear the fixed call bar. */
@media (max-height:520px) and (orientation:landscape){
  .hero{ min-height:auto }
  .hero__content{
    padding-top:calc(var(--hdr-h) + .6rem);
    padding-bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 1rem);
  }
  .hero__where{ font-size:.98rem; margin-top:.55rem }
  .hero .btn-row{ margin-top:1rem }
  .hero .btn{ min-height:48px; padding:.65rem 1.25rem; font-size:.95rem }
}
@media (max-height:430px) and (orientation:landscape){
  .hero__h1--logo img{ width:min(150px, 30vw) }
  .hero__where{ font-size:.9rem; margin-top:.45rem }
  .hero .btn-row{ margin-top:.8rem }
}
@media (max-height:600px) and (orientation:portrait){
  .hero__h1--logo img{ width:min(250px, 66vw) }
}

/* --- Reading sizes, every page ------------------------------------------ */
body{ font-size:1.1875rem }                          /* 17px -> 19px */
:root{ --fs-h3:clamp(1.2rem, 1.8vw, 1.4rem) }
.lede{ font-size:clamp(1.2rem, 2.1vw, 1.42rem) }
.small{ font-size:.98rem }
.eyebrow{ font-size:.9rem }
.btn{ font-size:1.02rem }

.nav > a{ font-size:.88rem }
.mobmenu nav a{ font-size:1.12rem }

/* One row only where one row fits. At this size the four labels and three
   dividers measure 1291px, so the two-up grid that used to start at 1180 now
   starts at 1380 (1291 plus the side padding). */
.trustbar__item{ font-size:.86rem }
@media (max-width:1379px){
  .trustbar__in{
    display:grid; grid-template-columns:repeat(2,auto);
    justify-content:center; gap:.85rem 1.75rem;
  }
  .trustbar__div{ display:none }
  .trustbar__item{ white-space:normal }
}
/* ...and phones keep their own wrapping cluster, which the grid above would
   otherwise replace (it comes later in the file). */
@media (max-width:520px){
  .trustbar__in{
    display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
    gap:.4rem 1rem;
  }
  .trustbar__item{ font-size:.84rem }
}

.quote p{ font-size:1.14rem }
.quote footer{ font-size:.94rem }
figcaption{ font-size:.95rem }
.work-card figcaption{ font-size:1rem }

.rev p{ font-size:1.08rem }
.rev__src{ font-size:.76rem }
.rev footer{ font-size:.92rem }
.marquee__ctrl{ font-size:.82rem }

.tile__label{ font-size:1.15rem }
@media (max-width:560px){ .tile__label{ font-size:1.08rem } }

.rates{ font-size:1.08rem }
.rates caption{ font-size:.95rem }
.rates thead th{ font-size:.88rem }
.towns li{ font-size:1rem }

.field label,legend{ font-size:1rem }
.form-status{ font-size:1.05rem }

.ftr h2,.ftr h3{ font-size:.88rem }
.ftr li,.ftr__nap{ font-size:1.02rem }
.ftr__base{ font-size:.92rem }

.prose h3{ font-size:1.25rem }
.prose .updated{ font-size:1rem }

/* The header phone button is a .btn with its own compact size, set because the
   header row only just fits at 1041px. The .btn step-up above is later in the
   file and would otherwise win; this holds it to a small step instead. */
.hdr__tel{ font-size:.9rem }

/* Inner-page title block on a short landscape phone. At 568x320 the bigger
   buttons (302 + 231px) no longer fit side by side in the 511px column, so the
   Call button wrapped onto a second row and landed under the fixed call bar.
   Compact buttons here, exactly as the home hero does at this height, keep
   them on one row (about 470px) and above the bar. */
@media (max-height:540px) and (orientation:landscape){
  .pagehead .btn{ min-height:44px; padding:.6rem 1.1rem; font-size:.9rem }
}

/* ===========================================================================
   13 Oct 2026 — John: "On the top row I want bigger clearer fonts. My customer
   base is over 60 and it needs to be really easy to see. The font size on the
   top row needs to be as big as the font lower. You can spread things out and
   make the page bigger and bigger fonts on the bottom row on the first page
   also. My number one priority is to be able to open the first page and see
   things without glasses."
   Measured before, on a 1366px laptop: the menu was 14px at 82% white straight
   on the photograph, the header phone button 14px, the strip under the hero
   14px grey; the county line below was 22px and the hero buttons 17px.
   Last in the file on purpose: it restates sizes from every earlier layer.
   =========================================================================== */

/* --- The top row ---------------------------------------------------------- */
/* A solid navy bar from the first paint, not only once scrolled: thin white
   type on a busy photograph is what made it hard to read, and John has a new
   hero photo coming that may be lighter still. */
:root{ --hdr-h:84px }
.hdr{ background:rgba(15,49,99,.97); border-bottom-color:rgba(255,255,255,.14) }

.nav{ gap:.2rem }
.nav > a{
  font-size:1.25rem; font-weight:700; font-stretch:100%;     /* 14px -> 20px */
  letter-spacing:.035em; color:#fff; padding:.55rem .62rem;
}
.nav > a::after{ left:.62rem; right:.62rem; bottom:.15rem; height:3px }

.hdr__logo{ gap:.7rem }
.hdr__mark{ height:52px }
.hdr__name b{ font-size:1.5rem }
.hdr__name span{ font-size:.74rem; letter-spacing:.14em; color:#fff }

.hdr .social{ width:44px; height:44px; color:#fff }
.hdr .social svg{ width:22px; height:22px }

.hdr__tel{ font-size:1.25rem; min-height:52px; padding:.7rem 1.15rem; gap:.55rem }
.hdr__tel svg{ width:20px; height:20px }

/* The menu that opens from the button */
.mobmenu nav a{ font-size:1.4rem; font-weight:700; font-stretch:100%; color:#fff; letter-spacing:.04em }
.mobmenu__foot .btn{ font-size:1.2rem; min-height:56px }

/* --- The hero: the "font lower" goes up a step too ----------------------- */
@media (min-width:701px){
  .hero__where{ font-size:clamp(1.2rem, 1.9vw, 1.55rem) }    /* 22px -> 25px */
  .hero .btn-row{ margin-top:2.1rem; gap:1rem }
  .hero .btn{ font-size:1.25rem; min-height:64px; padding:1rem 2rem }   /* 17 -> 20px */
}

/* --- The bottom row: the strip under the hero ----------------------------- */
.trustbar{ padding:1.5rem 0 }
.trustbar__item{
  font-size:1.2rem; color:var(--ink);                          /* 14px grey -> 19px near-black */
  letter-spacing:.035em; gap:.7rem;
}
.trustbar__icon{ width:28px; height:28px }
.trustbar__div{ height:30px }

/* The bigger menu needs 1223px (measured: 454 + logo 203 + 413 + gaps and side
   padding), and the header was capped at 1180px wide on every screen, so it
   could never fit. The cap goes to 1320px, and below 1260px the ☰ menu takes
   over (the five "phone menu" breakpoints above moved from 1060 to 1259px;
   the fixed call bar at the bottom of the screen still starts at 1060px). */
.hdr__in{ max-width:1320px }

/* 1061-1259px: ☰ menu, but no bottom call bar at this width, so the phone
   number stays in the top row beside the menu button. */
@media (min-width:1061px) and (max-width:1259px){
  .hdr__in{ grid-template-columns:1fr auto auto }
  .nav--right{ display:flex; justify-self:end; order:2 }
  .nav--right > a,
  .nav--right .hdr__socials{ display:none }
  .hdr__burger{ order:3; width:52px; height:52px }
}

/* Phones: a slightly shorter bar, still a bigger logo than before. */
@media (max-width:700px){
  :root{ --hdr-h:74px }
  .hdr__mark{ height:46px }
  .hdr__name b{ font-size:1.32rem }
  .hdr__name span{ font-size:.68rem }
  .hdr__burger{ width:52px; height:52px }
}

/* The strip as a two-by-two block on screens that can hold it (one row at this
   size would need 1742px), one column below 900px, and phones keep their own
   cluster with only the free-measuring line. */
.trustbar__in{
  display:grid; grid-template-columns:repeat(2, auto);
  justify-content:center; gap:1rem 3rem;
}
.trustbar__div{ display:none }
.trustbar__item{ white-space:normal }
@media (max-width:899px){
  .trustbar__in{ grid-template-columns:auto; gap:.85rem }
}
@media (max-width:520px){
  .trustbar{ padding:1.15rem 0 }
  .trustbar__in{ display:flex; flex-wrap:wrap; justify-content:center; gap:.5rem 1rem }
  .trustbar__item{ font-size:1.08rem; text-align:left }      /* text stays beside its icon when it wraps */
  .trustbar__icon{ width:22px; height:22px }
}

/* Full menu: each side takes what it needs and the logo centres in what is
   left. Equal halves (1fr auto 1fr) pushed the wider right-hand group 9-29px
   into the side padding between 1260 and 1300px; the logo now sits at most
   ~20px off true centre, which nobody can see. */
@media (min-width:1260px){
  .hdr__in{ grid-template-columns:auto 1fr auto }
  .hdr__logo{ justify-self:center }
}

/* ===========================================================================
   13 Oct 2026, second pass — Kody: "please make the rest of the words on the
   main page larger as well. i think his concern is with the whole main page".
   Measured first (1366px laptop): section labels 14px, review names and
   places 15px, review source tags 12px, review quotes 17px, buttons 16px,
   photo captions 15px, fine print 16px, town list 16px, form labels 16px,
   footer 14-16px; paragraphs 19px.
   First scoped to the homepage; since 15 Oct 2026 it applies to every page, so
   the inner pages read at the same sizes as the first page. Each size is about
   a quarter up; nothing stays under 17px. (:root keeps the specificity the
   old .home selectors had.)
   =========================================================================== */
:root body{ font-size:1.3125rem }                                   /* 19 -> 21px */
:root .lede{ font-size:clamp(1.3rem, 2.2vw, 1.6rem) }              /* 22.7 -> 25.6 */
:root .small{ font-size:1.15rem }                                   /* 15.7 -> 18.4 */
:root .eyebrow{ font-size:1.1rem }                                  /* 14.4 -> 17.6 */
:root h2{ font-size:clamp(2.15rem, 4.6vw, 3.25rem) }                /* phone 29.6 -> 34.4 */
:root h3{ font-size:clamp(1.4rem, 2vw, 1.7rem) }                    /* 22.4 -> 27 */
:root .section .btn{ font-size:1.2rem; min-height:58px; padding:.95rem 1.7rem }  /* 16.3 -> 19.2 */
:root .tile__label{ font-size:1.45rem }                             /* 18.4 -> 23 */
@media (max-width:560px){ :root .tile__label{ font-size:1.25rem } }
:root figcaption{ font-size:1.12rem }                               /* 15.2 -> 18 */
:root .num{ font-size:1.3rem }                                      /* 17 -> 21 */

:root .rev p{ font-size:1.3rem }                                    /* 17.3 -> 20.8 */
:root .rev footer,
:root .rev cite{ font-size:1.12rem }                                /* 14.7 -> 18 */
:root .rev__src{ font-size:1.08rem }                                /* 12.2 -> 17.3 */
:root .marquee__ctrl{ font-size:1.08rem; min-height:48px }          /* 13 -> 17.3 */

:root .towns li{ font-size:1.25rem }                                /* 16 -> 20 */

:root .field label,
:root legend{ font-size:1.2rem }                                    /* 16 -> 19.2 */
:root .field input,
:root .field select,
:root .field textarea{ font-size:1.2rem }
:root .field input,
:root .field select{ min-height:56px }
:root .form-status{ font-size:1.2rem }

/* Parts that only the inner pages have, brought to the same sizes */
:root .quote p{ font-size:1.3rem }                                  /* 18.2 -> 20.8, as the home reviews */
:root .quote footer,
:root .quote cite{ font-size:1.12rem }                              /* 15 -> 18 */
:root .prose .updated{ font-size:1.08rem }                          /* 16 -> 17.3 */
.pagehead .eyebrow{ margin-inline:auto }   /* its capped box hugged the left, so the label sat off-centre over the title */
@media (min-width:701px) and (min-height:541px){
  .pagehead .btn{ font-size:1.25rem; min-height:64px; padding:1rem 2rem }  /* 16.3 -> 20, as the home hero */
}
/* Short screens keep the compact title block (section 29). Measured at
   320x568: the bigger label wrapped to three lines on /flooring and the
   lede alone was 144px, so the buttons sat up to 62px under the call bar (31px
   before the size change, too). The lede goes on short portrait screens, the
   same trade section 29 makes on landscape phones: the headline says it and
   the buttons are the point. */
/* Phones: the title block has to fit above the call bar on first paint. At
   21px the lede wrapped one or two lines more and pushed the buttons under the
   bar at 375x667 and 360x640 (clear before), so inside the title block only
   it keeps its 19px and the label 16px; everything below stays at the new
   sizes. */
@media (max-width:700px){
  :root .pagehead .lede{ font-size:1.2rem }
  :root .pagehead .eyebrow{ font-size:1rem }
}
@media (max-height:540px) and (orientation:landscape){
  :root .pagehead .lede{ font-size:1rem }
  :root .pagehead .eyebrow{ font-size:1rem }
}
@media (max-height:600px) and (orientation:portrait){
  :root .pagehead .lede{ display:none }
  :root .pagehead .eyebrow{ font-size:1rem }
}

/* Footer and phone call bar: every page */
.ftr h2,.ftr h3{ font-size:1.1rem }                                 /* 14 -> 17.6 */
.ftr li,.ftr__nap,.ftr p,
.ftr a:not(.ftr__phone):not(.social){ font-size:1.2rem }            /* 16.3 -> 19.2 */
.ftr .small{ font-size:1.12rem }
.ftr__base,.ftr__base p,.ftr__base a,.ftr__base span{ font-size:1.08rem }  /* 14.7 -> 17.3 */
@media (max-width:1060px){ .callbar{ font-size:1.2rem } }           /* 16.8 -> 19.2 */

/* 13-15 Oct 2026: the hero install animation (.hero__video, .hero__note) was retired; the hero is John's still photographs again. */

/* 15 Oct 2026. Narrow landscape phones (568x320): with Archivo loaded the two
   hero buttons measure 277 + 211px plus the gap, 9px wider than the 493px row,
   so they wrapped to two rows and the Call button sat under the call bar
   (it was like this before the 15 Oct changes too). Slightly less side padding
   keeps them on one row. */
@media (max-height:540px) and (orientation:landscape) and (max-width:640px){
  .hero .btn{ padding-inline:.95rem }
}
/* Same defect one size up (also older than 15 Oct): the 13 Oct "font lower" rule
   gives the hero buttons 20px type and 2rem side padding on every screen over
   700px wide, short landscape phones included. At 740x360 they wrapped to two
   rows, 82px under the call bar. Keep the 20px type and trim only the padding. */
@media (max-height:540px) and (orientation:landscape) and (min-width:701px){
  .hero .btn{ padding-inline:1.1rem; min-height:56px }
  .hero .btn-row{ gap:.6rem }
}

/* 27 Oct 2026, John (email): "On the website where is says our services can you
   do 2x3, 2 across and 3 down, so that the pictures are larger." Two columns
   from 900px up (it was three), so six tiles sit 2 across and 3 down. Narrower
   screens already show two across, and phones one per row, which is larger
   still, so they are unchanged. */
@media (min-width:900px){ .tiles{ grid-template-columns:repeat(2,1fr); gap:1.25rem } }

/* ===========================================================================
   27 Oct 2026, John (email): "Also on the front page I don't like the dark blue
   band at the top. Maybe something softer/lighter. It looks cheap, it should
   feel warmer/softer."
   The band is the header, solid navy (#0F3163) since 13 Oct. Tried in turn:
   warm cream (kodyn: "i dont think he meant to change the color to white"),
   then a softer denim blue #3A6896, then kodyn: "maybe try a light grey".
   So: a light grey bar, #E4E7EB, with deep-navy type (10.3:1). The type keeps
   every 13 Oct size, because John still has to read it without glasses. The
   phone number becomes a solid brand-blue button, white type 9.05:1. Same
   header on every page; the phone menu that drops from it matches.
   =========================================================================== */
.hdr,
.hdr.is-stuck,
.hdr--solid{
  background:#E4E7EB;
  -webkit-backdrop-filter:none; backdrop-filter:none;
  border-bottom-color:#D3D8DF;
  box-shadow:0 2px 14px rgba(20,40,70,.10);
}
.hdr__name,
.hdr__name span{ color:#0F3163 }
.nav > a,
.nav > a:hover,
.nav > a[aria-current="page"]{ color:#0F3163 }
.nav > a::after{ background:#184888 }
.hdr .social{ color:#0F3163 }
.hdr .social:hover{ color:#0F3163; background:rgba(15,49,99,.08) }

.hdr__tel.btn--onDark{ background:#184888; color:#fff; border-color:#184888 }
.hdr__tel.btn--onDark:hover{ background:#0F3163; border-color:#0F3163; color:#fff }

.hdr__burger{ border-color:rgba(15,49,99,.35) }
.hdr__burger span,
.hdr__burger span::before,
.hdr__burger span::after{ background:#0F3163 }
.hdr__burger[aria-expanded="true"] span{ background:transparent }

.hdr :focus-visible{ outline-color:#0F3163 }

@media (max-width:1259px){
  .mobmenu{
    background:#E4E7EB;
    -webkit-backdrop-filter:none; backdrop-filter:none;
    border-bottom-color:#D3D8DF;
    box-shadow:0 18px 30px -18px rgba(20,40,70,.35);
  }
  .mobmenu nav a{ color:#0F3163; border-bottom-color:rgba(15,49,99,.12) }
  .mobmenu nav a:hover{ color:#184888 }
  .mobmenu nav a[aria-current="page"]{ color:#0F3163; box-shadow:inset 3px 0 0 #184888 }
  .mobmenu .social{ color:#0F3163; border-color:rgba(15,49,99,.25) }
  .mobmenu__foot .btn--onDark{ background:#184888; color:#fff; border-color:#184888 }
  .mobmenu :focus-visible{ outline-color:#0F3163 }
}

/* ===========================================================================
   27 Oct 2026, John (email), computer version: "The links at the top of the
   website I would like them stretched out and larger font, so it takes up the
   whole page horizontally."
   From 1260px (where the full menu shows; below it the ☰ menu is unchanged):
   the bar's 1320px cap is lifted so it runs the full width of the screen, each
   side's links spread evenly across its half, and the type grows with the
   screen: about 21px on a 1280 laptop, 22px at 1440, 25px at 1600, 28px on a
   1920 monitor (it was 20px). The logo and phone button grow with it.
   Measured at 1260-2560px: no overlap, no wrapping.
   =========================================================================== */
@media (min-width:1260px){
  .hdr__in{
    max-width:none;
    grid-template-columns:minmax(max-content,1fr) max-content minmax(max-content,1fr);
    padding-inline:clamp(1.25rem, 2.6vw, 3.5rem);
    gap:clamp(1rem, 2.4vw, 3.25rem);
  }
  .nav--left,
  .nav--right{ justify-self:stretch; justify-content:space-evenly; gap:.4rem }
  .nav > a{ font-size:clamp(1.3rem, 1.46vw, 1.75rem); padding-inline:.4rem }
  .nav > a::after{ left:.4rem; right:.4rem }
  .hdr__mark{ height:clamp(52px, 3.6vw, 64px) }
  .hdr__name b{ font-size:clamp(1.5rem, 1.75vw, 1.95rem) }
  .hdr__name span{ font-size:clamp(.74rem, .85vw, .95rem) }
  .hdr__tel{ font-size:clamp(1.25rem, 1.4vw, 1.6rem); min-height:clamp(52px, 3.8vw, 62px) }
  .hdr__tel svg{ width:clamp(20px, 1.4vw, 24px); height:clamp(20px, 1.4vw, 24px) }
  .hdr .social{ width:clamp(44px, 3vw, 52px); height:clamp(44px, 3vw, 52px) }
  .hdr .social svg{ width:clamp(22px, 1.6vw, 26px); height:clamp(22px, 1.6vw, 26px) }
  .hdr__call{ margin-left:0 }
}

/* ===========================================================================
   29 Oct 2026, John (email).
   1-2. The strip under the hero: "Please put these 3 bullet points on the same
   line and the list of materials sold underneath." Free measuring · Whatcom &
   Skagit County · Family owned since 1986 in one row, and his materials list
   on its own line beneath. Phones (520px and under) keep showing only Free
   measuring from the row (the hero right above already says "Whatcom & Skagit
   County · Since 1986"), plus the materials line.
   =========================================================================== */
.trustbar__in{
  display:flex; flex-direction:column; align-items:center;
  gap:1rem; text-align:center;
}
.trustbar__row{
  display:flex; align-items:center; justify-content:center;
  flex-wrap:wrap; gap:.75rem clamp(1.25rem, 3vw, 2.5rem);
}
@media (min-width:1000px){
  .trustbar__row{ flex-wrap:nowrap }
  .trustbar__row .trustbar__div{ display:block; height:30px }
  .trustbar__row .trustbar__item{ white-space:nowrap }
}
.trustbar__materials{
  display:flex; align-items:center; justify-content:center; gap:.7rem;
  margin:0; max-width:none;
  font-family:'Inter',sans-serif; font-weight:700; font-size:1.25rem;
  line-height:1.4; color:var(--ink);
}
.trustbar__materials .trustbar__icon{ width:28px; height:28px }
@media (max-width:520px){
  .trustbar__in{ gap:.7rem }
  .trustbar__materials{ font-size:1.08rem; align-items:flex-start; text-align:left }
  .trustbar__materials .trustbar__icon{ width:22px; height:22px; margin-top:.15rem }
}

/* ===========================================================================
   3. "Tell us what you need": "please make it wider to fit the whole page
   (large font)". The form now uses the full width (up to 1400px) instead of a
   640px column: name, phone and address side by side on a computer, bigger
   labels, boxes, tick boxes and button. Phones stack one field per row.
   =========================================================================== */
#book > .wrap{ max-width:1400px }
#book .form{ max-width:none; gap:1.5rem }
@media (min-width:900px){
  #book .field-row--3{ grid-template-columns:repeat(3, minmax(0, 1fr)); gap:1.5rem }
}
#book .field label,
#book legend{ font-size:1.45rem; font-weight:700 }
#book .field input,
#book .field textarea{ font-size:1.35rem; padding:.85rem 1rem }
#book .field input{ min-height:64px }
#book .field textarea{ min-height:170px }
#book .checks{ grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:.9rem 2rem }
#book .checks label{ font-size:1.35rem; font-weight:600 }
#book .checks input{ width:28px; height:28px }
#book .book__send{
  font-size:1.35rem; min-height:68px; padding:1rem 2.2rem;
  white-space:normal; line-height:1.25;
}
#book .form > p.small{ font-size:1.1rem }
@media (max-width:600px){
  #book .field label,
  #book legend{ font-size:1.3rem }
  #book .book__send{ width:100%; font-size:1.2rem }
}

/* 3 Nov 2026, John (email): the homepage warehouse row is three photos across
   (carpet display, forklift, hardwood display). They are three different
   shapes, so all three are cut to the same 4:3 frame and line up. Three
   across from 900px, one under another below that (auto-fit gave an orphan
   at tablet widths). */
.warehouse__trio{ grid-template-columns:1fr }
@media (min-width:900px){ .warehouse__trio{ grid-template-columns:repeat(3, minmax(0, 1fr)) } }
.warehouse__trio figure{ margin:0 }
.warehouse__trio img{ aspect-ratio:4/3; height:auto; object-fit:cover; display:block }
