/* ==========================================================================
   GLOW AUTO WASH  |  Design System
   Dark "Glow" Luxury · Signature Teal #1A8181 on Obsidian Black
   ========================================================================== */

/* ---------- Design Tokens ---------- */
:root {
  /* Brand colours (official palette) */
  --teal:            #1A8181;  /* Signature Teal */
  --teal-bright:     #35C2C2;  /* luminous variant for glows / large accents on dark */
  --teal-soft:       #5ED4D4;  /* small accent text on dark, AA safe */
  --glacier:         #87BFC5;  /* Glacier Blue */
  --tube:            #4FD0D0;  /* lit neon tube — headline accent word */
  --tube-white:      #F4FEFE;  /* lit neon tube — the rest of the headline */
  --platinum:        #C4CBD6;  /* Platinum Mist */
  --champagne:       #B3A79E;  /* Champagne Graphite, lightened for dark bg */
  --champagne-deep:  #968880;
  --racing:          #2B442F;  /* Racing Green */
  /* Burgundy, for the graphene wax line on the Champagne package. True
     burgundy (#800020) measures 1.7:1 on the card surface — unreadable as
     text — so this is a lifted burgundy that still reads burgundy and clears
     4.5:1 for body text. Text only; it is not a fill colour. */
  --graphene:        #CC6076;  /* 4.84:1 on the price-card surface */
  /* Champagne gold, for the Champagne card's edge, tier name and currency mark.
     Distinct from --champagne above, which is Champagne Graphite (a warm grey)
     from the brand palette. The deep value is for borders and washes only. */
  --gold:            #D9BE7E;  /* 10.3:1 on the price-card surface */
  --gold-deep:       #8A6D2F;
  --black:           #000000;  /* Obsidian Black */
  --white:           #FFFFFF;

  /* Surfaces */
  --bg:              #050708;
  --surface-1:       #0B1112;
  --surface-2:       #101819;
  --surface-3:       #15201F;
  --line:            rgba(196, 203, 214, 0.12);
  --line-strong:     rgba(196, 203, 214, 0.22);

  /* Text */
  --text-hi:         #F4F7F7;
  --text-body:       #C0CBCD;
  --text-dim:        #8FA0A2;

  /* Type */
  --font-display: "Montserrat", "Helvetica Neue", Arial, sans-serif;
  --font-body:    "Inter", "Helvetica Neue", Arial, sans-serif;

  /* Spacing scale (8px base) */
  --s-1: 4px;  --s-2: 8px;  --s-3: 16px; --s-4: 24px;
  --s-5: 32px; --s-6: 48px; --s-7: 64px; --s-8: 80px; --s-9: 96px;

  /* Radii & shadows */
  --r-sm: 8px; --r-md: 14px; --r-lg: 22px; --r-full: 999px;
  --shadow-card: 0 18px 50px rgba(0,0,0,.55);
  --glow-teal:   0 0 42px rgba(26,129,129,.45);
  --glow-teal-sm:0 0 18px rgba(53,194,194,.35);

  --container: 1180px;
  --nav-h: 76px;
}

/* ---------- Reset / Base ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

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

body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-body);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }

a { color: var(--teal-soft); text-decoration: none; }
a:focus-visible, button:focus-visible {
  outline: 3px solid var(--teal-bright);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: var(--teal); color: var(--white); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--teal); color: var(--white);
  padding: 12px 20px; border-radius: 0 0 var(--r-sm) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

/* ---------- Typography ---------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--text-hi);
  line-height: 1.12;
  text-wrap: balance;
}

.display {
  font-size: clamp(2.6rem, 6.5vw, 4.8rem);
  font-weight: 800;
  letter-spacing: .02em;
  text-transform: uppercase;
}

h2, .h2 {
  font-size: clamp(1.7rem, 3.4vw, 2.6rem);
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
}

h3, .h3 { font-size: 1.25rem; font-weight: 700; letter-spacing: .02em; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-display);
  font-size: .78rem; font-weight: 700;
  letter-spacing: .34em; text-transform: uppercase;
  color: var(--teal-soft);
  margin-bottom: var(--s-3);
}
.eyebrow::before,
.eyebrow::after {
  content: ""; width: 34px; height: 2px; flex-shrink: 0;
}
.eyebrow::before { background: linear-gradient(90deg, transparent, var(--teal-bright)); }
.eyebrow::after  { background: linear-gradient(90deg, var(--teal-bright), transparent); }
/* .eyebrow is letter-spaced .34em; the trailing space after the last glyph is
   absorbed below so both gaps read as optically equal. */
.eyebrow::after { margin-left: -.34em; }

/* The mirrored rules only look right while the label stays on one line. Tighten
   the label on small screens so the longest eyebrow still fits beside them. */
@media (max-width: 600px) {
  .eyebrow { font-size: .68rem; letter-spacing: .2em; gap: 9px; }
  .eyebrow::before, .eyebrow::after { width: 20px; }
  .eyebrow::after { margin-left: -.2em; }
}
/* Floor of .7rem (11.2px). Going lower fitted the flanking rules on a 320px
   screen but the label itself stopped being readable, which is the wrong trade
   — shrink the rules instead, they are decoration. */
@media (max-width: 380px) {
  .eyebrow { font-size: .7rem; letter-spacing: .1em; gap: 7px; }
  .eyebrow::before, .eyebrow::after { width: 12px; }
  .eyebrow::after { margin-left: -.1em; }
}

