/* ==========================================================================
   Tri State Hydrovac — static rebuild
   Built to the measured geometry in capture-tristatehydrovac/geometry.md.
   Deviations from the original are limited to the defect list (findings.md
   list A) and the four layout fixes approved on 2026-07-21 (list B).
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------
   Self-hosted, latin subset, variable. Replaces three Google Fonts families
   (Lexend, Roboto, Open Sans) with two files. Open Sans was Divi's inherited
   default, not a brand choice — dropped per B6.                            */

@font-face {
  font-family: 'Lexend';
  src: url('../fonts/lexend-latin-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Roboto';
  src: url('../fonts/roboto-latin-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens -------------------------------------------------------------- */

:root {
  /* Brand, measured from the original */
  --orange: #ba3f1d;
  --orange-dark: #993316;        /* hover only; 7.0:1 on white */
  --blue: #1e73be;               /* the skewed band under the services block */
  --blue-underline: #5b85ae;     /* measured from span.underline-effect */
  /* The original's mobile menu panel and burger icon are a brighter red than
     the brand orange — measured #e02b20, not #ba3f1d. White on it is 4.63:1,
     which clears AA for the 16px links. */
  --menu-red: #e02b20;

  --ink: #333333;                /* headings */
  --body: #666666;               /* body copy */
  --black: #000000;
  --white: #ffffff;
  --tint: #f4f4f4;               /* FAQ closed-toggle tile */
  --line: #e4e4e4;
  --label: #344054;              /* form labels, measured */
  --field-ink: #1d2939;          /* form input text, measured */

  /* Contrast deviation, documented per visual-design-standards.md §6:
     original footer text #a9a9a9 = 2.35:1 on white — fails AA badly.
     #707070 = 4.95:1. Minimum change that clears the 4.8 target.          */
  --footer-text: #707070;

  /* Layout — B1. The original ran six different content widths down one page
     (1344 / 1037 / 1920 / 1080 / 1536 / 1080). One measure now, taken from
     the header row, which was already 1344 at 1920. Full-bleed bands stay
     full-bleed; only their inner content aligns.                           */
  --wrap-max: 1344px;
  --gutter: clamp(20px, 4vw, 48px);
  --wrap: min(var(--wrap-max), 100% - (2 * var(--gutter)));

  /* --wrap contains a percentage, so it re-resolves against whatever element
     uses it. Inside a 420px grid track it evaluated to 324px and a "bleed to
     the viewport edge" margin came out at -798px instead of -288px, which
     threw the FAQ photo almost entirely off-screen. --bleed is the same
     measurement expressed only in vw, so it is the same number everywhere.
     Anything breaking out of .wrap must use this, never --wrap. */
  --bleed: calc((100vw - min(var(--wrap-max), 100vw - (2 * var(--gutter)))) / 2);

  /* Type scale — measured steps, plus the missing one between 25 and 64 (B6).
     Body floor raised 14 -> 16 (A5: below the iOS auto-zoom threshold).    */
  --fs-display: clamp(2.5rem, 1.15rem + 3.3vw, 4rem);      /* 40 -> 64, both measured */
  --fs-h2:      clamp(2.125rem, 0.95rem + 3.1vw, 4rem);    /* 34 -> 64 */
  --fs-h3:      clamp(1.5rem, 1.28rem + 0.6vw, 1.75rem);   /* 24 -> 28  <- added step */
  --fs-eyebrow: clamp(1.125rem, 0.9rem + 0.6vw, 1.5625rem);/* 18 -> 25 */
  --fs-lede:    clamp(1.0625rem, 1rem + 0.28vw, 1.25rem);  /* 17 -> 20 */
  --fs-body:    1rem;                                       /* 16 */
  --fs-sm:      0.9375rem;                                  /* 15 */

  --font-display: 'Lexend', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --section-pad: 54px;
  --header-h: 76px;
  --radius: 3px;
}

/* --- 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; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.6;
  color: var(--body);
  background: var(--white);
  overflow-x: hidden;
}

/* height:auto is not optional alongside max-width. Every <img> carries width
   and height attributes (they reserve space and keep CLS at 0), and without
   height:auto those attributes stay authoritative once max-width shrinks the
   width — which squashed the contact photo to 240x364 at 360px, a 50%
   distortion, while its natural ratio is 636x486. */
img, picture, svg { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4, h5, h6, p, ul, ol, figure { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; }

/* Visible focus everywhere — the original had none (A19). */
:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--orange); color: var(--white);
  padding: 12px 20px; font-weight: 700; text-decoration: none;
}
.skip-link:focus { left: 0; }

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

/* --- Layout primitives --------------------------------------------------- */

.wrap { width: var(--wrap); margin-inline: auto; }

/* Explicit white rather than transparent — the original set it explicitly and
   the geometry diff reports the difference even though it renders the same. */
.section, .faq, .contact, .thanks, .notfound, .site-footer { background: var(--white); }
.section { padding-block: var(--section-pad); position: relative; }

/* Short rule above section headings. Measured 141x5 / 184x5 / 102x5 — the
   original varied its width per instance; reproduced at the modal 184x5. */
.rule {
  width: 184px; height: 5px; background: var(--orange);
  border: 0; margin: 0 0 28px;
}
.rule--center { margin-inline: auto; }
.rule--light { background: var(--white); }
/* The original varied the rule width per instance: 141 / 184 / 102. */
.rule--141 { width: 141px; }
.rule--102 { width: 102px; }

/* Small utilities so no element needs a style attribute — that is what lets
   the Content-Security-Policy ship without 'unsafe-inline' for styles. */
.pb-0 { padding-bottom: 0; }
.mt-24 { margin-top: 24px; }
.mb-20-auto { margin: 0 auto 20px; }

/* --- Type ---------------------------------------------------------------- */

h1, h2 { font-family: var(--font-display); color: var(--ink); line-height: 1.05; }
h1 { font-size: var(--fs-display); font-weight: 600; text-wrap: balance; }
h2 { font-size: var(--fs-h2); font-weight: 700; text-wrap: balance; }
h3 { font-family: var(--font-body); font-size: 1.5rem; font-weight: 700; color: var(--ink); line-height: 1.25; }

.eyebrow {
  font-size: var(--fs-eyebrow); font-weight: 500; line-height: 1.2;
  color: var(--white); margin-bottom: 14px;
}
/* Measured as Open Sans 18/500 #000. Open Sans is dropped (B6) and Roboto 500
   sits noticeably heavier at the same size, so this is Roboto 400 — the
   nearest match in the substituted family, not a restyle. */
.lede { font-size: var(--fs-lede); font-weight: 400; line-height: 1.8; color: var(--black); }

/* --- Buttons -------------------------------------------------------------
   One component. The original shipped two visually identical "GET FREE QUOTE"
   buttons with different border-radius (3px vs 0px) — A12.                 */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 51px; padding: 10px 20px;   /* measured on the original: 181x51 */
  font-family: var(--font-body); font-size: 1.125rem; font-weight: 500;
  line-height: 1.2; text-decoration: none; text-align: center;
  color: var(--white); background: var(--orange);
  border: 0; border-radius: var(--radius); cursor: pointer;
  transition: background-color .18s ease, transform .18s ease;
}
.btn:hover { background: var(--orange-dark); }
.btn:active { transform: translateY(1px); }

.btn--sm { min-height: 44px; padding: 10px 20px; font-size: var(--fs-body); }

/* --- Header --------------------------------------------------------------
   Measured: 76px tall, row 1344 wide, white ground.                        */

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: var(--white);
  transition: box-shadow .2s ease;
}
.site-header.is-stuck { box-shadow: 0 2px 14px rgba(0, 0, 0, .10); }

