/* ==========================================================================
   Zoom Express Car Wash — design system
   Single source of truth. /brand-guide displays these same variables.

   COLOR CONTEXTS: components never hardcode a color. Each section sets a
   context (.section--paper / --pale / --blue / dark default) which redefines
   the same token names. That is what lets one card component sit on a white
   band and a near-black band and stay AA compliant in both.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. BRAND CONSTANTS — sampled from the logo. These never change per context.
   -------------------------------------------------------------------------- */
:root {
  --brand-blue: #0071BB;   /* wordmark blue — 5.15:1 on white, CTA fill */
  --brand-blue-dk: #005A96;/* CTA hover — 7.23:1 with white text */
  --club-muted: #2B343C;   /* muted copy on the bright club band — 5.35:1 on sky */
  --brand-sky:  #49B3E6;   /* accent on DARK only — 2.37:1 on white, never text there */
  --brand-foam: #ABE0F9;   /* eyebrows/borders on DARK only */

  --ink:      #231F20;     /* the logo's own outline black */
  /* Deep blues used by the filled tier card and the photoless contact hero.
     They were literals inside rules, which meant /brand-guide could not see
     them. Every one is measured where it is used. */
  --brand-blue-deep: #0A3A5C;   /* foot of the featured tier gradient */
  --hero-ramp-1: #08121C; --hero-ramp-2: #0B1E2D; --hero-ramp-3: #0A2336;
  --hero-ramp-4: #0C2233; --hero-ramp-5: #0A1C2B;
  /* The tunnel drift on the Unlimited Club band. All darker than --brand-blue
     except ramp-3 and ramp-4; the lightest, #0A76C0, is 4.81:1 for white. */
  --club-ramp-1: #0A5F9C; --club-ramp-2: #004E82; --club-ramp-3: #0A6AAE;
  --club-ramp-4: #0A76C0; --club-ramp-5: #00548C;
  --ink-deep: #171314;
  --surface:  #302B2C;
  --mist:     #C9D6DE;
  --white:    #FFFFFF;

  --paper: #FFFFFF;
  --pale:  #EAF6FD;        /* pale blue wash */
  --text-dk:    #1C2226;   /* 16.08:1 on white */
  --text-dk-mut:#4A5560;   /* 7.61:1 on white, 6.92:1 on pale */
  --on-blue:    #E8F4FB;   /* 4.60:1 on brand blue */

  --line:      rgba(171, 224, 249, 0.18);
  --line-soft: rgba(171, 224, 249, 0.10);

  --font: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --w-reg: 400; --w-med: 600; --w-bold: 800; --w-black: 900;

  /* Multiplied by the reading-size control in the accessibility panel. 1 by
     default, so nothing changes until someone asks for it. */
  --a11y-scale: 1;
  --fs-body: calc(17px * var(--a11y-scale)); --fs-lead: calc(20px * var(--a11y-scale));
  --fs-small: calc(14px * var(--a11y-scale));
  --fs-h1: calc(54px * var(--a11y-scale)); --fs-h2: calc(42px * var(--a11y-scale));
  --fs-h3: calc(26px * var(--a11y-scale)); --fs-eyebrow: calc(14px * var(--a11y-scale));
  --fs-mega: clamp(64px, 13vw, 190px);   /* oversized display numerals */

  --lh-tight: 0.95; --lh-head: 1.1; --lh-body: 1.65;

  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px;
  --s-6:32px; --s-7:48px; --s-8:64px; --s-9:96px; --s-10:128px;
  --section-y: var(--s-9);
  --gutter: var(--s-5);

  --btn-radius: 6px; --btn-pad-y: 17px; --btn-pad-x: 34px; --btn-track: 0.05em;
  --maxw: 1200px; --maxw-prose: 68ch;
  --header-h: 126px;      /* utility 42 + masthead 84 */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 220ms;
  --slant: -7deg;          /* matches the italic lean of the logo wordmark */

  /* ---- DARK CONTEXT (the default) ---- */
  --bg: var(--ink);
  --text: var(--white);
  --text-muted: var(--mist);
  --heading: var(--white);
  --eyebrow-c: var(--brand-foam);
  --card-bg: var(--surface);
  --card-line: var(--line-soft);
  --accent: var(--brand-sky);
  --accent-on: var(--ink);
  --accent-soft: rgba(73, 179, 230, 0.14);
  --link-c: var(--brand-foam);
  --link-c-hover: var(--brand-sky);
  --btn1-bg: var(--brand-blue);
  --btn1-fg: var(--white);
  --btn1-bg-hover: var(--brand-sky);
  --btn1-fg-hover: var(--ink);
  --btn2-fg: var(--brand-foam);
  --btn2-line: var(--brand-foam);
  --btn2-fg-hover: var(--ink);
  --btn2-bg-hover: var(--brand-foam);
  --rule: var(--line);
}

/* ---- LIGHT CONTEXT ---- */
.section--paper, .section--pale, .ctx-light {
  --bg: var(--paper);
  --text: var(--text-dk);
  --text-muted: var(--text-dk-mut);
  --heading: var(--ink);
  --eyebrow-c: var(--brand-blue);
  --card-bg: var(--paper);
  --card-line: rgba(0, 113, 187, 0.18);
  --accent: var(--brand-blue);          /* sky is NEVER text on light */
  --accent-on: var(--white);
  --accent-soft: rgba(0, 113, 187, 0.09);
  --link-c: var(--brand-blue);
  --link-c-hover: var(--brand-blue-dk);
  --btn1-bg: var(--brand-blue);
  --btn1-fg: var(--white);
  --btn1-bg-hover: var(--brand-blue-dk);
  --btn1-fg-hover: var(--white);
  --btn2-fg: var(--brand-blue);
  --btn2-line: var(--brand-blue);
  --btn2-fg-hover: var(--white);
  --btn2-bg-hover: var(--brand-blue);
  --rule: rgba(0, 113, 187, 0.20);
}
.section--pale { --bg: var(--pale); --card-bg: var(--paper); }

/* ---- BLUE BAND CONTEXT ---- The one sanctioned CTA inversion: on a solid
   brand-blue band the primary button flips to white fill with blue text.
   Same two colors, swapped — still reads as the same button. ---- */
.section--blue, .ctx-blue {
  --bg: var(--brand-blue);
  /* Pure white, not --on-blue. The tint measured 4.60:1 — barely over the floor,
     which left no room for anything to sit behind it. White gives 5.15:1. */
  --text: var(--white);
  --text-muted: var(--white);
  --heading: var(--white);
  --eyebrow-c: var(--white);            /* foam is only 3.62:1 here — banned */
  --card-bg: rgba(255, 255, 255, 0.10);
  --card-line: rgba(255, 255, 255, 0.28);
  --accent: var(--white);
  --accent-on: var(--brand-blue);
  --accent-soft: rgba(255, 255, 255, 0.14);
  --link-c: var(--white);
  --link-c-hover: var(--on-blue);
  --btn1-bg: var(--white);
  --btn1-fg: var(--brand-blue);
  --btn1-bg-hover: var(--pale);
  --btn1-fg-hover: var(--brand-blue-dk);
  --btn2-fg: var(--white);
  --btn2-line: var(--white);
  --btn2-fg-hover: var(--brand-blue);
  --btn2-bg-hover: var(--white);
  --rule: rgba(255, 255, 255, 0.3);
}

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

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  /* clip is preferred over hidden: hidden makes body a scroll container, which
     can break position:sticky on descendants (our header). clip contains
     overflow without that side effect. hidden stays first as the fallback. */
  overflow-x: hidden;
  overflow-x: clip;
}
img, picture, video, svg { max-width: 100%; display: block; }
img, video { height: auto; }

h1, h2, h3, h4 {
  margin: 0 0 var(--s-4);
  color: var(--heading);
  font-weight: var(--w-black);
  line-height: var(--lh-head);
  letter-spacing: -0.025em;
}
h1 { font-size: var(--fs-h1); line-height: var(--lh-tight); text-transform: uppercase; }
h2 { font-size: var(--fs-h2); text-transform: uppercase; }
h3 { font-size: var(--fs-h3); font-weight: var(--w-bold); letter-spacing: -0.01em; }

p { margin: 0 0 var(--s-4); }
p:last-child { margin-bottom: 0; }
a { color: var(--link-c); text-underline-offset: 3px; }
a:hover { color: var(--link-c-hover); }
ul, ol { margin: 0 0 var(--s-4); padding-left: var(--s-5); }
li { margin-bottom: var(--s-2); }
strong { color: var(--heading); }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: 200;
  padding: var(--s-3) var(--s-5);
  background: var(--brand-sky); color: var(--ink);
  font-weight: var(--w-bold); border-radius: var(--btn-radius);
  text-decoration: none; transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

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

/* --------------------------------------------------------------------------
   3. LAYOUT
   -------------------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.wrap--narrow { max-width: 880px; }

.section {
  position: relative;
  padding-block: var(--section-y);
  background: var(--bg);
  color: var(--text);
}
.section--deep { --bg: var(--ink-deep); }
/* Unlimited Club — a slow drift through the wash tunnel. All blues, no black.

   The movement is made of DARKER blues passing through, never lighter ones.
   White text on --brand-blue is 5.15:1, so lightening the band is capped at
   about 0.15 alpha before it fails — invisible in practice. Darkening has no
   ceiling and only raises contrast, so depth comes from shadow drifting past
   rather than highlights streaking across.

   Every moving layer is a large soft radial with a vertical radius of ~42%, so
   it fades out well before the top and bottom edges. That leaves both edges a
   flat --brand-blue, which is what lets the wave dividers meet it cleanly — the
   previous version had colour changing right at the boundary. */
.section--club {
  --bg: var(--brand-blue);
  --text: var(--white);
  --text-muted: rgba(255, 255, 255, 0.88);
  --heading: var(--white);
  --eyebrow-c: var(--brand-foam);
  --card-bg: transparent;
  --card-line: rgba(255, 255, 255, 0.30);
  --accent: var(--brand-foam);
  --accent-on: var(--ink);
  --accent-soft: rgba(255, 255, 255, 0.08);
  --link-c: var(--brand-foam);
  --link-c-hover: var(--white);
  --btn1-bg: var(--white);
  --btn1-fg: var(--brand-blue);
  --btn1-bg-hover: var(--pale);
  --btn1-fg-hover: var(--brand-blue-dk);
  --btn2-fg: var(--white);
  --btn2-line: var(--white);
  --btn2-fg-hover: var(--brand-blue);
  --btn2-bg-hover: var(--white);
  --rule: rgba(255, 255, 255, 0.28);

  /* One continuous sweep of blues, no beams and no tiling seam. The radial
     version repeated on the x-axis and every tile join showed as a hard vertical
     band down the section — this is a single linear gradient whose first and
     last stops are the same colour, so it loops seamlessly at 300% width.

     Every stop was measured: the lightest is #0A76C0 at 4.81:1 for white text,
     the rest sit between 5.1:1 and 8.7:1. Nothing in the sweep goes brighter
     than the ceiling white text can survive. */
  background-color: var(--brand-blue);
  background-image: linear-gradient(100deg,
    var(--brand-blue) 0%, var(--club-ramp-1) 14%, var(--club-ramp-2) 28%, var(--club-ramp-3) 42%,
    var(--brand-blue-dk) 56%, var(--club-ramp-4) 70%, var(--club-ramp-5) 85%, var(--brand-blue) 100%);
  background-size: 300% 100%;
  background-repeat: repeat-x;
  animation: tunnelDrift 60s linear infinite;
}
@keyframes tunnelDrift {
  from { background-position:    0% 50%; }
  to   { background-position: -300% 50%; }
}