.lead { font-size: 1.13rem; color: var(--text-body); max-width: 46ch; }

/* ---------- Layout ---------- */
.container { width: min(var(--container), calc(100% - 48px)); margin-inline: auto; }

.section { padding-block: var(--s-9); position: relative; }
.section--tight { padding-block: var(--s-8); }

.section-head { max-width: 640px; margin-bottom: var(--s-7); }
.section-head p { margin-top: var(--s-3); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .eyebrow { justify-content: center; }

.grid { display: grid; gap: var(--s-4); }
@media (min-width: 768px) {
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
}
/* .grid-4 used to go four-across at 1024px, where a quarter-column leaves only
   ~160px of usable width. That is a pixel or two short of the longest
   .card-link at its normal .16em tracking, so "See the settings" and "Vacuum
   details" wrapped to two lines and broke the row.
   1228px is where .container stops growing (--container 1180 + 48 of gutter),
   so four-across is now only ever used at the container's full width, i.e. its
   roomiest case. Below that it is a 2x2. Do not lower this without re-checking
   that every .card-link still fits on one line in the real display font. */
@media (min-width: 1228px) {
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

/* ---------- Navigation ---------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--nav-h);
  display: flex; align-items: center;
  transition: background .35s ease, border-color .35s ease, backdrop-filter .35s ease;
  border-bottom: 1px solid transparent;
}
.nav.scrolled {
  background: rgba(5, 7, 8, .94);
  backdrop-filter: blur(22px) saturate(140%);
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  border-bottom-color: var(--line);
}
.nav-inner {
  width: min(var(--container), calc(100% - 48px));
  margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
}
.nav-logo img { height: 40px; width: auto; }
.nav-links { display: flex; align-items: center; gap: 6px; list-style: none; }
.nav-links a {
  display: inline-block; padding: 10px 14px;
  font-family: var(--font-display);
  font-size: .8rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--platinum);
  border-radius: var(--r-full);
  transition: color .2s ease, background .2s ease;
}
.nav-links a:hover { color: var(--white); background: rgba(26,129,129,.18); }
.nav-links a[aria-current="page"] { color: var(--teal-soft); }
.nav-cta { margin-left: 8px; }

/* The CTA is a .btn living inside .nav-links, so `.nav-links a` (0,1,1) wins
   over `.btn` / `.btn--primary` (0,1,0) and strips the button's display,
   padding, colour and hover fill. Restore them at higher specificity. */
.nav-links a.nav-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 14px 26px;
  color: var(--white);
  background: linear-gradient(135deg, var(--teal) 0%, #106868 100%);
}
.nav-links a.nav-cta:hover {
  color: var(--white);
  background: linear-gradient(135deg, var(--teal) 0%, #106868 100%);
  box-shadow: var(--glow-teal);
  transform: translateY(-2px);
}
.nav-links a.nav-cta[aria-current="page"] {
  color: var(--white);
  background: linear-gradient(135deg, var(--teal) 0%, #106868 100%);
  box-shadow: 0 0 0 2px rgba(94, 212, 212, .55);
}

.nav-toggle {
  display: none;
  background: none; border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  width: 46px; height: 46px;
  cursor: pointer; color: var(--white);
  align-items: center; justify-content: center;
}
.nav-toggle svg { width: 24px; height: 24px; }

@media (max-width: 1023px) {
  .nav-toggle { display: inline-flex; }
  .nav-links {
    position: fixed; inset: var(--nav-h) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 4px;
    background: rgba(5,7,8,.97);
    backdrop-filter: blur(20px);
    padding: var(--s-4) var(--s-4) var(--s-6);
    border-bottom: 1px solid var(--line);
    transform: translateY(-110%);
    opacity: 0;
    visibility: hidden;
    transition: transform .3s ease, opacity .3s ease, visibility .3s;
  }
  .nav-links.open { transform: translateY(0); opacity: 1; visibility: visible; }
  .nav-links a { padding: 14px 16px; font-size: .95rem; border-radius: var(--r-sm); }
  .nav-cta { margin: 12px 0 0; width: 100%; }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 52px; padding: 14px 30px;
  font-family: var(--font-display);
  font-size: .82rem; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  border-radius: var(--r-full);
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform .2s ease, box-shadow .25s ease, background .25s ease, border-color .25s ease;
}
.btn:active { transform: scale(.97); }
.btn svg { width: 17px; height: 17px; flex-shrink: 0; }

.btn--primary {
  background: linear-gradient(135deg, var(--teal) 0%, #106868 100%);
  color: var(--white);
  box-shadow: var(--glow-teal-sm);
}
.btn--primary:hover { box-shadow: var(--glow-teal); transform: translateY(-2px); }

.btn--ghost {
  background: rgba(255,255,255,.03);
  border-color: var(--line-strong);
  color: var(--text-hi);
}
.btn--ghost:hover { border-color: var(--teal-bright); color: var(--teal-soft); transform: translateY(-2px); }

/* Buttons carry generous tracking and padding, which runs longer labels onto a
   second line on small phones. Tighten both below 420px so labels stay on one
   line. Min-height is untouched, so the tap target is still 52px. */
@media (max-width: 420px) {
  .btn { padding: 14px 20px; letter-spacing: .1em; }
}

/* Placeholder: a button whose destination does not exist yet. Rendered live so
   the layout is final, but inert, and without the hover lift so it does not
   promise a click it cannot honour. Delete this class the moment an href
   is added. */

.btn--light { background: var(--white); color: #0A0E0F; }
.btn--light:hover { box-shadow: 0 0 30px rgba(255,255,255,.25); transform: translateY(-2px); }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: min(100dvh, 940px);
  display: flex; align-items: center;
  padding-top: var(--nav-h);
  isolation: isolate;
  overflow: hidden;
}
.hero--sub { min-height: 72dvh; }
/* The sub-page scrim is tuned for the darkest photo in the set, which leaves
   the brightest one — the vacuum bay, all white concrete and lit windows —
   noticeably tighter than the rest: 3.95:1 behind the neon headline against a
   site median near 8:1. It still clears the 3:1 floor for large text, but a
   300-weight tube has thin strokes and loses more to a busy ground than the
   number implies. Deepen the left third, where every sub-page's text sits,
   and leave the right side alone so the photograph still reads.
   Re-measure if a hero photo is ever swapped; brightness is what drives this. */
.hero--sub .hero-scrim {
  background:
    linear-gradient(180deg, rgba(5,7,8,.66) 0%, rgba(5,7,8,.34) 45%, var(--bg) 100%),
    linear-gradient(100deg, rgba(5,7,8,.93) 0%, rgba(5,7,8,.55) 55%, rgba(5,7,8,.16) 100%);
}

.hero-media, .hero-media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: -2;
}

/* Video hero. The element itself is .hero-media, so it inherits the absolute
   cover box above. Muted + playsinline are what let iOS autoplay at all; the
   poster carries the first frame so there is no black flash before it decodes. */
.hero-media--video { object-position: center; background: var(--bg); }
/* Reduced motion is handled in JS by pausing the video, which leaves its first
   frame on screen as a still. Hiding the element instead would strip the hero
   back to flat background, which is worse than a static image. */
.hero-scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(5,7,8,.62) 0%, rgba(5,7,8,.30) 45%, var(--bg) 100%),
    linear-gradient(100deg, rgba(5,7,8,.88) 0%, rgba(5,7,8,.42) 55%, rgba(5,7,8,.15) 100%);
}
/* On desktop the hero text sits in the left third, which the 100deg gradient
   darkens heavily while leaving the right side of the photo bright. On a phone
   the text spans the full width, so it runs into that bright side: the eyebrow
   measured 3.3:1 over the 10th Street photo at 390px, under the 4.5:1 floor for
   text that small. Flatten the horizontal gradient so the whole width is
   covered. The homepage pattern hero keeps its own scrim, which outranks this. */
@media (max-width: 700px) {
  .hero-scrim {
    background:
      linear-gradient(180deg, rgba(5,7,8,.72) 0%, rgba(5,7,8,.50) 45%, var(--bg) 100%),
      linear-gradient(100deg, rgba(5,7,8,.88) 0%, rgba(5,7,8,.70) 55%, rgba(5,7,8,.58) 100%);
  }
}
/* That scrim is sized to tame a bright still photo. Over video it flattens the
   motion, which is the only reason the video is there. Ease it back for the
   video hero specifically; contrast is re-measured across the clip, not assumed. */
@media (max-width: 700px) {
  .hero-media--video ~ .hero-scrim {
    background:
      linear-gradient(180deg, rgba(5,7,8,.58) 0%, rgba(5,7,8,.34) 45%, var(--bg) 100%),
      linear-gradient(100deg, rgba(5,7,8,.80) 0%, rgba(5,7,8,.54) 55%, rgba(5,7,8,.40) 100%);
  }
}
/* A brand pattern must tile at a fixed scale, not stretch with the viewport —
   object-fit:cover blows the logos up enormously on a phone. */
.hero-media--pattern {
  background-image: url(../img/hero-pattern.webp);   /* relative to THIS stylesheet */
  background-repeat: repeat;
  background-size: 1150px auto;   /* fewer, larger marks reads as a watermark;
                                     small and dense reads as wrapping paper */
  opacity: .38;
}
@media (max-width: 700px) {
  .hero-media--pattern { background-size: 760px auto; opacity: .30; }
}

/* The standard scrim is built to tame bright photography. Over a near-black
   pattern it erases it, and the text needs no help, so keep only the fade that
   blends the hero into the section below. */
.hero-media--pattern ~ .hero-scrim {
  background:
    linear-gradient(180deg, rgba(5,7,8,.30) 0%, rgba(5,7,8,.10) 42%, rgba(5,7,8,.80) 90%, var(--bg) 100%),
    linear-gradient(100deg, rgba(5,7,8,.86) 0%, rgba(5,7,8,.40) 58%, rgba(5,7,8,.05) 100%);
}

.hero-inner { padding-block: var(--s-9); max-width: 720px; }

/* Rotating hero: two stacked images, the second crossfading over a 10s cycle,
   so each holds for ~5s with a 1s fade between. */
.hero-media--rotate img + img {
  opacity: 0;
  animation: hero-rotate 10s ease-in-out infinite;
}
@keyframes hero-rotate {
  0%, 40%  { opacity: 0; }
  50%, 90% { opacity: 1; }
  100%     { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-media--rotate img + img { animation: none; opacity: 0; }
}
.hero-tagline {
  font-family: var(--font-display);
  color: var(--platinum);
  font-size: clamp(1rem, 2vw, 1.25rem);
  font-weight: 600;
  letter-spacing: .06em;
  margin-top: var(--s-4);
  max-width: 52ch;
}
/* Measured contrast on the homepage was already ~6:1, so the problem there is
   not the ratio — it is that the type sits over MOVING footage, which a
   still-frame measurement cannot capture. Weight and a brighter grey do most of
   the work; this shadow separates it from whatever the video is doing behind it,
   and is scoped to video heroes so the photo heroes stay clean. */
.hero-media--video ~ .container .hero-tagline {
  text-shadow: 0 1px 14px rgba(5, 7, 8, .8);
}

/* Opening hours directly under a sub-page h1. A chip rather than a plain line,
   because it sits over photography and has to stay readable there. */
.hero-hours {
  display: inline-flex; align-items: center; flex-wrap: wrap;
  gap: 6px 12px;
  margin-top: var(--s-3);
  padding: 10px 20px;
  border: 1px solid rgba(53, 194, 194, .38);
  border-radius: var(--r-full);
  background: rgba(5, 7, 8, .58);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-family: var(--font-display);
  font-size: .82rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-hi);
  font-variant-numeric: tabular-nums;
}
.hero-hours svg {
  width: 16px; height: 16px;
  color: var(--teal-soft);
  flex-shrink: 0;
  margin-right: -4px; /* the row gap already spaces the icon from the first range */
}
.hero-hours .sep { color: var(--teal-soft); opacity: .75; }
/* Tunnel carries two ranges, which stop fitting on one line just under 430px.
   Tightening the tracking buys back enough room down to ~380px. */
@media (max-width: 430px) {
  .hero-hours { letter-spacing: .06em; padding: 10px 16px; gap: 6px 10px; }
}
/* Below this it genuinely wraps. A pill outline looks wrong around two lines,
   and the middle dot stops separating anything on the same row. */
@media (max-width: 380px) {
  .hero-hours { border-radius: var(--r-md); gap: 2px 10px; }
  .hero-hours .sep { display: none; }
}
/* The Vacuums chip carries a longer pair ("Outdoor 24/7 · Indoor bay 6am–9pm")
   and wraps ~50px earlier than the Tunnel one, so it needs the two-line
   treatment sooner. Marked with a class rather than moving the shared
   breakpoint, which would strip the Tunnel dot while it still has a row to
   separate. Re-measure this number if either chip's wording changes. */
@media (max-width: 430px) {
  .hero-hours--long { border-radius: var(--r-md); gap: 2px 10px; }
  .hero-hours--long .sep { display: none; }
}
/* Visit Us carries two street addresses, longer again, so it wraps later still.
   Each modifier exists because a chip's own wording decides where it breaks —
   measure the chip and pick the class, do not move a shared breakpoint. */
@media (max-width: 519px) {
  .hero-hours--xlong { border-radius: var(--r-md); gap: 2px 10px; }
  .hero-hours--xlong .sep { display: none; }
}
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-6); }