.site-header__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; min-height: var(--header-h);
}

.site-logo { display: flex; align-items: center; flex: 0 0 auto; }
.site-logo img { width: 159px; height: 50px; }

/* Measured against the original: 44px between items, links padded 31px 0 so
   they fill the full 76px header height, CTA 119x52 with square corners. */
.site-nav { display: flex; align-items: center; gap: 44px; }
/* Scoped away from .btn so the nav's link styling cannot capture the CTA —
   the specificity collision called out in platform-traps.md.              */
.site-nav a:not(.btn) {
  font-size: var(--fs-body); font-weight: 500; color: var(--black);
  text-decoration: none; position: relative;
  /* 14 + 31 + 31 = 76, the original's exact header height. Inheriting the
     body's 1.6 line-height instead made each link 88px and pushed the whole
     header to 88. */
  line-height: 14px; padding: 31px 0;
}
.site-nav a:not(.btn)::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 25px;
  height: 2px; background: var(--orange);
  transform: scaleX(0); transform-origin: left;
  transition: transform .2s ease;
}
.site-nav a:not(.btn):hover::after { transform: scaleX(1); }

/* The header CTA is its own variant: the original ran it 52px tall with a
   0px radius, not the 3px the hero button uses. Matching the header exactly
   was a direct instruction, so this one keeps the original's square corner. */