/* Week cells stay transparent so the drift shows through instead of sitting
   behind flat panels. The 1px grid gaps carry the rule colour. */
.section--club .week__day { background: transparent; }
.section--club .week { background: var(--rule); }
.section--tight { padding-block: var(--s-7); }
/* For sections that should read in a single screen. Compresses further as the
   viewport gets shorter rather than holding a fixed 96px. */
.section--snug { padding-block: clamp(32px, 4vw, 60px); }
/* Where a wave divider follows immediately, the divider's own flat area already
   reads as section padding. Carrying full padding as well left a dead band. */
.section--tight-b { padding-bottom: var(--s-4); }

/* Display italic — echoes the lean of the logo wordmark. Used on one or two
   words per headline, never a whole paragraph. */
.tilt { display: inline-block; transform: skewX(var(--slant)); font-style: italic; }
.hl { color: var(--accent); }

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-4);
  color: var(--eyebrow-c);
  font-size: var(--fs-eyebrow); font-weight: var(--w-black);
  letter-spacing: 0.2em; text-transform: uppercase;
}
.eyebrow::before {
  content: ""; width: 28px; height: 3px;
  background: currentColor; transform: skewX(var(--slant));
}
.center .eyebrow::before { display: none; }

.lead { font-size: var(--fs-lead); color: var(--text-muted); max-width: var(--maxw-prose); }
.muted { color: var(--text-muted); }
.fine { font-size: var(--fs-small); color: var(--text-muted); }
.center { text-align: center; }
.center .lead { margin-inline: auto; }

.grid { display: grid; gap: var(--s-5); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

/* --------------------------------------------------------------------------
   4. WAVE DIVIDERS — the logo's crest, used as the edge between bands
   -------------------------------------------------------------------------- */
/* A wave sits BETWEEN two bands, so it has to know both colors: the flat area
   above it takes the outgoing band's color, the curve takes the incoming one.
   Set both per instance:  style="--wave-from:var(--ink);--wave-to:var(--paper)" */
.wave {
  display: block; width: 100%;
  height: clamp(44px, 6vw, 96px);
  margin-block: -2px; position: relative; z-index: 1;
  background: var(--wave-from, var(--ink));
  fill: var(--wave-to, var(--paper));
  /* The drift is applied to the <path> INSIDE the svg, not to the svg box. An
     oversized box would widen the document and expose the body background down
     the right-hand side; an svg clips to its own viewBox, so the motion stays
     contained. */
  /* Nudges the raster onto whole pixels; without it the curve's lower edge can
     land on a half pixel and reveal a hairline of the band behind. */
  shape-rendering: geometricPrecision;
}
.wave--up { transform: rotate(180deg); }

/* Under a hero, both the crest and the curve of the lines are dropped: the photo
   runs to a straight edge and two level lines cross it. Everywhere else the
   dividers stay curved — this is the hero treatment only.

   The lines become background stripes rather than paths, because a path cannot
   be straightened from CSS and the alternative was six hand-edited copies of the
   same geometry. They sit in the same band the drawn lines occupied, and stay
   white because every flat divider sits under a dark hero. */
.wave--flat .wave__crest, .wave-in--flat .wave__crest,
.wave--flat .wave__line,  .wave-in--flat .wave__line { display: none; }
.wave--flat, .wave-in--flat {
  background-image: linear-gradient(to bottom,
    transparent 0 43%,
    rgba(255,255,255,0.20) 43% 46%,
    transparent 46% 56%,
    rgba(255,255,255,0.40) 56% 60%,
    transparent 60%);
}

/* Dividers for a section whose background MOVES.
   A normal .wave paints its flat area a solid colour, which can never match an
   animated gradient — that mismatch is the hard line at the top and bottom of
   the club band. These sit INSIDE the section instead, with a transparent flat
   area, so the section's own gradient shows straight through and only the curve
   is painted in the neighbouring band's colour. Nothing to keep in sync. */
.wave-in {
  position: absolute; left: 0; right: 0; width: 100%;
  height: clamp(44px, 6vw, 96px);
  display: block; background: transparent;
  fill: var(--paper);
  z-index: 0; pointer-events: none;
}
.wave-in--top { top: -1px; transform: scaleY(-1); }
.wave-in--bot { bottom: -1px; }

/* The logo's wave is asymmetric — a long tapering tail rising into a tall
   curling crest — with lighter strokes layered along it. A plain sine curve
   carried none of that. The crest path is the fill; the two slivers above it
   are those layered strokes, drawn in the same colour at lower opacity so they
   work on any pair of bands without needing a third colour. */
.wave .wave__line,
.wave-in .wave__line { fill-opacity: 0.40; }
.wave .wave__line--2,
.wave-in .wave__line--2 { fill-opacity: 0.20; }
.section--club {
  position: relative; overflow: hidden;
  /* The inner divider overlays the top of this section, so the copy needs to
     clear it rather than starting right under the curve. */
  padding-top: calc(var(--section-y) + 56px);
  /* The closing buttons need the same clearance from the bottom divider that the
     heading already has from the top one. */
  padding-bottom: calc(var(--section-y) + 56px);
}
.wave path {
  transform-origin: 50% 50%;
  animation: waveDrift 17s ease-in-out infinite alternate;
}
/* Scaled 6% wider than the viewBox so the ends stay covered through the travel. */
@keyframes waveDrift {
  from { transform: scaleX(1.06) translateX(-9px); }
  to   { transform: scaleX(1.06) translateX(9px); }
}

/* --------------------------------------------------------------------------
   5. BUTTONS — two styles, colors come from the active context
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border-radius: var(--btn-radius);
  font: var(--w-black) 15px/1 var(--font);
  letter-spacing: var(--btn-track); text-transform: uppercase;
  text-decoration: none; border: 2px solid transparent; cursor: pointer;
  min-height: 52px;
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.btn--primary {
  background: var(--btn1-bg); color: var(--btn1-fg); border-color: var(--btn1-bg);
  position: relative; overflow: hidden;
}
/* A light sweep on hover — the site is selling shine, so the CTA may as well. */
.btn--primary::after {
  content: ""; position: absolute; top: 0; left: -75%;
  width: 45%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.38), transparent);
  transform: skewX(-22deg);
  transition: left 700ms var(--ease);
  pointer-events: none;
}
.btn--primary:hover::after, .btn--primary:focus-visible::after { left: 130%; }
.btn--primary:hover, .btn--primary:focus-visible {
  background: var(--btn1-bg-hover); border-color: var(--btn1-bg-hover); color: var(--btn1-fg-hover);
  transform: translateY(-2px); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.28);
}
.btn--secondary { background: transparent; color: var(--btn2-fg); border-color: var(--btn2-line); }
.btn--secondary:hover, .btn--secondary:focus-visible {
  background: var(--btn2-bg-hover); color: var(--btn2-fg-hover); transform: translateY(-2px);
}
.btn--block { width: 100%; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.center .btn-row { justify-content: center; }

/* --------------------------------------------------------------------------
   6. HEADER
   -------------------------------------------------------------------------- */
.utility {
  background: var(--ink-deep); border-bottom: 1px solid var(--line-soft);
  font-size: 13px;
  transition: font-size var(--dur) var(--ease);
}
.utility__inner { display: flex; align-items: center; justify-content: flex-end; gap: var(--s-4); min-height: 42px;
  transition: min-height var(--dur) var(--ease); }
/* Social icons live in the footer only. */
.social { display: flex; align-items: center; gap: var(--s-1); }
.social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; color: var(--mist); border-radius: var(--btn-radius);
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.social a:hover, .social a:focus-visible { color: var(--brand-sky); background: rgba(73,179,230,0.12); }
.social svg { width: 18px; height: 18px; fill: currentColor; }

.utility__links { display: flex; align-items: center; gap: var(--s-1); }
.utility__link {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); min-height: 38px;
  color: var(--white); font-weight: var(--w-black);
  letter-spacing: 0.1em; text-transform: uppercase; text-decoration: none;
  white-space: nowrap; border-radius: var(--btn-radius);
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease),
              padding var(--dur) var(--ease), min-height var(--dur) var(--ease);
}
.utility__link:hover, .utility__link:focus-visible { color: var(--brand-sky); background: rgba(73,179,230,0.12); }
.utility__link svg { width: 15px; height: 15px; fill: currentColor; flex: none;
  transition: width var(--dur) var(--ease), height var(--dur) var(--ease); }
.utility__sep { color: var(--line); }

/* utility bar + masthead stick as ONE unit, so the logo that rises through the
   utility strip stays contained once the header is stuck. */
.siteheader { position: sticky; top: 0; z-index: 100; }
/* The header is sticky, so an in-page anchor would otherwise scroll its target
   underneath it. Applies to every #id on the site — #faq, #wash-packages,
   #request, #apply, #fleet-form — not just the one that prompted it. */
:target { scroll-margin-top: calc(var(--header-h) + var(--s-4)); }

/* Compact state, applied by site.js once the page scrolls past ~40px.
   The utility strip shrinks but never disappears — "Manage My Plan" has to stay
   reachable from every page without hunting, scrolled or not. */
.siteheader.is-stuck .utility__inner   { min-height: 32px; }
.siteheader.is-stuck .masthead__inner  { min-height: 62px; }
.siteheader.is-stuck .masthead__logo   { margin-top: -20px; margin-bottom: 12px; }
.siteheader.is-stuck .masthead__logo img { width: 108px; height: 65px; }
.siteheader.is-stuck .masthead__cta    { padding: 11px 18px; min-height: 40px; }

/* Links and buttons only shrink where the nav is horizontal. Below 1161px the
   nav is a collapsed panel and the utility links are thumb targets, so they keep
   their full size no matter the scroll position. */