/* The whole hero headline is a lit sign built from two gas colours: the opening
   words are a white tube, the accent word a teal one. Scoped to .hero so the
   .display class stays reusable if it is ever needed outside a hero.
   Both halves are FILLED light weights — never a stroke. See .glow-text below
   for why that matters. */
.hero .display {
  font-weight: 300;
  letter-spacing: .13em;
  color: var(--tube-white);
  text-shadow:
    0 0 4px  rgba(255, 255, 255, .75),
    0 0 14px rgba(190, 225, 230, .60),
    0 0 40px rgba(150, 200, 210, .40);
}

/* The headline accent is a neon tube, not a gradient.
   The letterform is FILLED at a light weight, so each stroke of the letter is
   itself the tube. Do not switch this to -webkit-text-stroke on a heavy weight:
   a stroke traces both edges of a bold glyph, which leaves a doubled line
   inside every bowl and stem — most visibly in the P of "Up".
   The tracking is part of the effect, not decoration: real tubes need bending
   room between characters, and the air stops the glow pooling between letters.
   Weight 300 must be in the Montserrat request in _shell.html. */
.glow-text {
  font-weight: 300;
  letter-spacing: .13em;
  margin-right: -.13em;   /* reclaim the trailing space the tracking adds */
  color: var(--tube);
  text-shadow:
    0 0 4px  rgba(79, 208, 208, .80),
    0 0 14px rgba(26, 129, 129, .85),
    0 0 38px rgba(26, 129, 129, .60);
}

