/* ==========================================================================
   Richard Holt Plumbing — homepage
   Direction: "Hometown Pro". Depth comes from 1px borders and hairline rules,
   plus a paper -> blue -> paper section rhythm. No gradients, no shadow cards.

   Palette note: the brand hues are the actual logo colours, #F70207 red and
   #074495 blue. #F70207 only reaches 3.96:1 on paper, which fails AA for text,
   so per the brand doc every text-bearing red usage (button fills, overlines,
   the licence chip) uses --red-cta #C90206 (5.62:1). The literal logo red is
   kept for non-text accents and for the logo artwork itself.
   ========================================================================== */

/* --- Fonts ---------------------------------------------------------------- */

/* Anton carries the shout — it is the closest thing on the web to the painted
   condensed lettering on the company's own van. Archivo (American gothic) runs
   the interface, Newsreader sets the reading copy. */

@font-face {
  font-family: 'Anton';
  src: url('../assets/fonts/anton-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('../assets/fonts/archivo-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('../assets/fonts/archivo-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('../assets/fonts/archivo-700.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Newsreader';
  src: url('../assets/fonts/newsreader-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Newsreader';
  src: url('../assets/fonts/newsreader-400-italic.woff2') format('woff2');
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Newsreader';
  src: url('../assets/fonts/newsreader-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}

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

:root {
  --paper: #FAF7F2;
  --blue: #074495;            /* logo blue: display type, mid dark band */
  --blue-deep: #052E63;       /* darkened step: body text, utility bar, footer */
  --red: #F70207;             /* logo red: non-text accents only (fails AA as text) */
  --red-cta: #C90206;         /* AA-safe red: button fills, red text, chip */
  --red-cta-hover: #A50105;   /* darker step for hover/active */
  --rule: #E2DACC;
  --gold: #D9A404;
  --open: #1F7A3D;            /* open/closed status dot — decorative, text carries the meaning */
  --closed: #8A94A6;
  --paper-on-blue: #F5F1E8;
  --rule-on-blue: rgba(245, 241, 232, 0.28);
  --body-on-blue: #CBD6EA;

  --focus: var(--red-cta);

  --ff-display: 'Anton', 'Arial Narrow', 'Helvetica Neue', Helvetica, sans-serif;
  --ff-body: 'Newsreader', Georgia, 'Times New Roman', serif;
  --ff-ui: 'Archivo', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  /* 8px spacing scale */
  --s1: 8px;  --s2: 16px; --s3: 24px; --s4: 32px;
  --s5: 48px; --s6: 64px; --s7: 88px; --s8: 112px;

  --wrap: 1140px;
  --gut: 20px;
}

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

/* Anchor jumps (skip link, back-to-top) glide rather than snap. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--blue-deep);
  font-family: var(--ff-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }

/* Anton ships a single weight. Asking for 700 would make the browser
   synthesise a bold and smear the letterforms, so display type stays at 400
   and gets its emphasis from size instead. */
h1, h2 {
  font-family: var(--ff-display);
  font-weight: 400;
  letter-spacing: 0.004em;
  line-height: 1.06;
  margin: 0;
  color: var(--blue);
  text-wrap: balance;
}

h3 {
  font-family: var(--ff-ui);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin: 0;
  color: var(--blue);
  text-wrap: balance;
}

p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
dl, dd, dt { margin: 0; }

a { color: inherit; }

button { font: inherit; color: inherit; }

/* --- Layout helpers ------------------------------------------------------- */

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

.section { padding-block: var(--s6); }

/* Newspaper-masthead motif: two hairlines with a 3px gap. */
.rule-double {
  height: 3px;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

/* Decorative only — the literal logo red is fine here (not text). */
.rule-red {
  display: block;
  width: 56px;
  height: 3px;
  background: var(--red);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--blue-deep);
  color: var(--paper-on-blue);
  font-family: var(--ff-ui);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* Visible to assistive tech only — keeps a real accessible name on icon
   buttons instead of relying on aria-label. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* --- Blue bands ----------------------------------------------------------- */

.on-blue {
  color: var(--paper-on-blue);
  --rule: var(--rule-on-blue);
  --focus: var(--paper-on-blue);
}
.on-blue--mid { background: var(--blue); }
.on-blue--deep { background: var(--blue-deep); }
.on-blue h1, .on-blue h2, .on-blue h3 { color: var(--paper-on-blue); }
.on-blue .prose { color: var(--body-on-blue); }

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

.overline {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 13px;
  line-height: 1.3;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--red-cta);
  margin: 0 0 var(--s2);
}
.overline--heading { margin-bottom: var(--s4); }

.prose {
  font-size: clamp(17px, 1.15vw, 18px);
  line-height: 1.7;
  color: var(--blue-deep);
}

/* --- Chips (recurring licence trust mark) -------------------------------- */

.chip {
  display: inline-block;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  line-height: 1;
  padding: 6px 10px;
  border: 1px solid currentColor;
  border-radius: 2px;
  white-space: nowrap;
}
.chip--red { color: var(--red-cta); }
.chip--blue { color: var(--blue); }
.chip--on-blue { color: var(--paper-on-blue); }

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: 0.02em;
  line-height: 1;
  padding: 13px 20px;
  border: 1px solid transparent;
  border-radius: 2px;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.btn--lg { padding: 16px 26px; }
.btn--xl { padding: 20px 34px; font-size: 17px; letter-spacing: 0.03em; }

/* Outlined button for use on photography / dark bands. */
.btn--clear {
  background: transparent;
  border-color: rgba(245, 241, 232, .55);
  color: var(--paper-on-blue);
  --btn-rise: var(--paper-on-blue);
}
.btn--clear:hover {
  border-color: var(--paper-on-blue);
  color: var(--blue-deep);
}

/* Water rises to fill the button on hover, with a curved surface that flattens
   as it reaches the top — a meniscus. Replaces a flat background-colour swap. */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.btn::before {
  content: '';
  position: absolute;
  z-index: -1;
  left: -2px;
  right: -2px;
  top: 0;
  height: 100%;
  background: var(--btn-rise, transparent);
  border-radius: 50% 50% 0 0 / 14px 14px 0 0;
  transform: translateY(102%);
  transition: transform .42s cubic-bezier(.3, .72, .38, 1),
              border-radius .42s cubic-bezier(.3, .72, .38, 1);
}
.btn:hover::before,
.btn:focus-visible::before {
  transform: translateY(0);
  border-radius: 0;
}

.btn--red {
  background: var(--red-cta);
  border-color: var(--red-cta);
  color: var(--paper);
  --btn-rise: var(--red-cta-hover);
}
.btn--red:hover { border-color: var(--red-cta-hover); }

.btn--ghost {
  background: transparent;
  border-color: var(--blue);
  color: var(--blue);
  --btn-rise: var(--blue);
}
.btn--ghost:hover { color: var(--paper-on-blue); }

.btn .i { width: 17px; height: 17px; fill: currentColor; flex: none; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s4);
}

/* --- Utility bar ---------------------------------------------------------- */

.utility {
  font-family: var(--ff-ui);
  font-size: 13px;
  font-weight: 500;
}
.utility__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px 20px;
  padding-block: 9px;
}
.utility__item {
  text-decoration: none;
  color: inherit;
  padding-block: 2px;
}
.utility__item:hover { text-decoration: underline; text-underline-offset: 3px; }
.utility__tel b { font-weight: 600; }

/* Licence trust mark, in the same position the client's own site had it. */
.utility__chip { font-size: 11px; padding: 5px 9px; letter-spacing: 0.06em; }

@media (min-width: 900px) {
  .utility__inner { justify-content: space-between; gap: 0 var(--s4); }
}

/* --- Header --------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

.header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding-block: 10px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-right: auto;
  min-width: 0;
}

/* The logo must never be squeezed by its flex siblings. */
.brand__link { flex: none; display: block; }

/* The real logo, white background removed so it sits straight on --paper. */
.brand__logo {
  display: block;
  width: 158px;
  max-width: none;
  height: auto;
}

.header__call { flex: none; padding-inline: 15px; }

/* Hamburger — only shown once main.js has confirmed it is wired up. */
.nav-toggle { display: none; }

.js-nav .nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: transparent;
  border: 1px solid var(--blue);
  border-radius: 2px;
  color: var(--blue);
  padding: 12px;
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}
.nav-toggle__bars { display: block; width: 18px; }
.nav-toggle__bars span { display: block; height: 2px; background: currentColor; }
.nav-toggle__bars span + span { margin-top: 4px; }

.site-nav { flex-basis: 100%; }

/* Collapsed state only exists when JS is running. */
.js-nav .site-nav[data-open='false'] { display: none; }

.site-nav__list {
  display: flex;
  flex-direction: column;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-top: 1px solid var(--rule);
  margin-top: 4px;
  color: var(--blue-deep);
}
.site-nav__list a {
  display: block;
  padding: 13px 2px;
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  /* Never let a label break across two lines in the header row. */
  white-space: nowrap;
}
.site-nav__list a[aria-current='page'] { color: var(--blue); }

@media (min-width: 900px) {
  .header__inner { flex-wrap: nowrap; padding-block: 12px; }
  .brand { order: 1; }
  .brand__logo { width: 200px; }
  .site-nav { order: 2; flex-basis: auto; }
  .header__call { order: 3; padding-inline: 20px; }
  .js-nav .nav-toggle { display: none; }
  .js-nav .site-nav[data-open='false'] { display: block; }

  .site-nav__list {
    flex-direction: row;
    gap: var(--s3);
    border-top: 0;
    margin-top: 0;
    font-size: 14px;
  }
  .site-nav__list a { padding: 6px 0; border-bottom: 2px solid transparent; }
  .site-nav__list a:hover { border-bottom-color: var(--blue-deep); }
  .site-nav__list a[aria-current='page'] { border-bottom-color: var(--blue); }
}

@media (min-width: 1240px) {
  .site-nav__list { gap: var(--s4); font-size: 15px; }
}

/* --- Hero ----------------------------------------------------------------- */

/* Full-bleed and photo-led. The previous hero put a small framed photo beside
   mid-sized type inside a 1140px column, which read as a document rather than
   a front page. The photography now runs edge to edge and the headline is the
   largest thing on the site by a wide margin. */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  min-height: clamp(520px, 78vh, 820px);
  /* Kept tight on purpose: the header eats ~150px, so generous hero padding
     pushes the carousel dots below the fold and the slideshow reads as broken
     because nothing visibly changes. */
  padding-block: var(--s5) var(--s5);
  overflow: hidden;
  background: var(--blue-deep);
  color: var(--paper-on-blue);
  --rule: var(--rule-on-blue);
  --focus: var(--paper-on-blue);
}

.hero__media { position: absolute; inset: 0; z-index: -1; }

/* Scrim, not decoration: the headline has to clear AA over any frame of the
   carousel, and the photographs vary a lot in luminance. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(3, 22, 50, .95) 0%, rgba(3, 22, 50, .88) 42%, rgba(3, 22, 50, .58) 100%),
    rgba(5, 46, 99, .34);
}

.hero__inner { position: relative; width: 100%; }

.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-on-blue);
  margin-bottom: var(--s3);
}
.hero__tick { display: block; flex: none; width: 46px; height: 3px; background: var(--red); }

.hero h1 {
  color: var(--paper-on-blue);
  font-size: clamp(44px, 7.6vw, 112px);
  line-height: 0.9;
  letter-spacing: 0.002em;
  text-transform: uppercase;
  max-width: 16ch;
}

.hero__lead {
  font-size: clamp(17px, 1.3vw, 21px);
  line-height: 1.6;
  color: var(--body-on-blue);
  margin-top: var(--s4);
  max-width: 44ch;
}

/* Short laptops: trade a little headline size for keeping the carousel dots
   above the fold, so the slideshow is visibly a slideshow. */
@media (min-width: 900px) and (max-height: 820px) {
  .hero { min-height: 0; padding-block: var(--s4) var(--s4); }
  .hero h1 { font-size: clamp(40px, 5.4vw, 76px); }
  .hero__lead { margin-top: var(--s3); }
}

/* Phones: the whole point of this page is the Call button, and on a 667px-tall
   handset it was landing ~90px below the fold. Tighten the stack so it is
   reachable without scrolling. svh (not vh) because mobile browsers measure vh
   against the toolbar-hidden viewport, which overshoots on first paint. */
@media (max-width: 640px) {
  .hero {
    min-height: 0;
    padding-block: var(--s4) var(--s4);
  }
  .hero h1 { font-size: clamp(32px, 8.4vw, 46px); }
  .hero__eyebrow { font-size: 11px; letter-spacing: 0.16em; margin-bottom: var(--s2); }
  .hero__lead { margin-top: var(--s3); font-size: 16.5px; }
  .hero .btn-row { margin-top: var(--s3); gap: 10px; }
  .hero .btn--xl { padding: 16px 20px; font-size: 15.5px; }
  .hours--hero { margin-top: var(--s3); }
  .js-deck .deck__controls { margin-top: var(--s3); }
}

/* Short phones (iPhone SE and similar): the Call button was still ~11px under
   the fold. Tighten the stack further rather than drop any content. */
@media (max-width: 640px) and (max-height: 700px) {
  .hero { padding-block: var(--s3) var(--s3); }
  .hero__eyebrow { margin-bottom: 10px; }
  .hero h1 { font-size: clamp(29px, 7.8vw, 40px); }
  .hero__lead { margin-top: var(--s2); }
  .hero .btn-row { margin-top: var(--s3); }
}

@supports (height: 100svh) {
  .hero { min-height: clamp(520px, 78svh, 820px); }
  @media (max-width: 640px) { .hero { min-height: 0; } }
}

.hero .btn-row { margin-top: var(--s4); }
/* Two classes deep on purpose: the base .hours colour rule is declared later in
   this file, so a single-class selector here loses and puts navy on navy. */
.hero .hours--hero { margin-top: var(--s4); color: var(--paper-on-blue); }

/* Hero copy animates on load via CSS only — never via the scroll observer.
   Reveal-on-scroll would leave the headline unpainted until the observer
   fires, which delays LCP and shows a photo with no message on it. */
.hero__eyebrow,
.hero h1,
.hero__lead,
.hero .btn-row { animation: heroIn .62s cubic-bezier(.22, .61, .36, 1) both; }
.hero h1 { animation-delay: .05s; }
.hero__lead { animation-delay: .12s; }
.hero .btn-row { animation-delay: .18s; }

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

/* --- Water-fill headlines ------------------------------------------------- */
/* A waterline rises through the letterforms. Only the *colour* animates — the
   text is fully set and readable at every frame — so this can never hide
   content, delay LCP, or strand a headline if the animation does not run.
   The resting state is the filled state, which is what shows if animation is
   unsupported or the element never enters the viewport. */

.waterfill {
  --fill-on: var(--blue);
  --fill-off: rgba(7, 68, 149, .30);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .waterfill {
    background-image: linear-gradient(to top,
      var(--fill-on) 47%, var(--fill-off) 54%);
    background-size: 100% 215%;
    background-repeat: no-repeat;
    background-position: 50% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
  }

  /* Hero fills as it arrives; section headings fill as they scroll in. */
  .hero h1.waterfill { animation: waterfill 2.3s cubic-bezier(.32, .72, .38, 1) forwards; }
  .waterfill[data-reveal].is-in { animation: waterfill 1.9s cubic-bezier(.32, .72, .38, 1) forwards; }
}

.on-blue .waterfill,
.hero h1.waterfill {
  --fill-on: var(--paper-on-blue);
  --fill-off: rgba(245, 241, 232, .34);
}
.discount .waterfill {
  --fill-on: var(--paper);
  --fill-off: rgba(250, 247, 242, .38);
}

@keyframes waterfill {
  from { background-position: 50% 0%; }
  to   { background-position: 50% 100%; }
}

/* --- Hero carousel -------------------------------------------------------- */

.deck__slides { position: absolute; inset: 0; }
.deck__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .9s ease;
}
.deck__slide[data-active='true'] { opacity: 1; }
.deck__slide img { width: 100%; height: 100%; object-fit: cover; }