.site-nav .btn {
  min-height: 52px;
  border-radius: 0;
  padding: 10px 20px;
  font-size: var(--fs-body);
}

/* On desktop the wrappers dissolve so the three links stay direct flex
   children of .site-nav and keep the measured 44px gaps. Everything that
   belongs only to the mobile panel is hidden. */
.site-nav__links { display: contents; }
.site-nav__foot,
.site-nav__mark { display: none; }

.nav-toggle {
  display: none; width: 44px; height: 44px; padding: 0;
  background: none; border: 0; cursor: pointer;
  color: var(--menu-red);   /* the original's burger is red, not dark */
}
.nav-toggle svg { width: 26px; height: 26px; margin-inline: auto; stroke-width: 2.5; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded='true'] .icon-close { display: block; }
.nav-toggle[aria-expanded='true'] .icon-open { display: none; }

@media (max-width: 900px) {
  /* The original's mobile header measured 70px; ours had drifted to 76.
     The mobile standard suggests <=56, but the logo lockup is 159x50 and
     shrinking it would change a measured value — 70px matches the original
     and is the deliberate call. */
  :root { --header-h: 70px; }
  .site-header__inner { min-height: 70px; }
  .nav-toggle { display: block; }

  /* --- Mobile menu -------------------------------------------------------
     A full-height panel under the header rather than the Divi dropdown box.
     Built only from things the site already owns: brand orange, the Lexend
     display face, the asterisk mark that is already watermarked into three
     of the site's photos, and the same phone glyph used in the contact
     block. No new ornament, no new copy — the hours are the contact
     section's line verbatim.

     It sits below the sticky header (z-index 90 against the header's 100),
     so the logo and the close control stay put and the panel never covers
     them. 100dvh so an iOS toolbar cannot clip the bottom.               */
  .site-nav {
    position: fixed;
    top: var(--header-h); left: 0; right: 0;
    height: calc(100vh - var(--header-h));
    height: calc(100dvh - var(--header-h));
    z-index: 90;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: clamp(20px, 6vw, 34px) clamp(20px, 6vw, 32px) clamp(24px, 6vw, 34px);
    background: var(--orange);
    /* overflow-x must be stated. Setting only overflow-y makes overflow-x
       compute to auto, and the watermark's deliberate 78px bleed then made
       the open panel draggable sideways into empty orange. */
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* hidden but still laid out, so the open transition has something to
       animate from and the links can be measured */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    transition: opacity .26s ease, transform .26s ease, visibility 0s linear .26s;
  }
  .site-nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity .26s ease, transform .26s ease, visibility 0s;
  }

  .site-nav__links { display: flex; flex-direction: column; }

  /* Specificity matches the desktop rule so this actually wins. */
  .site-nav a:not(.btn),
  .site-nav .btn {
    position: relative;
    display: flex; align-items: center;
    justify-content: flex-start;   /* .btn centres its label; these are rows */
    font-family: var(--font-display);
    font-size: clamp(1.75rem, 7.5vw, 2.25rem);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--white);
    background: none;
    padding: clamp(15px, 4vw, 20px) 0;
    margin: 0;
    min-height: 0;
    border-radius: 0;
    border-bottom: 1px solid rgba(255, 255, 255, .22);
    text-align: left;
  }
  .site-nav a:not(.btn)::after,
  .site-nav .btn::after { display: none; }

  /* Staggered entrance. The reduced-motion rule at the top of this file
     shortens durations rather than setting animation:none, so `both` still
     applies the end state and nothing is left invisible. */
  .site-nav.is-open .site-nav__links a { animation: navItemIn .38s cubic-bezier(.2, .7, .3, 1) both; }
  .site-nav.is-open .site-nav__links a:nth-child(1) { animation-delay: .05s; }
  .site-nav.is-open .site-nav__links a:nth-child(2) { animation-delay: .10s; }
  .site-nav.is-open .site-nav__links a:nth-child(3) { animation-delay: .15s; }
  .site-nav.is-open .site-nav__foot { animation: navItemIn .38s cubic-bezier(.2, .7, .3, 1) .22s both; }

  /* Phone is the conversion action on a trade site, so it gets the weight:
     inverted button, full width, pinned to the bottom of the panel. */
  .site-nav__foot { display: block; margin-top: auto; padding-top: clamp(24px, 7vw, 34px); }

  /* a.nav-phone, not .nav-phone — it has to match the (0,2,1) of the
     a:not(.btn) rule above or that one keeps winning and the CTA renders as
     just another text row. */
  .site-nav a.nav-phone {
    display: flex; align-items: center; justify-content: center; gap: 12px;
    font-family: var(--font-body);
    font-size: 1.125rem; font-weight: 700; line-height: 1;
    letter-spacing: .01em;
    color: var(--orange);
    background: var(--white);
    border: 0;
    padding: 18px 20px;
    min-height: 56px;
    text-align: center;
  }
  .site-nav a.nav-phone svg { width: 22px; height: 22px; flex: 0 0 auto; }

  .nav-hours {
    margin-top: 14px;
    font-size: .9375rem;
    line-height: 1.75;
    color: var(--white);          /* solid, not faded — 5.5:1 on the orange */
    text-align: center;
  }

  /* The brand mark, oversized and bled off the corner. Same device the
     site already uses inside truck-graphics-3-1, truck-graphics-2 and
     truck-graphics. Filtered to white so it reads as a watermark. */
  .site-nav__mark {
    display: block;
    position: absolute;
    right: -20%;
    top: 42%;
    width: 62%;
    height: auto;
    opacity: .06;          /* a watermark, not a graphic — .08 at 72% read as a logo */
    filter: brightness(0) invert(1);
    pointer-events: none;
    z-index: -1;
  }
}