/* A phone turned sideways is only ~390px tall, and the hero is sized by its
   content, not by dvh — headline, hours chip, tagline and buttons measured
   510–592px on every page, so the visitor landed on a hero they had to scroll
   through before reaching anything else. Nothing here is broken at that size,
   it is just badly proportioned, so pull the type and the vertical padding in
   until the whole block fits a short screen. Scoped to landscape orientation so
   a portrait phone — where a tall hero is the point — is untouched.
   Placed after the neon block on purpose: a media query adds no specificity, so
   .hero .display below would otherwise win on source order alone. */
@media (max-height: 500px) and (orientation: landscape) {
  .hero { min-height: 0; }
  .hero--sub { min-height: 0; }
  .hero-inner { padding-block: var(--s-6); }
  .hero .display { font-size: clamp(2rem, 5vw, 2.6rem); }
  .hero-tagline { font-size: 1rem; margin-top: var(--s-3); }
  .hero-hours { margin-top: var(--s-3); }
  .hero-actions { margin-top: var(--s-4); }
}

/* ambient orbs */
.orb {
  position: absolute; border-radius: 50%;
  filter: blur(90px); opacity: .5;
  pointer-events: none; z-index: -1;
}
.orb--teal  { background: rgba(26,129,129,.55); width: 520px; height: 520px; }
.orb--green { background: rgba(43,68,47,.6);   width: 420px; height: 420px; }