@media (min-width: 1161px) {
  .siteheader.is-stuck .utility            { font-size: 12px; }
  .siteheader.is-stuck .utility__link      { padding: var(--s-1) var(--s-2); min-height: 28px; }
  .siteheader.is-stuck .utility__link svg  { width: 13px; height: 13px; }
  .siteheader.is-stuck .nav__link,
  .siteheader.is-stuck .nav__toggle        { font-size: 12.5px; padding: var(--s-2) 8px; min-height: 36px; }
  .siteheader.is-stuck .nav__toggle svg    { width: 9px; height: 9px; }
  .siteheader.is-stuck .masthead__cta      { font-size: 12.5px; padding: 10px 17px; min-height: 38px; }
  .siteheader.is-stuck .nav__menu          { top: calc(100% + 6px); }
}
.masthead {
  background: rgba(23, 19, 20, 0.96);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line-soft);
}
.masthead__inner { display: flex; align-items: center; gap: var(--s-4); min-height: 84px;
  transition: min-height var(--dur) var(--ease); }
/* The artwork is larger than the bar and overlaps upward into the utility strip.
   Negative block margins shrink what the link contributes to layout, so the bar
   height stays fixed. At rest this leaves 12px clear above and below the mark. */
.masthead__logo {
  display: inline-flex; align-items: center; flex: none;
  margin-right: auto;
  margin-top: -26px; margin-bottom: 16px;    /* contributes 78px, artwork is 88px */
  position: relative; z-index: 5;
  transition: margin var(--dur) var(--ease);
}
.masthead__logo img {
  width: 146px; height: 88px; object-fit: contain;
  transition: width var(--dur) var(--ease), height var(--dur) var(--ease);
}

.nav { display: flex; align-items: center; gap: 2px; }
.nav__link, .nav__toggle {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: var(--s-3) 10px; min-height: 44px;
  color: var(--white); font: var(--w-black) 13.5px/1 var(--font);
  letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none;
  white-space: nowrap;                    /* stops WASH PACKAGES wrapping to 2 lines */
  background: none; border: 0; cursor: pointer; border-radius: var(--btn-radius);
  transition: color var(--dur) var(--ease), font-size var(--dur) var(--ease),
              padding var(--dur) var(--ease), min-height var(--dur) var(--ease);
}
.nav__link:hover, .nav__link:focus-visible,
.nav__toggle:hover, .nav__toggle:focus-visible { color: var(--brand-sky); }
.nav__link[aria-current="page"] { color: var(--brand-sky); }
.nav__toggle svg { width: 10px; height: 10px; fill: currentColor; transition: transform var(--dur) var(--ease); }
.nav__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }

.nav__group { position: relative; }
.nav__menu {
  position: absolute; top: calc(100% + 10px); left: 0; min-width: 214px;
  padding: var(--s-2); margin: 0; list-style: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 18px 44px rgba(0,0,0,0.5);
}
.nav__menu[hidden] { display: none; }
/* Invisible bridge across the gap between the toggle and the panel, so the
   pointer never leaves .nav__group on the way down. */
.nav__menu::before {
  content: ""; position: absolute;
  left: 0; right: 0; top: -14px; height: 14px;
}
.nav__menu li { margin: 0; }
.nav__menu a {
  display: block; padding: var(--s-3) var(--s-4);
  color: var(--white); font-size: 14px; font-weight: var(--w-med);
  text-decoration: none; border-radius: var(--btn-radius); white-space: nowrap;
}
.nav__menu a:hover, .nav__menu a:focus-visible { background: rgba(73,179,230,0.16); color: var(--brand-sky); }

.masthead__cta { flex: none; margin-left: var(--s-3); padding: 14px 22px; min-height: 46px; font-size: 13.5px;
  transition: padding var(--dur) var(--ease), min-height var(--dur) var(--ease),
              font-size var(--dur) var(--ease), background-color var(--dur) var(--ease),
              color var(--dur) var(--ease), transform var(--dur) var(--ease); }

.burger {
  display: none; align-items: center; justify-content: center;
  width: 48px; height: 48px; flex: none;
  background: transparent; border: 2px solid var(--line);
  border-radius: var(--btn-radius); color: var(--white); cursor: pointer;
}
.burger svg { width: 22px; height: 22px; fill: currentColor; }

/* --------------------------------------------------------------------------
   7. HERO — stays dark: white text over a photo needs the overlay
   -------------------------------------------------------------------------- */
.hero {
  /* Foam is the eyebrow colour on a dark band, which is right over solid ink but
     not over a photograph: measured on these heroes it fell to 4.09:1 on the
     homepage and 4.47:1 on the location page, under the 4.5:1 a 14px uppercase
     label needs. White is 5.82:1 at its worst across all twelve hero and
     breakpoint combinations, so the hero overrides the token. --mist on the note
     was fine at 7.33:1 worst and is unchanged. */
  --eyebrow-c: var(--white);
  position: relative; isolation: isolate; overflow: hidden;
  background: var(--ink-deep); color: var(--white);
  /* Starts below the header, which has its own solid bar, and fills the rest of
     the viewport. Height lives here alone — splitting it between this element's
     padding and the inner element's min-height is what previously let the two
     disagree. Fallback chain: a browser lacking svh drops only that line. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 640px;
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100svh - var(--header-h));
}
.hero__media {
  position: absolute; z-index: -2;
  /* Extended above the hero and clipped, which lifts the subject in the frame.
     object-position alone cannot do this at full-screen height: the landscape
     photo scales to cover the width and leaves only ~50px of vertical overflow,
     so the whole 0-100% range moves the image about 8px. */
  inset: -5% 0 0 0;
}
/* The base rule makes <picture> a block with auto height, so without this the
   img's height:100% has nothing to resolve against and falls back to the
   photo's own aspect. A photo wider than the hero then stops short and leaves
   a flat band at the bottom. */
.hero__media picture { height: 100%; }
.hero__media img, .hero__media video {
  width: 100%; height: 100%; object-fit: cover;
  object-position: 50% 38%;
}
/* The homepage hero scrolls with a parallax drift — see site.js. */
.hero__media[data-parallax] { will-change: transform; }
/* The homepage driver sits high in his frame. At the shared 38% the top of his
   head lands on the hero's edge at 1920x900 and is cut on anything wider or
   shorter; 24% keeps it in frame with his chin well clear of the fold. */
.hero--home .hero__media img { object-position: 50% 24%; }

/* A hero with no photograph. Flat --ink-deep read as a dead black band, so this
   drifts through deep blues instead — the same tunnel motion the Unlimited Club
   band uses, pitched dark enough to carry white copy.

   The ramp is deliberately kept in a narrow, very dark range: at every stop the
   brand-blue CTA still clears 3:1 against it (3.12:1 at the lightest), which no
   photo hero manages. White copy runs 16.07:1 at worst, the foam eyebrow
   11.30:1, the mist note 10.84:1. First and last stop match so the loop is
   seamless. */
.hero--plain {
  background-color: var(--hero-ramp-1);
  background-image: linear-gradient(105deg,
    var(--hero-ramp-1) 0%, var(--hero-ramp-2) 18%, var(--hero-ramp-3) 36%,
    var(--hero-ramp-4) 56%, var(--hero-ramp-5) 78%, var(--hero-ramp-1) 100%);
  background-size: 300% 100%;
  background-repeat: repeat-x;
  animation: tunnelDrift 60s linear infinite;
}
/* A soft light source behind the copy, so the band has a centre rather than
   reading as flat paint. Sits under the text at z-index 0. */
.hero--plain::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(120% 90% at 18% 38%, rgba(73, 179, 230, 0.16) 0%, rgba(73, 179, 230, 0) 62%),
    radial-gradient(90% 70% at 88% 84%, rgba(0, 113, 187, 0.20) 0%, rgba(0, 113, 187, 0) 60%);
}
.hero--plain .hero__inner { position: relative; z-index: 1; }
/* Fleet: white foam on a white truck, so the copy gets more scrim than the
   default. Measured on it: 6.39:1. */
.hero--fleet .hero__overlay {
  background:
    linear-gradient(90deg, rgba(10,8,9,0.95) 0%, rgba(10,8,9,0.88) 50%, rgba(10,8,9,0.26) 72%, rgba(10,8,9,0) 100%),
    linear-gradient(180deg, rgba(23,19,20,0.08) 0%, rgba(23,19,20,0.06) 45%, rgba(35,31,32,0.82) 93%, var(--ink) 100%);
}

/* Location: the white CAR WASH sign sits right behind the headline. Set for
   AAA body-text contrast (7:1), not just AA: white copy 9.97:1 at worst
   (1280x720), and the storefront still shows on the right. */
.hero--location .hero__overlay {
  background:
    linear-gradient(90deg, rgba(10,8,9,0.92) 0%, rgba(10,8,9,0.85) 55%, rgba(10,8,9,0.30) 80%, rgba(10,8,9,0) 100%),
    linear-gradient(180deg, rgba(23,19,20,0.08) 0%, rgba(23,19,20,0.06) 45%, rgba(35,31,32,0.82) 93%, var(--ink) 100%);
}

.hero__overlay {
  position: absolute; inset: 0; z-index: -1;
  /* Asymmetric on purpose: the copy sits left, the subject sits right, so the
     scrim is weighted left rather than dimming the whole frame evenly.
     Shared by the club and team heroes. Each page's comment holds the
     contrast measured on its own photo — re-measure if a photo changes. */
  /* No header scrim needed — the nav has its own bar now. */
  background:
    linear-gradient(90deg, rgba(10,8,9,0.95) 0%, rgba(10,8,9,0.82) 50%, rgba(10,8,9,0.12) 72%, rgba(10,8,9,0) 100%),
    /* Lands on solid --ink at the very bottom so a wave placed under the hero
       meets an exact colour. At 0.78 over a photograph the hero ended on a
       translucent, uneven edge and the divider showed a seam. */
    linear-gradient(180deg, rgba(23,19,20,0.08) 0%, rgba(23,19,20,0.06) 45%, rgba(35,31,32,0.82) 93%, var(--ink) 100%);
}

/* Height lives on .hero now; this just holds the copy. */
.hero__inner {
  position: relative; z-index: 1;
  width: 100%;
  padding-block: clamp(40px, 5vw, 72px);
}
.hero__content { max-width: 700px; width: 100%; }
.hero__content.center { margin-inline: auto; }
.hero h1 {
  margin-bottom: var(--s-4); color: var(--white);
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.55);
  /* .tilt is an inline-block, because the skew needs one, and an inline-block
     cannot break inside itself. So a headline ending in a long tilted phrase
     wraps the whole phrase as a unit and can strand one word on a line of its
     own. Balancing spreads the remainder evenly instead. Browsers without it
     fall back to the ragged break, which is what shipped before. */
  text-wrap: balance;
}
/* Same reason, for section headings in a narrow column. */
h2 { text-wrap: balance; }
.hero .lead {
  color: var(--white); margin-bottom: var(--s-5); max-width: 52ch;
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.5);
}
/* Hero entrance. Written as an animation, not a transition, so the resting state
   is visible: if animations never run the copy is simply there. */