/* No JS: show the truck photo and hide the controls entirely. */
.deck__slide:first-child { opacity: 1; }
.deck__controls { display: none; }
.js-deck .deck__controls {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s3);
}
.js-deck .deck__slide:first-child { opacity: 0; }
.js-deck .deck__slide[data-active='true'] { opacity: 1; }
/* The auto-advance timer only means anything once the deck is actually running. */
.js-deck .deck__progress { display: block; }

.deck__dots { display: flex; align-items: center; gap: 10px; }
.deck__dot {
  display: block;
  width: 34px;
  height: 22px;
  padding: 9px 0;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.deck__dot::before {
  content: '';
  display: block;
  height: 3px;
  background: rgba(245, 241, 232, .38);
  transition: background-color .15s ease;
}
.deck__dot:hover::before { background: var(--paper-on-blue); }
.deck__dot[aria-current='true']::before { background: var(--red); }

/* Timer runs along the very bottom edge of the hero. */
.deck__progress {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: rgba(245, 241, 232, .16);
  overflow: hidden;
}
.deck__progress::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left center;
}
.deck__progress.is-running::after { animation: fill var(--auto-ms, 7500ms) linear both; }
@keyframes fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* --- Inner page hero ------------------------------------------------------ */
/* Same treatment as the homepage hero at reduced scale, with one still photo
   instead of the carousel. Inherits the hero's colour context and entrance. */

