/* ==========================================================================
   McFarland Floor Sanding - warm timber / editorial
   Built by Click n Call. Copied to site/assets/app.<hash>.css by build.mjs.

   Palette note: --ink-deep is the LOGO ARTWORK'S OWN background colour. The logo ships as an
   opaque image (see build/prepare-images.py for why an alpha key was rejected), so every
   surface the logo sits on - header, footer, mobile menu - must be exactly this value or the
   artwork's black rectangle becomes visible. Do not "tidy" it to a rounder number.
   ========================================================================== */

/* ---- font ---------------------------------------------------------------
   One weight, self-hosted, 35 kB. Fraunces at wght 600 with the optical-size axis left
   variable, so the same file serves a 64px hero and a 19px sub-head at the right contrast.
   Body copy is the system sans - no second request, and it is what the reader's OS already
   renders best. */
@font-face {
  font-family: 'Fraunces';
  src: url('/assets/fonts/fraunces-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
                 U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --ink-deep: #242021;
  --ink:      #201c1a;
  --ink-2:    #57504a;
  --paper:    #faf7f2;
  --paper-2:  #f3eee5;
  --paper-3:  #ebe4d8;
  --line:     #e5ded2;
  --line-2:   #d6cbb9;

  /* Timber tan, pulled from the rimu in the photographs. Two values, because one cannot do
     both jobs.

       --tan      a FILL. Buttons, rules, the floating bar. Its job is to be light enough that
                  the near-black --btn-ink on top of it clears 4.5:1. The first value tried
                  here was #a96a22, which measured 4.10:1 - it looked completely fine and
                  failed AA on every button on the site. MEASURED, not chosen by eye.
       --tan-ink  the same hue darkened until it clears 4.5:1 as TEXT on --paper. 5.85:1.

     build/check-live.mjs measures both in a real browser on every page at two widths, so
     changing either of these without re-running it will fail the check rather than ship. */
  --tan:      #b4762f;   /* 4.79:1 with --btn-ink */
  --tan-hi:   #c4883c;   /* hover. 5.96:1 - lighter, so the feedback also raises contrast */
  --tan-ink:  #8a5416;   /* 5.85:1 on --paper */
  --tan-soft: #f0e2cf;
  --btn-ink:  #1b1512;   /* the text that sits on --tan */

  /* Logo yellow. Used only inside the logo artwork and the favicon - it is an acid lemon
     that fights the warm palette anywhere else, so there is deliberately no --brand
     utility class. */

  --wrap:  1180px;
  --wrap-narrow: 720px;
  --gut:   clamp(1.25rem, 4vw, 2.75rem);

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

  --shadow-1: 0 1px 2px rgba(36,32,33,.06), 0 4px 14px rgba(36,32,33,.06);
  --shadow-2: 0 2px 6px rgba(36,32,33,.08), 0 18px 46px rgba(36,32,33,.13);

  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
          'Noto Sans', sans-serif;
  --serif: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;

  --step: clamp(3.5rem, 7vw, 6.5rem);   /* vertical rhythm between bands */
}

/* ---- reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.68;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4, h5, h6, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button { font: inherit; color: inherit; }

:where(a):focus-visible,
:where(button):focus-visible,
:where(input, textarea, select):focus-visible,
:where([tabindex]):focus-visible {
  outline: 3px solid var(--tan-ink);
  outline-offset: 3px;
  border-radius: 3px;
}

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

.vh {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---- type ---------------------------------------------------------------
   Headings carry data-len, stamped by the builder from the character count of the heading
   itself. The copy on this site is migrated verbatim and the H1s run from 21 to 57
   characters; a single clamp() that suits "Contact" leaves the 57-character Kaipara/Rodney/
   Mangawhai H1 running to four lines on a laptop and pushing the CTA off the fold. */
h1, h2, h3, h4 { font-family: var(--serif); font-weight: 600; font-optical-sizing: auto;
                 line-height: 1.1; letter-spacing: -.015em; color: var(--ink); }

h1 { font-size: clamp(2.35rem, 5.6vw, 4rem); line-height: 1.04; letter-spacing: -.025em; }
h1[data-len="long"]  { font-size: clamp(2.1rem, 4.8vw, 3.4rem); }
h1[data-len="xlong"] { font-size: clamp(1.95rem, 4.2vw, 3rem); line-height: 1.08; }

h2 { font-size: clamp(1.75rem, 3.2vw, 2.6rem); }
h2[data-len="long"]  { font-size: clamp(1.55rem, 2.7vw, 2.15rem); }
h2[data-len="xlong"] { font-size: clamp(1.4rem, 2.3vw, 1.85rem); line-height: 1.18; }

h3 { font-size: clamp(1.3rem, 2vw, 1.65rem); }
h4 { font-size: 1.2rem; }

.eyebrow {
  font-family: var(--sans);
  font-size: .78rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--tan-ink); margin-bottom: 1rem; display: block;
}
/* --tan-ink is the accent tuned for dark text on PAPER. On a dark surface it measures 2.58:1,
   which is a fail at this size - and an eyebrow is small, tracked-out, uppercase type, i.e.
   the hardest thing on the page to read when it is a little too dark. Every dark surface must
   therefore be listed, not just the ones carrying .on-dark. */