@keyframes heroIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}
.hero__content > * { animation: heroIn 720ms var(--ease) both; }
.hero__content > *:nth-child(1) { animation-delay:  80ms; }
.hero__content > *:nth-child(2) { animation-delay: 180ms; }
.hero__content > *:nth-child(3) { animation-delay: 300ms; }
.hero__content > *:nth-child(4) { animation-delay: 420ms; }
.hero__content > *:nth-child(5) { animation-delay: 540ms; }

.hero__note { margin-top: var(--s-4); font-size: var(--fs-small); color: var(--mist); }
.hero__note a { color: var(--brand-foam); }

.media-pause {
  position: absolute; right: var(--s-5); bottom: var(--s-5); z-index: 3;
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4); min-height: 44px;
  background: rgba(23,19,20,0.88); color: var(--white);
  border: 1px solid var(--line); border-radius: var(--btn-radius);
  font: var(--w-bold) var(--fs-small)/1 var(--font);
  letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer;
}
.media-pause:hover { border-color: var(--brand-sky); color: var(--brand-sky); }

/* --------------------------------------------------------------------------
   8. TICKER — scrolling benefit strip. Motion that suits a brand called Zoom.
   -------------------------------------------------------------------------- */
.ticker {
  background: var(--brand-blue); color: var(--white);
  border-block: 0; overflow: hidden; padding-block: var(--s-4);
}
.ticker__track { display: flex; width: max-content; animation: ticker 34s linear infinite; }
.ticker__group { display: flex; align-items: center; flex: none; }
.ticker__item {
  display: inline-flex; align-items: center; gap: var(--s-4);
  padding-inline: var(--s-5);
  font: var(--w-black) 17px/1 var(--font);
  letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap;
}
.ticker__item::after {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--brand-foam); flex: none;
}
@keyframes ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.ticker:hover .ticker__track { animation-play-state: paused; }

/* --------------------------------------------------------------------------
   9. EDITORIAL BLOCKS — used instead of uniform card grids
   -------------------------------------------------------------------------- */
/* Oversized numeral paired with copy */
.bignum { display: grid; grid-template-columns: auto 1fr; gap: var(--s-6); align-items: start; }
.bignum__n {
  font: var(--w-black) clamp(56px, 8vw, 104px)/0.8 var(--font);
  color: transparent;
  -webkit-text-stroke: 2px var(--accent);
  letter-spacing: -0.04em;
  transform: skewX(var(--slant));
  flex: none;
}
.bignum h3 { margin-bottom: var(--s-2); }
.bignum p { color: var(--text-muted); margin: 0; }

/* Asymmetric split — deliberately not 50/50 */
.split { display: grid; grid-template-columns: 1.15fr 0.85fr; gap: var(--s-8); align-items: center; }
.split--flip { grid-template-columns: 0.85fr 1.15fr; }
/* Equal columns. .split is asymmetric by default, which suits a section where
   one side leads; this one reads better balanced. */
.split--even { grid-template-columns: 1fr 1fr; }
/* Columns of unequal length line up at the top rather than floating centred
   against each other. */
.split--top { align-items: start; }

/* Media column matches the height of the copy beside it rather than floating at
   its own aspect ratio. object-position is per-image — set it inline. */
.split--stretch { align-items: stretch; }
/* The image aligns to the headline rather than the eyebrow above it. The inset
   is the eyebrow's own height plus its margin, derived from the tokens that
   element uses so it stays correct if the eyebrow changes.
   Desktop only; the columns stack below 1040px. */
@media (min-width: 1041px) {
  .split--stretch .split__media {
    margin-top: calc(var(--fs-eyebrow) * 1.65 + var(--s-4));
  }
  /* Opt in where the image should stop at the last card instead of running down
     to the button — the inset is the button's height plus its row margin. */
  .split--to-cards .split__media {
    margin-bottom: calc(52px + var(--s-6));
  }
}
/* No min-height here: align-items:stretch already fills the row, and a
   min-height of 100% would force the box back to full height and overflow the
   margins that inset it — which is what made the image run past the cards. */
.split--stretch .split__media { display: flex; }
.split--stretch .split__media picture { display: block; width: 100%; }
.split--stretch .split__media img { width: 100%; height: 100%; max-height: 560px; object-fit: cover; border-radius: 14px; }
.split__media img { border-radius: 14px; width: 100%; }

/* Stat slab — big figures on a rule, no boxes */
/* Stat slabs. Flat figures on hairlines read as a spec sheet. These are the three
   facts that separate this wash from the one down the road, so each gets a tile it
   owns: a heavy accent rule it hangs from, and a ghosted repeat of the figure behind
   it for depth. The ghost sits at 0.05 alpha, well under text already measured. */
.slabs { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-4); }
.slab {
  position: relative; overflow: hidden;
  padding: var(--s-6) var(--s-5) var(--s-5);
  border-radius: 14px;
  /* A tint rather than --card-bg, which is white on a white band and left the
     tile invisible. Measured on it: the figure 4.53:1 and the label 6.70:1 on
     paper, 4.15:1 and 6.14:1 on pale, all over the ghost as well. */
  background: var(--accent-soft);
  border: 1px solid var(--card-line); border-top: 4px solid var(--accent);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.slab:hover { transform: translateY(-4px); box-shadow: 0 14px 34px rgba(10, 8, 9, 0.10); }
/* An icon rather than a repeat of the figure: the figure is already the loudest
   thing on the tile, so echoing it said nothing the eye had not read. Bled off
   the corner and kept at 0.12 — measured with text sitting on a stroke, the
   label holds 5.74:1 and the figure 3.88:1 on paper, 5.26:1 and 3.56:1 on pale. */
.slab__ghost {
  position: absolute; right: -18px; bottom: -22px;
  width: 124px; height: 124px;
  color: var(--accent); opacity: 0.12;
  fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
  pointer-events: none;
}
.slabs--3 { grid-template-columns: repeat(3, 1fr); }

/* The original hairline row. On a blue band the tinted tile has almost no
   contrast against the band itself, so the rules read better than a box. */
.slabs--rule { gap: 0; border-top: 2px solid var(--rule); }
.slabs--rule .slab {
  padding: var(--s-5) var(--s-4);
  background: none; border-radius: 0;
  border: 0; border-bottom: 2px solid var(--rule); border-right: 2px solid var(--rule);
}
.slabs--rule .slab:hover { transform: none; box-shadow: none; }
.slabs--rule .slab:nth-child(4n) { border-right: 0; }
.slabs--rule.slabs--3 .slab:nth-child(3n) { border-right: 0; }
.slabs--rule .slab__n { transform: none; font-size: clamp(40px, 5vw, 62px); letter-spacing: -0.03em; }
.slabs--rule .slab__l { margin-top: var(--s-2); }
.slab__n {
  position: relative; z-index: 1;
  display: block; font: var(--w-black) clamp(46px, 5.6vw, 70px)/1 var(--font);
  color: var(--accent); letter-spacing: -0.04em;
  transform: skewX(var(--slant)); transform-origin: 0 100%;
}
.slab__l { position: relative; z-index: 1;
  display: block; margin-top: var(--s-3); font-size: var(--fs-small);
  font-weight: var(--w-black); letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); }

/* Card — used sparingly now */
.card { padding: var(--s-6); background: var(--card-bg); border: 1px solid var(--card-line); border-radius: 14px; }
.card h3 { margin-bottom: var(--s-3); }
.card p { color: var(--text-muted); }
.card p:last-child { margin-bottom: 0; }

/* A photograph presented as one, with its own caption. Replaces the dashed
   placeholder box, which was scaffolding, not a layout. */
/* Checklist — what to have ready before a form. Ticks rather than bullets,
   because the point is "you can finish this in one sitting", not "here is a
   list". Two up on desktop so it reads as a glance, not a to-do. */
.checklist {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  /* A fixed row height, so one item wrapping cannot knock the second row out of
     rhythm with the first — which is what made the gap look accidental. */
  gap: var(--s-4) var(--s-6);
  align-items: start;
  width: fit-content; margin-inline: auto;
}
.checklist li {
  position: relative; padding-left: 28px;
  font-size: var(--fs-lead); line-height: 1.35; color: var(--text);
  text-align: left;
}
.checklist li::before {
  content: ""; position: absolute; left: 1px; top: 0.4em;
  width: 12px; height: 7px;
  border-left: 2.5px solid currentColor; border-bottom: 2.5px solid currentColor;
  transform: rotate(-45deg);
  opacity: 0.85;
}
@media (max-width: 720px) { .checklist { grid-template-columns: 1fr; gap: var(--s-3); } }

/* Location detail rail — the same facts, but scannable. Four labelled rows on
   an icon column, under a live open/closed line. Replaces a stack of h3 + p
   that made the reader parse prose to find a phone number. */
.info { list-style: none; margin: var(--s-6) 0 0; padding: 0; }
.info__row {
  display: flex; gap: var(--s-4); align-items: flex-start;
  padding: var(--s-4) 0; border-bottom: 1px solid var(--rule);
}
.info__row:first-child { border-top: 1px solid var(--rule); }
.info__icon {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 10px;
  background: var(--accent-soft); color: var(--accent);
}
.info__icon svg {
  width: 21px; height: 21px; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.info__k {
  display: block; margin-bottom: 2px;
  /* --fs-small, not a smaller one-off: 14px is this site's floor for fine
     print and labels like this are exactly that. */
  font-size: var(--fs-small); font-weight: var(--w-black);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted);
}
.info__v { margin: 0; font-size: var(--fs-lead); font-style: normal; color: var(--text); }
.info__v strong { color: var(--heading); }

/* Open / closed. Blue for open and muted for closed, because the palette has no
   green or red and inventing one would break the role rules — the words carry
   the state, the colour only reinforces it. */
.status {
  display: inline-flex; align-items: center; gap: var(--s-3);
  margin: var(--s-3) 0 0; padding: var(--s-1) var(--s-3);
  border-radius: 999px; background: var(--accent-soft);
  font-size: var(--fs-small); font-weight: var(--w-black);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent);
}
.status__dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 0 currentColor;
  animation: statusPulse 2.4s var(--ease) infinite;
}
.status--shut { background: var(--rule); color: var(--text-muted); }
.status--shut .status__dot { animation: none; }
@keyframes statusPulse {
  0%   { box-shadow: 0 0 0 0 rgba(0, 113, 187, 0.45); }
  70%  { box-shadow: 0 0 0 9px rgba(0, 113, 187, 0); }
  100% { box-shadow: 0 0 0 0 rgba(0, 113, 187, 0); }
}