/* ---------- Cards ---------- */
.card {
  background: linear-gradient(160deg, var(--surface-2), var(--surface-1));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  position: relative;
  overflow: hidden;
  /* Column flex so .card-link can be pushed to the bottom edge. The grid
     already equalises card heights, so this lines every link up on one
     baseline regardless of how many lines the copy above it runs to. */
  display: flex;
  flex-direction: column;
  transition: transform .28s ease, border-color .28s ease, box-shadow .28s ease;
}
.card:hover { transform: translateY(-6px); border-color: rgba(53,194,194,.4); box-shadow: var(--shadow-card); }
.card-icon {
  width: 56px; height: 56px;
  display: grid; place-items: center;
  border-radius: var(--r-md);
  background: rgba(26,129,129,.14);
  border: 1px solid rgba(53,194,194,.28);
  color: var(--teal-soft);
  margin-bottom: var(--s-4);
}
.card-icon svg { width: 26px; height: 26px; }
.card h3 { margin-bottom: var(--s-2); }
.card p { font-size: .95rem; color: var(--text-dim); }
.card-link {
  display: inline-flex; align-items: center; gap: 8px;
  /* auto pushes it to the card's bottom; padding keeps a floor on the gap to
     the copy above when the card has no slack. Set both, not margin alone. */
  margin-top: auto;
  padding-top: var(--s-4);
  align-self: flex-start;
  font-family: var(--font-display);
  font-size: .78rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--teal-soft);
}
.card-link svg { width: 15px; height: 15px; transition: transform .2s ease; }
.card:hover .card-link svg, .card-link:hover svg { transform: translateX(5px); }

/* stretched-link pattern */
.card-link--stretched::after { content: ""; position: absolute; inset: 0; }

/* ---------- Media split ---------- */
.split { display: grid; gap: var(--s-6); align-items: center; }
.split-media {
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  position: relative;
}
/* A split panel can hold a photo or a looping video; both crop identically. */
.split-media img,
.split-media video { display: block; width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/3; }

/* Two-column: stretch the photo to the full height of the text beside it, so the
   column reads as a deliberate image panel rather than a floating thumbnail.
   Must come after the base rules above, since media queries add no specificity. */
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--s-8); align-items: stretch; }
  .split--flip > .split-media { order: 2; }
  .split-media { min-height: 420px; }
  .split-media img,
  .split-media video {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    aspect-ratio: auto;
    object-fit: cover;
  }
}
.split-media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(200deg, rgba(26,129,129,.16), transparent 45%);
  pointer-events: none;
}

/* ---------- Fleet sign-up form ----------
   Sits in a .split where a photo normally goes, so it has to hold its own
   column height next to the copy beside it. Panel styling matches .price-card
   rather than .card, because it is a self-contained thing you act on. */
.split-form {
  /* The nav is fixed, so an #signup jump would otherwise park the panel's top
     edge underneath it. Clear the nav height plus a little breathing room. */
  scroll-margin-top: calc(var(--nav-h) + var(--s-4));
  background: linear-gradient(170deg, var(--surface-2), var(--surface-1));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  padding: var(--s-5);
}
.fleet-form { display: grid; gap: var(--s-4); }
.fleet-form .eyebrow { margin-bottom: var(--s-2); }
.form-title {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.6vw, 1.8rem);
  font-weight: 800; letter-spacing: .02em; text-transform: uppercase;
  color: var(--text-hi); margin: 0;
}
.field { display: grid; gap: 8px; }
/* .75rem is the floor for a form label — below 12px an uppercase tracked label
   stops being comfortably readable, and a label you squint at is a field people
   fill in wrong. */
.field label {
  font-family: var(--font-display);
  font-size: .75rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--text-body);
}
/* Four of the five fields are required, so the one that is not is the one worth
   marking — an asterisk on everything else would be noise. */
.field .label-opt { color: var(--text-dim); opacity: .7; letter-spacing: .1em; }
/* Native validation styles the field itself once the visitor has tried to submit
   or has left an invalid value behind; :invalid alone would light up every empty
   required field on page load, which reads as an accusation before they start. */
.fleet-form.was-submitted .field input:invalid,
.fleet-form.was-submitted .field textarea:invalid { border-color: #E07A6B; }
.field input,
.field textarea {
  width: 100%; font: inherit; font-size: .95rem; color: var(--text-hi);
  background: rgba(5, 7, 8, .55);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  padding: 13px 14px; min-height: 48px;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.field textarea { resize: vertical; min-height: 110px; line-height: 1.55; }
.field input::placeholder,
.field textarea::placeholder { color: var(--text-dim); opacity: .8; }
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--teal-bright);
  box-shadow: 0 0 0 3px rgba(53, 194, 194, .22);
}
/* Two short numeric fields share a row once there is room for both. */
.field-row { display: grid; gap: var(--s-4); }
@media (min-width: 520px) { .field-row { grid-template-columns: 1fr 1fr; } }
.field-prefix { position: relative; }
.field-prefix > span {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  font-family: var(--font-display); font-weight: 700; color: var(--teal-soft);
  pointer-events: none;
}
.field-prefix input { padding-left: 30px; }
/* Hide the spinner arrows: they crowd a 48px field and invite mis-clicks. */
.field input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.field input[type="number"]::-webkit-outer-spin-button,
.field input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.fleet-form .btn { width: 100%; }
.form-note {
  margin: 0; font-size: .82rem; line-height: 1.55; color: var(--text-dim);
}
.form-status { margin: 0; font-size: .88rem; line-height: 1.5; color: var(--teal-soft); }
.form-status:empty { display: none; }