.hero--page {
  min-height: clamp(320px, 46vh, 500px);
  padding-block: var(--s6);
}
.hero__still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero--page h1 {
  font-size: clamp(32px, 5.2vw, 74px);
  max-width: 20ch;
}
.hero--page .hero__lead { max-width: 52ch; }

/* Breadcrumb sitting in the eyebrow slot. */
.crumb a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(245, 241, 232, .4);
  padding-bottom: 1px;
}
.crumb a:hover { border-bottom-color: var(--paper-on-blue); }
.crumb__sep { opacity: .5; }

/* --- Feature rows --------------------------------------------------------- */
/* Prose beside a photograph, alternating sides down the page. */

.feature__grid {
  display: grid;
  gap: var(--s5);
  align-items: center;
}
.feature h2 {
  font-size: clamp(27px, 3.6vw, 46px);
  line-height: 0.98;
  text-transform: uppercase;
  margin-block: var(--s3) var(--s3);
  max-width: 20ch;
}
.feature__figure { margin: 0; }
.feature__figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--rule);
}
.feature .prose { max-width: 54ch; }

@media (min-width: 900px) {
  .feature__grid { grid-template-columns: 1fr 1fr; gap: var(--s7); }
  /* Flip puts the photo on the left without reordering the document. */
  .feature--flip .feature__text { order: 2; }
}

/* --- 404 ------------------------------------------------------------------ */
/* No photograph here — a solid field with an oversized ghosted numeral reads
   better than dressing up a dead end. */

.hero--404 {
  background: var(--blue-deep);
  min-height: clamp(360px, 58vh, 560px);
  overflow: hidden;
}
.hero__ghost {
  position: absolute;
  right: -0.05em;
  bottom: -0.3em;
  z-index: 0;
  font-family: var(--ff-display);
  font-size: clamp(220px, 34vw, 560px);
  line-height: 1;
  color: var(--paper-on-blue);
  opacity: 0.07;
  pointer-events: none;
  user-select: none;
}
.hero--404 .hero__inner { z-index: 1; }

/* Signposts out of the dead end. */
.lostnav {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-top: 1px solid var(--rule);
}
.lostnav a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--ff-display);
  font-size: clamp(22px, 3vw, 34px);
  letter-spacing: 0.004em;
  text-transform: uppercase;
  color: var(--blue);
  text-decoration: none;
  transition: color .18s ease, padding-left .25s cubic-bezier(.3,.72,.38,1);
}
.lostnav a:hover { color: var(--red-cta); padding-left: 12px; }
.lostnav__go {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.16em;
  color: var(--blue-deep);
  white-space: nowrap;
}
.lostnav a:hover .lostnav__go { color: var(--red-cta); }