.shot { margin: var(--s-8) 0 0; }
.shot picture { display: block; }
.shot img {
  display: block; width: 100%; height: auto;
  max-height: 460px; object-fit: cover;
  border-radius: 14px;
}
.shot figcaption {
  margin-top: var(--s-3); text-align: center;
  font-size: var(--fs-small); color: var(--text-muted);
}

/* Photo slider — a filmstrip. Every slide shares one height and keeps its own
   shape, so portraits are not cropped to a landscape box. Native sideways
   scroll with snap points does the work: swipe, trackpad and keyboard all
   function with JS off. site.js only adds the arrows. */
.slider { --slide-h: clamp(300px, 42vw, 460px); margin-top: var(--s-8); }
.slider__track {
  display: flex; gap: var(--s-3);
  margin: 0; padding: 0 0 var(--s-3); list-style: none;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  scrollbar-width: thin; scrollbar-color: var(--accent) transparent;
  border-radius: 14px;
}
.slider__track:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
.slider__slide { flex: 0 0 auto; scroll-snap-align: start; margin: 0; }
.slider__slide picture { display: block; }
.slider__slide img {
  display: block; height: var(--slide-h); width: auto;
  /* A landscape photo never runs wider than the screen on a phone. */
  max-width: min(86vw, 820px);
  object-fit: cover; border-radius: 14px;
}
.slider__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); margin-top: var(--s-3);
}
.slider__caption { margin: 0; font-size: var(--fs-small); color: var(--text-muted); }
.slider__nav { display: flex; gap: var(--s-2); flex-shrink: 0; }
.slider__nav[hidden] { display: none; }
/* Icon controls, not CTAs, so they sit outside the two-button rule. 44px hits
   the touch target size; the 2px edge uses the per-context outline token. */
.slider__btn {
  width: 44px; height: 44px; display: grid; place-items: center;
  background: transparent; color: var(--accent);
  border: 2px solid var(--btn2-line); border-radius: 50%; cursor: pointer;
  transition: background-color .2s ease, color .2s ease;
}
.slider__btn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.slider__btn:hover:not(:disabled),
.slider__btn:focus-visible { background: var(--btn2-line); color: var(--bg); }
.slider__btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.slider__btn:disabled { opacity: .35; cursor: default; }
.slider__btn[hidden] { display: none; }
/* The pause button swaps its icon; its aria-label says which action it takes. */
.slider__icon-play { display: none; }
.slider__pause.is-paused .slider__icon-pause { display: none; }
.slider__pause.is-paused .slider__icon-play { display: block; }
/* JS drives a slow drift by setting scrollLeft every frame. Snap points would
   pull it back each frame and smooth scrolling would ease every step, so both
   are off once JS takes over. With JS off the track keeps snap and scrolls by hand. */
.slider__track.is-auto { scroll-snap-type: none; scroll-behavior: auto; }
@media (prefers-reduced-motion: reduce) { .slider__track { scroll-behavior: auto; } }
:root[data-a11y-motion="off"] .slider__track { scroll-behavior: auto; }

/* Wash tiers. The point is hierarchy you can read at a glance: every card
   carries a blue accent bar and a large price, and the top plan is a filled blue
   card rather than the same card with a different border.

   Contrast measured on the filled card: white text runs 5.15:1 at the lightest
   point of the gradient to 11.85:1 at the foot, and the inverted white button
   clears 5.15:1 both for its edge and its blue label. The note under the price
   uses --pale rather than translucent white, which fell to 4.18:1. */
.tiers { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); align-items: center; }
.tiers--4 { grid-template-columns: repeat(2, 1fr); gap: var(--s-5); align-items: stretch; }
.tiers--4 .tier { padding: var(--s-7) var(--s-6) var(--s-6); }
.tiers--4 .tier__name { font-size: 24px; }
.tiers--4 .tier__price { font-size: 46px; }
/* The contents run two-up inside the card. Eleven items down one column was
   most of the card's height and made the top plan twice the height of the
   bottom one; side by side they are six rows and the four cards even out.

   `columns: 2 210px` rather than a breakpoint: the shorthand sets a count and a
   minimum width, and the browser drops to one column on its own when 210px each
   will not fit. That tracks the card, which is what matters here — a media
   query only knows the viewport. */
.tiers--4 .tier__list { columns: 2 210px; column-gap: var(--s-5); }
.tiers--4 .tier__list li { break-inside: avoid; }
/* No scale on the featured card here: at two across it would overlap the card
   beside it. Depth comes from the fill and the shadow instead. */

.tier {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  padding: var(--s-6);
  background: var(--card-bg);
  border: 2px solid var(--card-line);
  border-radius: 16px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
/* Accent bar across the top of every card */
.tier::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 5px;
  background: linear-gradient(90deg, var(--brand-blue), var(--brand-sky));
}
.tier:hover {
  transform: translateY(-6px);
  border-color: var(--brand-blue);
  box-shadow: 0 22px 46px rgba(0, 113, 187, 0.20);
}
.tiers--4 .tier--featured:hover { transform: translateY(-6px); }

/* The top plan is a filled card, not an outlined one */
.tier--featured {
  --text-muted: var(--pale);
  --btn1-bg: var(--white);
  --btn1-fg: var(--brand-blue);
  --btn1-bg-hover: var(--pale);
  --btn1-fg-hover: var(--brand-blue-dk);
  background: linear-gradient(165deg, var(--brand-blue) 0%, var(--brand-blue-dk) 58%, var(--brand-blue-deep) 100%);
  border-color: transparent;
  color: var(--white);
  box-shadow: 0 26px 54px rgba(0, 58, 96, 0.34);
}
.tier--featured::before { background: linear-gradient(90deg, var(--brand-sky), var(--white)); }
.tier--featured .tier__name, .tier--featured .tier__price { color: var(--white); }
/* The card is a blue fill inside a light section, so --accent is still
   brand-blue here — blue ticks on blue. Foam reads at 4.51:1 against the
   lightest stop of the gradient, well over the 3:1 a tick needs. */
.tier--featured .tier__list li::before { border-color: var(--brand-foam); }
.tier--featured .tier__list li { color: var(--pale); }
.tier--featured:hover { border-color: transparent; box-shadow: 0 30px 62px rgba(0, 58, 96, 0.42); }

.tier__flag {
  align-self: flex-start; margin-bottom: var(--s-4);
  padding: var(--s-1) var(--s-4);
  background: var(--white); color: var(--brand-blue);
  border-radius: 999px; font-size: 12px; font-weight: var(--w-black);
  letter-spacing: 0.12em; text-transform: uppercase;
  transform: skewX(var(--slant));
}
.tier__name {
  font-size: var(--fs-h3); margin-bottom: var(--s-3);
  text-transform: uppercase; letter-spacing: -0.01em; color: var(--heading);
}
.tier__price {
  font: var(--w-black) 46px/1 var(--font);
  color: var(--brand-blue); letter-spacing: -0.03em; margin-bottom: var(--s-2);
}
.tier__price span { font-size: var(--fs-body); font-weight: var(--w-med); color: var(--text-muted); letter-spacing: 0; }
.tier__list { list-style: none; padding: 0; margin: var(--s-5) 0; flex: 1; }
.tier__list li { position: relative; padding-left: var(--s-6); margin-bottom: var(--s-3); color: var(--text-muted); }
.tier__list li::before {
  content: ""; position: absolute; left: 0; top: 9px;
  width: 14px; height: 8px;
  border-left: 3px solid var(--accent); border-bottom: 3px solid var(--accent);
  transform: rotate(-45deg);
}

/* Week strip — the whole point of unlimited is that every day qualifies, so the
   week itself becomes the graphic instead of another bullet list. Grid gap over
   a tinted background draws the rules, which means it reflows cleanly when the
   columns wrap on smaller screens. */
.week {
  list-style: none; margin: var(--s-7) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); border-radius: 14px;
  /* Not clipped, because the Today pill straddles a cell's top edge. The
     rounding moves to the corner cells instead, and is re-pointed in the two
     wrap breakpoints below where different cells sit on the corners. */
  overflow: visible;
}
.week__day:first-child { border-radius: 13px 0 0 13px; }
.week__day:last-child  { border-radius: 0 13px 13px 0; }
.week__day {
  margin: 0; padding: var(--s-5) var(--s-2);
  text-align: center; background: var(--bg);
  position: relative;
  transition: background-color var(--dur) var(--ease);
}
.week__day:hover, .week__day:focus-within { background: rgba(10, 8, 9, 0.12); }

/* Today's cell is marked by site.js. The highlight DARKENS rather than brightens
   — white text on this blue is only 5.15:1, so a lighter fill would fail, while
   a darker one takes it to 6.8:1. The foam ring and glow do the visual work.
   The "Today" pill means it is never colour alone. */
.week__day--today {
  background: rgba(10, 8, 9, 0.18);
  box-shadow: inset 0 0 0 2px var(--brand-foam), 0 0 26px rgba(171, 224, 249, 0.28);
}
.week__day--today .week__d { color: var(--brand-foam); }
/* Absolute, so marking a day costs no height. In the flow it padded the today
   cell and the grid stretched the other six to match. */
.week__today {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -50%); white-space: nowrap;
  padding: 2px 9px; border-radius: 999px;
  background: var(--brand-foam); color: var(--ink);
  font-size: 10px; font-weight: var(--w-black);
  letter-spacing: 0.14em; text-transform: uppercase;
}
.week__day:not(.week__day--today) .week__today { display: none; }

.week__d {
  display: block; font: var(--w-black) 19px/1 var(--font);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--heading);
}

.week__r { display: block; margin-top: var(--s-2); font-size: var(--fs-small); color: var(--text-muted); }

/* Add-a-car callout — the one hard number we actually have for memberships */
.addcar {
  display: flex; align-items: center; justify-content: center;
  gap: var(--s-5); flex-wrap: wrap;
  width: fit-content; margin: var(--s-7) auto 0;
  padding: var(--s-4) var(--s-7);
  border: 1px solid var(--rule); border-radius: 999px;
  background: rgba(10, 8, 9, 0.12);
}
.addcar__n {
  font: var(--w-black) clamp(46px, 6vw, 68px)/1 var(--font);
  color: var(--heading); letter-spacing: -0.04em;
  transform: skewX(var(--slant));
}
.addcar__t { font-size: var(--fs-lead); color: var(--text); text-align: left; margin: 0; }