/* ---------- Pricing ---------- */
.pricing { display: grid; gap: var(--s-4); align-items: stretch; }
@media (min-width: 900px) { .pricing { grid-template-columns: repeat(3, 1fr); } }

/* Four tiers instead of three. Scoped rather than applied to .pricing, because
   the tunnel page still has three cards and would be left with a hole.
   Three across at 900px would strand the fourth card alone on a second row, so
   go 2x2 through the tablet range and only break to four abreast at 1100.
   1100 rather than 1024: at 1024 a column is 226px, which wraps "Find the bay"
   onto two lines and stacks the longest feature into a word ladder. At 1100 a
   column is 245px, and every button in the row measures one line (52px). */
@media (min-width: 620px) and (max-width: 1099px) {
  .pricing--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1100px) {
  .pricing--4 { grid-template-columns: repeat(4, 1fr); }
  .pricing--4 .price-card { padding: var(--s-4); }
  .pricing--4 .price-features li { font-size: .88rem; }
}

.price-card {
  display: flex; flex-direction: column;
  background: linear-gradient(170deg, var(--surface-2), var(--surface-1));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  position: relative;
  transition: transform .28s ease, border-color .28s ease, box-shadow .28s ease;
}
.price-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-card); }

.price-card--featured {
  border-color: rgba(53,194,194,.55);
  background:
    linear-gradient(170deg, rgba(26,129,129,.16), rgba(11,17,18,.9)),
    var(--surface-1);
  box-shadow: var(--glow-teal-sm);
}
.price-card--featured:hover { box-shadow: var(--glow-teal); }

/* The Champagne tier, in champagne gold. Deliberately NOT --featured: the teal
   badge and glow mark the value pick, and two highlighted cards in one row
   cancel each other out. A gold edge is enough to say "this is the other end of
   the range", and it names the tier without repeating the word. */
.price-card--champagne {
  border-color: rgba(217, 190, 126, .42);
  background:
    linear-gradient(170deg, rgba(138,109,47,.16), rgba(11,17,18,.9)),
    var(--surface-1);
  box-shadow: 0 0 18px rgba(138,109,47,.30);
}
.price-card--champagne:hover { box-shadow: 0 0 42px rgba(138,109,47,.42); }
.price-card--champagne .price-tier,
.price-card--champagne .price-amount sup { color: var(--gold); }

/* The one line that separates Champagne from Diamond, so it is the one line
   that is neither the teal of everything above it nor the gold of the card.
   Burgundy is carried by this line alone now.
   The check stays teal like every other one — the user asked for the label to
   carry the colour, not the tick, and a row of matching ticks is what makes the
   one odd word out read as deliberate rather than as an error state. */
.price-features li.is-graphene { color: var(--graphene); font-weight: 600; }

.price-badge {
  position: absolute; top: -14px; left: 50%; transform: translateX(-50%);
  /* The dark end of this gradient used to be --teal (#1A8181), which puts the
     near-black label at 3.65:1 — under the 4.5:1 floor, because at .68rem the
     badge is small text however bold it is. #2BACAC is the same hue a few
     steps lighter and measures 6.17:1, so the whole sweep now clears AA.
     Do not put --teal back; and if the label colour ever changes, re-measure
     against BOTH ends of the gradient, not just the bright one. */
  background: linear-gradient(135deg, var(--teal-bright), #2BACAC);
  color: #04201F;
  font-family: var(--font-display);
  font-size: .68rem; font-weight: 800;
  letter-spacing: .22em; text-transform: uppercase;
  padding: 7px 18px; border-radius: var(--r-full);
  white-space: nowrap;
}

.price-tier {
  font-family: var(--font-display);
  font-size: .8rem; font-weight: 700;
  letter-spacing: .3em; text-transform: uppercase;
  /* Same teal as the currency mark in .price-amount sup, so the tier name and
     the price read as one unit. The featured card no longer needs an override. */
  color: var(--teal-soft);
}

.price-amount {
  font-family: var(--font-display);
  font-size: 3.4rem; font-weight: 800;
  color: var(--text-hi);
  line-height: 1;
  margin: var(--s-3) 0 var(--s-2);
  font-variant-numeric: tabular-nums;
}
.price-amount sup { font-size: 1.4rem; font-weight: 700; color: var(--teal-soft); margin-right: 2px; }
.price-note { font-size: .85rem; color: var(--text-dim); margin-bottom: var(--s-4); }

.price-features { list-style: none; display: grid; gap: 12px; margin-bottom: var(--s-5); }
.price-features li {
  display: flex; gap: 12px; align-items: flex-start;
  font-size: .93rem; color: var(--text-body);
}
.price-features svg {
  width: 18px; height: 18px; flex-shrink: 0; margin-top: 3px;
  color: var(--teal-bright);
}
.price-card .btn { margin-top: auto; width: 100%; }

/* The three-up grid starts at 900px, where a column leaves only 143px inside
   the button — and "Find the tunnel" needs 153px at the default .18em tracking,
   so every button in the tunnel row sat on two lines until about 944px.
   Tighten tracking and side padding for buttons in the three-up grid only;
   `.pricing--4` is excluded because its cards are two-up and roomy through this
   whole band. The band runs to 1099 rather than stopping at 944 so there is
   ~30% headroom: the container cannot reach Google Fonts, so this is measured
   in the fallback face and Montserrat may set wider.
   `min-height: 52px` is untouched, so the tap target does not change. */
@media (min-width: 900px) and (max-width: 1099px) {
  .pricing:not(.pricing--4) .price-card .btn { padding-inline: 14px; letter-spacing: .09em; }
}

/* ---------- Stats ---------- */
.stats {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 900px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat {
  text-align: center;
  padding: var(--s-5) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, rgba(26,129,129,.07), transparent 70%);
}
.stat-value {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 2.9rem);
  font-weight: 800;
  color: var(--teal-soft);
  font-variant-numeric: tabular-nums;
}
.stat-label {
  font-size: .8rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-dim); margin-top: 6px;
  font-family: var(--font-display); font-weight: 600;
}