@media (min-width: 760px) {
  .lostnav { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s6); }
}
@media (prefers-reduced-motion: reduce) {
  .lostnav a:hover { padding-left: 0; }
}

/* --- Long-form legal ------------------------------------------------------ */
/* Single readable column. Deliberately plain — a privacy policy should be easy
   to read top to bottom, not designed at. */

.legal { max-width: 68ch; }
.legal h2 {
  font-size: clamp(22px, 2.4vw, 30px);
  line-height: 1.06;
  text-transform: uppercase;
  margin-top: var(--s6);
  margin-bottom: var(--s3);
}
.legal h2:first-child { margin-top: 0; }
.legal p,
.legal li {
  font-family: var(--ff-body);
  font-size: 17px;
  line-height: 1.7;
  color: var(--blue-deep);
}
.legal p + p { margin-top: var(--s3); }
.legal ul {
  list-style: none;
  margin-top: var(--s3);
  padding: 0;
}
.legal li {
  position: relative;
  padding-left: 22px;
  margin-bottom: 10px;
}
.legal li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 7px;
  height: 7px;
  background: var(--red);
  transform: rotate(45deg);
}
.legal a { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }
.legal a:hover { color: var(--red-cta); }
.legal__updated {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--red-cta);
  margin-bottom: var(--s4);
}

/* --- Dealer claim -------------------------------------------------------- */
/* Differentiators the client asked to call out (Rheem, Krystal Klear). Given a
   bordered block so they read as a credential rather than as body copy. */

.claim {
  border: 1px solid var(--rule);
  border-left: 4px solid var(--red);
  padding: var(--s4);
  max-width: 64ch;
}
.claim__label {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red-cta);
  margin-bottom: var(--s2);
}
.claim__body {
  font-family: var(--ff-body);
  font-size: clamp(17.5px, 1.5vw, 21px);
  line-height: 1.55;
  color: var(--blue-deep);
}

/* Page intro: the stamp holds the left column so a single lede paragraph is not
   left floating beside a dead half-row. Mirrors the homepage intro's split. */
.pintro__grid { display: grid; gap: var(--s5); }
.lede {
  max-width: 62ch;
  font-size: clamp(18px, 1.4vw, 20.5px);
  line-height: 1.68;
}

@media (min-width: 900px) {
  .pintro__grid {
    grid-template-columns: auto 1fr;
    column-gap: var(--s7);
    align-items: start;
  }
  .pintro .stamp { margin-top: 6px; }
}

/* --- Service ticker ------------------------------------------------------- */
/* A moving banner of what the shop actually does. Decorative repetition of the
   capability list below, so it is hidden from assistive tech. */

.ticker {
  overflow: hidden;
  background: var(--red-cta);
  color: var(--paper);
  padding-block: 15px;
}
.ticker__track {
  display: flex;
  width: max-content;
  animation: ticker 42s linear infinite;
}
.ticker__set {
  display: flex;
  align-items: center;
  font-family: var(--ff-display);
  font-size: clamp(19px, 2.1vw, 28px);
  line-height: 1;
  letter-spacing: 0.015em;
  text-transform: uppercase;
  white-space: nowrap;
}
.ticker__set i {
  display: block;
  flex: none;
  width: 9px;
  height: 9px;
  background: var(--paper);
  transform: rotate(45deg);
  margin: 0 26px;
}
@keyframes ticker {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* --- Trust strip ---------------------------------------------------------- */
/* Not used on the homepage any more — the stats band replaced it. Kept because
   it is the slim variant the four inner pages are likely to want; delete it if
   they end up using the stats band instead. */

.trust {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.trust__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--blue-deep);
}
.trust__list li {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s3) var(--s2);
  min-height: 68px;
}
.trust__list li:nth-child(even) { border-left: 1px solid var(--rule); }
.trust__list li:nth-child(1),
.trust__list li:nth-child(2) { border-bottom: 1px solid var(--rule); }

/* The licence chip is nowrap by default; inside the narrow 2x2 grid its
   min-content width would push the page wider than the viewport, so let it
   break onto two lines here. */
.trust__list .chip { white-space: normal; }

@media (min-width: 780px) {
  .trust__list { grid-template-columns: repeat(4, 1fr); }
  .trust__list li { border-left: 1px solid var(--rule); border-bottom: 0; }
  .trust__list li:first-child { border-left: 0; }
}

/* --- Intro ---------------------------------------------------------------- */

/* Editorial split rather than a centred column — a centred block of body copy
   under a centred heading is the single most templated section on the web. */
/* Never set max-width on .intro__inner — it also carries .wrap, so overriding
   max-width there cancels the site container and the block spans the whole
   viewport. Constrain the measure on the paragraph instead. */
.intro h2 {
  font-size: clamp(30px, 4.4vw, 56px);
  line-height: 0.96;
  /* Every other section heading is uppercase; this was the odd one out. */
  text-transform: uppercase;
  margin-bottom: var(--s4);
}
.intro .prose { max-width: 62ch; }

/* Stamp plate — a maker's mark for the left column, so the heading isn't left
   sitting over dead space. Decorative: the founding year is already in the copy. */
.stamp {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  border: 1px solid var(--rule);
  border-left: 3px solid var(--red);
  padding: 14px 20px 13px;
  margin-top: var(--s5);
  width: max-content;
}
.stamp__est,
.stamp__loc {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--blue-deep);
}
.stamp__year {
  font-family: var(--ff-display);
  font-size: 44px;
  line-height: 0.9;
  letter-spacing: 0.02em;
  color: var(--blue);
}

/* Drop cap. Old-fashioned on purpose — it is the kind of detail a template
   never bothers with, and it anchors the column against the big heading. */
.dropcap::first-letter,
.intro__prose::first-letter {
  float: left;
  font-family: var(--ff-display);
  font-size: 4.1em;
  line-height: 0.78;
  padding-right: 0.09em;
  margin-top: 0.07em;
  color: var(--red-cta);
}

@media (min-width: 900px) {
  .intro__inner {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s7);
    align-content: start;
  }
  .intro .overline { grid-column: 1; grid-row: 1; }
  .intro h2 { grid-column: 1; grid-row: 2; margin-bottom: 0; }
  .intro .stamp { grid-column: 1; grid-row: 3; }
  /* Copy sits alongside the heading and runs the full height of the block. */
  .intro .prose { grid-column: 2; grid-row: 1 / span 3; max-width: 54ch; }

  /* Hairline down the gutter — turns two floating blocks into one spread. */
  .intro__inner::before {
    content: '';
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: 50%;
    width: 1px;
    background: var(--rule);
  }
}