/* Callout — a short closing note that ends a section on an action. Card on
   the band's own card color with an accent edge; works on light bands. */
.callout {
  margin-top: var(--s-6); padding: var(--s-5) var(--s-6);
  background: var(--card-bg); border-left: 4px solid var(--accent);
  border-radius: 0 10px 10px 0; box-shadow: 0 6px 20px rgba(0, 40, 70, 0.08);
}
.callout p { margin: 0; color: var(--text-muted); }
.callout .callout__k {
  font-weight: var(--w-black); font-size: var(--fs-lead);
  color: var(--heading); margin-bottom: var(--s-1);
}
.callout__link {
  display: inline-block; margin-top: var(--s-3);
  font-weight: var(--w-black); text-transform: uppercase; letter-spacing: var(--btn-track);
  color: var(--link-c); text-decoration: none;
}
.callout__link:hover, .callout__link:focus-visible { color: var(--link-c-hover); text-decoration: underline; }
.callout__link span { display: inline-block; transition: transform .2s ease; }
.callout__link:hover span, .callout__link:focus-visible span { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) { .callout__link span { transition: none; } }

/* Proof list — term + one-line payoff on a rule. Replaces dense paragraphs
   where the content is really a set of discrete claims. */
.proof { margin: var(--s-5) 0 0; border-top: 2px solid var(--rule); }
.proof__row {
  display: grid; grid-template-columns: 186px 1fr;
  gap: var(--s-5); align-items: baseline;
  padding: var(--s-3) 0; border-bottom: 1px solid var(--rule);
}
.proof__k {
  margin: 0; color: var(--accent);
  font-size: 15px; font-weight: var(--w-black);
  letter-spacing: 0.08em; text-transform: uppercase;
}
.proof__v { margin: 0; color: var(--text-muted); }

/* Suds at the foot of a blue band. Two distinct things, as real foam is:
   an opaque white mass with an uneven crest, and separate translucent bubbles
   floating above it. The previous version used dozens of tiny semi-transparent
   dots, which read as spray paint rather than foam.

   Confined to the section's lower padding, which is widened on blue bands to
   give the bubbles room. White foam behind white text would erase the text, so
   keeping it out of the copy area is structural. */
.foam {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: calc(var(--section-y) + 66px);
  overflow: hidden; pointer-events: none; z-index: 0;
}

/* Feature row — informational, not a control, so the hover is pure enhancement:
   nothing is revealed on hover that is not already readable, and nothing is
   added to the tab order. */
.feature {
  position: relative;
  display: flex; gap: var(--s-4); align-items: flex-start;
  padding: var(--s-3) var(--s-5);
  border: 1px solid transparent; border-radius: 14px;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.feature::after {
  content: ""; position: absolute;
  left: var(--s-5); bottom: var(--s-3);
  height: 3px; width: 0; background: var(--accent);
  transform: skewX(var(--slant));
  transition: width 320ms var(--ease);
}
.feature:hover {
  background: var(--card-bg);
  border-color: var(--card-line);
  transform: translateY(-4px);
  box-shadow: 0 16px 38px rgba(0, 0, 0, 0.10);
}
.feature:hover::after { width: 52px; }
.feature__icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; background: var(--accent-soft);
  border-radius: 14px; color: var(--accent);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.feature:hover .feature__icon {
  background: var(--accent); color: var(--accent-on);
  transform: rotate(-6deg) scale(1.07);
}
.feature__icon svg {
  width: 26px; height: 26px;
  fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.feature h3 { font-size: 19px; margin-bottom: var(--s-2); }
.feature p { color: var(--text-muted); margin: 0; }

/* Staggered scroll reveal */
.stagger > .reveal:nth-child(1) { transition-delay: 0ms; }
.stagger > .reveal:nth-child(2) { transition-delay: 70ms; }
.stagger > .reveal:nth-child(3) { transition-delay: 140ms; }
.stagger > .reveal:nth-child(4) { transition-delay: 210ms; }
.stagger > .reveal:nth-child(5) { transition-delay: 280ms; }
.stagger > .reveal:nth-child(6) { transition-delay: 350ms; }
.stagger > .reveal:nth-child(7) { transition-delay: 420ms; }
.stagger > .reveal:nth-child(8) { transition-delay: 490ms; }

/* Numbered steps */
.steps { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-6); }
.step { display: flex; gap: var(--s-5); align-items: flex-start; margin: 0; }
.step__num {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 58px; height: 58px; background: var(--accent); color: var(--accent-on);
  border-radius: 50%; font: var(--w-black) 23px/1 var(--font);
}
.step h3 { margin-bottom: var(--s-2); }
.step p { color: var(--text-muted); margin: 0; }

/* Placeholders */


.embed-slot {
  padding: var(--s-7) var(--s-6); background: var(--card-bg);
  border: 2px dashed var(--accent); border-radius: 14px; text-align: center;
}
.embed-slot h2, .embed-slot h3 { color: var(--accent); margin-bottom: var(--s-3); }
.embed-slot p { color: var(--text-muted); margin-bottom: var(--s-2); }
.embed-slot code {
  display: inline-block; padding: 2px 8px; background: var(--accent-soft);
  border-radius: 4px; font-size: var(--fs-small); color: var(--accent);
}

/* Form card — a solid white panel behind a Paperform embed. Paperform draws
   dark labels on a transparent background, which fails contrast on a blue
   band. White behind them restores it. Keep the privacy note outside the
   card so it stays on the band's own text color. */
.form-card {
  background: var(--paper); border-radius: 14px;
  padding: var(--s-6); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}
@media (max-width: 640px) { .form-card { padding: var(--s-4); } }

/* --------------------------------------------------------------------------
   10. FAQ
   -------------------------------------------------------------------------- */
/* Two columns once there is room. Grid rather than CSS columns: an open answer
   grows its own row, where columns would reflow every item below it into the
   other track. align-items:start keeps a closed card from stretching to match an
   open one beside it. */
.faq { display: grid; gap: var(--s-3); }
/* 1040px, not 861: below that a two-up card is too narrow for a question to
   hold one line, and a wrapped question is worse than a single column. */
@media (min-width: 1040px) {
  .faq { grid-template-columns: 1fr 1fr; gap: var(--s-3) var(--s-4); align-items: start; }
  /* The questions are written to fit; this stops a browser or a font fallback
     that measures slightly wider from breaking one onto a second line.
     min-width:0 goes with it: a nowrap string in a 1fr track would otherwise
     widen the track and knock the two columns out of true. */
  .faq summary { white-space: nowrap; }
  .faq details { min-width: 0; }
  /* An odd number of questions leaves the last one alone in the left column with
     a hole beside it. :last-child:nth-child(odd) only matches when the count is
     odd, so it spans the row and centres at one column's width — no rule needed
     per page, and it follows if a question is added or removed. */
  .faq details:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    width: calc(50% - (var(--s-4) / 2));
    margin-inline: auto;
  }
}
/* A CTA inside an answer, where the answer implies an obvious next step. */
.faq__cta { margin-top: var(--s-4); }
.faq__cta .btn { min-height: 44px; padding: var(--s-3) var(--s-5); font-size: 14px; }
.faq details { background: var(--card-bg); border: 1px solid var(--card-line); border-radius: 12px; overflow: hidden; }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-5); cursor: pointer; color: var(--heading);
  font-weight: var(--w-bold); font-size: 18px; list-style: none; min-height: 52px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 12px; height: 12px;
  border-right: 3px solid var(--accent); border-bottom: 3px solid var(--accent);
  transform: rotate(45deg) translateY(-3px); transition: transform var(--dur) var(--ease);
}
.faq details[open] summary::after { transform: rotate(225deg) translateY(-3px); }
.faq summary:hover { color: var(--accent); }
.faq__body { padding: 0 var(--s-5) var(--s-5); color: var(--text-muted); }

/* --------------------------------------------------------------------------
   11. FOOTER
   -------------------------------------------------------------------------- */
.footer { background: var(--ink-deep); color: var(--mist); border-top: 1px solid var(--line-soft); padding-block: var(--s-8) var(--s-6); }
/* Landing-page footer: the same band, without the four-column grid. Nothing to
   explore, because exploring is how you leave without converting. */
.footer--slim { padding-block: var(--s-7); }
.footer--slim .footer__logo img { margin-bottom: var(--s-4); }
.footer__legal--center { justify-content: center; }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.2fr; gap: var(--s-7); }
.footer__logo { display: inline-block; border-radius: var(--btn-radius); }
.footer__logo img { width: 190px; height: 115px; object-fit: contain; margin-bottom: var(--s-4); }
.footer h3 {
  font-size: var(--fs-small); font-weight: var(--w-black); letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--brand-foam); margin-bottom: var(--s-4);
}
.footer ul { list-style: none; padding: 0; margin: 0; }
.footer li { margin-bottom: var(--s-3); }
.footer a { color: var(--mist); text-decoration: none; }
.footer a:hover, .footer a:focus-visible { color: var(--brand-sky); text-decoration: underline; }
.footer address { font-style: normal; color: var(--mist); }
.footer strong { color: var(--white); }
.footer__bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-4);
  margin-top: var(--s-8); padding-top: var(--s-5);
  border-top: 1px solid var(--line-soft); font-size: var(--fs-small); color: var(--mist);
}
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--s-5); }
.privacy-note { margin-top: var(--s-4); font-size: var(--fs-small); color: var(--text-muted); }

/* --------------------------------------------------------------------------
   12. PROSE
   -------------------------------------------------------------------------- */
.prose { max-width: var(--maxw-prose); }
.prose h2 { font-size: 27px; margin-top: var(--s-8); text-transform: none; }
.prose h3 { font-size: 20px; margin-top: var(--s-6); }
.prose p, .prose li { color: var(--text-muted); }
.prose a { color: var(--link-c); }

/* --------------------------------------------------------------------------
   13. MOTION
   -------------------------------------------------------------------------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity 600ms var(--ease), transform 600ms var(--ease); }
.reveal.is-in { opacity: 1; transform: none; }
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .stagger > .reveal { transition-delay: 0ms !important; }
  .hero__content > * { animation: none !important; }
  .wave path { animation: none !important; transform: none !important; }
  [data-parallax] { transform: none !important; }
    .section--club, .hero--plain { animation: none !important; }
  .btn--primary::after { display: none !important; }
  .feature:hover { transform: none !important; }
  .feature:hover .feature__icon { transform: none !important; }
  .btn:hover, .tier:hover, .slab:hover { transform: none !important; }
  .status__dot { animation: none !important; }
  .ticker__track { animation: none !important; transform: none !important; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* --------------------------------------------------------------------------
   14. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1160px) {
  /* Burger appears well before the nav can squeeze — this is what was breaking
     WASH PACKAGES and UNLIMITED CLUB onto two lines. */
  .burger { display: inline-flex; }
  .nav {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: var(--s-4); background: var(--surface);
    border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  }
  .nav.is-open { display: flex; }
  .nav__link, .nav__toggle { width: 100%; justify-content: space-between; padding: var(--s-4); font-size: 15px; }
  .nav__group { position: static; }
  .nav__menu { position: static; box-shadow: none; border: 0; background: transparent; padding: 0 0 0 var(--s-4); }
  .masthead__cta { display: none; }
  .nav__cta { display: flex; margin-top: var(--s-3); }
}
@media (min-width: 1161px) { .nav__cta { display: none; } }