@keyframes navItemIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* Page behind the open menu must not scroll. */
html.nav-open, html.nav-open body { overflow: hidden; }

/* --- Hero ----------------------------------------------------------------
   Measured 1004px tall, background Hero-truck-hero-image.webp at 50% 100%,
   cover. The white wedge at the bottom is baked into the image's alpha.    */

/* The hero height is a fraction of the VIEWPORT, not a fixed pixel value —
   this is what made the image render larger than the original and pushed the
   white wedge below the fold on any window shorter than 1080.

   Measured on the original across widths and heights, exact to 0.1%:
     >=981px   93vh   (1004.4 @1080, 883.5 @950, 854.7 @919, 744 @800)
     768-980   50vh
     <=767     90vh
   Top padding 120px puts the scrim box top at y=196, where the original's is. */
.hero {
  position: relative;
  min-height: 93vh;
  display: flex; align-items: flex-start;
  padding-block: clamp(30px, 6.25vw, 120px) var(--section-pad);
  /* 900px variant below 900px viewport — 99KB against 257KB, and it is the
     LCP element, so this is the single biggest mobile win available. The white
     wedge along the bottom is baked into the alpha of both files. */
  background: var(--white) url('../img/Hero-truck-hero-image-900.webp') no-repeat 50% 100% / cover;
}
@media (min-width: 901px) {
  .hero { background-image: url('../img/Hero-truck-hero-image.webp'); }
}
/* Divi's own breakpoints, matching the original exactly. */
@media (max-width: 980px) { .hero { min-height: 50vh; } }
@media (max-width: 767px) { .hero { min-height: 90vh; } }

/* B8 reverted on instruction: the top of the page has to look identical to
   the original, and the original's scrim is a hard-edged rgba(0,0,0,.5) box
   on the row itself — 1037x329 at x=442, centred in the viewport, not a
   gradient. Measured contrast under it is 3.70:1 worst pixel, which clears
   the 3.0 large-text threshold for the 64px heading and the 25px eyebrow. */

.hero .wrap { position: relative; z-index: 1; }

/* The scrim box IS the content row, exactly as the original builds it:
   1037px wide, centred (x=442 at 1920 — not aligned to the 1344 measure),
   40px vertical padding, and everything inside centred. The eyebrow, heading
   and button sit flush against each other with no margins between them —
   35 + 164 + 51 + 80 padding = 330, against the original's 329.            */
.hero__content {
  width: min(1037px, 100%);
  margin-inline: auto;
  padding: 40px 0;
  background: rgba(0, 0, 0, .5);
  text-align: center;
}
.hero .eyebrow { line-height: 1.4; margin-bottom: 0; }   /* 35px at 25px */