/* --- Services ------------------------------------------------------------- */

/* Deliberately asymmetric: one tall feature card beside two horizontal ones.
   Three equal cards in a row is the most templated pattern on the web and it
   was reading as exactly that. */

/* Masthead row: title, a rule that eats the remaining width, then a kicker.
   The title used to sit alone on an empty line. */
.services__head {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  margin-bottom: var(--s5);
}
.services__title {
  font-size: clamp(36px, 6vw, 84px);
  line-height: 0.94;
  text-transform: uppercase;
  flex: none;
}
.services__rule {
  flex: 1;
  height: 1px;
  min-width: var(--s3);
  background: var(--rule);
  transform: translateY(-0.35em);
}
.services__count {
  flex: none;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--red-cta);
  transform: translateY(-0.35em);
}

@media (max-width: 639px) {
  .services__count { display: none; }
}

.svc-grid {
  display: grid;
  gap: var(--s3);
}

.svc {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--rule);
  transition: border-color .2s ease;
}
.svc:hover { border-color: var(--blue); }
.svc:has(.svc__link:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.svc__media {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--rule);
  background: var(--blue);
}
.svc__media img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  filter: saturate(0.9);
}
/* The water level rises over the photo on hover — the same meniscus used by
   the buttons and the headline fill. An expanding ring was tried here first and
   binned: it is the stock Material ripple, and it introduced a second, unrelated
   water metaphor. One idea used consistently is what stops this reading cheap. */
.svc__media::before {
  content: '';
  position: absolute;
  z-index: 2;
  left: -2%;
  right: -2%;
  top: 0;
  height: 100%;
  background: rgba(7, 68, 149, .46);
  border-radius: 50% 50% 0 0 / 26px 26px 0 0;
  transform: translateY(101%);
  transition: transform .58s cubic-bezier(.3, .72, .38, 1),
              border-radius .58s cubic-bezier(.3, .72, .38, 1);
  pointer-events: none;
}
.svc:hover .svc__media::before,
.svc:has(.svc__link:focus-visible) .svc__media::before {
  transform: translateY(0);
  border-radius: 0;
}

/* A light common tint so three unrelated photographs read as one set. */
.svc__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--blue);
  opacity: 0.10;
  mix-blend-mode: multiply;
  pointer-events: none;
}

/* Printed index tab on the corner of the photo. */
.svc__num {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  font-family: var(--ff-display);
  font-size: 15px;
  line-height: 1;
  letter-spacing: 0.05em;
  color: var(--blue);
  background: var(--paper);
  padding: 9px 11px 8px;
}

.svc__body {
  flex: 1;
  padding: var(--s3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}

.svc__name {
  font-family: var(--ff-display);
  font-weight: 400;
  font-size: clamp(21px, 2vw, 26px);
  letter-spacing: 0.004em;
  color: var(--blue);
}
.svc__link { text-decoration: none; color: inherit; }
/* Stretch the link over the whole card; accessible name stays the service. */
.svc__link::after {
  content: '';
  position: absolute;
  inset: 0;
}

.svc__go {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-deep);
  white-space: nowrap;
}
.svc__arrow { display: inline-block; }

@media (min-width: 860px) {
  .svc-grid { grid-template-columns: 1.3fr 1fr; }

  /* Feature card holds the left column across both rows. */
  .svc--feature { grid-row: span 2; }
  .svc--feature .svc__media { flex: 1; min-height: 330px; }
  .svc--feature .svc__media img {
    position: absolute;
    inset: 0;
    height: 100%;
    aspect-ratio: auto;
  }
  .svc--feature .svc__body { flex: none; padding: var(--s4); }
  .svc--feature .svc__name { font-size: clamp(26px, 2.6vw, 34px); }
  .svc--feature .svc__num { font-size: 18px; padding: 11px 13px 10px; }

  /* The two secondary cards run sideways: photo left, name right. */
  .svc:not(.svc--feature) {
    display: grid;
    grid-template-columns: 190px 1fr;
    align-items: stretch;
  }
  .svc:not(.svc--feature) .svc__media {
    border-bottom: 0;
    border-right: 1px solid var(--rule);
    min-height: 168px;
  }
  .svc:not(.svc--feature) .svc__media img {
    position: absolute;
    inset: 0;
    height: 100%;
    aspect-ratio: auto;
  }
  /* Side by side, the name and the "see more" fought for the same line and the
     name wrapped mid-phrase. Stack them instead. */
  .svc:not(.svc--feature) .svc__body {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
  }
  .svc:not(.svc--feature) .svc__name { font-size: clamp(20px, 1.7vw, 24px); }
}

/* --- Best of Longview ----------------------------------------------------- */

/* Full-bleed split: solid colour field on one side, photography running to the
   viewport edge on the other. The text still lines up with the site container
   via padding rather than a wrapper, so the band itself can span the screen. */
.bestof__grid { display: grid; }

.bestof__text {
  position: relative;
  overflow: hidden;
  padding: var(--s6) var(--gut);
}
.bestof__body { position: relative; z-index: 1; }

/* Oversized tally sitting behind the copy. Decorative only. */
.bestof__ghost {
  position: absolute;
  right: -0.06em;
  bottom: -0.28em;
  font-family: var(--ff-display);
  font-size: clamp(180px, 30vw, 400px);
  line-height: 1;
  color: var(--paper-on-blue);
  opacity: 0.09;
  pointer-events: none;
  user-select: none;
}

.bestof__figure { margin: 0; }
.bestof__figure img {
  width: 100%;
  height: 100%;
  min-height: 280px;
  object-fit: cover;
  /* Bias the crop left so the van's lettering isn't sliced mid-word. */
  object-position: 32% center;
}

@media (min-width: 900px) {
  .bestof__grid { grid-template-columns: 54% 46%; align-items: stretch; }
  .bestof__text {
    display: flex;
    align-items: center;
    padding-block: var(--s8);
    /* Aligns the copy with every other section's left edge while the coloured
       field keeps running to the edge of the screen. */
    padding-left: max(var(--gut), calc((100% / 0.54 - var(--wrap)) / 2 + var(--gut)));
    padding-right: var(--s6);
  }
}
.bestof h2 {
  font-size: clamp(32px, 4.8vw, 62px);
  line-height: 0.96;
  text-transform: uppercase;
  margin-block: var(--s3) var(--s3);
  max-width: 17ch;
}
.bestof .prose { max-width: 52ch; }

/* --- Testimonials --------------------------------------------------------- */