.on-dark .eyebrow, .hero .eyebrow, .band--deep .eyebrow, .ctaband .eyebrow { color: #e6b478; }

.lede { font-size: clamp(1.1rem, 1.6vw, 1.28rem); line-height: 1.6; color: var(--ink-2); }

/* Body links. Scoped with :not() so a .btn or .tlink inside prose keeps its own colour -
   .prose a is (0,2,1) and .btn is (0,1,0), so without this every call to action inside a
   copy block silently loses its text colour to the link rule. That exact bug shipped on
   another site in this fleet at 1.00:1 contrast. */
.prose a:not(.btn):not(.tlink),
.rt a:not(.btn):not(.tlink) {
  color: var(--tan-ink);
  text-decoration: underline;
  text-underline-offset: .18em;
  text-decoration-thickness: from-font;
}
.prose a:not(.btn):not(.tlink):hover,
.rt a:not(.btn):not(.tlink):hover { color: var(--ink); }

.prose > * + * { margin-top: 1.15em; }
.prose h2, .prose h3 { margin-top: 2.2em; }
.prose h2 + p, .prose h3 + p { margin-top: .9em; }
.prose p { color: var(--ink-2); }
.prose strong { color: var(--ink); font-weight: 600; }

/* ---- layout -------------------------------------------------------------- */
.wrap { width: min(100% - (var(--gut) * 2), var(--wrap)); margin-inline: auto; }
.wrap--narrow { width: min(100% - (var(--gut) * 2), var(--wrap-narrow)); margin-inline: auto; }

.band { padding-block: var(--step); }
.band--tight { padding-block: calc(var(--step) * .62); }
.band--paper2 { background: var(--paper-2); }
.band--deep   { background: var(--ink-deep); color: #ddd5cc; }
.band--deep h1, .band--deep h2, .band--deep h3, .band--deep h4 { color: #fbf7f1; }
.band--deep p { color: #c3b9ae; }

/* 🔴 A LIGHT SURFACE INSIDE A DARK BAND has to restate every text colour, or it inherits the
   band's and renders white on white.

   This is not hypothetical: the service cards at the foot of the dark "Our other floor
   services" band rendered their H3 at #fbf7f1 on the card's own #fff - measured 1.07:1,
   invisible - while the photograph above and the card's own body copy looked perfectly
   correct, so the section read as nearly right rather than broken. The .tlink beneath it was
   2.10:1 by the same route.

   The winning rule has to be MORE SPECIFIC, not merely later: .band--deep h3 is (0,1,1) and a
   bare h3 is (0,0,1), so source order never comes into it. Two classes plus the element does
   it. Never rely on a tie here - a caching layer that recombines stylesheets can reorder them,
   and then the fix silently reverses. */
.band--deep .card h1, .band--deep .card h2, .band--deep .card h3, .band--deep .card h4,
.band--deep .formcard h1, .band--deep .formcard h2, .band--deep .formcard h3,
.ctaband .card h2, .ctaband .card h3 { color: var(--ink); }

.band--deep .card p, .band--deep .formcard p, .ctaband .card p { color: var(--ink-2); }

.band--deep .card .tlink, .band--deep .formcard .tlink, .ctaband .card .tlink { color: var(--tan-ink); }
.band--deep .card .tlink:hover, .band--deep .formcard .tlink:hover { color: var(--ink); }

.band--deep .card .eyebrow, .band--deep .formcard .eyebrow { color: var(--tan-ink); }

.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* ---- header -------------------------------------------------------------- */
.hdr {
  position: sticky; top: 0; z-index: 90;
  background: var(--ink-deep);
  border-bottom: 1px solid rgba(255,255,255,.07);
}
.hdr__in {
  display: flex; align-items: center; gap: 1.25rem;
  min-height: 78px; padding-block: .6rem;
}
.hdr__logo { flex: 0 0 auto; line-height: 0; }
.hdr__logo img { width: 168px; height: auto; }
@media (max-width: 480px) { .hdr__logo img { width: 132px; } }

.nav { margin-left: auto; display: flex; align-items: center; gap: .35rem; }
.nav a {
  display: block; padding: .6rem .8rem; border-radius: var(--r-sm);
  color: #d8cfc5; text-decoration: none; font-size: .95rem; font-weight: 500;
  white-space: nowrap;
}
.nav a:hover { color: #fff; background: rgba(255,255,255,.07); }
.nav a[aria-current="page"] { color: #fff; box-shadow: inset 0 -2px 0 var(--tan); }

.hdr__cta { display: flex; align-items: center; gap: .6rem; }
.hdr__phone {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .68rem 1.15rem; border-radius: 999px;
  background: var(--tan); color: var(--btn-ink); text-decoration: none;
  font-weight: 700; font-size: .95rem; white-space: nowrap;
}
.hdr__phone:hover { background: var(--tan-hi); }
.hdr__phone svg { width: 17px; height: 17px; }

.burger {
  display: none; margin-left: auto;
  width: 46px; height: 46px; border: 1px solid rgba(255,255,255,.22);
  border-radius: var(--r-sm); background: transparent; cursor: pointer;
  align-items: center; justify-content: center;
}
.burger span { display: block; width: 20px; height: 2px; background: #f0e9e1; position: relative; }
.burger span::before, .burger span::after {
  content: ''; position: absolute; left: 0; width: 20px; height: 2px; background: #f0e9e1;
}
.burger span::before { top: -6px; }
.burger span::after  { top: 6px; }

@media (max-width: 900px) {
  .nav, .hdr__cta .hdr__phone--label { display: none; }
  .burger { display: flex; }
  .hdr__cta { margin-left: auto; }
  .hdr__in { gap: .75rem; }
}
@media (max-width: 560px) {
  .hdr__phone { padding: .6rem .8rem; }
  .hdr__phone span { display: none; }
}

/* Mobile menu. Painted --ink-deep like the header, so the logo inside it also sits on its
   own colour. Closed state is genuinely inert - visibility + pointer-events, not opacity
   alone, so the links leave the tab order instead of being invisible and still focusable. */
/* The visibility transition is asymmetric on purpose.
   Closing:  visibility flips to hidden AFTER the fade, so the panel does not vanish mid-fade.
   Opening:  it flips IMMEDIATELY (0s delay), because .focus() on an element that is still
             computed visibility:hidden is silently refused by the browser - which left the
             opened menu with focus stranded on <body>, so the first Tab went to the page
             behind the overlay instead of into the menu. Transitioning visibility symmetrically
             is the usual way to write this and it is what caused the bug. */
.mnu {
  position: fixed; inset: 0; z-index: 120; background: var(--ink-deep);
  display: flex; flex-direction: column;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .22s ease, visibility 0s linear .22s;
}
.mnu[data-open] {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity .22s ease, visibility 0s linear 0s;
}
.mnu__top { display: flex; align-items: center; min-height: 78px; padding-block: .6rem; }
.mnu__close {
  margin-left: auto; width: 46px; height: 46px; border-radius: var(--r-sm);
  border: 1px solid rgba(255,255,255,.22); background: transparent; color: #f0e9e1;
  font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.mnu__list { padding-block: 1rem 2rem; display: grid; gap: .25rem; }
.mnu__list a {
  display: block; padding: .95rem .25rem; color: #ece4da; text-decoration: none;
  font-family: var(--serif); font-size: 1.6rem; border-bottom: 1px solid rgba(255,255,255,.09);
}
.mnu__list a[aria-current="page"] { color: #e8a64f; }
.mnu__foot { margin-top: auto; padding-block: 1.5rem 2.5rem; display: grid; gap: .75rem; }

/* ---- buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  padding: .95rem 1.6rem; border-radius: 999px; border: 1px solid transparent;
  background: var(--tan); color: var(--btn-ink);
  font-family: var(--sans); font-weight: 700; font-size: 1rem; line-height: 1.2;
  text-decoration: none; cursor: pointer;
  transition: transform .15s ease, background .15s ease, box-shadow .15s ease;
}
.btn:hover { background: var(--tan-hi); transform: translateY(-1px); box-shadow: var(--shadow-1); }
.btn:active { transform: translateY(0); }
.btn svg { width: 18px; height: 18px; }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line-2); }
.btn--ghost:hover { background: var(--paper-3); border-color: var(--ink-2); }

/* Every dark surface must be listed here. .ctaband was missing on the first pass and its ghost
   button rendered --ink on --ink-deep: dark text on a dark pill, effectively invisible while the
   outline still looked correct. build/check-live.mjs now measures this rather than trusting the
   selector list. */
.on-dark .btn--ghost, .band--deep .btn--ghost, .hero .btn--ghost, .ctaband .btn--ghost {
  color: #f5efe7; border-color: rgba(255,255,255,.42); background: rgba(255,255,255,.06);
}
.on-dark .btn--ghost:hover, .band--deep .btn--ghost:hover, .hero .btn--ghost:hover,
.ctaband .btn--ghost:hover {
  background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.7);
}

.btns { display: flex; flex-wrap: wrap; gap: .8rem; }

.tlink {
  display: inline-flex; align-items: center; gap: .45rem;
  color: var(--tan-ink); font-weight: 600; text-decoration: none;
}
.tlink::after { content: '\2192'; transition: transform .15s ease; }
.tlink:hover { color: var(--ink); }
.tlink:hover::after { transform: translateX(3px); }
.on-dark .tlink, .band--deep .tlink, .ctaband .tlink { color: #e8a64f; }
.on-dark .tlink:hover, .band--deep .tlink:hover, .ctaband .tlink:hover { color: #fff; }

/* ---- hero ---------------------------------------------------------------- */
.hero { position: relative; background: var(--ink-deep); overflow: hidden; isolation: isolate; }
.hero__media { position: absolute; inset: 0; z-index: -2; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: center 58%; }
/* Two scrims, not one. The left-weighted linear gradient buys text contrast where the copy
   sits; the bottom gradient stops the photograph colliding with the band below it. */
.hero::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(100deg, rgba(24,20,18,.93) 0%, rgba(24,20,18,.82) 34%,
                            rgba(24,20,18,.45) 62%, rgba(24,20,18,.28) 100%),
    linear-gradient(to top, rgba(24,20,18,.55) 0%, rgba(24,20,18,0) 42%);
}
.hero__in { padding-block: clamp(4.5rem, 12vw, 8.5rem); position: relative; }
.hero__copy { max-width: 40ch; }
.hero h1 { color: #fdfaf5; text-wrap: balance; }
.hero .lede { color: #cfc4b8; margin-top: 1.35rem; }
.hero .btns { margin-top: 2.1rem; }

.hero--sub .hero__in { padding-block: clamp(3.25rem, 8vw, 5.5rem); }

/* Trust strip sitting on the hero's bottom edge. */
.trust {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 1px; background: rgba(255,255,255,.14);
  border-top: 1px solid rgba(255,255,255,.14);
}
.trust__i { background: var(--ink-deep); padding: 1.5rem var(--gut); }
@media (max-width: 560px) { .trust__i { padding: 1.15rem var(--gut); } }
/* clamp, not a fixed size: at 390px the strip is two columns of ~155px of content and a fixed
   1.85rem broke "Guaranteed" across two lines as "Guarantee / d". overflow-wrap is reset to
   normal because the body sets break-word globally, which is right for prose and wrong for a
   single display word. */
.trust__n {
  font-family: var(--serif); font-size: clamp(1.35rem, 4.4vw, 1.85rem); line-height: 1.05;
  color: #f6b64a; display: block; margin-bottom: .35rem;
  overflow-wrap: normal; word-break: keep-all; hyphens: none;
}
.trust__l { font-size: .88rem; color: #b8ada1; line-height: 1.4; }

/* ---- breadcrumb ---------------------------------------------------------- */
.crumb { font-size: .87rem; color: #b3a89c; margin-bottom: 1.1rem; }
.crumb ol { display: flex; flex-wrap: wrap; gap: .4rem; }
.crumb li::after { content: '/'; margin-left: .4rem; opacity: .5; }
.crumb li:last-child::after { content: none; }
.crumb a { color: #d9cfc4; text-decoration: none; }
.crumb a:hover { color: #fff; text-decoration: underline; }
.crumb [aria-current] { color: #f6b64a; }

/* ---- section heading ----------------------------------------------------- */
.shead { max-width: 62ch; margin-bottom: clamp(2rem, 4vw, 3.25rem); }
.shead .lede { margin-top: 1rem; }
.shead--center { margin-inline: auto; text-align: center; }

/* ---- service cards ------------------------------------------------------- */
.cards { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); grid-template-columns: repeat(auto-fit, minmax(275px, 1fr)); }
.card {
  display: flex; flex-direction: column; background: #fff;
  border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden;
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); border-color: var(--line-2); }
.card__img { aspect-ratio: 4 / 3; overflow: hidden; background: var(--paper-3); }
.card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.card:hover .card__img img { transform: scale(1.035); }
.card__body { padding: 1.5rem 1.5rem 1.7rem; display: flex; flex-direction: column; flex: 1; }
.card__body h3 { margin-bottom: .6rem; }
.card__body p { color: var(--ink-2); font-size: .97rem; }
.card__body .tlink { margin-top: 1.1rem; }
.card__link { position: absolute; inset: 0; }

/* ---- split (copy + image) ------------------------------------------------ */
.split {
  display: grid; gap: clamp(2rem, 5vw, 4.5rem); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1.05fr .95fr; }
  .split--flip .split__media { order: -1; }
}
.split__media { position: relative; }
.split__media img {
  width: 100%; border-radius: var(--r-lg); box-shadow: var(--shadow-2);
}
/* Portrait job photos are 1000x1773 - nearly 1:1.8. Left alone they tower over the copy
   column and the section becomes three screens tall on a laptop. Cap the frame and crop. */
.split__media--tall img { aspect-ratio: 4 / 5; object-fit: cover; }
.split__media figcaption { margin-top: .8rem; font-size: .85rem; color: var(--ink-2); }

/* Little tan rule that ties the media to the palette without another photograph. */
.split__media::after {
  content: ''; position: absolute; left: -14px; bottom: -14px; width: 96px; height: 96px;
  border-left: 3px solid var(--tan); border-bottom: 3px solid var(--tan);
  border-radius: 0 0 0 var(--r-lg); z-index: -1;
}

/* ---- checklist ----------------------------------------------------------- */
.checks { display: grid; gap: .8rem; }
.checks li { position: relative; padding-left: 2.1rem; color: var(--ink-2); }
.checks li::before {
  content: ''; position: absolute; left: 0; top: .42em; width: 1.2rem; height: .62rem;
  border-left: 2.5px solid var(--tan); border-bottom: 2.5px solid var(--tan);
  transform: rotate(-45deg);
}
.band--deep .checks li { color: #c8bdb1; }

/* ---- process steps ------------------------------------------------------- */
/* Capped at THREE columns rather than left to auto-fit. A six-step sequence in a four-column
   grid resolves to 4 + 2, which reads as a layout that ran out of items; 3 + 3 reads as two
   halves of a process. The three-step version fills one row either way. */
.steps { counter-reset: s; display: grid; gap: clamp(1.5rem, 3vw, 2.25rem);
         grid-template-columns: 1fr; }
@media (min-width: 620px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 940px) { .steps { grid-template-columns: repeat(3, 1fr); } }
.step { counter-increment: s; position: relative; padding-top: 3.4rem; }
.step::before {
  content: counter(s, decimal-leading-zero);
  position: absolute; top: 0; left: 0;
  font-family: var(--serif); font-size: 2.1rem; color: var(--tan);
  line-height: 1;
}
.step::after {
  content: ''; position: absolute; top: 2.9rem; left: 0; width: 2.2rem; height: 2px;
  background: var(--line-2);
}
.step h3 { font-size: 1.15rem; margin-bottom: .5rem; }
.step p { font-size: .95rem; color: var(--ink-2); }

/* ---- FAQ ----------------------------------------------------------------
   Native <details>/<summary>. No JavaScript, so it works before app.js loads and for anyone
   who never gets it, and the answers are in the HTML a crawler reads whether or not the panel
   is open - which is the whole point of putting them on the page.

   The marker is drawn with a pseudo-element rather than left to the browser: the default
   triangle sits at a different size and baseline in every engine, and it cannot be given the
   accent colour. `list-style: none` plus the WebKit pseudo-element removes both defaults. */
.faq { display: grid; gap: 0; border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.15rem 0; cursor: pointer; list-style: none;
  font-family: var(--serif); font-size: clamp(1.05rem, 1.6vw, 1.2rem); line-height: 1.35;
  color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ''; flex: 0 0 auto; margin-left: auto; margin-top: .34em;
  width: .62rem; height: .62rem;
  border-right: 2px solid var(--tan); border-bottom: 2px solid var(--tan);
  transform: rotate(45deg); transition: transform .2s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--tan-ink); }
.faq .faq__a { padding: 0 0 1.3rem; max-width: 68ch; }
.faq .faq__a p { color: var(--ink-2); font-size: .99rem; }
.faq .faq__a p + p { margin-top: .8rem; }
.faq .faq__a a { color: var(--tan-ink); text-decoration: underline; text-underline-offset: .18em; }

/* ---- comparison panels --------------------------------------------------
   Two coating systems side by side. Not a table: a table of two columns and five rows reads
   as data, and this is prose that happens to be parallel. */
.compare { display: grid; gap: clamp(1.25rem, 2.5vw, 2rem); grid-template-columns: 1fr; }
@media (min-width: 820px) { .compare { grid-template-columns: 1fr 1fr; } }
.compare__i {
  background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: clamp(1.4rem, 3vw, 2rem);
}
.compare__i .compare__k {
  font-size: .76rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--tan-ink); display: block; margin-bottom: .7rem;
}
.compare__i h3 { margin-bottom: .8rem; }
.compare__i p { color: var(--ink-2); font-size: .98rem; }
.compare__i .checks { margin-top: 1.1rem; }
.compare__i .checks li { font-size: .95rem; }
.band--paper2 .compare__i { border-color: var(--line-2); }

/* ---- gallery ------------------------------------------------------------- */
.gal { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); }
/* The home page teaser is a fixed SIX across, not auto-fill. auto-fill at 1180px resolves to
   five columns, which left the sixth tile stranded alone on a second row and read as a
   mistake. A strip wants an exact column count; only the full gallery wants to reflow. */
.gal--strip { grid-template-columns: repeat(6, 1fr); }
@media (max-width: 900px) { .gal--strip { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 520px) { .gal--strip { grid-template-columns: repeat(2, 1fr); } }
.gal__b {
  padding: 0; border: 0; background: var(--paper-3); cursor: zoom-in;
  border-radius: var(--r); overflow: hidden; position: relative; display: block;
  aspect-ratio: 1 / 1;
}
.gal__b img { width: 100%; height: 100%; object-fit: cover; transition: transform .45s ease, filter .3s ease; }
.gal__b:hover img { transform: scale(1.05); filter: brightness(1.04); }
.gal__b::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(24,20,18,.35), rgba(24,20,18,0) 45%);
  opacity: 0; transition: opacity .3s ease;
}
.gal__b:hover::after { opacity: 1; }
/* A few tiles run tall so the grid does not read as a spreadsheet. */
.gal__b--tall { aspect-ratio: 1 / 1.4; grid-row: span 2; }
@media (max-width: 620px) { .gal__b--tall { aspect-ratio: 1 / 1; grid-row: auto; } }

/* Before / after pair. The source site had these as four loose blocks - image, "BEFORE",
   image, "AFTER" - which is exactly the shape that loses the second image in a naive
   template. Here the pair is one component and the labels belong to the pictures. */
.ba { display: grid; gap: 12px; grid-template-columns: 1fr 1fr; }
.ba figure { margin: 0; position: relative; border-radius: var(--r); overflow: hidden; }
.ba img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; }
.ba figcaption {
  position: absolute; top: .7rem; left: .7rem;
  background: rgba(24,20,18,.86); color: #f6f1ea;
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  padding: .35rem .7rem; border-radius: 999px;
}
.ba figure:last-child figcaption { background: var(--tan); color: var(--btn-ink); }

/* ---- lightbox -----------------------------------------------------------
   WordPress had one; a static rebuild does not unless it is written. Thumbnails are
   <button> so they are keyboard reachable, arrows/Escape work, focus returns to the opener
   and the sticky header is hidden while it is open. */
.lb {
  position: fixed; inset: 0; z-index: 200; display: none;
  background: rgba(16,13,12,.94);
  align-items: center; justify-content: center; padding: clamp(1rem, 4vw, 3rem);
}
.lb[data-open] { display: flex; }
body.lb-open { overflow: hidden; }
body.lb-open .hdr { visibility: hidden; }
.lb__fig { margin: 0; max-width: 100%; max-height: 100%; display: flex; flex-direction: column; gap: .9rem; }
.lb__fig img {
  max-width: 100%; max-height: calc(100vh - 11rem); width: auto; margin-inline: auto;
  border-radius: var(--r); object-fit: contain;
}
.lb__cap { color: #cdc3b8; font-size: .92rem; text-align: center; max-width: 62ch; margin-inline: auto; }
.lb__n { color: #8d8279; font-size: .82rem; }
.lb__btn {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 52px; height: 52px; border-radius: 999px; cursor: pointer;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.24); color: #fff;
  font-size: 1.4rem; line-height: 1; display: grid; place-items: center;
}
.lb__btn:hover { background: rgba(255,255,255,.2); }
.lb__prev { left: clamp(.5rem, 2vw, 1.5rem); }
.lb__next { right: clamp(.5rem, 2vw, 1.5rem); }
.lb__x {
  position: absolute; top: clamp(.75rem, 2vw, 1.5rem); right: clamp(.75rem, 2vw, 1.5rem);
  width: 46px; height: 46px; border-radius: 999px; cursor: pointer;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.24); color: #fff;
  font-size: 1.35rem; line-height: 1;
}
.lb__x:hover { background: rgba(255,255,255,.2); }
@media (max-width: 640px) { .lb__btn { width: 42px; height: 42px; } }

/* ---- form ---------------------------------------------------------------
   Every text element is styled at two or more classes and nothing uses color:inherit, so a
   dark section can never bleed its text colour into the card. */
.formcard {
  background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: clamp(1.5rem, 3.5vw, 2.5rem); box-shadow: var(--shadow-1);
}
.formcard .formcard__title { font-size: clamp(1.5rem, 2.6vw, 2rem); color: var(--ink); margin-bottom: .5rem; }
.formcard .formcard__sub { color: var(--ink-2); font-size: .97rem; margin-bottom: 1.6rem; }

.f-grid { display: grid; gap: 1.05rem; grid-template-columns: 1fr 1fr; }
.f-grid .f-row--full { grid-column: 1 / -1; }
@media (max-width: 620px) { .f-grid { grid-template-columns: 1fr; } }

.f-row { display: grid; gap: .4rem; }
.f-row .f-label { font-size: .87rem; font-weight: 600; color: var(--ink); }
.f-row .f-label .f-req { color: var(--tan-ink); }
.f-row .f-label .f-opt {
  font-weight: 400; font-size: .8rem; color: var(--ink-2);
  text-transform: lowercase; letter-spacing: .01em;
}
.f-row .f-input, .f-row .f-textarea {
  width: 100%; padding: .8rem .95rem;
  font-family: var(--sans); font-size: 1rem; line-height: 1.45;
  color: var(--ink); background: #fdfcfa;
  -webkit-text-fill-color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.f-row .f-input:focus, .f-row .f-textarea:focus {
  outline: none; border-color: var(--tan); box-shadow: 0 0 0 3px rgba(180,118,47,.2);
}
.f-row .f-textarea { min-height: 128px; resize: vertical; }
.f-row .f-input::placeholder, .f-row .f-textarea::placeholder { color: #9b9188; opacity: 1; }

/* Honeypot. Not type=hidden and not display:none - capable bots skip both. Positioned off
   canvas, removed from the tab order and hidden from assistive tech. */
.f-hp {
  position: absolute !important; left: -9999px; top: 0;
  width: 1px; height: 1px; overflow: hidden;
}

.formcard .f-note { font-size: .82rem; color: var(--ink-2); margin-top: 1rem; }
.formcard .f-msg { margin-top: 1rem; padding: .85rem 1rem; border-radius: var(--r-sm); font-size: .93rem; }
.formcard .f-msg--err { background: #fdeceb; color: #8a2418; border: 1px solid #f3c9c4; }
.formcard .f-msg--ok  { background: #eaf5ec; color: #1d5c30; border: 1px solid #c3e2ca; }
.formcard .f-submit { margin-top: 1.4rem; width: 100%; }
.formcard .f-submit[disabled] { opacity: .6; cursor: progress; }

.contact-split { display: grid; gap: clamp(2rem, 4vw, 3.5rem); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 940px) { .contact-split { grid-template-columns: .85fr 1.15fr; } }

.cinfo { display: grid; gap: 1.6rem; }
.cinfo__item { display: grid; gap: .3rem; }
.cinfo__k { font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--tan-ink); }
.cinfo__v { font-size: 1.12rem; color: var(--ink); }
.cinfo__v a { color: var(--ink); text-decoration: none; }
.cinfo__v a:hover { color: var(--tan-ink); text-decoration: underline; }
.band--deep .cinfo__k { color: #e0b476; }
.band--deep .cinfo__v, .band--deep .cinfo__v a { color: #f3ece3; }

/* ---- areas --------------------------------------------------------------- */
.areas { display: flex; flex-wrap: wrap; gap: .55rem; }
.areas li {
  padding: .5rem 1rem; border-radius: 999px; font-size: .92rem;
  background: var(--paper-2); border: 1px solid var(--line); color: var(--ink-2);
}
.band--deep .areas li { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); color: #cec4b8; }

/* ---- CTA band ------------------------------------------------------------ */
.ctaband { background: var(--ink-deep); color: #e5ddd3; position: relative; overflow: hidden; }
.ctaband__in {
  padding-block: clamp(3rem, 6vw, 4.75rem);
  display: grid; gap: 1.75rem; align-items: center;
}
@media (min-width: 880px) { .ctaband__in { grid-template-columns: 1.35fr auto; gap: 2.5rem; } }
.ctaband h2 { color: #fdfaf5; }
.ctaband p { color: #b9aea2; margin-top: .85rem; max-width: 56ch; }

/* ---- footer -------------------------------------------------------------- */
.ftr { background: var(--ink-deep); color: #a99e93; padding-block: clamp(3rem, 5vw, 4.25rem) 0; }
.ftr__grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 760px) { .ftr__grid { grid-template-columns: 1.4fr 1fr 1fr; gap: 3rem; } }
.ftr__logo img { width: 168px; margin-bottom: 1.1rem; }
.ftr p { color: #a99e93; font-size: .95rem; }
.ftr h2 { font-family: var(--sans); font-size: .8rem; font-weight: 700; letter-spacing: .14em;
          text-transform: uppercase; color: #e8dfd4; margin-bottom: 1rem; }
.ftr ul { display: grid; gap: .55rem; }
.ftr a { color: #bdb2a6; text-decoration: none; font-size: .95rem; }
.ftr a:hover { color: #fff; text-decoration: underline; }
.ftr__phone { font-family: var(--serif); font-size: 1.5rem; color: #f6b64a !important; }
.ftr__bottom {
  margin-top: clamp(2.5rem, 4vw, 3.5rem); padding-block: 1.5rem;
  border-top: 1px solid rgba(255,255,255,.1);
  display: flex; flex-wrap: wrap; gap: .6rem 1.5rem; justify-content: space-between;
  font-size: .87rem;
}
.ftr__credit { color: #a99e93; text-decoration: none; }
.ftr__credit:hover { color: #fff; text-decoration: underline; }

/* ---- floating contact bar -----------------------------------------------
   Fleet component. Icons take --ink on the tan fill, not white: white on this tan measures
   2.6:1, under the 3:1 WCAG 1.4.11 wants for a UI component. The dark ink measures 7.4:1.
   Collapsed state is visibility+pointer-events so the buttons genuinely leave the tab order. */
.floatbar { position: fixed; right: 16px; bottom: 16px; z-index: 110; display: grid; gap: 10px; justify-items: center; }
.floatbar__btn, .floatbar__toggle {
  width: 52px; height: 52px; border-radius: 999px; display: grid; place-items: center;
  background: var(--tan); color: var(--btn-ink); border: 0; cursor: pointer; text-decoration: none;
  box-shadow: var(--shadow-2);
  transition: transform .15s ease, background .15s ease;
}
.floatbar__btn svg, .floatbar__toggle svg { width: 23px; height: 23px; fill: currentColor; }
.floatbar__btn { opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(8px) scale(.85); }
.floatbar[data-open] .floatbar__btn { opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
/* Restated AFTER the [data-open] rule above: an equally specific transform:none written later
   in the file silently cancels a hover scale. That bug survived weeks in the fleet reference. */
.floatbar[data-open] .floatbar__btn:hover { transform: scale(1.08); background: var(--tan-hi); }
.floatbar__toggle:hover { background: var(--tan-hi); }
.floatbar[data-open] .floatbar__toggle { background: var(--ink-deep); color: #f3ece3; }
@media (max-width: 560px) { .floatbar { right: 12px; bottom: 12px; } }

.fbpop {
  position: fixed; inset: 0; z-index: 130; display: none; place-items: center; padding: 1.5rem;
  background: rgba(16,13,12,.6);
}
.fbpop[data-open] { display: grid; }
.fbpop__card {
  background: #fff; border-radius: var(--r-lg); padding: 2rem 2.25rem; text-align: center;
  position: relative; box-shadow: var(--shadow-2); max-width: 380px; width: 100%;
}
.fbpop__card .fbpop__t { font-size: .8rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--tan-ink); margin-bottom: .6rem; }
.fbpop__card .fbpop__num { font-family: var(--serif); font-size: 2rem; color: var(--ink); text-decoration: none; }
.fbpop__card .fbpop__num:hover { color: var(--tan-ink); }
.fbpop__x { position: absolute; top: .6rem; right: .6rem; width: 36px; height: 36px;
  border-radius: 999px; border: 0; background: var(--paper-2); color: var(--ink); cursor: pointer; }

/* ---- scroll reveal ------------------------------------------------------
   threshold 0 with rootMargin only. A ratio threshold never fires for an element taller
   than the viewport, and the copy column on a service page is well over 4000px - the whole
   body would sit at opacity 0 until the visitor scrolled. Scoped to .js so no-JS visitors
   and crawlers always see everything. */
.js [data-rv] { opacity: 0; transform: translateY(14px); }
.js [data-rv].is-in { opacity: 1; transform: none; transition: opacity .6s ease, transform .6s ease; }
@media (prefers-reduced-motion: reduce) {
  .js [data-rv] { opacity: 1; transform: none; }
  .js [data-rv].is-in { transition: none; }
}

/* ---- print --------------------------------------------------------------- */
@media print {
  .hdr, .floatbar, .lb, .ctaband, .formcard, .burger { display: none !important; }
  body { background: #fff; color: #000; }
}