/* Desktop measures 7.25:1 under the .5 scrim — comfortably clear. At 360 the
   photo behind the text is lighter and it drops to 4.25:1, and the eyebrow
   there is 18px/500, i.e. small text needing the full 4.5. .56 is the
   minimum that clears a ~4.8 target (measures 5.4:1). Mobile only, so the
   desktop view is byte-for-byte the original's .5.                         */
@media (max-width: 700px) {
  .hero__content { background: rgba(0, 0, 0, .56); }
}
/* 2 x 76.8 = 153.6, but the original's h1 box is 164 — Divi puts a 10px
   padding-bottom on headings, and that 10px is what sets the button's
   position and the scrim's total height. */
.hero h1 {
  color: var(--white); line-height: 1.2;
  margin-bottom: 0; padding-bottom: 10px;
}

/* The measured span.underline-effect: a #5b85ae bar drawn behind the words.
   The original set it 12.8px thick at 88% of the line box, which put it
   through the descenders. Thinner and lower, so it reads as an underline. */
/* Reproduced at the original's exact values: a #5b85ae bar 12.8px thick at
   88% of the line box. (An earlier pass thinned it to 9px at 96%; the brief
   is now to match the original.) */
.underline-effect {
  background-image: linear-gradient(120deg, var(--blue-underline) 0%, var(--blue-underline) 100%);
  background-repeat: no-repeat;
  background-size: 100% 12.8px;
  background-position: 0 88%;
  /* Without this the bar is drawn once across the whole inline box, so when
     the phrase wraps at 360 it strikes through the second line instead of
     underlining each fragment. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* --- About + Services ----------------------------------------------------
   One section in the original (2150px). Kept as one block so the geometry
   diff pairs correctly.                                                    */

/* The original ran the photo out to the viewport's right edge (x=1082..1920)
   at its natural 838x552, with the copy starting at x=110. Constraining both
   to a half-wrap column shrank the photo to ~640 and crushed the asterisk
   watermark baked into its alpha.

   So this row spans the viewport and pads its left edge to the measure: the
   copy starts on the same line as every other section (B1) and the photo
   keeps its bleed. */
.about {
  width: 100vw;
  margin-left: calc(-1 * var(--bleed));
  padding-left: var(--bleed);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(32px, 4.5vw, 80px);
  align-items: center;          /* B2 — closes the ~150x870 dead zone */
  padding-bottom: 54px;
}
/* margin-left:auto pins the image's right edge to the viewport edge. */
.about__media img { width: 100%; max-width: 838px; height: auto; margin-left: auto; }

@media (max-width: 900px) {
  .about {
    width: auto; margin-left: 0; padding-left: 0;
    grid-template-columns: minmax(0, 1fr);
  }
  .about__media img { margin-inline: auto; }
}

.services { padding-top: 54px; text-align: center; }
.services__intro { max-width: 1080px; margin: 0 auto 46px; }
.services h2 { margin-bottom: 22px; }

/* B4 — the original's cards went ragged the moment a heading wrapped to two
   lines: the description dropped with it and the row stopped lining up.

   Subgrid rather than a fixed min-height on the heading. A reserved two-line
   box wastes a blank line on every card at widths where nothing wraps;
   subgrid lets each row of cards agree on a media / heading / copy height
   from the actual content, so it is tight when nothing wraps and still
   aligned when something does. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px 60px;
  margin-bottom: 48px;
}
.card {
  grid-row: span 3;
  display: grid;
  grid-template-rows: subgrid;
  gap: 0;
  text-align: center;
}
.card__media {
  aspect-ratio: 352 / 309;
  width: 100%; margin-bottom: 22px; overflow: hidden;
}
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card h3 { margin-bottom: 10px; align-self: start; }
.card p { font-size: var(--fs-body); font-weight: 300; line-height: 1.7; color: var(--black); }

/* Older engines without subgrid fall back to a plain block card — still
   readable, just not row-aligned. */
@supports not (grid-template-rows: subgrid) {
  .card { display: flex; flex-direction: column; }
}

/* The blue is NOT a free-standing band. Measured on the original: a 1840x124
   block at y=3171 skewed -2deg, and the orange Why Choose section starts at
   y=3230 — so the orange covers everything below 3230 and only a wedge of
   blue shows above it, 27px at the left growing to 91px at the right.
   Reproducing it as a full-height band with white underneath was the miss.

   So: the band's own box is just the visible sliver; ::after overflows well
   below it and .why paints over the remainder.                            */
/* Every value below is interpolated between two measured points on the
   original — 360px and 1920px — because none of these scale proportionally.

                         @360    @1920
   band box height        70      124
   band top -> orange     40      60
   button -> band top     159     86
   truck width            309     477   (86% of the viewport at 360)
   truck over the orange  42      27

   Getting this wrong at mobile is what made the truck collide with the
   GET FREE QUOTE button: it was 210px wide sitting 38px *over* the button,
   where the original leaves 79px of clear space below it.                 */
.slant-band {
  position: relative;                 /* no z-index — the truck must be able
                                         to stack above .why from in here */
  height: calc(35.4px + 1.28vw);      /* visible sliver: 40 @360, 60 @1920 */
  margin-top: calc(175.9px - 4.68vw); /* 159 @360, 86 @1920 */
}
.slant-band::after {
  content: ''; position: absolute; left: 0; right: 80px; top: 0;
  height: calc(57.5px + 3.46vw);      /* box 70 @360, 124 @1920; .why covers the rest */
  background: var(--blue);
  transform: skewY(-2deg);            /* default origin: centre, as measured */
}
/* The truck straddles the boundary — its shadow falls on the orange in the
   original — so it sits above .why. */
.slant-band__truck {
  position: absolute; left: 0; z-index: 2;
  width: clamp(260px, 10.77vw + 270px, 477px);   /* 309 @360, 477 @1920 */
  height: auto;
  bottom: clamp(-45px, 0.96vw - 45.5px, -27px);  /* -42 @360, -27 @1920 */
}

@media (max-width: 900px) {
  .about { grid-template-columns: minmax(0, 1fr); }
  .card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 40px; }
}
@media (max-width: 600px) {
  .card-grid { grid-template-columns: minmax(0, 1fr); }
  .card { display: flex; flex-direction: column; grid-row: auto; }  /* see .why__card note */
  .card h3 { align-self: center; }
}