.testimonials__head { margin-bottom: var(--s5); }
.testimonials h2 {
  font-size: clamp(32px, 5.2vw, 72px);
  line-height: 0.94;
  text-transform: uppercase;
  margin-top: var(--s3);
}

/* Gold is reserved for review stars (brand rule). */
.stars {
  color: var(--gold);
  font-size: 17px;
  letter-spacing: 0.14em;
  line-height: 1;
  margin-bottom: var(--s2);
}

.review { margin: 0; }
.review__quote {
  font-family: var(--ff-body);
  font-size: 17px;
  line-height: 1.7;
  color: var(--blue-deep);
}
.review__by {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--blue);
  margin-top: var(--s2);
}

/* The lead review is a statement, not a boxed card. A thick red rule above it
   and an oversized quote glyph do the work the border used to do. */
.review--featured {
  position: relative;
  overflow: hidden;
  border-top: 4px solid var(--red);
  padding-block: var(--s5);
  margin-bottom: var(--s5);
}
.review__mark {
  position: absolute;
  right: -0.04em;
  top: -0.12em;
  font-family: var(--ff-display);
  font-size: clamp(150px, 20vw, 280px);
  line-height: 1;
  color: var(--red);
  opacity: 0.11;
  pointer-events: none;
  user-select: none;
}
.review--featured .stars { font-size: 22px; }
.review--featured .review__quote {
  position: relative;
  font-size: clamp(21px, 2.7vw, 36px);
  line-height: 1.36;
  max-width: 36ch;
}
.review--featured .review__by { font-size: 14px; }

.reviews { display: grid; gap: var(--s5); }
.reviews > li {
  border-top: 1px solid var(--rule);
  padding-top: var(--s3);
}

@media (min-width: 820px) {
  .reviews { grid-template-columns: repeat(2, 1fr); gap: var(--s5) var(--s6); }
  .review--featured { padding-block: var(--s6); margin-bottom: var(--s6); }
  .review__quote { font-size: 18px; }
}

/* --- Discount banner ------------------------------------------------------ */

/* Full-bleed red. This is the offer the shop most wants people to act on, and
   it was previously the quietest thing on the page. */
.discount {
  padding-block: 0;
  background: var(--red-cta);
  color: var(--paper);
  --focus: var(--paper);
}
.discount .rule-double { display: none; }
.discount__inner {
  padding-block: var(--s7);
  text-align: center;
}
.discount h2 {
  color: var(--paper);
  font-size: clamp(32px, 5.2vw, 72px);
  line-height: 0.94;
  text-transform: uppercase;
  max-width: 26ch;
  margin-inline: auto;
  margin-bottom: var(--s5);
}

/* Solid paper button for use on the red band. */
.btn--paper {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--red-cta);
  --btn-rise: var(--blue-deep);
}
.btn--paper:hover { border-color: var(--blue-deep); color: var(--paper); }

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

.site-footer {
  border-top: 4px solid var(--red);
  padding-top: var(--s7);
  font-family: var(--ff-ui);
  font-size: 15px;
}

/* The phone number is the whole point of the page — it should not be set at
   the same size as the mailing address. */
.footer__phone {
  font-family: var(--ff-display);
  font-size: clamp(30px, 3.4vw, 44px);
  line-height: 1;
  letter-spacing: 0.01em;
  margin-bottom: var(--s4);
}
.footer__phone a { text-decoration: none; }
.footer__phone a:hover { color: var(--red); text-decoration: none; }

.footer__grid {
  display: grid;
  gap: var(--s5);
}

/* The logo's blue would disappear on the deep blue footer, so it keeps a white
   plate here — reads as a printed card and preserves legibility. */
.logo-plate {
  display: inline-block;
  background: #FFF;
  border: 1px solid var(--rule-on-blue);
  padding: 12px;
  width: max-content;
}
.logo-plate img { width: 240px; height: auto; }

.footer__brand .chip { margin-top: var(--s2); display: block; width: max-content; }
.footer__brand .social { margin-top: var(--s3); }

.footer__nav ul {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.footer__nav a { text-decoration: none; }
.footer__nav a:hover { text-decoration: underline; text-underline-offset: 3px; }
.footer__nav a[aria-current='page'] { color: var(--body-on-blue); }

.footer__meta dt {
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--body-on-blue);
  margin-bottom: 3px;
}
.footer__meta dd { margin-bottom: var(--s3); line-height: 1.5; }
.footer__meta dd + dd { margin-top: calc(var(--s3) * -1 + 3px); }
.footer__meta dd:last-child { margin-bottom: 0; }
.footer__meta a { text-decoration: none; }
.footer__meta a:hover { text-decoration: underline; text-underline-offset: 3px; }

.social {
  display: flex;
  gap: var(--s2);
  align-items: center;
}
.social a {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--rule-on-blue);
  border-radius: 2px;
  color: var(--paper-on-blue);
  transition: color .15s ease, border-color .15s ease;
}
/* Same rising meniscus as the buttons. */
.social a::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--red-cta);
  border-radius: 50% 50% 0 0 / 10px 10px 0 0;
  transform: translateY(102%);
  transition: transform .4s cubic-bezier(.3, .72, .38, 1),
              border-radius .4s cubic-bezier(.3, .72, .38, 1);
}
.social a:hover::before,
.social a:focus-visible::before { transform: translateY(0); border-radius: 0; }
.social a:hover { color: var(--paper); border-color: var(--red-cta); }
.social .ic { width: 21px; height: 21px; }

.footer__legal {
  margin-top: var(--s6);
  padding-block: var(--s3) var(--s4);
}
.footer__legal::before {
  content: '';
  display: block;
  border-top: 1px solid var(--rule-on-blue);
  margin-bottom: var(--s3);
}
.footer__legal p {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--body-on-blue);
  max-width: 90ch;
}

.footer__links a { color: var(--paper-on-blue); text-decoration: none; }
.footer__links a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Agency credit. Dimmed by default so it stays subordinate to the client's own
   branding, full strength on hover. */
.powered {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: var(--s2);
}
.powered__label {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--body-on-blue);
}
.powered__logo {
  display: block;
  width: 104px;
  height: auto;
  opacity: 0.55;
  transition: opacity .2s ease;
}
.powered a:hover .powered__logo,
.powered a:focus-visible .powered__logo { opacity: 1; }

@media (min-width: 760px) {
  /* Legal note left, credit right — one row instead of two stacked lines. */
  .footer__legal {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4);
    flex-wrap: wrap;
  }
  .powered { margin-top: 0; }
}

@media (min-width: 760px) {
  .footer__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s5); }
}

@media (min-width: 1000px) {
  /* brand | nav | mailing+physical | phone+hours */
  .footer__grid {
    grid-template-columns: auto auto 1fr 1fr;
    gap: var(--s5);
  }
}