@media (max-width: 1040px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .split, .split--flip { grid-template-columns: 1fr; gap: var(--s-6); }
  .slabs, .slabs--3 { grid-template-columns: repeat(2, 1fr); }
  .slabs--rule .slab:nth-child(2n) { border-right: 0; }
  .slabs--rule.slabs--3 .slab:nth-child(3n) { border-right: 2px solid var(--rule); }
  .tiers { grid-template-columns: 1fr; }
  .tier--featured { padding-block: var(--s-6); }
}

@media (max-width: 900px) {
  .grid--3 { grid-template-columns: 1fr; }
  /* Seven days do not divide into four columns — the old layout left an empty
     eighth cell. A 12-track grid lets row one hold 4 (span 3) and row two hold
     3 (span 4), so both rows fill edge to edge with no gap. */
  .week { grid-template-columns: repeat(12, 1fr); }
  .week__day:nth-child(-n+4) { grid-column: span 3; }
  .week__day:nth-child(n+5)  { grid-column: span 4; }
  /* Two rows: 1-4 then 5-7, so the rounded corners move. */
  .week__day { border-radius: 0; }
  .week__day:nth-child(1) { border-top-left-radius: 13px; }
  .week__day:nth-child(4) { border-top-right-radius: 13px; }
  .week__day:nth-child(5) { border-bottom-left-radius: 13px; }
  .week__day:nth-child(7) { border-bottom-right-radius: 13px; }
  .week__d { font-size: 17px; }
}

@media (max-width: 560px) {
  /* Tighter still: three across, then Sunday takes the full width. */
  .week__day:nth-child(-n+6) { grid-column: span 4; }
  .week__day:nth-child(7)    { grid-column: span 12; }
  /* Three rows: 1-3, 4-6, then 7 full width. */
  .week__day { border-radius: 0; }
  .week__day:nth-child(1) { border-top-left-radius: 13px; }
  .week__day:nth-child(3) { border-top-right-radius: 13px; }
  .week__day:nth-child(7) { border-radius: 0 0 13px 13px; }
  .week__day { padding: var(--s-4) var(--s-2); }
}

@media (max-width: 720px) {
  :root {
    --fs-body: calc(16px * var(--a11y-scale)); --fs-lead: calc(17px * var(--a11y-scale));
    --fs-h1: calc(36px * var(--a11y-scale)); --fs-h2: calc(28px * var(--a11y-scale));
    --fs-h3: calc(20px * var(--a11y-scale));
    --section-y: var(--s-8);
    --gutter: var(--s-4);
    --header-h: 118px;    /* utility 42 + masthead 76 */
  }
  /* Phones load a portrait crop, which already frames the subject, so no
     horizontal bias is needed here — that was only ever compensating for a
     landscape photo in a tall viewport. */
  .hero__inner { padding-block: var(--s-7); }
  .hero { min-height: 520px;
          min-height: calc(100vh - var(--header-h));
          min-height: calc(100svh - var(--header-h)); }
  .hero__media { inset: 0; }
  /* .hero--home is named here too: its desktop framing outranks the bare
     selector, so without this the phone would inherit it as well. */
  .hero__media img,
  .hero--home .hero__media img { object-position: 50% 50%; }
  /* Copy runs edge to edge here, so it needs a heavier, wider grade than the
     desktop scrim provides. Measured 7.12:1 at 390x640. */
  /* .hero--location is listed here too: it outranks the bare selector, so
     without it the location hero would keep its desktop side-scrim on a phone,
     where the copy runs edge to edge and walks straight out of it. Measured on
     that photo under this grade: 7.21:1. */
  .hero__overlay,
  .hero--fleet .hero__overlay {
    background:
      radial-gradient(95% 80% at 50% 50%, rgba(10,8,9,0.46) 0%, rgba(10,8,9,0.20) 62%, rgba(10,8,9,0) 100%),
      linear-gradient(180deg, rgba(23,19,20,0.58) 0%, rgba(23,19,20,0.60) 45%, rgba(35,31,32,0.94) 93%, var(--ink) 100%);
  }
  /* Location runs a heavier phone grade: the standard one held 5.88:1 on the
     storefront. This measures 8.23:1. */
  .hero--location .hero__overlay {
    background:
      radial-gradient(95% 80% at 50% 50%, rgba(10,8,9,0.48) 0%, rgba(10,8,9,0.26) 62%, rgba(10,8,9,0.06) 100%),
      linear-gradient(180deg, rgba(23,19,20,0.66) 0%, rgba(23,19,20,0.68) 45%, rgba(35,31,32,0.94) 93%, var(--ink) 100%);
  }
  .hero .lead { max-width: none; }
  .grid--2 { grid-template-columns: 1fr; }
  .tiers--4 { grid-template-columns: 1fr; }
  .slabs { grid-template-columns: 1fr 1fr; }
  .footer__grid { grid-template-columns: 1fr; gap: var(--s-6); }
  .utility__inner { justify-content: center; padding-block: var(--s-1); }
  .utility__links { justify-content: center; flex-wrap: wrap; }
  .masthead__logo { margin-top: 0; margin-bottom: 0; }
  .masthead__logo img { width: 104px; height: 63px; }
  .masthead__inner { min-height: 76px; }
  .siteheader.is-stuck .masthead__inner { min-height: 62px; }
  .siteheader.is-stuck .masthead__logo img { width: 88px; height: 53px; }
  .siteheader.is-stuck .masthead__logo { margin-top: 0; margin-bottom: 0; }
  .btn { width: 100%; }
  .btn-row { flex-direction: column; }
  .step__num { width: 48px; height: 48px; font-size: 19px; }
  .bignum {
    grid-template-columns: 1fr; gap: var(--s-2);
    justify-items: center; text-align: center;
  }
  .bignum__n { justify-self: center; }
  .addcar__t { text-align: center; }
  .proof__row { grid-template-columns: 1fr; gap: var(--s-1); }
  .ticker__item { font-size: 14px; padding-inline: var(--s-4); }
  .media-pause { right: var(--s-4); bottom: var(--s-4); }
}

@media (max-width: 380px) {
  :root { --fs-h1: calc(32px * var(--a11y-scale)); }
  .masthead__logo img { width: 92px; height: 56px; }
}