/* --- Why choose ----------------------------------------------------------
   Full-bleed orange band, measured #ba3f1d, 4 white cards.                 */

/* position + z-index so this section's orange paints OVER the absolutely
   positioned blue wedge above it. Without it the blue sits on top, because
   an absolutely positioned element outranks a plain block background. */
.why {
  position: relative;
  z-index: 1;
  background: var(--orange);
  color: var(--white);
  padding-block: 0 var(--section-pad);
  text-align: center;
}
.why h2 { color: var(--white); margin-bottom: 44px; }
.why__head { padding-top: 54px; }

.why__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 38px;
  text-align: center;
}

/* B3 — four fixed-height cards with wildly unequal fill and headings that
   did not share a baseline. Heading height is reserved so every body starts
   on the same line, and the cards stretch together instead of leaving a
   ragged floor.                                                            */
/* B3 — same subgrid treatment. In the original all four cards were a fixed
   380px with "Expertise and Experience" leaving ~140px of white at the floor,
   and "Responsive and Reliable Service" wrapping to two lines so its body
   started a line lower than the other three. Now icon, heading and copy each
   share a row height across all four. */
.why__card {
  background: var(--white);
  padding: 29px 24px 33px;
  grid-row: span 3;
  display: grid;
  grid-template-rows: subgrid;
  gap: 0;
  justify-items: center;
}
/* height:auto — two of the four source icons are 70x69 and 69x69, so forcing
   70x70 stretched them ~1.4%. */
.why__card img { width: 70px; height: auto; margin-bottom: 22px; align-self: start; }
.why__card h3 { margin-bottom: 12px; align-self: start; }
.why__card p { font-size: var(--fs-body); font-weight: 300; line-height: 1.7; color: var(--body); }

@supports not (grid-template-rows: subgrid) {
  .why__card { display: flex; flex-direction: column; align-items: center; }
}