/* ---------- Feature list rows ---------- */
.feature-row {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--line);
}
.feature-row:last-child { border-bottom: none; }
.feature-row svg { width: 22px; height: 22px; color: var(--teal-bright); flex-shrink: 0; margin-top: 3px; }
.feature-row strong { color: var(--text-hi); display: block; margin-bottom: 2px; font-family: var(--font-display); font-weight: 700; font-size: .98rem; }
/* Between 768 and 900 the split has not gone two-column yet, so these rows run
   the full container width and hit ~107 characters a line on a tablet — far past
   the ~75 the eye tracks comfortably. The cap only bites in that band; inside a
   split column the text is already narrower than this. */
.feature-row p { font-size: .92rem; color: var(--text-dim); max-width: 68ch; }

/* ---------- Location cards ---------- */
.loc-card {
  background: linear-gradient(165deg, var(--surface-2), var(--surface-1));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  /* Column flex all the way down to .loc-actions, so the buttons sit on the
     card's bottom edge and line up across the row however many hours rows or
     header lines a given location has. */
  display: flex;
  flex-direction: column;
  transition: transform .28s ease, border-color .28s ease, box-shadow .28s ease;
}
.loc-card:hover { transform: translateY(-6px); border-color: rgba(53,194,194,.4); box-shadow: var(--shadow-card); }
.loc-card-head {
  padding: var(--s-5) var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--line);
  background: linear-gradient(120deg, rgba(26,129,129,.14), transparent 65%);
}
.loc-card-head h3 { font-size: 1.5rem; text-transform: uppercase; letter-spacing: .08em; }
.loc-card-head p { color: var(--glacier); font-size: .95rem; margin-top: 6px; }
/* The weather caveat sits directly above the hours table it qualifies. Give it
   a hairline rule below so it reads as a preamble to the table, not a stray
   line floating under the header band. */
.loc-card-body .note {
  margin-bottom: var(--s-3);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
  line-height: 1.5;
}
.loc-card-body { padding: var(--s-4) var(--s-5) var(--s-5); }
.loc-hours { width: 100%; border-collapse: collapse; font-size: .92rem; }
.loc-hours th, .loc-hours td { text-align: left; padding: 9px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.loc-hours th { color: var(--text-hi); font-weight: 600; font-family: var(--font-display); font-size: .85rem; letter-spacing: .04em; padding-right: 16px; }
.loc-hours td { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.loc-hours tr:last-child th, .loc-hours tr:last-child td { border-bottom: none; }
.loc-card-body { flex: 1; display: flex; flex-direction: column; }
/* auto pushes the row to the bottom; padding keeps a floor on the gap to the
   table above when the card has no slack. Set both, not margin alone. */
.loc-actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: auto; padding-top: var(--s-4); }

/* ---------- Marquee band ---------- */
.band {
  border-block: 1px solid var(--line);
  background: linear-gradient(90deg, rgba(26,129,129,.10), rgba(43,68,47,.14), rgba(26,129,129,.10));
  padding-block: var(--s-4);
  overflow: hidden;
}
.band-track {
  display: flex; gap: var(--s-7); width: max-content;
  animation: band-scroll 32s linear infinite;
}
.band-item {
  display: inline-flex; align-items: center; gap: 14px;
  font-family: var(--font-display);
  font-size: .82rem; font-weight: 700;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--platinum);
  white-space: nowrap;
}
.band-item svg { width: 15px; height: 15px; color: var(--teal-bright); }
/* The track holds two identical halves. Half the track width falls half a gap
   short of one full half (n gaps vs 2n-1 across the whole), so add it back to
   keep the loop seamless. */
@keyframes band-scroll { from { transform: translateX(0); } to { transform: translateX(calc(-50% - var(--s-7) / 2)); } }
@media (prefers-reduced-motion: reduce) {
  .band-track { animation: none; flex-wrap: wrap; width: auto; }
}

/* ---------- CTA panel ---------- */
.cta-panel {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid rgba(53,194,194,.3);
  background:
    radial-gradient(600px 300px at 15% 0%, rgba(26,129,129,.4), transparent 60%),
    radial-gradient(500px 320px at 90% 110%, rgba(43,68,47,.5), transparent 60%),
    linear-gradient(170deg, var(--surface-2), #060A0B);
  padding: clamp(40px, 7vw, 88px);
  text-align: center;
}
.cta-panel h2 { max-width: 22ch; margin-inline: auto; }
.cta-panel p { max-width: 54ch; margin: var(--s-3) auto 0; }
.cta-panel .hero-actions { justify-content: center; }

/* ---------- Footer ---------- */
.footer {
  border-top: 1px solid var(--line);
  background: #030405;
  padding: var(--s-8) 0 var(--s-5);
  margin-top: var(--s-9);
}
.footer-grid {
  display: grid; gap: var(--s-6);
  grid-template-columns: 1fr;
  margin-bottom: var(--s-7);
}
@media (min-width: 768px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } }
.footer-brand img { height: 52px; width: auto; }
/* The mark is a link back to the top of the home page. inline-block so the
   hit area is the logo itself rather than the full column width, and the
   margin lives here rather than on the img so the link box wraps it. */