@media print {
  .masthead, .utility, .footer, .btn, .media-pause, .ticker { display: none !important; }
  body { background: #fff; color: #000; }
}

/* --------------------------------------------------------------------------
   15. ACCESSIBILITY PANEL

   A first-party preferences panel, not a third-party overlay. It changes real
   CSS on this site — reading size, contrast, motion, link emphasis — rather
   than sitting on top of the page reinterpreting it for a screen reader.
   Everything it does is a preference the visitor chose; none of it is a
   substitute for the page being built accessibly in the first place.

   Settings persist per browser in localStorage and are applied to <html> by an
   inline snippet in the head, before first paint, so nothing flashes.
   -------------------------------------------------------------------------- */
.a11y-fab {
  position: fixed; right: 18px; bottom: 18px; z-index: 200;
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%;
  /* The wave blue from the logo, not the CTA blue. This is a utility control and
     should not read as another "Get Your Free Wash" — the two sit 2.18:1 apart,
     so they never look like the same button.
     The icon and the ring are --ink, not white: white on sky is 2.37:1, and sky
     on a white page is the same, so a white icon would fail and a sky ring would
     vanish. Ink clears both, at 6.89:1 and 16.30:1. */
  background: var(--brand-sky); color: var(--ink);
  border: 2px solid var(--ink);
  box-shadow: 0 6px 22px rgba(10, 8, 9, 0.34);
  cursor: pointer;
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.a11y-fab:hover, .a11y-fab:focus-visible { background: var(--brand-foam); transform: translateY(-2px); }
.a11y-fab svg { width: 22px; height: 22px; fill: currentColor; }

.a11y-panel {
  position: fixed; right: 18px; bottom: 74px; z-index: 201;
  width: min(300px, calc(100vw - 36px));
  max-height: min(70vh, 520px); overflow-y: auto;
  padding: var(--s-4);
  /* Literal sizes throughout, not the --fs-* tokens. Those are declared on :root
     as calc(px * --a11y-scale) and are substituted there, so they arrive here
     already multiplied — redeclaring --a11y-scale on this element cannot undo
     it. The panel is the control, not the content it controls, so it holds its
     size while the page behind it grows. */
  background: var(--paper); color: var(--text-dk);
  border: 2px solid var(--ink); border-radius: 14px;
  /* A wave-coloured cap, so the panel and the button read as one widget. */
  border-top: 5px solid var(--brand-sky);
  box-shadow: 0 18px 50px rgba(10, 8, 9, 0.28);
}
.a11y-panel[hidden] { display: none; }
.a11y-panel h2 {
  font-size: 17px; text-transform: none; letter-spacing: 0; text-wrap: initial;
  color: var(--ink); margin-bottom: var(--s-1);
}
.a11y-panel > p { font-size: 14px; line-height: 1.45; color: var(--text-dk-mut); margin-bottom: var(--s-4); }
.a11y-group { margin-bottom: var(--s-3); }
.a11y-group__label {
  display: block; margin-bottom: var(--s-2);
  font-size: 14px; font-weight: var(--w-black); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-dk-mut);
}
/* Equal tracks rather than flex basis: three buttons share the row exactly, and
   minmax(0,...) lets a track be narrower than its label instead of overflowing. */
.a11y-opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: var(--s-2); }
.a11y-opt {
  min-width: 0; min-height: 44px;
  display: flex; align-items: center; justify-content: center; gap: 2px;
  padding: var(--s-2) var(--s-2);
  background: var(--paper); color: var(--ink);
  border: 2px solid var(--ink); border-radius: 8px;
  font: var(--w-bold) 14px/1.2 var(--font); cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.a11y-opt:hover { background: var(--brand-foam); }
/* Selected state is a filled swap plus a tick, so it never rests on colour
   alone — and aria-pressed carries it for assistive tech. The tick is always in
   the box and only hidden, so selecting one does not make it wider than the two
   beside it, which is what pushed "Largest" out of its button. */
.a11y-opt::before {
  content: "\2713"; flex: none; visibility: hidden;
}
.a11y-opt[aria-pressed="true"] { background: var(--brand-sky); color: var(--ink); }
.a11y-opt[aria-pressed="true"]::before { visibility: visible; }
.a11y-reset {
  width: 100%; min-height: 44px; margin-top: var(--s-2);
  background: none; border: 0; cursor: pointer;
  color: var(--brand-blue); font: var(--w-bold) 14px/1.2 var(--font);
  text-decoration: underline;
}
.a11y-note { margin-top: var(--s-4); font-size: 14px; line-height: 1.45; color: var(--text-dk-mut); }
.a11y-note a { color: var(--brand-blue); }

/* ---- What the settings actually do ---- */

/* Larger click/tap targets are already 44px+, so reading size only moves type. */

/* Higher contrast: pushes body and muted text to full ink, darkens the blue so
   it clears 7:1 on white, and makes every hairline visible. */
:root[data-a11y-contrast="more"] {
  --text-dk: #000000;          /* 21:1 on white, from 16.08:1  */
  --text-dk-mut: #26313A;      /* 13.27:1, from 7.61:1         */
  --brand-blue: #005A96;       /* 7.23:1, from 5.15:1 — clears AAA */
  --brand-blue-dk: #00406E;    /* hover has to stay darker than the new base */
  --link-c: #005A96;
  --mist: #FFFFFF;             /* muted text on dark bands goes to full white */
  --on-blue: #FFFFFF;
  /* These three are literal rgba of the old blue, so they do not follow
     --brand-blue. Hairlines and tints are what separate a card from the band
     behind it, and at 0.18-0.20 alpha they are the first thing to disappear. */
  --rule: rgba(0, 64, 110, 0.55);
  --card-line: rgba(0, 64, 110, 0.55);
  --accent-soft: rgba(0, 90, 150, 0.16);
}
:root[data-a11y-contrast="more"] .card,
:root[data-a11y-contrast="more"] .faq details,
:root[data-a11y-contrast="more"] .tier,
:root[data-a11y-contrast="more"] .slab { border-color: currentColor; }
:root[data-a11y-contrast="more"] .hero__overlay { background: rgba(10, 8, 9, 0.78); }

/* Link emphasis: underline every in-content link and thicken focus rings. */
:root[data-a11y-links="on"] main a:not(.btn) {
  text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px;
}
:root[data-a11y-links="on"] a:focus-visible,
:root[data-a11y-links="on"] button:focus-visible { outline-width: 4px; }

/* Motion off: the same switch prefers-reduced-motion throws, chosen here rather
   than at the OS. Duplicated deliberately — a visitor may want it on this site
   without changing their whole machine. */
:root[data-a11y-motion="off"] *,
:root[data-a11y-motion="off"] *::before,
:root[data-a11y-motion="off"] *::after {
  animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important; scroll-behavior: auto !important;
}
:root[data-a11y-motion="off"] .reveal { opacity: 1 !important; transform: none !important; }
:root[data-a11y-motion="off"] .ticker__track,
:root[data-a11y-motion="off"] .wave path,
:root[data-a11y-motion="off"] .section--club,
:root[data-a11y-motion="off"] .hero--plain,
:root[data-a11y-motion="off"] .foam span,
:root[data-a11y-motion="off"] .status__dot { animation: none !important; }
:root[data-a11y-motion="off"] .hero__content > * { animation: none !important; }
:root[data-a11y-motion="off"] [data-parallax] { transform: none !important; }

/* --------------------------------------------------------------------------
   THANK YOU PAGE — /thank-you
   One full-height blue band. Modelled on the reference in PAGES.md: check,
   headline, three steps, location card, one button out.
   The gradient runs from --brand-blue-dk to --brand-blue, so it only ever
   darkens the band: white text is 5.15:1 at worst. The step and card panels
   darken too (black, not white) — a white tint behind white text is banned.
   -------------------------------------------------------------------------- */
.ty {
  min-height: 100vh; min-height: 100svh;
  display: flex; align-items: center;
  background: linear-gradient(160deg, var(--brand-blue-dk) 0%, var(--brand-blue) 70%);
}
.ty__inner { width: 100%; }

/* Conversion pages: the headline block is a <header> outside <main>, so one
   paper band is split in two. These close the seam to the old single-band
   spacing — header drops its bottom padding, the form keeps the 48px gap. */
.conv-head { padding-bottom: 0; }
.conv-body { padding-top: var(--s-7); }
.ty__check {
  width: 104px; height: 104px; margin: 0 auto var(--s-6);
  display: flex; align-items: center; justify-content: center;
  background: var(--white); color: var(--brand-blue); border-radius: 50%;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.25);
  animation: ty-pop 600ms var(--ease) both;
}
.ty__check svg {
  width: 52px; height: 52px; fill: none; stroke: currentColor;
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
@keyframes ty-pop {
  0% { transform: scale(0); opacity: 0; }
  60% { transform: scale(1.15); opacity: 1; }
  100% { transform: scale(1); }
}
.ty .lead { color: var(--text); }
.ty__steps {
  list-style: none; padding: 0; margin: var(--s-7) 0 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4);
}
.ty__step {
  margin: 0; padding: var(--s-5) var(--s-4);
  background: rgba(0, 0, 0, 0.18); border: 1px solid var(--rule); border-radius: 14px;
}
.ty__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-bottom: var(--s-3);
  background: var(--white); color: var(--brand-blue); border-radius: 50%;
  font: var(--w-black) 20px/1 var(--font);
}
.ty__step p { margin: 0; font-weight: var(--w-bold); color: var(--text); }
.ty__card {
  margin-top: var(--s-6); padding: var(--s-6);
  background: rgba(0, 0, 0, 0.18); border: 1px solid var(--rule); border-radius: 14px;
}
.ty__card-h { font-size: var(--fs-h3); margin-bottom: var(--s-3); }
.ty__card address { font-style: normal; color: var(--text); }
.ty__card p { margin: var(--s-3) 0 0; color: var(--text); }
.ty__card .btn-row { margin-top: var(--s-5); }
@media (max-width: 720px) {
  .ty__steps { grid-template-columns: 1fr; }
  .ty__step { display: flex; align-items: center; gap: var(--s-4); text-align: left; }
  .ty__num { flex: none; margin-bottom: 0; }
}
@media (prefers-reduced-motion: reduce) { .ty__check { animation: none; } }
:root[data-a11y-motion="off"] .ty__check { animation: none !important; }

@media print { .a11y-fab, .a11y-panel { display: none !important; } }

/* --------------------------------------------------------------------------
   16. WASH MENU — one expandable row per plan

   Four cards repeated the same twelve service names up to four times each, and
   a comparison table needed four columns of ticks, which means sideways
   scrolling on a phone. This site is mobile first, so neither works.

   One row per plan instead. Collapsed it is a name, a price and a line — four
   of those fit a phone screen and compare at a glance. Open it is that plan's
   contents. <details>, so it works with no JavaScript and is keyboard operable
   without anything being wired up.
   -------------------------------------------------------------------------- */
.plans { display: grid; gap: var(--s-4); max-width: 820px; margin: var(--s-8) auto 0; }
.plan {
  background: var(--card-bg);
  border: 2px solid var(--card-line); border-radius: 16px;
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.plan:hover { border-color: var(--accent); }
.plan[open] { box-shadow: 0 14px 34px rgba(10, 8, 9, 0.08); }

.plan__head {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-5); cursor: pointer;
  list-style: none; min-height: 56px;
}
.plan__head::-webkit-details-marker { display: none; }
.plan__id { flex: 1; min-width: 0; }
.plan__flag {
  display: inline-block; margin-bottom: var(--s-2);
  padding: 2px var(--s-3); border-radius: 999px;
  background: var(--accent); color: var(--accent-on);
  font-size: 11px; font-weight: var(--w-black);
  letter-spacing: 0.12em; text-transform: uppercase;
}
.plan__name {
  display: block; font-size: 21px; font-weight: var(--w-black);
  text-transform: uppercase; letter-spacing: -0.01em; color: var(--heading);
  line-height: 1.15;
}
.plan__desc { display: block; margin-top: var(--s-2); color: var(--text-muted); }
.plan__price {
  flex: none; font: var(--w-black) 34px/1 var(--font);
  color: var(--accent); letter-spacing: -0.03em;
  transform: skewX(var(--slant));
}
.plan__price span { font-size: var(--fs-small); font-weight: var(--w-med); color: var(--text-muted); letter-spacing: 0; }
/* The chevron is the affordance, so it gets its own flex item rather than a
   pseudo-element on the price. */
.plan__head::after {
  content: ""; flex: none; width: 12px; height: 12px; margin-left: var(--s-2);
  border-right: 3px solid var(--accent); border-bottom: 3px solid var(--accent);
  transform: rotate(45deg) translateY(-3px);
  transition: transform var(--dur) var(--ease);
}
.plan[open] .plan__head::after { transform: rotate(225deg) translateY(-3px); }

.plan__body { padding: 0 var(--s-5) var(--s-5); }
.plan__label {
  margin: 0 0 var(--s-3); font-size: var(--fs-small); font-weight: var(--w-black);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted);
}
.plan__list {
  list-style: none; margin: 0 0 var(--s-5); padding: 0;
  columns: 2 220px; column-gap: var(--s-6);
}
.plan__list li {
  position: relative; padding-left: var(--s-6); margin-bottom: var(--s-2);
  color: var(--text); break-inside: avoid;
}
.plan__list li::before {
  content: ""; position: absolute; left: 0; top: 9px;
  width: 13px; height: 8px;
  border-left: 3px solid var(--accent); border-bottom: 3px solid var(--accent);
  transform: rotate(-45deg);
}
.plan__cta { width: 100%; }

/* The top plan is a filled card, the same inversion the old featured tier used. */
.plan--top {
  --text-muted: var(--pale);
  --text: var(--pale);
  --heading: var(--white);
  --accent: var(--brand-foam);
  --accent-on: var(--ink);
  --btn1-bg: var(--white);
  --btn1-fg: var(--brand-blue);
  --btn1-bg-hover: var(--pale);
  --btn1-fg-hover: var(--brand-blue-dk);
  background: linear-gradient(165deg, var(--brand-blue) 0%, var(--brand-blue-dk) 58%, var(--brand-blue-deep) 100%);
  border-color: transparent;
}
.plan--top:hover { border-color: transparent; }

@media (max-width: 560px) {
  .plan__head { flex-wrap: wrap; }
  .plan__price { font-size: 30px; }
  .plan__name { font-size: 19px; }
}