/* --- Stats band ----------------------------------------------------------- */
/* Replaces the flat trust strip. Same four facts, given the weight they earn.
   The year count is filled in by JS from the 1999 founding date so it can
   never go stale; the markup ships the correct value as a no-JS fallback. */

/* Dark band directly under the hero. It breaks a long run of identical paper
   sections and lets the numbers carry real weight. */
.stats { padding-block: var(--s5); }
.on-blue.stats .stat__num { color: var(--paper-on-blue); }
.on-blue.stats .stat__label { color: var(--body-on-blue); }
.on-blue.stats .stats__note { color: var(--paper-on-blue); }

.stats__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: var(--s2) var(--s2);
}
.stats__list > li:nth-child(even) { border-left: 1px solid var(--rule); }
.stats__list > li:nth-child(1),
.stats__list > li:nth-child(2) {
  padding-bottom: var(--s4);
  margin-bottom: var(--s4);
  border-bottom: 1px solid var(--rule);
}

.stat__num {
  font-family: var(--ff-display);
  font-weight: 400;
  font-size: clamp(54px, 8vw, 104px);
  line-height: 0.92;
  letter-spacing: 0.004em;
  color: var(--blue);
  font-variant-numeric: tabular-nums;
}
/* The licence number is a string, not a tally, so it sits a step down — but it
   still has to hold its own beside 100px numerals. */
.stat__num--sm {
  font-size: clamp(30px, 4.2vw, 56px);
  letter-spacing: 0.01em;
}
.stat__label {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 12.5px;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-deep);
  margin-top: var(--s1);
  max-width: 22ch;
  text-wrap: balance;
}

.stats__note {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--red-cta);
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
}

@media (min-width: 780px) {
  .stats__list { grid-template-columns: repeat(4, 1fr); }
  .stats__list > li { border-left: 1px solid var(--rule); }
  .stats__list > li:first-child { border-left: 0; }
  .stats__list > li:nth-child(1),
  .stats__list > li:nth-child(2) {
    padding-bottom: var(--s2);
    margin-bottom: 0;
    border-bottom: 0;
  }
}

/* --- Open / closed status ------------------------------------------------- */
/* Computed live from the published hours in America/Chicago, so a visitor in
   any timezone sees the shop's real state. Hidden until JS fills it in. */

.hours {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.03em;
  color: var(--blue-deep);
}
.hero .hours { margin-top: var(--s3); }

.hours__dot {
  position: relative;
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--closed);
}
.hours[data-open='true'] .hours__dot { background: var(--open); }
/* Soft pulse only while actually open — a closed shop shouldn't blink. */
.hours[data-open='true'] .hours__dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px solid var(--open);
  animation: pulse 2.4s ease-out infinite;
}
.on-blue .hours { color: var(--paper-on-blue); }

@keyframes pulse {
  0%   { opacity: .7; transform: scale(.6); }
  70%  { opacity: 0;  transform: scale(1.25); }
  100% { opacity: 0;  transform: scale(1.25); }
}

/* --- Capability grid ------------------------------------------------------ */
/* The client's own full service list, verbatim from their Plumbing page.
   1px grid gaps over a rule-coloured backdrop give hairline dividers with
   no borders to double up at the seams. */

.capabilities__head { margin-bottom: var(--s5); }
.capabilities h2 {
  font-size: clamp(30px, 4.6vw, 58px);
  line-height: 0.96;
  text-transform: uppercase;
  margin-top: var(--s3);
}

/* Hairlines are drawn as a 1px ring on each cell rather than as gaps over a
   coloured backdrop: adjacent rings meet inside the 1px gap to form a single
   crisp line, and a part-filled last row leaves paper behind instead of a
   stray block of rule colour. */
/* minmax(0, 1fr), not 1fr: grid tracks default to min-width:auto, so a long
   item ("Line Repair and Installation") refuses to shrink and pushes the whole
   grid past the viewport. That was 7px of horizontal scroll on a 344px phone.
   Single column on the narrowest phones. */
.caps {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--s4);
}

@media (min-width: 460px) {
  .caps { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s5); }
}
.caps li {
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 21px 0;
  border-bottom: 1px solid var(--rule);
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: clamp(14px, 1.2vw, 17px);
  line-height: 1.25;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--blue);
}
/* Decorative diamond — literal logo red is fine here, it carries no text. */
.caps li::before {
  content: '';
  flex: none;
  width: 7px;
  height: 7px;
  background: var(--red);
  transform: rotate(45deg);
}

@media (min-width: 1080px) {
  .caps { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s6); }
}

/* --- Contact page --------------------------------------------------------- */

.contact__grid { display: grid; gap: var(--s6); }

@media (min-width: 900px) {
  .contact__grid { grid-template-columns: 1.05fr 1fr; gap: var(--s7); }
}

.cform { display: grid; gap: var(--s3); }
.cform__row { display: grid; gap: var(--s3); }
.cform__trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

@media (min-width: 560px) {
  .cform__row { grid-template-columns: 1fr 1fr; }
}

.field { display: grid; gap: 7px; }
.field label {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--blue-deep);
}
.field input,
.field textarea {
  font-family: var(--ff-body);
  font-size: 17px;
  line-height: 1.5;
  color: var(--blue-deep);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 13px 14px;
  width: 100%;
  transition: border-color .18s ease;
}
.field input:hover,
.field textarea:hover { border-color: var(--blue); }
.field textarea { min-height: 148px; resize: vertical; }

/* Status messages ship hidden and are only revealed by the form script. */
.cform__msg {
  display: none;
  font-family: var(--ff-ui);
  font-size: 14.5px;
  line-height: 1.5;
  padding: 14px 16px;
  border-left: 3px solid var(--blue);
  background: rgba(7, 68, 149, .05);
  color: var(--blue-deep);
}
.cform__msg--error { border-left-color: var(--red-cta); background: rgba(201, 2, 6, .05); }
.cform__fallback { display: block; margin-top: 8px; font-size: 13.5px; opacity: .85; }
.cform__fallback a { color: var(--red-cta); }
.cform__msg[data-show='true'] { display: block; }

.cform__note {
  font-family: var(--ff-ui);
  font-size: 13px;
  line-height: 1.55;
  color: var(--blue-deep);
  opacity: .75;
}

.contact h2 {
  font-size: clamp(27px, 3.4vw, 44px);
  line-height: 0.98;
  text-transform: uppercase;
  /* Without this the red rule above collides with the cap height. */
  margin-block: var(--s3) var(--s4);
}