@media (max-width: 1100px) { .why__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* At one column there is no row to align to, and subgrid then stretches every
   card to the tallest card's rows — reintroducing exactly the empty floors it
   exists to remove. Drop back to flow. */
@media (max-width: 600px) {
  .why__grid { grid-template-columns: minmax(0, 1fr); }
  .why__card { display: flex; flex-direction: column; align-items: center; grid-row: auto; }
  /* align-self:start is the row-top in the subgrid layout, but the horizontal
     cross-axis once the card becomes a flex column — it pinned the icon and
     heading to the left edge of a centred card. */
  .why__card img, .why__card h3 { align-self: center; }
}

/* --- FAQ -----------------------------------------------------------------
   Photo bleeds off the left edge in the original (x=0, w=590); toggles sit
   in the right column. Built as <details> so it works with JS off.         */

.faq { padding-block: var(--section-pad); text-align: center; }
.faq h2 { margin-bottom: 46px; }

/* The photo bleeds off the left viewport edge in the original (x=0, w=590)
   while the toggles sit in the right column at 894 wide. The media track is
   narrower than the photo on purpose: the negative margin carries the image
   out to x=0 and the toggles keep a usable measure. */
.faq__body {
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  gap: clamp(32px, 3vw, 48px);
  align-items: start;
  text-align: left;
}
.faq__media { margin-left: calc(-1 * var(--bleed)); }
.faq__media img { width: 590px; max-width: 100%; height: auto; }

.faq__list { display: grid; gap: 12px; }

.faq__item {
  background: var(--tint);
  border: 1px solid transparent;
  transition: background-color .18s ease;
}
.faq__item[open] { background: var(--white); border-color: var(--line); }

.faq__item summary {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 20px; cursor: pointer; list-style: none;
  font-size: var(--fs-body); font-weight: 700; color: var(--body);
  min-height: 44px;
}
.faq__item[open] summary { color: var(--ink); }
.faq__item summary::-webkit-details-marker { display: none; }

.faq__item summary::after {
  content: ''; flex: 0 0 auto; width: 24px; height: 24px;
  border-radius: 50%; background: var(--orange);
  /* plus sign drawn in the middle */
  background-image:
    linear-gradient(var(--white), var(--white)),
    linear-gradient(var(--white), var(--white));
  background-size: 10px 2px, 2px 10px;
  background-position: center, center;
  background-repeat: no-repeat;
  transition: transform .2s ease;
}
.faq__item[open] summary::after {
  background-size: 10px 2px, 2px 2px;
  transform: rotate(180deg);
}
.faq__answer {
  padding: 0 20px 22px;
  font-size: var(--fs-body); font-weight: 300; line-height: 1.7; color: var(--body);
}

@media (max-width: 900px) {
  .faq__body { grid-template-columns: minmax(0, 1fr); }
  .faq__media { margin-left: calc(-1 * var(--gutter)); margin-right: calc(-1 * var(--gutter)); }
}

/* --- Contact -------------------------------------------------------------
   Orange panel with the form on the left and the detail blurbs on the right.
   B5 — the original panel ran ~315px past its last line and the form card
   stopped 80px short of the bottom edge. Both columns now resolve together. */

.contact { padding-block: 0 var(--section-pad); position: relative; }

/* Padding, gap and column split all tightened so the form fields take more of
   the orange panel. Before: the input was 537px inside a 1344px panel at
   1920, and only 192px of a 320px panel at 360 — 60%, with 64px of chrome on
   each side (40px panel + 24px card). */
.contact__panel {
  background: var(--orange);
  color: var(--white);
  padding: clamp(24px, 2.9vw, 56px);
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  gap: clamp(24px, 2.5vw, 48px);
  align-items: stretch;
  position: relative;
}

/* B5 — the original left ~315px of empty orange below the right column and
   stopped the form card 80px short of the panel floor. The photo sits in the
   right column's flow (pulled up so it still breaks the panel's top edge the
   way the original did) and the detail blocks distribute down the column, so
   both columns finish together without needing filler content. */
.contact__media {
  width: 476px; max-width: 100%;
  margin: -120px 0 30px auto;
  position: relative; z-index: 2;
}
.contact h2 { color: var(--white); }
.contact__lede { color: var(--white); font-weight: 400; margin: 24px 0 34px; }

.contact__details {
  display: flex; flex-direction: column;
  gap: 26px;
  justify-content: space-between;
}

.detail { display: grid; grid-template-columns: 32px minmax(0, 1fr); gap: 14px; align-items: start; }
.detail svg { width: 26px; height: 26px; fill: none; stroke: var(--white); stroke-width: 1.8; }
.detail h3 { font-size: 1.5rem; font-weight: 500; color: var(--white); margin-bottom: 4px; }
.detail p, .detail a {
  font-size: var(--fs-body); font-weight: 400; line-height: 1.7;
  color: var(--white); text-decoration: none;
}
.detail a:hover { text-decoration: underline; }

/* The email and phone links render ~27px tall — over the 24px minimum but
   under the 44px touch target the mobile standard asks for.
   Real padding pulled back by an equal negative margin, rather than a
   pseudo-element: the finger target and the element's own bounding box are
   both 44px+ (an invisible ::after grows the hit area but audits still
   measure the 27px rect), and the visual baseline does not move. */
.detail a {
  display: inline-block;
  padding: 9px 10px;
  margin: -9px -10px;
}

@media (max-width: 1000px) {
  .contact__panel { grid-template-columns: minmax(0, 1fr); }
  /* Once the panel stacks there is no column above to break out of, and the
     -100px pull dropped the photo straight onto the submit button (68px into
     the form card, 24px over the button itself). */
  .contact__media { margin: 0 0 24px 0; width: 100%; max-width: 476px; }
  /* Stacked, there is no second column to balance against — even gaps read
     better than a spread. */
  .contact__details { justify-content: flex-start; }
}

/* The original hides this photo below 768px (Divi module visibility —
   verified: hidden at 360/600/767, shown from 768 up). Matching that both
   keeps the phone layout as the client knows it and saves the download. */
@media (max-width: 767px) {
  .contact__media { display: none; }
}

/* --- Form ----------------------------------------------------------------
   The original's Formidable render leaked a bare "Payment" label with no
   input (A6) and printed the honeypot instruction on screen (A7). Neither
   is reproduced.                                                           */

/* The details column bottoms out exactly on the panel's 64px padding, but the
   form column was finishing 202px short of it — the last of B5's dead space.
   The card now fills its column and the message field takes the slack, so the
   extra height becomes a bigger message box instead of empty orange. */
.contact__form { display: flex; flex-direction: column; }

.form-card {
  background: var(--white);
  padding: clamp(18px, 1.6vw, 28px);
  display: grid; gap: 20px;
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  flex: 1 1 auto;
}
.field { display: grid; gap: 8px; grid-template-rows: auto minmax(0, 1fr) auto; }
.field label { font-size: var(--fs-sm); font-weight: 400; color: var(--label); }
.field input, .field textarea {
  width: 100%; padding: 12px 14px;
  font-family: var(--font-body); font-size: var(--fs-body); color: var(--field-ink);
  background: var(--white); border: 1px solid #d0d5dd; border-radius: var(--radius);
  transition: border-color .16s ease, box-shadow .16s ease;
}
.field textarea { min-height: 132px; height: 100%; resize: vertical; }
.field input:focus, .field textarea:focus {
  border-color: var(--orange);
  box-shadow: 0 0 0 3px rgba(186, 63, 29, .16);
  outline: none;
}
.field input[aria-invalid='true'], .field textarea[aria-invalid='true'] { border-color: #b42318; }

.field__error { font-size: var(--fs-sm); color: #b42318; min-height: 0; }
.field__error:empty { display: none; }

/* Honeypot — genuinely hidden, not printed under the button. */
.hp { position: absolute; left: -9999px; opacity: 0; height: 0; overflow: hidden; }

.form-card .btn { justify-self: start; }
.form-status { font-size: var(--fs-sm); }
.form-status[data-state='error'] { color: #b42318; }

/* --- Footer -------------------------------------------------------------- */

.site-footer { padding-block: 0 var(--section-pad); text-align: center; }
.site-footer p {
  font-size: var(--fs-sm); font-weight: 500;
  color: var(--footer-text);   /* 4.95:1 — was #a9a9a9 at 2.35:1 */
}

/* --- Thank-you ----------------------------------------------------------- */

/* Measured: 154px block padding, h1 at 40px/500, eyebrow 20px, body 17px.
   The original's heading here is NOT the 64px display face — kept as measured. */
.thanks { padding-block: 154px; text-align: center; }
.thanks h1 {
  font-size: clamp(1.75rem, 1.1rem + 1.8vw, 2.5rem);  /* 28 -> 40 */
  font-weight: 500; color: var(--ink); margin-bottom: 20px;
}
/* Measured: 20px/500 uppercase #000, not the hero's white 25px eyebrow. */
.thanks__eyebrow {
  color: var(--black); text-transform: uppercase;
  font-size: 1.25rem; font-weight: 500; margin-bottom: 8px;
}
.thanks p { max-width: 1080px; margin: 0 auto 44px; }

/* --- 404 ----------------------------------------------------------------- */

.notfound { padding-block: 120px; text-align: center; }
.notfound h1 { color: var(--ink); margin-bottom: 20px; }
.notfound p { max-width: 640px; margin: 0 auto 32px; }