.footer-logo {
  display: inline-block; margin-bottom: var(--s-4);
  border-radius: var(--r-sm);
  transition: opacity .2s ease;
}
.footer-logo:hover { opacity: .78; }
.footer-brand p { font-size: .92rem; color: var(--text-dim); max-width: 34ch; }
/* Column headings were Champagne Graphite, a warm grey-beige that read as
   washed-out against the near-black footer. --text-hi is the system's own
   high-contrast neutral and is very slightly cool, which sits better beside the
   teal than a pure #FFF would. */
.footer h4, .footer h2 {
  font-size: .78rem; font-weight: 700;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--text-hi);
  margin-bottom: var(--s-4);
}
.footer ul { list-style: none; display: grid; gap: 10px; }
.footer ul a, .footer address a { color: var(--text-dim); font-size: .93rem; transition: color .2s ease; font-style: normal; }
.footer ul a:hover, .footer address a:hover { color: var(--teal-soft); }
.footer address { font-style: normal; font-size: .93rem; color: var(--text-dim); display: grid; gap: 10px; }
/* Sizing for these links lives in the Touch targets section near the bottom of
   this file — 30px against a mouse, the full 44px on any coarse pointer. Do not
   add height here; it will be overridden there and the two will drift apart. */
.footer-bottom {
  border-top: 1px solid var(--line);
  padding-top: var(--s-4);
  display: flex; flex-wrap: wrap; gap: var(--s-3);
  align-items: center; justify-content: space-between;
  font-size: .82rem; color: #66787A;
}
/* The footer tagline is a small white tube, echoing the hero headlines. Weight
   stays at 400 rather than the headlines' 300: at .82rem a 300 hairline plus a
   glow turns to mush. The bloom radii are scaled down to match the type size —
   reusing the hero's would leave a halo wider than the words. */
.footer-bottom .tagline {
  letter-spacing: .22em;
  text-transform: uppercase;
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--tube-white);
  text-shadow:
    0 0 2px rgba(255, 255, 255, .70),
    0 0 7px rgba(190, 225, 230, .55),
    0 0 18px rgba(150, 200, 210, .35);
}

/* ---------- Reveal animations ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s ease, transform .7s ease; }
.reveal.visible { opacity: 1; transform: none; }
.reveal-d1 { transition-delay: .08s; }
.reveal-d2 { transition-delay: .16s; }
.reveal-d3 { transition-delay: .24s; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
  .card:hover, .price-card:hover, .loc-card:hover, .btn--primary:hover, .btn--ghost:hover { transform: none; }
}

/* ---------- Utility ---------- */
.text-center { text-align: center; }
.mt-6 { margin-top: var(--s-6); }
.mt-7 { margin-top: var(--s-7); }
.note { font-size: .82rem; color: var(--text-dim); }

/* Plain bulleted list for prose inside a card. The global reset strips list
   markers, so the dot is drawn as a pseudo-element and hung in the left gutter. */
.list-bullets { list-style: none; display: grid; gap: 9px; }
.list-bullets li {
  position: relative;
  padding-left: 18px;
  font-size: .93rem;
  line-height: 1.55;
  color: var(--text-body);
}
.list-bullets li::before {
  content: "";
  position: absolute; left: 2px; top: .62em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--teal-soft);
}

/* ---------- Touch targets ---------- */
/* Footer links rendered 16px tall and card links 21px, under WCAG 2.5.8's 24px
   AA floor and well under the 44px iOS/WCAG 2.5.5 guideline. Give them real
   height, and the full 44px wherever a finger is the likely pointer. */
.footer ul { gap: 4px; }
.footer ul a,
.footer address a {
  display: inline-flex; align-items: center;
  min-height: 30px;
}
.card-link { min-height: 30px; overflow-wrap: anywhere; }

@media (max-width: 1023px), (pointer: coarse) {
  .footer ul a,
  .footer address a { min-height: 44px; }
  .footer address { gap: 4px; }
  .card-link { min-height: 44px; }
  .loc-hours th, .loc-hours td { padding: 12px 0; }
  /* the logo is a link too */
  .nav-logo { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Mobile menu rows should fill the panel, so the whole row is tappable. */
@media (max-width: 1023px) {
  .nav-links a { display: flex; align-items: center; min-height: 44px; }
}
/* Touch on a wide screen (tablet landscape) still shows the desktop nav,
   whose links sit at 41px. Lift them to the 44px guideline. */
@media (min-width: 1024px) and (pointer: coarse) {
  .nav-links a {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px;
  }
}
.divider-glow {
  height: 1px; border: none;
  background: linear-gradient(90deg, transparent, rgba(53,194,194,.5), transparent);
}

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

/* ---------- Prose rhythm ---------- */
h1 + .lead, h2 + .lead, h3 + .lead { margin-top: var(--s-4); }
.lead + .lead { margin-top: var(--s-4); }
.card p + p { margin-top: var(--s-3); }
.cta-panel .eyebrow + h2 { margin-top: 0; }

.cta-panel .eyebrow { justify-content: center; }