/* Contact details column.
   Inherited properties live on the container, NOT on `.cinfo dd` — a
   descendant selector there (0,1,1) outranks single-class component selectors
   like .cinfo__phone and .hours (0,1,0) and silently overrides them. */
.cinfo {
  font-family: var(--ff-body);
  font-size: 17px;
  line-height: 1.55;
  color: var(--blue-deep);
}
.cinfo dt {
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red-cta);
  margin-bottom: 4px;
}
.cinfo dd { margin-bottom: var(--s4); }
.cinfo dd + dd { margin-top: calc(var(--s4) * -1 + 4px); }
.cinfo dd:last-child { margin-bottom: 0; }
.cinfo a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); }
.cinfo a:hover { border-bottom-color: var(--blue); }

.cinfo__phone {
  font-family: var(--ff-display);
  font-size: clamp(34px, 4.4vw, 56px);
  line-height: 1;
  letter-spacing: 0.01em;
}
.cinfo__phone a { border-bottom: 0; color: var(--blue); }
.cinfo__phone a:hover { color: var(--red-cta); }

/* --- Floating controls ---------------------------------------------------- */
/* Both ship `hidden` and are revealed by JS, so neither can become a dead
   control if the script fails to load. */

.to-top {
  position: fixed;
  right: 16px;
  bottom: 84px;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  background: var(--paper);
  border: 1px solid var(--blue);
  border-radius: 2px;
  color: var(--blue);
  text-decoration: none;
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity .25s ease, transform .25s ease,
              background-color .15s ease, color .15s ease;
}
.to-top.is-in { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: var(--blue); color: var(--paper-on-blue); }
.to-top svg { width: 18px; height: 18px; fill: currentColor; }

/* Thumb-reach call bar. Phone-only — the header call button covers desktop. */
.callbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  gap: 1px;
  background: var(--rule);
  border-top: 1px solid var(--rule);
  transform: translateY(100%);
  transition: transform .28s ease;
}
.callbar.is-in { transform: none; }
.callbar__btn {
  flex: 1;
  padding: 16px 10px;
  background: var(--paper);
  color: var(--blue);
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.02em;
  text-align: center;
  text-decoration: none;
}
.callbar__btn--red { background: var(--red-cta); color: var(--paper); }

/* Only reserved once JS has actually shown the bar. Safe-area padding keeps the
   buttons clear of the home indicator on notched phones. */
.callbar { padding-bottom: env(safe-area-inset-bottom, 0px); }
.js-callbar body { padding-bottom: calc(54px + env(safe-area-inset-bottom, 0px)); }
.to-top { bottom: calc(84px + env(safe-area-inset-bottom, 0px)); }

@media (min-width: 760px) {
  .callbar { display: none; }
  .to-top { bottom: 24px; right: 24px; }
  .js-callbar body { padding-bottom: 0; }
}

/* Touch: the utility links (25px) and carousel dots (22px) were far under a
   comfortable tap target. Padded out only for coarse pointers so the desktop
   strip stays compact. */
@media (pointer: coarse) {
  .utility__item { padding-block: 9px; }
  .deck__dot { height: auto; padding: 18px 0; }
  .crumb a { padding-block: 8px; }
  .cinfo a, .cform__note a { padding-block: 4px; display: inline-block; }
  /* Footer nav was 15px tall with 10px between rows — a mis-tap waiting to
     happen. Padding does the work, so the visual gap comes back down. */
  .footer__nav ul { gap: 0; }
  .footer__nav a { display: inline-block; padding-block: 10px; }
  .footer__meta a { display: inline-block; padding-block: 9px; }
}

/* --- Scroll reveal -------------------------------------------------------- */
/* Gated on .js-reveal, which main.js only sets when IntersectionObserver is
   available. Without it every element stays at its natural opacity. */

.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .65s cubic-bezier(.22,.61,.36,1),
              transform .65s cubic-bezier(.22,.61,.36,1);
  transition-delay: calc(var(--reveal-i, 0) * 70ms);
}
.js-reveal [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* Printing never scrolls, so nothing would ever be revealed on paper. */
@media print {
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; }
  .to-top, .callbar { display: none !important; }
}

/* --- Motion ---------------------------------------------------------------- */

/* Header tightens once the page scrolls, giving content more room. Shrinking
   the logo only ever frees header space, so the nav cannot be pushed to wrap. */
.site-header {
  transition: box-shadow .25s ease;
}
.header__inner { transition: padding-block .25s ease; }
.brand__logo { transition: width .25s ease; }

.site-header.is-stuck {
  box-shadow: 0 1px 2px rgba(5, 46, 99, .08), 0 8px 20px rgba(5, 46, 99, .05);
}
.site-header.is-stuck .header__inner { padding-block: 6px; }
.site-header.is-stuck .brand__logo { width: 136px; }

/* Slow drift on the live hero photo. Restarted per slide by the deck script.
   Runs longer than the 7.5s advance so it never reaches the end and snap-holds. */
.js-deck .deck__slide[data-active='true'] img { animation: kenburns 11s ease-out both; }
@keyframes kenburns {
  from { transform: scale(1.02); }
  to   { transform: scale(1.10); }
}

.svc__media img { transition: transform .5s cubic-bezier(.22,.61,.36,1); }
.svc:hover .svc__media img { transform: scale(1.04); }
.svc__arrow { transition: transform .18s ease; }
.svc:hover .svc__arrow { transform: translateX(3px); }

.btn { transition: background-color .15s ease, color .15s ease,
                   border-color .15s ease, transform .12s ease; }
.btn:active { transform: translateY(1px); }

@media (min-width: 900px) {
  .site-header.is-stuck .brand__logo { width: 172px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
  /* Reveal targets must not be left invisible when transitions are stripped. */
  .js-reveal [data-reveal] { opacity: 1; transform: none; }
  .js-deck .deck__slide[data-active='true'] img { animation: none; }
  .hours[data-open='true'] .hours__dot::after { animation: none; opacity: 0; }
  .deck__progress.is-running::after { animation: none; }
  /* A continuously scrolling banner is exactly what this preference is for. */
  .ticker__track { animation: none; }
  /* Hero copy must land instantly, not fade. */
  .hero__eyebrow, .hero h1, .hero__lead, .hero .btn-row {
    animation: none;
    opacity: 1;
    transform: none;
  }
  /* Headlines skip the waterline and sit at their filled colour. */
  .hero h1.waterfill,
  .waterfill[data-reveal].is-in { animation: none; }
  .waterfill { background-position: 50% 100%; }

  /* Hover water: no rise — the colour change alone still reads. */
  .btn::before,
  .social a::before,
  .svc__media::before { transition: none; border-radius: 0; }
  .svc:hover .svc__media img { transform: none; }
  .svc:hover .svc__arrow { transform: none; }
}
