/* ============================================================
   Sayali TransLink India Pvt Ltd — site stylesheet
   Palette drawn from the company logo (teal globe / navy type)
   ============================================================ */

/* ---------- Self-hosted IBM Plex (latin subset) ----------
   Was Google Fonts: two extra origins (DNS+TLS) on the critical render path
   and a third party seeing every visitor's IP. The Sans file is a variable
   font covering 400-700; Mono ships the two weights the site uses. Non-latin
   text falls back to the system stacks below. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 700;
  font-stretch: 100%;
  font-display: swap;
  src: url("../assets/fonts/plex-sans-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/plex-mono-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/plex-mono-600.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* <picture> is a wrapper for sources, never a layout box. Without this it
   introduces an inline element between a flex/grid parent and its image. */
picture { display: contents; }
/* ...except where the <picture> IS the styled box. `display: contents` would
   remove it from the grid and the portrait would lose its column. */
picture.director-portrait { display: block; }

:root {
  /* ---------- Brand tokens (redesign brief, 2026-08-11) ----------
     Taken from the logo: navy wordmark, teal-to-cyan sphere, cyan accent
     squares, amber for action. The OLD names are kept as the working
     variables so 1,300 lines of CSS did not need rewriting — only their
     values changed. The brief's names are aliases beside them. */
  --color-navy:     #0E3A5C;
  --color-teal:     #1F7A9C;
  --color-cyan:     #4FC6DE;
  --color-fog:      #F4F6F7;
  --color-graphite: #22303A;
  --color-amber:    #F2A93B;

  --navy:      var(--color-navy);
  --navy-deep: #082A45;              /* one step darker, for the footer */
  --teal:      var(--color-teal);
  --teal-dark: #175F7A;
  --teal-soft: #E4F2F7;
  /* CYAN IS AN ACCENT, NEVER A BACKGROUND (brief 1.1). It appears on the
     live-stat dots, ticks and chart highlights and nowhere else. */
  --cyan:      var(--color-cyan);
  --ink:       var(--color-graphite);
  --ink-soft:  #55636D;
  /* Distinctly cooler than --cloud. These were the SAME value, which made
     .tint and .tint-blue render identically and adjacent sections merge. */
  --sky:       #EDF2F7;
  --cloud:     var(--color-fog);
  --line:      #DFE5E9;
  /* AMBER IS FOR CLICKABLE ACTIONS AND LIVE BADGES ONLY (brief 1.1), and
     never in the same component as cyan — brand accent and action accent
     stay visually distinct. */
  --amber:     var(--color-amber);
  --white:     #ffffff;

  /* Page measure. RESTORED — this was lost when :root was rewritten, and
     `max-width: var(--wrap)` silently became `max-width: none`, so every page
     ran full-bleed. Slightly wider than the original 1140 to suit the board and
     the service rail, which are the widest components on the site. */
  --wrap: 1200px;

  /* VERTICAL RHYTHM. Every section uses this and nothing else — the page had
     `padding: 5rem 0` plus ad-hoc inline overrides, including a padding-top:0
     that left the closing card welded to the section above it. Scales with the
     viewport so the rhythm holds from a phone to an ultrawide. */
  --section-y: clamp(3.25rem, 6vw, 5.5rem);
  /* Space between a section heading and its content. There were THREE values in
     play (2.2rem, 1.4rem, 3rem) plus nine inline overrides across the pages. */
  --head-gap: 2.4rem;

  /* MEASURES. Six different inline max-widths were in use (680, 760, 820, 860,
     900, 980) with nothing choosing between them. Three named ones instead:
     default (the wrap), content, and a reading column. */
  --measure:      900px;   /* focused content: a stepper, a plan, a voice */
  --measure-read: 720px;   /* long prose: privacy, FAQ answers */
  /* Amber for TEXT on a light background. The brand amber (#F2A93B) measures
     2.0:1 on white — it is a fill colour, not an ink. This is the lightest warm
     amber that clears 4.5:1 (measured 4.82). Bright amber stays correct on navy,
     where it measures 5.91:1. */
  --amber-ink: #A0650B;

  /* IBM Plex: Sans for everything, Mono for NUMBERS — monospace numerals
     read like a dashboard readout and separate 99.4% from prose (brief 1.2). */
  --font-head: "IBM Plex Sans", "Segoe UI", sans-serif;
  --font-body: "IBM Plex Sans", "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* Keyboard users land here first (WCAG 2.4.1). Off-screen until focused. */
.skip-link {
  position: absolute;
  left: 1rem;
  top: -100px;
  z-index: 1000;
  padding: 0.7rem 1.2rem;
  background: var(--navy);
  color: var(--white);
  border-radius: 0 0 10px 10px;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus-visible { top: 0; outline: 2px solid var(--amber); outline-offset: 2px; }

/* ---------- 404 ---------- */
.not-found { text-align: center; padding-top: 4rem; }
.not-found .nf-code { font-family: var(--font-mono); font-weight: 600; color: var(--teal-dark); }
.not-found h1 { margin: 0.4rem 0 0.8rem; }
.not-found .nf-lede { color: var(--ink-soft); margin-bottom: 1.8rem; }
.not-found .nf-links { margin-top: 1.6rem; font-size: 0.92rem; color: var(--ink-soft); }

html { scroll-behavior: smooth; scroll-padding-top: 90px; }

body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

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

a { color: var(--teal-dark); text-decoration: none; }
a:hover { color: var(--teal); }

h1, h2, h3, h4 {
  font-family: var(--font-head);
  color: var(--navy);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  /* Gutters scale with the viewport: 20px on a phone, up to 48px on a desktop,
     so text never runs to the edge of the glass on any page. */
  padding-inline: clamp(20px, 4vw, 48px);
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.8rem 1.6rem;
  border-radius: 999px;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
  white-space: nowrap;
}

.btn svg { flex-shrink: 0; }

/* AMBER IS THE ACTION COLOUR (brief 1.1) — the only warm colour on the site,
   and reserved strictly for things you click. Navy text on it rather than
   white: white on #F2A93B is about 2:1, which fails contrast at any size. */
.btn-primary {
  background: var(--amber);
  color: var(--navy);
  box-shadow: 0 6px 18px -8px rgba(242, 169, 59, 0.7);
}
.btn-primary:hover {
  background: #E39A28;
  color: var(--navy);
  transform: translateY(-2px);
}

.btn-ghost {
  background: transparent;
  color: var(--navy);
  border-color: var(--line);
}
.btn-ghost:hover {
  border-color: var(--teal);
  color: var(--teal-dark);
  transform: translateY(-2px);
}

.btn-light {
  background: var(--white);
  color: var(--navy);
}
.btn-light:hover { color: var(--teal-dark); transform: translateY(-2px); }

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.site-header.scrolled {
  border-bottom-color: var(--line);
  box-shadow: var(--shadow-sm);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: 76px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}
.brand img {
  height: 46px;
  width: auto;
}
.brand-text { line-height: 1.15; }
.brand-text .brand-name {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.12rem;
  color: var(--navy);
  letter-spacing: 0.02em;
}
.brand-text .brand-sub {
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--teal-dark);
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 1.5rem;
}
.main-nav a.nav-link {
  font-weight: 500;
  font-size: 0.95rem;
  white-space: nowrap;
  color: var(--ink-soft);
  padding: 0.35rem 0;
  position: relative;
}
.main-nav a.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 100%;
  height: 2.5px;
  border-radius: 2px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s ease;
}
.main-nav a.nav-link:hover { color: var(--navy); }
.main-nav a.nav-link:hover::after,
.main-nav a.nav-link.active::after { transform: scaleX(1); }
.main-nav a.nav-link.active { color: var(--navy); font-weight: 600; }

.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  border-radius: 8px;
}
.nav-toggle span {
  display: block;
  width: 24px;
  height: 2.5px;
  border-radius: 2px;
  background: var(--navy);
  margin: 5px 0;
  transition: transform 0.25s ease, opacity 0.25s ease;
}
.nav-toggle.open span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.nav-toggle.open span:nth-child(2) { opacity: 0; }
.nav-toggle.open span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }

/* ---------- Hero (home) ---------- */

.hero {
  position: relative;
  background:
    radial-gradient(1000px 500px at 85% -10%, rgba(33, 150, 189, 0.14), transparent 60%),
    linear-gradient(180deg, var(--sky) 0%, var(--white) 100%);
  overflow: hidden;
}

.hero-inner {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 3rem;
  align-items: center;
  padding-top: 4.5rem;
  padding-bottom: 4rem;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--teal-dark);
  background: var(--teal-soft);
  border: 1px solid rgba(33, 150, 189, 0.25);
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  margin-bottom: 1.2rem;
}
.eyebrow::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--teal);
}

.hero h1 {
  font-size: clamp(2.1rem, 4.2vw, 3.15rem);
  font-weight: 700;
  margin-bottom: 1.1rem;
}
.hero h1 .accent { color: var(--teal); }

.hero .lede {
  font-size: 1.08rem;
  color: var(--ink-soft);
  max-width: 34rem;
  margin-bottom: 1.8rem;
}

.hero-ctas { display: flex; gap: 0.9rem; flex-wrap: wrap; margin-bottom: 2.2rem; }

.hero-points {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.6rem;
  padding: 0;
  list-style: none;
}
.hero-points li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink-soft);
}
.hero-points svg { color: var(--teal); flex-shrink: 0; }

.hero-art { position: relative; }
.hero-art svg { width: 100%; height: auto; display: block; }

/* ---------- Stats strip ---------- */


/* ---------- Sections ---------- */

.section { padding-block: var(--section-y); }
/* Utility measures, so a section narrows by class instead of an inline style. */
.wrap.measure      { max-width: var(--measure); }
.wrap.measure-read { max-width: var(--measure-read); }
.section.tint { background: var(--cloud); }
.section.tint-blue { background: var(--sky); }
.service-block.tint { background: var(--cloud); }

/* ---------- Classes for what were inline styles (rule 5) ---------- */
/* The drawer/booking submit button, full width. */
.btn-block { width: 100%; justify-content: center; margin-top: 0.4rem; }
/* Footer bottom bar: the privacy link sits a step off the copyright line. */
.privacy-link { margin-left: 0.6rem; }
/* Small strong link (map link, "book a cab" pointers). */
.fine-link { font-size: 0.85rem; font-weight: 600; }
/* Dark feature card (fleet: "Every vehicle, every day"). */
.card.card-invert {
  display: flex; flex-direction: column; justify-content: center;
  background: linear-gradient(150deg, var(--navy-deep), var(--navy));
  border: none;
}
.card-invert h3 { color: #fff; font-size: 1.25rem; margin-bottom: 0.8rem; }
.card-invert .check-list { margin-top: 0; }
.card-invert .check-list li { color: rgba(255, 255, 255, 0.85); }
/* "Full service detail" pointer under the carousel — 44px tall tap target. */
.svc-more { margin-top: 1.4rem; }
.svc-more a { font-weight: 600; display: inline-flex; align-items: center; min-height: 44px; }
/* One reading measure for standalone paragraphs. */
.copy-max { max-width: 62ch; }
/* Footnote under the stats band (on navy). */
.stats-note { color: rgba(255, 255, 255, 0.66); font-size: 0.9rem; margin-top: 1.4rem; max-width: 60ch; }
/* Ghost button on a dark band. */
.btn-ghost.on-dark { border-color: rgba(255, 255, 255, 0.35); color: #fff; }
/* Privacy page spacing variants of shared components. */
.statement.spaced { margin: 1.6rem 0 2.4rem; }
.plan.spaced { margin: 1.4rem 0 2rem; }
.privacy-updated { color: var(--ink-soft); font-family: var(--font-mono); font-size: 0.8rem; }
/* Map legend footnote. */
.legend-note { font-family: var(--font-mono); font-size: 0.74rem; }

.section-head {
  max-width: 620px;
  margin-bottom: var(--head-gap);
}
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.kicker {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* teal is 4.48:1 on the fog tint these sections use; teal-dark clears it. */
  color: var(--teal-dark);
  margin-bottom: 0.7rem;
}
.section-head h2 {
  font-size: clamp(1.6rem, 3vw, 2.25rem);
  margin-bottom: 0.8rem;
}
.section-head p { color: var(--ink-soft); }

/* ---------- Cards ---------- */

.card-grid { display: grid; gap: 1.5rem; }
.card-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.card-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.card-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }

.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.9rem 1.7rem;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: rgba(33, 150, 189, 0.35);
}

.card .icon-badge {
  width: 52px;
  height: 52px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  background: var(--teal-soft);
  color: var(--teal-dark);
  margin-bottom: 1.2rem;
}

.card h3 { font-size: 1.15rem; margin-bottom: 0.6rem; }
.card p { font-size: 0.95rem; color: var(--ink-soft); }

.card .card-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 1rem;
  font-weight: 600;
  font-size: 0.9rem;
}
.card .card-link svg { transition: transform 0.15s ease; }
.card .card-link:hover svg { transform: translateX(3px); }

/* ---------- Feature rows (why us) ---------- */


.split {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 3.5rem;
  align-items: center;
}
.split.reverse { grid-template-columns: 1.1fr 0.9fr; }

/* ---------- Route / how-it-works timeline ---------- */

.route-steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
  position: relative;
  counter-reset: step;
}
.route-steps::before {
  content: "";
  position: absolute;
  top: 26px;
  left: 6%;
  right: 6%;
  border-top: 2px dashed rgba(33, 150, 189, 0.45);
}
.route-steps.steps-5 { grid-template-columns: repeat(5, 1fr); }
.route-step { position: relative; text-align: center; padding: 0 0.6rem; }
.route-step .pin {
  counter-increment: step;
  position: relative;
  z-index: 1;
  width: 52px;
  height: 52px;
  margin: 0 auto 1rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-head);
  font-weight: 700;
  color: var(--white);
  background: linear-gradient(145deg, var(--teal), var(--teal-dark));
  box-shadow: 0 0 0 6px var(--white), 0 6px 16px -6px rgba(33, 150, 189, 0.6);
}
.route-step .pin::after { content: counter(step, decimal-leading-zero); }
.route-step h3 { font-size: 1rem; margin-bottom: 0.35rem; }
.route-step p { font-size: 0.88rem; color: var(--ink-soft); }

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


/* ---------- CTA band ---------- */

.cta-band {
  background:
    radial-gradient(700px 300px at 15% 120%, rgba(143, 216, 239, 0.25), transparent 60%),
    linear-gradient(120deg, var(--navy-deep), var(--navy) 55%, var(--teal-dark));
  border-radius: var(--radius-lg);
  color: var(--white);
  padding: 3.2rem 3rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
}
.cta-band h2 { color: var(--white); font-size: clamp(1.4rem, 2.6vw, 1.9rem); margin-bottom: 0.5rem; }
.cta-band p { color: rgba(255, 255, 255, 0.8); max-width: 32rem; }
.cta-band .btn-primary { box-shadow: none; }

/* ---------- Page hero (inner pages) ---------- */

.page-hero {
  background:
    radial-gradient(800px 350px at 90% -20%, rgba(33, 150, 189, 0.16), transparent 60%),
    linear-gradient(180deg, var(--sky), var(--white));
  padding-block: calc(var(--section-y) * 0.8) calc(var(--section-y) * 0.7);
  border-bottom: 1px solid var(--line);
}
.page-hero h1 { font-size: clamp(1.9rem, 3.6vw, 2.7rem); margin-bottom: 0.8rem; }
.page-hero p { color: var(--ink-soft); max-width: 40rem; font-size: 1.05rem; }
.crumbs {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink-soft);
  margin-bottom: 1rem;
}
.crumbs a { color: var(--teal-dark); }
.crumbs span::before { content: "›"; margin: 0 0.5rem; color: var(--line); }

/* ---------- Service detail blocks ---------- */

.service-block { padding-block: var(--section-y); border-bottom: 1px solid var(--line); }
.service-block:last-of-type { border-bottom: none; }

.check-list { list-style: none; display: grid; gap: 0.65rem; margin-top: 1.2rem; }
.check-list li {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  font-size: 0.95rem;
  color: var(--ink-soft);
}
.check-list svg { color: var(--teal); flex-shrink: 0; margin-top: 3px; }

.tag-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.4rem; }
.tag {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--teal-dark);
  background: var(--teal-soft);
  border: 1px solid rgba(33, 150, 189, 0.25);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
}

/* ---------- Fleet ---------- */

.fleet-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  display: flex;
  flex-direction: column;
}
.fleet-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.fleet-card .fleet-art {
  background: linear-gradient(160deg, var(--sky), var(--teal-soft));
  padding: 1.6rem 1.6rem 1.2rem;
  display: grid;
  place-items: center;
}
.fleet-card .fleet-art svg { width: 100%; max-width: 260px; height: auto; }
.fleet-card .fleet-body { padding: 1.5rem 1.6rem 1.7rem; display: flex; flex-direction: column; flex: 1; }
.fleet-card h2, .fleet-card h3 { font-size: 1.12rem; margin-bottom: 0.3rem; }
.fleet-card .fleet-cap {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--teal-dark);
  margin-bottom: 0.7rem;
}
.fleet-card p { font-size: 0.92rem; color: var(--ink-soft); }
.fleet-specs {
  list-style: none;
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px dashed var(--line);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem 1rem;
}
.fleet-specs li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
}
.fleet-specs svg { color: var(--teal); flex-shrink: 0; }

/* ---------- Values / about ---------- */

.timeline { list-style: none; position: relative; padding-left: 2rem; display: grid; gap: 1.8rem; }
.timeline::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 6px;
  bottom: 6px;
  border-left: 2px dashed rgba(33, 150, 189, 0.4);
}
.timeline li { position: relative; }
.timeline li::before {
  content: "";
  position: absolute;
  left: -2rem;
  top: 5px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--white);
  border: 4px solid var(--teal);
  box-shadow: 0 0 0 4px var(--white);
}
.timeline .year {
  font-family: var(--font-head);
  font-weight: 700;
  color: var(--teal-dark);
  font-size: 0.95rem;
  display: block;
  margin-bottom: 0.15rem;
}
.timeline p { font-size: 0.95rem; color: var(--ink-soft); }

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

.contact-grid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 3rem; align-items: start; }

.contact-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.4rem 1.5rem;
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  margin-bottom: 1rem;
}
.contact-card .icon-badge {
  width: 46px;
  height: 46px;
  border-radius: 12px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  background: var(--teal-soft);
  color: var(--teal-dark);
}
.contact-card h2, .contact-card h3 { font-size: 0.98rem; margin-bottom: 0.2rem; }
.contact-card p { font-size: 0.92rem; color: var(--ink-soft); }

.form-panel {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 2.2rem;
  box-shadow: var(--shadow-md);
}
.form-panel h2 { font-size: 1.4rem; margin-bottom: 0.4rem; }
.form-panel > p { font-size: 0.93rem; color: var(--ink-soft); margin-bottom: 1.6rem; }

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.form-field { display: flex; flex-direction: column; gap: 0.35rem; }
.form-field.full { grid-column: 1 / -1; }
.form-field label, .form-field .form-label { font-size: 0.85rem; font-weight: 600; color: var(--navy); }
.form-field input,
.form-field select,
.form-field textarea {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--ink);
  padding: 0.75rem 0.9rem;
  border: 1.5px solid var(--line);
  border-radius: 10px;
  background: var(--cloud);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--teal);
  /* background-COLOR, not the shorthand: the shorthand also resets repeat,
     position and size, which is what tiled the select chevron (2026-08-11). */
  background-color: var(--white);
}
/* Keyboard focus gets a ring on top of the border swap — a 1px border colour
   change alone is a weak indicator (WCAG 2.4.7). Mouse focus stays subtle. */
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible {
  box-shadow: 0 0 0 3px var(--teal-soft), 0 0 0 4px var(--teal);
}
.form-field textarea { resize: vertical; min-height: 110px; }

/* ---------- Selects ----------
   The native control drew its own arrow in its own way on every platform —
   a thin grey caret on one, a chunky beige button on another — beside inputs
   that are otherwise identical. `appearance: none` takes that away and the
   chevron is drawn here, so a dropdown looks like the boxes around it.
   The right padding leaves room for it; without that, a long option runs
   underneath the arrow. */
.form-field select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 2.6rem;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2344586f' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 16px 16px;
  cursor: pointer;
}
.form-field select:hover { border-color: #c7dae6; }
.form-field select:focus {
  /* The chevron picks up the focus colour, and REPEAT/POSITION/SIZE are repeated
     here on purpose. The rule above uses the `background` SHORTHAND, which
     resets background-repeat to `repeat` — so setting only background-image
     here tiled the chevron across the whole control the moment it was focused.
     Any longhand that follows a shorthand has to restate what the shorthand
     wiped. */
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%231F7A9C' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.9rem center;
  background-size: 16px 16px;
  background-color: var(--white);
  box-shadow: 0 0 0 3px rgba(31, 122, 156, 0.16);
}
/* Firefox draws a dotted focus ring inside the control; the border and the
   ring above already say it. */
.form-field select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--ink); }
.form-field select option { color: var(--ink); background: var(--white); }

/* Date and time inputs: the same treatment for their native picker button, so
   a row of [date] [time] [select] reads as one family. */
.form-field input[type="date"],
.form-field input[type="time"] { cursor: pointer; }
.form-field input[type="date"]::-webkit-calendar-picker-indicator,
.form-field input[type="time"]::-webkit-calendar-picker-indicator {
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.form-field input[type="date"]:hover::-webkit-calendar-picker-indicator,
.form-field input[type="time"]:hover::-webkit-calendar-picker-indicator { opacity: 1; }

/* ---------- The days of a hire, as an accordion ----------
   Four days of open fields was roughly 3,400px of form — a wall on a phone,
   and the submit button somewhere far below it. Collapsed, each day is one
   line that SAYS what it holds (its route and time), so the whole hire can be
   read at a glance and only the day being changed is open. */
.day-acc { border: 1.5px solid var(--line); border-radius: 12px; background: var(--white); overflow: hidden; }
.day-acc + .day-acc { margin-top: 0.6rem; }
.day-acc.is-open { border-color: #c7dae6; }
.day-acc.has-issue { border-color: #dc2626; }

.day-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.85rem 0.95rem;
  background: var(--cloud);
  border: 0;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
  min-height: 56px; /* a comfortable tap target on a phone */
}
.day-acc.is-open .day-head { background: var(--white); border-bottom: 1px solid var(--line); }
.day-head:hover { background: var(--sky); }
.day-head:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }

.day-head-text { min-width: 0; flex: 1; }
.day-head-title { display: block; font-size: 0.9rem; font-weight: 700; color: var(--navy); }
.day-head-sub {
  display: block;
  font-size: 0.82rem;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.day-acc.has-issue .day-head-sub { color: #b91c1c; font-weight: 600; }

.day-chev { flex: none; color: var(--ink-soft); transition: transform 0.18s ease; }
.day-acc.is-open .day-chev { transform: rotate(180deg); }

.day-body { padding: 0.9rem 0.95rem 1rem; }
.day-acc:not(.is-open) .day-body { display: none; }

/* A dot that says the day is complete without adding a line of text. */
.day-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--teal); }
.day-acc.has-issue .day-dot { background: #dc2626; }
.day-dot.is-empty { background: var(--line); }

/* Where the one map lands when a day is opened. It needs a height of its own:
   an empty slot is zero-high, and Leaflet would measure that and draw nothing. */
.day-map-slot { min-height: 240px; }
.day-map-slot #map { height: 240px; border-radius: 10px; }

/* Copying the previous day is a button, not a silent default. */
.copy-day {
  align-self: flex-start;
  padding: 0.5rem 0.85rem;
  min-height: 40px;
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--teal-dark);
  background: var(--teal-soft);
  border: 1px solid #cfe7f1;
  border-radius: 8px;
  cursor: pointer;
}
.copy-day:hover { background: #d8eef6; }

.form-note { font-size: 0.8rem; color: var(--ink-soft); margin-top: 1rem; }

.form-success {
  display: none;
  background: #eaf7ef;
  border: 1px solid #bfe3cc;
  color: #1e6b3a;
  border-radius: 10px;
  padding: 0.9rem 1.1rem;
  font-size: 0.92rem;
  margin-top: 1.2rem;
}
.form-success.show { display: block; }

/* ---------- Coverage chips ---------- */

.city-row { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.city-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--navy);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.45rem 1rem;
  box-shadow: var(--shadow-sm);
}
.city-chip svg { color: var(--teal); }

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

.site-footer {
  background: var(--navy-deep);
  color: rgba(255, 255, 255, 0.78);
  padding: 4rem 0 0;
  margin-top: 0;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 0.8fr 0.9fr 1.1fr;
  gap: 2.5rem;
  padding-bottom: 3rem;
}
.footer-brand .logo-chip {
  display: inline-block;
  background: var(--white);
  border-radius: 12px;
  padding: 8px 14px;
  margin-bottom: 1.1rem;
}
.footer-brand .logo-chip img { height: 72px; width: auto; }
.footer-brand p { font-size: 0.9rem; max-width: 20rem; }

.site-footer h3 {
  color: var(--white);
  font-size: 0.95rem;
  margin-bottom: 1.1rem;
  letter-spacing: 0.04em;
}
.footer-links { list-style: none; display: grid; gap: 0.55rem; }
.footer-links a { color: rgba(255, 255, 255, 0.72); font-size: 0.9rem; }
.footer-links a:hover { color: #8fd8ef; }

.footer-contact { list-style: none; display: grid; gap: 0.8rem; }
.footer-contact li { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 0.9rem; }
.footer-contact svg { color: #8fd8ef; flex-shrink: 0; margin-top: 3px; }

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  padding: 1.3rem 0;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.55);
}
.footer-bottom a { color: rgba(255, 255, 255, 0.7); }
/* Statutory identifiers (CIN, GSTIN) under the copyright: codes, so mono; quieter than the line above. */
.footer-legal { display: inline-block; margin-top: 0.35rem; font-family: var(--font-mono); font-size: 0.76rem; letter-spacing: 0.02em; color: rgba(255, 255, 255, 0.45); }

/* ---------- Client wall ----------
   59 real customers, set as wordmarks. Two rows running opposite ways at
   different speeds: one row alone reads as a ticker, two crossing read as
   scale, which is the point of the section. Boxing each name turned it into
   the chip wall that was rejected on the sectors section, so there are no
   boxes — quantity and typography carry it. */

.wall {
  display: grid;
  gap: 0.75rem;
  /* A wide fade: a name sliced in half at the edge reads as a broken layout,
     so it has to be well gone before it reaches the cut. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 13%, #000 87%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 13%, #000 87%, transparent);
}
.wall-row { overflow: hidden; }
.wall-track {
  display: flex;
  align-items: center;
  width: max-content;
  padding: 0.5rem 0;
  animation: wall-scroll var(--dur, 64s) linear infinite;
}
/* The middle row runs the other way, and all three at different speeds, so they
   never lock into one moving block. */
.wall-row--back .wall-track { animation-direction: reverse; }
.wall:hover .wall-track,
.wall:focus-within .wall-track { animation-play-state: paused; }
/* Exactly -50%: the track is one set plus its duplicate, so this lands the copy
   where the original started. Any gap ON THE TRACK would break that. */
@keyframes wall-scroll { to { transform: translateX(-50%); } }
.wall-set { display: flex; align-items: center; flex-shrink: 0; }

.wall-name {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--navy);
  /* 0.75, not the 0.6 this started at: navy at 0.6 over the tint measures
     3.56:1, and at 16px on a phone these are not "large text", so that failed
     AA. 0.75 measures 5.35:1 and still sits back from the headline. */
  opacity: 0.75;
  transition: opacity 0.25s ease, color 0.25s ease;
}
.wall-name:hover { opacity: 1; color: var(--teal); }
/* The separator is a drawn dot, not a character, so it is never spoken. */
.wall-name::after {
  content: "";
  display: inline-block;
  width: 5px;
  height: 5px;
  margin: 0 1.5rem;
  border-radius: 50%;
  background: var(--cyan);
  vertical-align: 0.24em;
  opacity: 0.5;
}

@media (prefers-reduced-motion: reduce) {
  .wall-track { animation: none; flex-wrap: wrap; width: auto; justify-content: center; }
  .wall-set { display: contents; }
  .wall-set[aria-hidden="true"] { display: none; }
  .wall { -webkit-mask-image: none; mask-image: none; }
  .wall-name { opacity: 0.9; }
  /* Nothing is scrolling, so the dot has to space a wrapped grid, not a line. */
  .wall-name::after { margin: 0 1rem; }
}

/* ---------- Testimonial polish ---------- */


/* ---------- Photo frames ---------- */

.hero-photo {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.hero-photo > img, .hero-photo > picture > img {
  width: 100%;
  height: 100%;
  min-height: 380px;
  object-fit: cover;
  display: block;
}
.hero-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(200deg, rgba(14, 42, 74, 0) 55%, rgba(14, 42, 74, 0.55));
  pointer-events: none;
}
.float-badge {
  position: absolute;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 14px;
  padding: 0.65rem 1rem;
  box-shadow: var(--shadow-md);
}
.float-badge .fb-icon {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--teal-soft);
  color: var(--teal-dark);
  flex-shrink: 0;
}
.float-badge strong { display: block; font-family: var(--font-head); font-size: 0.95rem; color: var(--navy); line-height: 1.2; }
.float-badge span { font-size: 0.75rem; color: var(--ink-soft); }
.float-badge.tl { top: 18px; left: 18px; }
.float-badge.br { bottom: 18px; right: 18px; }

/* ---------- Service detail: the photo panel ----------
   Replaces `.svc-photo`, which was a plain rounded image. This one carries the
   service number over the picture and a caption under it, so the photograph
   states something instead of decorating.

   `--shot` is the aspect ratio, set per block: the source photographs are
   16:10, square and 3:4, and forcing one ratio on all of them cropped the
   subject out of two. */
.svc-shot { margin: 0; }
/* The frame owns position and clipping, NOT the <picture>: the number is a
   sibling of the picture (only <source> and <img> may live inside one), so it
   needs a positioned ancestor that wraps both. Positioned on the figure
   instead, `bottom: 0` would have landed under the caption. */
.svc-shot-frame {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}
.svc-shot picture { display: block; }
.svc-shot img {
  width: 100%;
  /* `height: auto` is what lets aspect-ratio govern. The width/height
     attributes on the tag are presentational hints, and the height hint wins
     over aspect-ratio unless it is cleared — which is why the first build
     rendered a 16:10 panel at 1.03:1. The attributes stay: they are what
     reserves the right box before the file arrives. */
  height: auto;
  aspect-ratio: var(--shot, 16 / 10);
  object-fit: cover;
  display: block;
}
/* Sits ON the picture, bottom-left, over a scrim of its own so it never lands
   on a light patch of sky and vanishes. */
.svc-shot-num {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 1;
  padding: 0.5rem 1.15rem 0.45rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  color: var(--white);
  background: var(--navy);
  border-radius: 0 12px 0 var(--radius-lg);
}
.svc-shot figcaption {
  margin-top: 0.85rem;
  padding-left: 0.15rem;
  font-size: 0.87rem;
  line-height: 1.6;
  color: var(--ink-soft);
  border-left: 2px solid var(--cyan);
  padding-inline-start: 0.8rem;
}

/* ---------- Service detail: the copy column ---------- */

.svc-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--teal-dark);
}
.svc-eyebrow b {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  background: var(--navy);
  color: var(--white);
  font-size: 0.8rem;
  letter-spacing: 0;
}
.svc-copy h2 { margin-bottom: 0.7rem; text-wrap: balance; }
.svc-lede { color: var(--ink-soft); font-size: 1.02rem; line-height: 1.7; max-width: 46ch; }
/* Who the service is for, stated plainly — the question every one of these
   blocks was leaving the reader to work out. */
.svc-fit {
  margin-top: 1.4rem;
  padding-top: 1rem;
  border-top: 1px dashed var(--line);
  font-size: 0.9rem;
  color: var(--navy);
}
.svc-fit b {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber-ink);
  margin-right: 0.55rem;
}

/* ---------- The five, at a glance ---------- */

.svc-index {
  display: grid;
  gap: 0.7rem;
}
.svc-index-item {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 1.1rem;
  padding: 1.05rem 1.3rem;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 14px;
  text-decoration: none;
  transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.svc-index-item:hover {
  border-color: var(--teal);
  transform: translateX(4px);
  box-shadow: var(--shadow-sm);
}
.sx-num {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 700;
  /* teal-dark, not cyan: cyan is 2.0:1 on the white card — a fill, never
     small text (the same rule amber follows). teal-dark measures 7.0:1. */
  color: var(--teal-dark);
}
.sx-name { font-family: var(--font-head); font-weight: 600; color: var(--navy); }
.sx-note { font-size: 0.86rem; color: var(--ink-soft); }
.sx-go { display: grid; place-items: center; color: var(--teal); }
.svc-index-item:hover .sx-go { color: var(--navy); }
@media (max-width: 620px) {
  /* The rhythm note drops below the name rather than squeezing four columns
     into 390px, where every one of them wraps. */
  .svc-index-item { grid-template-columns: auto 1fr auto; row-gap: 0.15rem; }
  .sx-note { grid-column: 2 / 4; }
}

.fleet-card .fleet-photo {
  height: 195px;
  overflow: hidden;
  background: var(--sky);
}
.fleet-card .fleet-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.45s ease;
}
.fleet-card:hover .fleet-photo img { transform: scale(1.05); }

/* ---------- Social icons ---------- */

.social-row { display: flex; gap: 0.6rem; margin-top: 1.2rem; }
.social-btn {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: #cfe6f2;
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.social-btn:hover {
  background: var(--teal);
  border-color: var(--teal);
  color: var(--white);
  transform: translateY(-2px);
}

/* ---------- Fleet at a glance table ---------- */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--white);
  box-shadow: var(--shadow-sm);
}
.fleet-table { width: 100%; border-collapse: collapse; min-width: 700px; }
.fleet-table th {
  background: var(--navy-deep);
  color: var(--white);
  text-align: left;
  font-family: var(--font-head);
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.95rem 1.3rem;
  white-space: nowrap;
}
.fleet-table td {
  padding: 0.95rem 1.3rem;
  border-top: 1px solid var(--line);
  font-size: 0.92rem;
  color: var(--ink-soft);
  vertical-align: top;
}
.fleet-table td:first-child { color: var(--navy); font-weight: 600; white-space: nowrap; }
.fleet-table tr:nth-child(even) td { background: var(--cloud); }
.seat-pill {
  display: inline-block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--teal-dark);
  background: var(--teal-soft);
  border: 1px solid rgba(33, 150, 189, 0.25);
  border-radius: 999px;
  padding: 0.15rem 0.7rem;
  white-space: nowrap;
}

/* ---------- Charts (brand palette validated: #14719a / #b07d3a) ---------- */

.chart-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 1.8rem 1.6rem;
  box-shadow: var(--shadow-sm);
}
.chart-card h3 { font-size: 1.05rem; margin-bottom: 0.2rem; }
.chart-card .chart-sub { font-size: 0.84rem; color: var(--ink-soft); margin-bottom: 1.2rem; }
.chart-legend {
  display: flex;
  gap: 1.2rem;
  flex-wrap: wrap;
  margin-bottom: 1.1rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
}
.chart-legend .swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  margin-right: 0.4rem;
  vertical-align: -1px;
}
.growth-chart svg { width: 100%; height: auto; display: block; }

/* ---------- Reveal animation ---------- */

.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal.visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- Responsive ---------- */

@media (max-width: 980px) {
  .hero-inner { grid-template-columns: 1fr; padding-top: 3.2rem; padding-bottom: 3.2rem; }
  .hero-art { max-width: 560px; margin: 0 auto; }
  .split, .split.reverse { grid-template-columns: 1fr; gap: 2.2rem; }
  /* Once the columns stack, the alternating blocks would lead with a photo on
     every second service and with the heading on the others. The heading always
     goes first on a phone — otherwise you scroll past a picture with no idea
     which service it belongs to. */
  .split .svc-shot { order: 2; }
  .split .svc-copy { order: 1; }
  .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
  .route-steps, .route-steps.steps-5 { grid-template-columns: repeat(2, 1fr); gap: 2rem 1.5rem; }
  .route-steps::before { display: none; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .contact-grid { grid-template-columns: 1fr; }
}

@media (max-width: 700px) {
  /* THE WHITE LINE ABOVE THE NAV BAR (fixed 2026-08-12).
     Two faults in one rule:

     1. `transform: translateY(-130%)` was supposed to park the closed menu off
        the top of the screen. It is a percentage of the menu's OWN height, so
        whether it clears anything depends on how many links the menu has —
        with six it landed at -337px on a 353px box, leaving its bottom edge at
        +16px. That 16px of white menu painted over the navy top strip, which
        is the line that was visible. Add a seventh link and it would have got
        worse; remove one and it would have "fixed itself". A hidden state must
        not be arithmetic that depends on content.

     2. `inset: 76px` hardcoded the header's height as a VIEWPORT offset, which
        is only correct once the top strip has scrolled away. At the top of the
        page the header sits at 45.6px, so its bottom is at ~122px and the open
        menu overlapped the header by half its height.

     Both go away by anchoring the menu to the header instead of the viewport:
     `top: 100%` of `.site-header` (which is sticky, so it is the containing
     block) is always exactly the header's bottom edge, at any scroll position.
     `visibility: hidden` is the belt to the transform's braces — whatever the
     geometry does, a hidden element cannot paint. */
  .main-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--white);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-lg);
    padding: 0.6rem 1.4rem 1.4rem;
    transform: translateY(-100%);
    visibility: hidden;
    /* visibility flips only after the slide finishes, so the close animates */
    transition: transform 0.28s ease, visibility 0s linear 0.28s;
    z-index: 55;
  }
  .main-nav.open {
    transform: translateY(0);
    visibility: visible;
    transition: transform 0.28s ease, visibility 0s;
  }
  .main-nav a.nav-link { padding: 0.85rem 0; border-bottom: 1px solid var(--cloud); }
  .main-nav .btn { margin-top: 1rem; justify-content: center; }
  .nav-toggle { display: block; }

  .eyebrow { font-size: 0.66rem; letter-spacing: 0.09em; padding: 0.35rem 0.75rem; }
  .hero-ctas { flex-direction: column; align-items: stretch; }
  .hero-ctas .btn { justify-content: center; }
  .hero h1 { font-size: 2rem; }
  .hero .lede { font-size: 1rem; }

  .card-grid.cols-2, .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: 1fr; }
  .hero-photo > img, .hero-photo > picture > img { min-height: 250px; }
  .float-badge { padding: 0.5rem 0.75rem; border-radius: 11px; }
  .float-badge .fb-icon { width: 28px; height: 28px; }
  .float-badge strong { font-size: 0.85rem; }
  .float-badge.tl { top: 10px; left: 10px; }
  .float-badge.br { bottom: 10px; right: 10px; }
  .form-grid { grid-template-columns: 1fr; }
  .route-steps, .route-steps.steps-5 { grid-template-columns: 1fr; }
  .cta-band { padding: 2.4rem 1.6rem; }
  .section { padding: 3.5rem 0; }
  .footer-grid { grid-template-columns: 1fr; gap: 2rem; }
  .brand-text .brand-sub { display: none; }
}


/* ============================================================
   Redesign brief, 2026-08-11
   ============================================================ */

/* NUMBERS IN MONO (brief 1.2). Monospace numerals read like a dashboard
   readout and stop 99.4% looking like prose. Applied to the stat strip, the
   benchmark table and the live-stat line rather than globally, because mono
   body text is harder to read, not more credible. */
.stat .num,
.live-stats b,
.bench-table td,
.tl-year { font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ---------- Hero live-stat strip ---------- */
.live-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.5rem;
  margin: 0 0 1.6rem;
  padding: 0;
  list-style: none;
  font-size: 0.92rem;
  color: var(--ink-soft);
}
.live-stats li { display: flex; align-items: center; gap: 0.5rem; }
/* The only place cyan appears at this size — echoing the accent squares in
   the logo. Never a background (brief 1.1). */
.live-stats li::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--cyan);
  flex: none;
}
.live-stats b { color: var(--navy); }


@media (min-width: 560px) { .quote-grid { grid-template-columns: 1fr 1fr; } .quote-grid .full { grid-column: 1 / -1; } }
.quote-note { font-size: 0.8rem; color: var(--ink-soft); margin: 0.9rem 0 0; }
.quote-msg { margin: 0.9rem 0 0; font-size: 0.9rem; font-weight: 600; display: none; }
.quote-msg.show { display: block; }
.quote-msg.ok { color: #15803d; }
.quote-msg.bad { color: #b91c1c; }

@keyframes orbit-draw { to { stroke-dashoffset: 0; } }

/* RESPECT REDUCED MOTION (brief 5). The line simply appears drawn. */



/* ---------- Women's safety ---------- */
.safety { background: var(--navy); color: #dbe7ef; }
.safety h2 { color: var(--white); }
.safety-list { list-style: none; padding: 0; margin: 1.4rem 0 0; display: grid; gap: 0.9rem; }
@media (min-width: 760px) { .safety-list { grid-template-columns: 1fr 1fr; gap: 0.9rem 2rem; } }
.safety-list li { display: flex; gap: 0.7rem; font-size: 0.95rem; line-height: 1.55; }
.safety-list svg { flex: none; margin-top: 3px; color: var(--cyan); }
.safety-list .todo { color: #a8bccb; font-style: italic; }

/* ---------- Leadership timeline + pull-quote ---------- */
.tl { display: grid; gap: 1.6rem; margin: 2rem 0 0; }
@media (min-width: 780px) { .tl { grid-template-columns: repeat(3, 1fr); } }
.tl-item { position: relative; padding-top: 1.6rem; border-top: 2px solid var(--line); }
.tl-item::before {
  content: ""; position: absolute; top: -7px; left: 0;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--white); border: 2.5px solid var(--teal);
}
.tl-item.now::before { background: var(--cyan); border-color: var(--cyan); }
.tl-year { display: block; font-size: 1.35rem; color: var(--navy); margin-bottom: 0.35rem; }
.tl-item p { margin: 0; font-size: 0.93rem; color: var(--ink-soft); }


/* ============================================================
   REVAMP, 2026-08-11 — the reference site's structure, our colours.
   Corporate, large-scale type, generous whitespace, grid modularity,
   dark bands for contrast. Navy/teal/cyan/amber throughout.
   ============================================================ */

/* ---------- Contact strip above the nav ---------- */
.top-strip {
  background: var(--navy);
  color: #cfe0ec;
  font-size: 0.84rem;
}
.top-strip .wrap {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.4rem;
  padding: 0.5rem 1.25rem;
}
/* 44px tall even in a thin strip: on a phone the phone number is the most
   tapped thing on a transport site, and a 20px link is a miss waiting to
   happen. Negative margin keeps the strip itself slim on desktop. */
.top-strip a {
  color: #cfe0ec;
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: 44px;
  margin-block: -0.45rem;
}
.top-strip a:hover { color: var(--white); }
.top-strip .sep { margin-left: auto; color: var(--cyan); font-weight: 600; }

/* ---------- Full-bleed hero ---------- */
.hero-full {
  position: relative;
  min-height: 82vh;
  display: grid;
  place-items: center;
  text-align: center;
  color: var(--white);
  overflow: hidden;
  padding: 5rem 1.25rem 4rem;
}
.hero-full > img, .hero-full > picture > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
}
/* Navy wash rather than plain black: the photograph sits inside the brand
   rather than under a grey filter. Dark enough for AA text at this size. */
.hero-full::after {
  content: "";
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(8, 42, 69, 0.78) 0%, rgba(8, 42, 69, 0.62) 45%, rgba(8, 42, 69, 0.9) 100%);
}
.hero-full .hero-body { position: relative; z-index: 2; max-width: 1000px; }
.hero-full h1 {
  font-size: clamp(2.1rem, 5.4vw, 4rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 0.8rem 0 1.1rem;
  color: var(--white);
  text-wrap: balance;
}
.hero-full h1 .accent { color: var(--cyan); }
.hero-full .lede { color: #d8e6ef; max-width: 60ch; margin: 0 auto 1.6rem; font-size: 1.05rem; }
.hero-full .eyebrow {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.22);
  color: var(--white);
}
.hero-full .live-stats { justify-content: center; color: #cfe0ec; }
.hero-full .live-stats b { color: var(--white); }
.hero-full .hero-ctas { justify-content: center; }
.hero-full .btn-ghost { color: var(--white); border-color: rgba(255, 255, 255, 0.45); }
.hero-full .btn-ghost:hover { background: rgba(255, 255, 255, 0.12); color: var(--white); }

/* An inner page gets the same treatment at two thirds the height — a full
   screen of hero before the first service is a cost, not a welcome. */
.hero-full.hero-sub { min-height: 56vh; padding-block: 4rem 3.25rem; }
.hero-full.hero-sub h1 { font-size: clamp(1.9rem, 4.2vw, 3rem); }
/* The breadcrumb is built for a light page; on the wash it needs its own ink.
   `.crumbs` is a block of inline text, not a flex row, so this centres with
   text-align — justify-content would have done nothing. */
.hero-full .crumbs { text-align: center; color: rgba(255, 255, 255, 0.72); }
.hero-full .crumbs span::before { color: rgba(255, 255, 255, 0.45); }
.hero-full .crumbs a { color: rgba(255, 255, 255, 0.72); }
.hero-full .crumbs a:hover { color: var(--white); }
.hero-full .crumbs span { color: var(--white); }

/* ---------- Six pillars ---------- */
.pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1.6rem 1rem;
  text-align: center;
}
.pillar-ico {
  width: 62px; height: 62px;
  margin: 0 auto 0.7rem;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--teal-soft);
  color: var(--teal);
  border: 1px solid #cfe7f1;
}
.pillars h3 { font-size: 0.95rem; margin: 0 0 0.2rem; color: var(--navy); }
.pillars p { font-size: 0.84rem; color: var(--ink-soft); margin: 0; }

/* ---------- Big-number stat band ---------- */
.scale-band { background: var(--cloud); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.scale-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 2.2rem 1.5rem;
}
.scale-item { text-align: center; }
.scale-num {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: clamp(1.9rem, 4vw, 2.9rem);
  line-height: 1;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.scale-num sup { font-size: 0.5em; color: var(--amber); top: -0.7em; }
.scale-label { margin-top: 0.5rem; font-size: 0.86rem; color: var(--ink-soft); line-height: 1.4; }


/* ---------- Sector grid ---------- */
.sector-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 0.9rem;
}
.sector {
  display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
  padding: 1.1rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--white);
  text-align: center;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--navy);
}
.sector svg { color: var(--teal); }
.sector:hover { border-color: var(--teal); background: var(--sky); }

/* ---------- Leadership card ---------- */
.leader {
  display: grid; gap: 1.6rem;
  align-items: center;
  padding: 1.8rem;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--white);
}
@media (min-width: 720px) { .leader { grid-template-columns: 200px 1fr; } }
.leader-photo {
  aspect-ratio: 1;
  border-radius: 14px;
  background: var(--teal-soft);
  display: grid; place-items: center;
  color: var(--teal);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-align: center;
  padding: 1rem;
  border: 1px dashed #bcdcea;
}
.leader h3 { margin: 0 0 0.15rem; color: var(--navy); }
.leader .role { font-size: 0.88rem; color: var(--teal); font-weight: 600; margin: 0 0 0.9rem; }

/* ---------- Fixed WhatsApp bubble ---------- */
.wa-fab {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 60;
  width: 54px; height: 54px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: #25D366; color: #08331a;
  box-shadow: 0 10px 26px -8px rgba(0, 0, 0, 0.45);
}
.wa-fab:hover { background: #1eb956; }
@media print { .wa-fab { display: none; } }

/* ============================================================
   THE QUOTE DRAWER — one form, every page (2026-08-11)
   Replaces the hero form and the contact-page form. There is now exactly ONE
   set of these fields in the codebase, so they cannot drift apart, and the
   form is one tap away from wherever somebody happens to be reading.
   ============================================================ */
.drawer-scrim {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(8, 42, 69, 0.55);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.drawer-scrim[hidden] { display: none; }
.drawer-scrim.open { opacity: 1; }

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 81;
  width: min(460px, 100%);
  background: var(--white);
  box-shadow: -20px 0 50px -30px rgba(8, 42, 69, 0.6);
  display: flex; flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
  /* The phone's home bar and notch. Without this the submit button sits under
     the gesture area on an iPhone. */
  padding-bottom: env(safe-area-inset-bottom);
}
.drawer[hidden] { display: none; }
.drawer.open { transform: translateX(0); }
@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-scrim { transition: none; }
}

.drawer-head {
  display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.3rem 1.3rem 0.9rem;
  border-bottom: 1px solid var(--line);
}
.drawer-head h2 { margin: 0 0 0.2rem; font-size: 1.2rem; color: var(--navy); }
.drawer-head p { margin: 0; font-size: 0.88rem; color: var(--ink-soft); }
.drawer-close {
  flex: none; margin-left: auto;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--white); color: var(--navy);
  cursor: pointer;
}
.drawer-close:hover { background: var(--cloud); }
.drawer-close:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* Scrolls on its own so the header and the button stay put on a short phone. */
.drawer-body { padding: 1.2rem 1.3rem 1.6rem; overflow-y: auto; flex: 1; }
.drawer-body .form-field { margin-bottom: 0.9rem; }

/* A button that reads as a link — for a drawer trigger sitting inside a
   sentence. A real <button> because it opens a dialog, not a destination. */
.link-btn {
  background: none; border: 0; padding: 0;
  font: inherit; color: var(--teal);
  font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px; cursor: pointer;
}
.link-btn:hover { color: var(--navy); }
.link-btn:focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; border-radius: 3px; }

/* ═══════════════════════════════════════════════════════════════════════
   COMPONENT SYSTEM — one shape per job (2026-08-12)

   The site had ONE component doing fourteen jobs: centred kicker, centred
   h2, grid of same-radius white cards. That is why every section read the
   same. Each section below gets the shape its CONTENT wants:

     proof points  -> a spec sheet (rows, rules, mono)  not cards
     numbers       -> one board with a real gauge       not three strips
     5 services    -> a rail and one detail panel       not 5 cards
     12 sectors    -> chips                             not 12 boxes
     a process     -> a stepper                         not cards
     one voice     -> an editorial quote                not a card
     logos         -> a continuous rail                 not a grid

   Deliberately NOT the neon/glass/glow look: this company moves 18,000
   people to factory gates at 05:40. Credible beats clever. The vocabulary
   is hairline rules, real whitespace, mono numerals and ONE warm accent.
   ═══════════════════════════════════════════════════════════════════════ */

/* ---------- 0. The drawer handle ----------
   The drawer had no affordance at all — you had to already know a CTA
   somewhere down the page opened it. This is a permanent grab tab, the way
   a real drawer has a handle. */
.quote-handle {
  position: fixed; z-index: 70;
  right: 0; top: 50%;
  transform: translateY(-50%) translateX(0);
  display: flex; align-items: center; gap: 0.55rem;
  padding: 1rem 0.7rem;
  border: 0; border-radius: 14px 0 0 14px;
  background: var(--navy); color: var(--white);
  font-family: var(--font-head); font-size: 0.82rem;
  font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  cursor: pointer;
  /* Sideways so a permanent tab costs 40px of width, not 140px. */
  writing-mode: vertical-rl;
  box-shadow: -6px 0 24px -12px rgba(8,42,69,0.5);
  transition: transform 0.2s ease, background 0.2s ease;
}
.quote-handle::before {
  /* The grab lines that say "pull me" — the whole point of a handle. */
  content: ""; flex: none;
  width: 12px; height: 2px;
  border-top: 2px solid currentColor; border-bottom: 2px solid currentColor;
  height: 6px; opacity: 0.55;
}
.quote-handle:hover, .quote-handle:focus-visible {
  background: var(--teal); transform: translateY(-50%) translateX(-3px);
}
.quote-handle:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
/* Hidden while the drawer it opens is already open. */
body.drawer-open .quote-handle { transform: translateY(-50%) translateX(110%); }

@media (max-width: 760px) {
  /* A right-edge tab would sit under the thumb and fight the WhatsApp
     bubble, so on a phone it becomes a pill on the OTHER side. */
  .quote-handle {
    writing-mode: horizontal-tb;
    top: auto; bottom: 1.1rem; right: auto; left: 1.1rem;
    transform: none; border-radius: 999px;
    padding: 0.8rem 1.15rem; min-height: 44px;
  }
  .quote-handle:hover, .quote-handle:focus-visible { transform: none; }
  body.drawer-open .quote-handle { transform: translateY(200%); }
}

/* ---------- 1. Spec sheet — for proof points ----------
   Six claims with evidence behind them. A card grid makes them look like
   marketing; numbered rows on hairlines make them look like a specification,
   which is what a transport manager is actually shopping for. */
.spec-sheet { border-top: 1px solid var(--line); }
.spec-row {
  display: grid;
  grid-template-columns: 3.2rem minmax(9rem, 14rem) 1fr auto;
  gap: 1.2rem; align-items: baseline;
  padding: 1.35rem 0.4rem;
  border-bottom: 1px solid var(--line);
  transition: background 0.18s ease, padding-left 0.18s ease;
}
.spec-row:hover { background: var(--sky); padding-left: 1rem; }
.spec-num {
  font-family: var(--font-mono); font-size: 0.8rem;
  /* NOT cyan: 2.01:1 on white. Cyan is for dots, ticks and fills. */
  color: var(--teal-dark); font-weight: 600;
}
.spec-label {
  font-family: var(--font-head); font-weight: 650;
  font-size: 1.05rem; color: var(--navy);
}
.spec-proof { color: var(--ink-soft); font-size: 0.95rem; line-height: 1.55; }
.spec-tick { color: var(--teal); flex: none; }
@media (max-width: 760px) {
  .spec-row { grid-template-columns: 2.4rem 1fr; gap: 0.3rem 0.9rem; padding: 1.1rem 0; }
  .spec-num { grid-row: span 2; }
  .spec-tick { display: none; }
}
   /* 5.91:1 on navy */


/* ---------- 3. Chips — for a taxonomy, not features ----------
   Twelve sectors were twelve identical white boxes carrying the SAME icon
   twelve times, which tells a reader nothing and costs a whole screen. They
   are labels. Labels want chips. */
.chips { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.chip {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.62rem 1.05rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--white); color: var(--ink);
  font-size: 0.92rem; font-weight: 550;
  transition: border-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.chip:hover { border-color: var(--teal); color: var(--navy); transform: translateY(-2px); }
.chip::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--cyan); flex: none;
}
/* The three biggest sectors carry weight, so the eye lands somewhere. */
.chip.key { background: var(--navy); border-color: var(--navy); color: var(--white); font-weight: 600; }
.chip.key::before { background: var(--amber); }
.chip.key:hover { color: var(--white); }

/* ---------- 4. Stepper — for a process ----------
   "Requirement audit -> SLA review" is a sequence. A card grid destroys the
   one thing that matters about a sequence: its order. */
.stepper { position: relative; padding-left: 0; list-style: none; margin: 0; }
.step { position: relative; padding: 0 0 2.1rem 3.4rem; }
.step:last-child { padding-bottom: 0; }
.step::before {
  /* the rail */
  content: ""; position: absolute; left: 15px; top: 2rem; bottom: -0.2rem;
  width: 2px; background: var(--line);
}
.step:last-child::before { display: none; }
.step-node {
  position: absolute; left: 0; top: 0;
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--white); border: 2px solid var(--teal);
  font-family: var(--font-mono); font-size: 0.8rem; font-weight: 600;
  color: var(--teal);
}
.step:first-child .step-node { background: var(--teal); color: var(--white); }
.step h3 { margin: 0.25rem 0 0.35rem; font-size: 1.08rem; color: var(--navy); }
.step p { margin: 0; color: var(--ink-soft); font-size: 0.95rem; line-height: 1.6; }
.step-when {
  display: inline-block; margin-top: 0.5rem;
  font-family: var(--font-mono); font-size: 0.74rem;
  /* teal on teal-soft is 4.24:1, just under the 4.5 floor. */
  color: var(--teal-dark); background: var(--teal-soft);
  padding: 0.2rem 0.55rem; border-radius: 5px;
}

/* ---------- 5. Editorial quote — for the one human voice ----------
   A person is not a feature card. */
.voice { display: grid; grid-template-columns: 1fr 1.35fr; gap: 2.6rem; align-items: center; }
.voice-portrait {
  aspect-ratio: 4/5; border-radius: 16px;
  background: linear-gradient(160deg, var(--teal-soft), var(--sky));
  display: grid; place-items: center; text-align: center;
  border: 1px dashed var(--line);
  font-family: var(--font-mono); font-size: 0.78rem; color: var(--teal);
  padding: 1rem;
}
.voice blockquote {
  margin: 0; border: 0; padding: 0;
  font-size: clamp(1.25rem, 2.4vw, 1.7rem);
  line-height: 1.45; color: var(--navy); font-weight: 500;
}
.voice blockquote::before {
  content: ""; display: block; width: 46px; height: 3px;
  background: var(--amber); margin-bottom: 1.2rem;
}
.voice cite { display: block; margin-top: 1.3rem; font-style: normal; }
.voice cite b { display: block; color: var(--navy); font-size: 1rem; }
.voice cite span { font-family: var(--font-mono); font-size: 0.8rem; color: var(--ink-soft); }
@media (max-width: 780px) {
  .voice { grid-template-columns: 1fr; gap: 1.6rem; }
  .voice-portrait { max-width: 220px; }
}



@media (max-width: 820px) {
  /* Accordion on a phone: the rail buttons become the headers, each pane
     following the button that opens it. Handled in JS by reordering. */
}

/* ---------- 9. Section headers that are not all identical ----------
   Fourteen centred kicker+h2 pairs in a row is the single biggest reason the
   page felt samey. The default is now LEFT aligned with a rule, and centring
   is opt-in for the two or three sections that earn it. */
.head-rule { display: grid; grid-template-columns: auto 1fr; gap: 1.4rem; align-items: start; margin-bottom: var(--head-gap); }
.head-rule .kicker {
  margin: 0; white-space: nowrap; padding: 0.3rem 0 0 0.9rem;
  border-left: 2px solid var(--amber);
}
.head-rule h2 { margin: 0 0 0.5rem; }
.head-rule p { margin: 0; color: var(--ink-soft); max-width: 62ch; line-height: 1.7; }
.head-rule::after { display: none; }
@media (max-width: 720px) { .head-rule { grid-template-columns: 1fr; gap: 0.5rem; } }

/* ---------- 10. Closing band ---------- */
.close-band {
  background: var(--navy); color: var(--white);
  border-radius: 22px; padding: clamp(2rem, 5vw, 3.4rem);
  display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: center;
}
.close-band h2 { color: var(--white); margin: 0 0 0.6rem; }
.close-band p { color: rgba(255,255,255,0.78); margin: 0; max-width: 50ch; }
.close-actions { display: flex; gap: 0.8rem; flex-wrap: wrap; }
@media (max-width: 820px) { .close-band { grid-template-columns: 1fr; } }

/* ---------- 11. Flow — an alternating route timeline ----------
   The page carried the SAME five steps twice: once as labels under the orbit
   curve, once as a five-card row directly below. This is the single surviving
   telling. Steps alternate above and below one rail so it reads as a route with
   stops rather than five equal boxes.

   Each step is a THREE-ROW grid — [space or text][node][text or space] — with the
   node row a fixed height. Because grid items in one row all stretch to the
   tallest, the node row lands at the same y in every column, which is what puts
   every node exactly on the rail. My first attempt did this with flex-direction
   and justify-content and the nodes drifted off the line; a fixed middle row is
   the thing that actually holds them. */
.flow { position: relative; margin-top: 1.2rem; }
.flow-rail {
  position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; margin-top: -1px; background: var(--line);
}
.flow-rail::after {
  /* The coloured run of the rail, now a PROGRESS fill rather than a decorative
     20% stub. It was a fixed width, which is why selecting step 2 lit the node
     and left the line sitting under step 1. `--progress` is set in px by the
     script, measured to the centre of the selected node, so it lands on the dot
     rather than near it at any column width. */
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: var(--progress, 10%);
  background: linear-gradient(90deg, var(--amber), var(--teal));
  transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) { .flow-rail::after { transition: none; } }
.flow-steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, 1fr);
  position: relative;
}
.flow-step {
  display: grid; grid-template-rows: 1fr 40px 1fr;
  text-align: center; padding: 0 0.8rem;
}
.flow-node {
  grid-row: 2; justify-self: center; align-self: center;
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  background-color: var(--white); border: 2px solid var(--teal);
  font-family: var(--font-mono); font-size: 0.82rem; font-weight: 600; color: var(--teal);
  position: relative; z-index: 1;
  padding: 0;
  cursor: pointer;
  /* Longhand `background-color` rather than the `background` shorthand, so the
     transition names exactly the property that changes. */
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease,
              transform 0.2s ease, box-shadow 0.2s ease;
}
/* The node is 38px, under the 44px target. The button keeps its drawn size and
   grows its HIT AREA instead, so the tap target is 44px without a 44px circle
   colliding with the one beside it. */
.flow-node::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}
.flow-node:hover {
  background-color: var(--teal);
  color: var(--white);
  transform: scale(1.08);
  box-shadow: 0 0 0 5px var(--teal-soft);
}
.flow-node:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 3px;
}
/* The selected step. This was `.flow-step:first-child`, which is exactly why the
   amber could never move off step one. */
.flow-step.is-active .flow-node {
  background-color: var(--amber);
  border-color: var(--amber);
  color: var(--navy);
  box-shadow: 0 0 0 5px rgba(240, 173, 78, 0.22);
}
.flow-step.is-active .flow-node:hover { background-color: var(--amber); color: var(--navy); }
/* Selecting a step highlights its TEXT, not just its dot — the dot is 38px and
   the words are what the reader is actually looking at. */
.flow-step.is-active h3 { font-weight: 700; }
.flow-step.is-active p { color: var(--ink); }
.flow-step.is-active .step-when {
  background-color: var(--navy);
  color: var(--white);
}
.flow-step h3, .flow-step p, .flow-step .step-when { transition: color 0.2s ease, background-color 0.2s ease; }
.flow-step:nth-child(odd) .flow-body { grid-row: 1; align-self: end; padding-bottom: 1rem; }
.flow-step:nth-child(even) .flow-body { grid-row: 3; align-self: start; padding-top: 1rem; }
/* 600 at rest so that the selected step's 700 is a visible step up. An h3 is
   already 700 by default, so "bold on select" would have changed nothing.
   IBM Plex Sans is loaded at 400/500/600/700 — 800 would only be synthesised. */
.flow-step h3 { font-size: 1rem; margin: 0 0 0.3rem; color: var(--navy); font-weight: 600; }
.flow-step p { font-size: 0.87rem; color: var(--ink-soft); line-height: 1.55; margin: 0; }
.flow-step .step-when { margin-top: 0.5rem; }

@media (max-width: 900px) {
  /* Vertical stepper: one column, rail down the left, no alternation. */
  .flow-rail { display: none; }
  .flow-steps { grid-template-columns: 1fr; }
  .flow-step {
    grid-template-rows: auto; grid-template-columns: 38px 1fr;
    gap: 0 1rem; text-align: left; padding: 0 0 1.9rem;
    position: relative; align-items: start;
  }
  .flow-node { grid-row: 1; grid-column: 1; align-self: start; }
  .flow-step:nth-child(odd) .flow-body,
  .flow-step:nth-child(even) .flow-body {
    grid-row: 1; grid-column: 2; padding: 0; align-self: start;
  }
  .flow-step::before {
    content: ""; position: absolute; left: 18px; top: 46px; bottom: 0;
    width: 2px; background: var(--line);
    transition: background 0.35s ease;
  }
  /* The rail is hidden on a phone, so the vertical connector carries the
     progress instead: every step above the selected one is filled in. */
  .flow-step.is-done::before { background: var(--teal); }
  .flow-step.is-done .flow-node { border-color: var(--teal); }
  .flow-step:last-child { padding-bottom: 0; }
  .flow-step:last-child::before { display: none; }
}

/* ---------- 12. Benchmark bars — for a comparison ----------
   "We publish our numbers" was a table of us-vs-industry. A table makes the
   reader do the arithmetic; two bars per row makes the gap the thing you see. */
.bench { display: grid; gap: 1.4rem; max-width: var(--measure-read); margin-inline: auto; }
.bench-row { display: grid; grid-template-columns: 13rem 1fr; gap: 1.2rem; align-items: center; }
.bench-label { font-size: 0.94rem; font-weight: 600; color: var(--navy); }
.bench-label span { display: block; font-weight: 400; font-size: 0.8rem; color: var(--ink-soft); }
.bench-bars { display: grid; gap: 0.42rem; }
.bench-bar { display: grid; grid-template-columns: 1fr auto; gap: 0.7rem; align-items: center; }
.bench-track { height: 22px; border-radius: 5px; background: var(--sky); overflow: hidden; }
.bench-fill { height: 100%; border-radius: 5px; background: var(--teal); width: var(--w); }
.bench-bar.them .bench-fill { background: #C6D2DA; }
.bench-val { font-family: var(--font-mono); font-size: 0.82rem; font-weight: 600; color: var(--navy); min-width: 4.4rem; text-align: right; }
.bench-bar.them .bench-val { color: var(--ink-soft); font-weight: 400; }
.bench-key { display: flex; gap: 1.3rem; font-size: 0.82rem; color: var(--ink-soft); margin-bottom: 0.4rem; justify-content: center; }
/* The heading for the comparison, on white. It was styled only inside
   `.stats-band`, so when the block moved out it arrived with no styling at all. */
.bench-sub { text-align: center; margin: 0 auto var(--head-gap); max-width: 52ch; }
.bench-sub h2 { font-size: clamp(1.4rem, 2.4vw, 1.85rem); margin: 0 0 0.5rem; }
.bench-sub p { color: var(--ink-soft); margin: 0; }
.bench-key i { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 0.4rem; vertical-align: -1px; }
@media (max-width: 640px) { .bench-row { grid-template-columns: 1fr; gap: 0.5rem; } }

/* ---------- 13. Corridor schematic — for coverage ----------
   The coverage section carried FOUR stat cards repeating figures already on the
   board above it (18,000+, 300+, 2 yards, 24/7 — a fourth telling on one page).
   Coverage is a geography question, so it gets a map. Deliberately SCHEMATIC and
   hand-authored: a tile provider costs a third-party request, a key and a cookie
   banner to show six labelled points that never move. */
.corridor { position: relative; }
.corridor svg { width: 100%; height: auto; display: block; }
.corridor-line { fill: none; stroke: var(--line); stroke-width: 2.5; stroke-linecap: round; }
.corridor-line.main { stroke: var(--teal); stroke-width: 3; }
.corridor-pin { fill: var(--white); stroke: var(--teal); stroke-width: 2.5; }
.corridor-pin.yard { fill: var(--amber); stroke: var(--amber); }
.corridor-hub { fill: var(--navy); stroke: var(--navy); }
.corridor text {
  font-family: var(--font-head); font-size: 13px; font-weight: 600;
  fill: var(--navy);
}
.corridor text.sub { font-family: var(--font-mono); font-size: 10.5px; font-weight: 400; fill: var(--ink-soft); }
.corridor-key { display: flex; flex-wrap: wrap; gap: 1.1rem; margin-top: 1rem; font-size: 0.82rem; color: var(--ink-soft); }
.corridor-key i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 0.4rem; vertical-align: -1px; }

/* ---------- 14. Statement — for a company's own two sentences ----------
   Vision and Mission were two plain cards, which files them as tiles. They are
   the two sentences the business makes about itself, so they get scale, a mono
   tag and an amber rule instead of a box. */
.statement { padding-left: 1.3rem; border-left: 3px solid var(--amber); margin-bottom: 1.8rem; }
.statement:last-child { margin-bottom: 0; }
.statement-tag {
  display: block; font-family: var(--font-mono); font-size: 0.74rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--teal); margin-bottom: 0.5rem;
}
.statement p { margin: 0; font-size: 1.06rem; line-height: 1.65; color: var(--navy); font-weight: 450; }

/* ---------- 15. Plan rows — for a roadmap ----------
   A card grid lets a pilot and a live service look identical. Status leads. */
.plan { border-top: 1px solid var(--line); }
.plan-item {
  display: grid; grid-template-columns: 7.5rem 1fr; gap: 1.4rem;
  padding: 1.5rem 0; border-bottom: 1px solid var(--line);
  align-items: start;
}
.plan-status {
  display: inline-block; padding: 0.3rem 0.7rem; border-radius: 999px;
  font-family: var(--font-mono); font-size: 0.72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap;
}
.plan-status.live    { background: var(--teal-soft); color: var(--teal-dark); }
.plan-status.pilot   { background: #FDF0DA; color: #8A5A12; }
.plan-status.planned { background: var(--sky); color: var(--ink-soft); }
.plan-item h3 { margin: 0 0 0.35rem; font-size: 1.06rem; color: var(--navy); }
.plan-item p { margin: 0; color: var(--ink-soft); line-height: 1.65; font-size: 0.95rem; }
@media (max-width: 620px) {
  .plan-item { grid-template-columns: 1fr; gap: 0.7rem; }
}


/* ---------- Contrast repairs found by measuring, not by eye (2026-08-12) ----------
   The footer's phone and email links inherited the global link colour
   (--teal-dark) and sat on the navy-deep footer at 2.07:1 — the worst contrast
   on the site, on the two links a customer most wants to click. */
.site-footer a { color: rgba(255, 255, 255, 0.86); }
.site-footer a:hover { color: var(--white); }
.site-footer .social-btn { color: inherit; }

/* The "24/7" separator was --line on white: 1.27:1. It is a character that
   carries meaning, so it needs text contrast, not decorative contrast. */

/* Same 4.48:1 near-miss as the kicker, same fix. */
.statement-tag { color: var(--teal-dark); }

/* ═══════════════════════════════════════════════════════════════════════
   TOUCH TARGETS (measured on an emulated 390px iPhone, 2026-08-12)
   Everything below was under the 44px minimum. Applied at the touch
   breakpoint rather than globally, because on a mouse-driven 1280px layout
   the tighter footer spacing is correct and 44px rows would stretch it.

   Worst offenders, and why they matter more than the numbers suggest:
     footer nav links      19px  the whole site map on a phone
     footer phone / email  19px  the two things a customer wants to tap
     .pin-btn              33px  the pickup/drop pin toggle — the booking
                                 form is unusable without hitting these
     .loc-btn              31px  "use my current location"
     leaflet zoom          30px  Leaflet's default, too small for a thumb
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width: 860px), (pointer: coarse) {
  /* Footer: every link becomes a real row. */
  .footer-links a,
  .site-footer li > a,
  .site-footer .footer-contact a,
  .site-footer span > a {
    display: inline-flex; align-items: center;
    min-height: 44px;
  }
  .footer-bottom a { display: inline-flex; align-items: center; min-height: 44px; }
  .crumbs a { display: inline-flex; align-items: center; min-height: 44px; }

  /* Social icons were 38x38. */
  .social-btn { width: 44px; height: 44px; }

  /* The hamburger was 40px wide. */
  .nav-toggle { min-width: 44px; min-height: 44px; }

  /* Booking form controls. These are the ones that actually cost a booking. */
  .loc-btn, .pin-btn { min-height: 44px; }
  .link-btn { min-height: 44px; }

  /* Leaflet ships 30px zoom buttons. Its own class, so it needs the override. */
  .leaflet-touch .leaflet-bar a,
  .leaflet-control-zoom a {
    width: 44px !important; height: 44px !important;
    line-height: 44px !important; font-size: 20px !important;
  }
}

/* Mono/uppercase labels were as small as 10.5px, which is below what is
   comfortably readable on a phone. Floor them at 12px there — the desktop sizes
   stay as designed. */
@media (max-width: 860px) {
  .eyebrow { font-size: 0.75rem; }
  .step-when, .statement-tag, .plan-status { font-size: 0.75rem; }
}

/* SVG text does not scale the way CSS text does: inside a viewBox it shrinks
   with the element. The corridor map declares 13px, but at 342px wide against a
   600-unit viewBox the scale is 0.57, so it RENDERS at 7.4px — measured, not
   guessed. Same trap on the About page's growth chart. Both get viewBox-space
   sizes chosen so the rendered result lands back around 12-13px. */
@media (max-width: 700px) {
  .corridor text      { font-size: 22px; }
  .corridor text.sub  { font-size: 18px; }
  .growth-chart svg text { font-size: 22px; }   /* 22 x 0.52 scale = 11.4px rendered */
}


/* The top strip on a phone.
   The 44px tap target is achieved with `margin-block: -0.45rem`, which absorbs
   into the strip's padding as long as everything stays on ONE row. At 390px the
   three items wrap, and the negative margin then pulls the links UP out of the
   navy band — they were rendering on the white above it, which is why the phone
   number vanished from the strip.
   Fix: no wrapping at this width. The phone stays (it is the most-tapped thing
   on a transport site) and the email drops, since it is in the footer, in the
   drawer and in the quote confirmation. */
@media (max-width: 700px) {
  .top-strip .wrap { flex-wrap: nowrap; gap: 0.6rem; padding-inline: 1rem; }
  .top-strip a[href^="mailto:"] { display: none; }
  .top-strip .sep { font-size: 0.78rem; white-space: nowrap; }
  .top-strip a[href^="tel:"] { white-space: nowrap; }
}

/* Two fixed controls live at the bottom on a phone — the quote pill bottom-left
   and the WhatsApp bubble bottom-right. Content scrolls under them, which is how
   a floating button is meant to work, but the LAST thing on the page would sit
   under them permanently with nothing left to scroll. Give the footer that much
   clearance back. */
@media (max-width: 760px) {
  .site-footer { padding-bottom: calc(5rem + env(safe-area-inset-bottom)); }
}

/* ---------- 16. Accordion — one implementation, and it cannot clip ----------
   The old FAQ measured `scrollHeight` in JS and wrote it into `max-height`. That
   is the classic accordion bug: the number is captured once, so it is wrong the
   moment anything reflows — a font swapping in, the window narrowing, the text
   wrapping to one more line. Combined with a second stylesheet block changing
   the padding underneath it, the answer was clipped after a single line.

   This animates `grid-template-rows: 0fr -> 1fr` instead. The browser resolves
   the real height itself, every time, so there is no number to get wrong and the
   JS shrinks to toggling a class. */
.faq { border-top: 1px solid var(--line); margin-top: 1.6rem; }
.faq-item {
  border-bottom: 1px solid var(--line);
  /* the amber rule that marks the open row */
  border-left: 3px solid transparent;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.faq-item.open { border-left-color: var(--amber); background: var(--white); }

.faq-q {
  width: 100%;
  display: flex; align-items: center; gap: 1.2rem;
  padding: 1.35rem 1.3rem;
  background: none; border: 0; cursor: pointer;
  text-align: left;
  font-family: var(--font-head); font-weight: 600;
  font-size: 1.02rem; line-height: 1.45; color: var(--navy);
}
.faq-q:hover { color: var(--teal-dark); }
.faq-q:focus-visible { outline: 2px solid var(--teal); outline-offset: -3px; }

/* A round target that reads as a control, not a decorative caret. */
.faq-q .chev {
  flex: none; margin-left: auto;
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--sky); color: var(--teal-dark);
  transition: transform 0.25s ease, background 0.2s ease, color 0.2s ease;
}
.faq-item.open .faq-q .chev {
  transform: rotate(180deg);
  background: var(--teal-dark); color: var(--white);
}

.faq-a {
  display: grid;
  grid-template-rows: 0fr;                 /* closed */
  transition: grid-template-rows 0.28s cubic-bezier(0.32,0.72,0,1);
}
.faq-item.open .faq-a { grid-template-rows: 1fr; }
/* The row's child must collapse to NOTHING — and that means no padding on it.
   `min-height: 0` zeroes the content box but padding is always added on top, so
   a padded child leaves its padding visible when the row is 0fr and the answer
   bleeds through under a closed question. Hence a bare clip box, with the
   padding on the text inside it. */
.faq-a > .faq-a-clip { overflow: hidden; min-height: 0; }
.faq-a-inner {
  padding: 0 4.2rem 1.4rem 1.3rem;
  color: var(--ink-soft); font-size: 0.97rem; line-height: 1.7;
  max-width: 72ch;
}
@media (prefers-reduced-motion: reduce) { .faq-a { transition: none; } }
@media (max-width: 600px) {
  .faq-q { padding: 1.15rem 0.9rem; font-size: 0.97rem; gap: 0.8rem; }
  .faq-a-inner { padding: 0 0.9rem 1.2rem; }
}



/* ---------- 18. Story lede + vision pairing (About) ----------
   The About page told its story three times: a timeline, then a prose paragraph
   restating the same three facts, then "What sets us apart" restating them
   again. It also carried the home page's proof list, the services page's list of
   services, and the MD quote verbatim from the home page. What About uniquely
   owns is the STORY, so that is all it does now.

   The vision pairing is the specific fix: the growth chart used to sit BELOW the
   two statements, because .split collapses under 980px and the statements alone
   are short. It is now its own two-column pairing with the chart as the anchor,
   and the statements sized to sit beside it. */
.story-lede {
  font-size: clamp(1.1rem, 1.9vw, 1.35rem);
  line-height: 1.6; color: var(--navy); font-weight: 450;
  max-width: 60ch; margin: 0 0 2.6rem;
}
.story-lede strong { font-weight: 650; }

.vision-pair {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: center;
}
/* One breakpoint, and it is the one where the CHART stops being readable —
   not an inherited 980px that stacked a perfectly fine two-column layout. */
@media (max-width: 900px) { .vision-pair { grid-template-columns: 1fr; } }

/* The chart is the anchor here, so it gets a surface rather than floating. */
.vision-pair .chart-card { margin: 0; }

/* ---------- 19. Director's message — the lead block on About ----------
   Moved off the home page 2026-08-12 at the owner's request, and it is a longer
   piece here than the single pulled quote it was there: identity and portrait on
   the left, the message on the right, the quote set apart at the end.

   The left column is sticky on a wide screen so the name and title stay beside
   the paragraph being read — this is a letter from a person, and the attribution
   scrolling away from it loses that. */
.director { display: grid; grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr); gap: clamp(2rem, 4vw, 3.6rem); align-items: start; }
.director-id { position: sticky; top: 7rem; }
.director-portrait {
  display: block;
  max-width: 320px;   /* it is a portrait, not the subject of the section */
}
.director-portrait img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 4/5; object-fit: cover;
  /* The crop is already 4:5, so object-fit only guards against a future
     re-crop at a different ratio silently stretching the face. */
  border-radius: 16px;
  box-shadow: 0 18px 40px -24px rgba(8, 42, 69, 0.45);
}
.director-body > p { color: var(--ink); line-height: 1.75; margin: 0 0 1.1rem; max-width: 66ch; }
.director-body > p:first-child { font-size: 1.1rem; color: var(--navy); }

/* The closing quote: the one warm mark on the page. */
.director-quote { margin: 2rem 0 0; padding-left: 1.4rem; border-left: 3px solid var(--amber); }
.director-quote blockquote {
  margin: 0; border: 0; padding: 0;
  font-size: clamp(1.15rem, 2.2vw, 1.5rem); line-height: 1.5;
  color: var(--navy); font-weight: 500;
}
.director-quote cite { display: block; margin-top: 0.9rem; font-style: normal; }
.director-quote cite b { display: block; color: var(--navy); font-size: 0.98rem; }
.director-quote cite span { font-family: var(--font-mono); font-size: 0.76rem; color: var(--ink-soft); }

@media (max-width: 860px) {
  .director { grid-template-columns: 1fr; gap: 1.6rem; }
  /* Sticky is wrong in one column — it would pin the portrait over the text. */
  /* Only the portrait lives here now, so no two-column grid — an empty second
     cell would just add dead space beside it. */
  .director-id { position: static; }
  .director-portrait { max-width: 200px; }
  .director-portrait { margin-bottom: 0; }
}

/* ---------- 20. Operate cards — reveal-on-hover, 4 up ----------
   Replaces the spec sheet in "How we operate" at the owner's request, based on
   the Uiverse card by SouravBandyopadhyay: a small circle in the corner that
   scales up to flood the card, and the text inverts to white. Recoloured to our
   palette — the original's #6293c8/#384c6c blues are not ours.

   THE TITLE SHOWS FIRST, THE DESCRIPTION ON HOVER, as asked. Two things that
   needs to survive:

   1. A PHONE HAS NO HOVER. Gating the proof behind a pointer would hide all
      eight claims from most of this site's visitors, so the reveal lives inside
      `@media (hover: hover)`. Anywhere else, both lines are simply visible.
   2. The description keeps its space when hidden (opacity, not display), so the
      card does not resize under the cursor — and it stays in the accessibility
      tree, so a screen reader reads it whether or not anything is hovered. */
.operate-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 1fr;   /* one height for all eight, so it reads as a block */
  gap: 1.1rem;
}
@media (max-width: 1080px) { .operate-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .operate-grid { grid-template-columns: 1fr; } }

.op-card {
  position: relative;
  z-index: 0;
  overflow: hidden;
  border-radius: 14px;
  padding: 1.5rem 1.3rem;
  background: linear-gradient(to bottom, var(--sky), #E2ECF3);
  border: 1px solid var(--line);
  min-height: 13.5rem;
  display: flex;
  flex-direction: column;
  /* Centred, so a card showing only its title looks intentional instead of
     half-empty — the row height is set by the longest hidden description. */
  justify-content: center;
}
/* Focusable so the hover-revealed text is reachable by keyboard. The ring sits
   outside the card because the card clips its own overflow. */
.op-card:focus-visible {
  outline: 2px solid var(--teal-dark);
  outline-offset: 3px;
}
/* The circle that floods the card. Sized from the corner, so it has to cover the
   diagonal — 32px scaled 28x is 896px, past the corner of any card in this grid. */
.op-card::before {
  content: "";
  position: absolute;
  z-index: -1;
  /* SIZED FROM THE CARD, not a pixel count. The circle grows from the top-right
     corner, so to fill the card it has to reach the OPPOSITE corner — the whole
     diagonal. The inherited `scale(28)` on a 32px circle gave a 448px radius,
     which covers the 390px diagonal of a 4-up desktop card and MISSES a
     full-width mobile card (491px) and a 2-up tablet card (545px). That is the
     pale wedge the owner photographed in the bottom-left.
     `inset: -60%` makes this box 220% of the card in both axes; the ellipse
     inscribed in it clears every corner at any card size, so there is no number
     left to outgrow. */
  inset: -60%;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--teal-dark), var(--navy));
  transform: scale(0);
  transform-origin: 100% 0;
  transition: transform 0.45s ease-out;
}
/* The corner tab, always visible: it is the affordance that says there is more. */
.op-corner {
  position: absolute;
  top: 0; right: 0;
  width: 2.1rem; height: 2.1rem;
  display: grid; place-items: center;
  border-radius: 0 14px 0 30px;
  background: linear-gradient(135deg, var(--teal), var(--navy));
  color: var(--white);
  transition: opacity 0.3s ease;
}
.op-corner svg { margin: -2px -2px 0 0; }

.op-num {
  font-family: var(--font-mono);
  /* A display numeral, not a label: it gives the closed card its weight, which
     is what stops a one-line title looking marooned. */
  font-size: 2rem;
  font-weight: 600;
  line-height: 1;
  color: var(--teal);
  opacity: 0.5;
  margin-bottom: 0.6rem;
  transition: color 0.4s ease-out, opacity 0.4s ease-out;
}
.op-title {
  font-family: var(--font-head);
  font-size: 1.16rem;
  font-weight: 650;
  line-height: 1.3;
  color: var(--navy);
  margin: 0;
  transition: color 0.4s ease-out;
}
.op-desc {
  margin: 0.7rem 0 0;
  font-size: 0.89rem;
  line-height: 1.55;
  color: var(--ink-soft);
  transition: color 0.4s ease-out, opacity 0.3s ease-out;
}

/* Only where a pointer exists: hide the description until hover, then flood. */
@media (hover: hover) and (pointer: fine) {
  .op-desc { opacity: 0; }
  .op-card:hover::before,
  .op-card:focus::before,
  .op-card:focus-within::before { transform: scale(1); }
  .op-card:hover .op-desc,
  .op-card:focus .op-desc,
  .op-card:focus-within .op-desc { opacity: 1; color: rgba(255, 255, 255, 0.88); }
  .op-card:hover .op-title,
  .op-card:focus .op-title,
  .op-card:focus-within .op-title { color: var(--white); }
  .op-card:hover .op-num,
  .op-card:focus .op-num,
  .op-card:focus-within .op-num { color: var(--cyan); opacity: 0.85; }
  .op-card:hover .op-corner,
  .op-card:focus .op-corner,
  .op-card:focus-within .op-corner { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .op-card::before { transition: none; }
  /* No flood at all — reveal the text in place instead of animating a wipe. */
  .op-card:hover::before, .op-card:focus-within::before { transform: scale(0); }
  .op-card:hover .op-desc, .op-card:focus-within .op-desc { color: var(--ink-soft); }
  .op-card:hover .op-title, .op-card:focus-within .op-title { color: var(--navy); }
  .op-card:hover .op-num, .op-card:focus-within .op-num { color: var(--teal); opacity: 0.5; }
}

/* ---------- Scroll-activated cards (2026-08-12) ----------
   A phone cannot hover, so the reveal is driven by position instead: js/main.js
   adds `.is-active` when the card sits in the middle band of the screen. The
   owner asked for exactly that. Kept outside the hover media query so it also
   works on a laptop in a narrow window, which is where the coverage bug was
   first seen. */
.op-card.is-active::before { transform: scale(1); }
.op-card.is-active .op-title { color: var(--white); }
.op-card.is-active .op-num { color: var(--cyan); opacity: 0.85; }
.op-card.is-active .op-desc { opacity: 1; color: rgba(255, 255, 255, 0.88); }
.op-card.is-active .op-corner { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  /* No wipe for anyone who asked for less motion — the card just stays light
     and readable, which it already is without the flood. */
  .op-card.is-active::before { transform: scale(0); }
  .op-card.is-active .op-title { color: var(--navy); }
  .op-card.is-active .op-num { color: var(--teal); opacity: 0.5; }
  .op-card.is-active .op-desc { opacity: 1; color: var(--ink-soft); }
  .op-card.is-active .op-corner { opacity: 1; }
}

/* ---------- 21. Stats band — scale & presence on a dark ground ----------
   Owner's reference: a dark section with light rising from the bottom, a centred
   heading, and a 3x2 grid of one big number over one sentence. Recoloured from
   the reference's green to navy with a light-blue glow.

   Replaces the bento board. The board's asymmetry earned its keep when one
   figure was the headline; six equal statements read better as a regular grid,
   which is what the reference is doing. */
.stats-band {
  position: relative;
  overflow: hidden;
  background: var(--navy-deep);
  color: var(--white);
}
/* The glow. A radial gradient anchored to the bottom centre, so the light rises
   the way it does in the reference — cyan into teal into nothing. Sits behind
   the content and takes no pointer events. */
.stats-band::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 72%;
  /* TWO layers, because one radial could not do both jobs. The first attempt put
     a single ellipse centred BELOW the band's bottom edge, so its bright core was
     clipped away and what remained had faded to ~0.5% alpha by the left third —
     measured flat navy end to end. Now: a linear rise that lifts the whole width
     evenly, plus a soft radial hotspot at the bottom centre for the bloom the
     reference has. */
  background:
    radial-gradient(70% 115% at 50% 100%,
      rgba(79, 198, 222, 0.26) 0%,
      rgba(79, 198, 222, 0.10) 42%,
      transparent 72%),
    linear-gradient(to top,
      rgba(31, 122, 156, 0.34) 0%,
      rgba(31, 122, 156, 0.14) 42%,
      transparent 100%);
  pointer-events: none;
}
.stats-band > .wrap { position: relative; }

.stats-head { text-align: center; max-width: 46ch; margin: 0 auto 3.2rem; }
.stats-head .kicker { color: var(--cyan); }
.stats-head h2 { color: var(--white); margin: 0 0 0.6rem; }
.stats-head p { color: rgba(255, 255, 255, 0.72); margin: 0; line-height: 1.65; }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 3.2rem 0;
  text-align: center;
}
/* Dividers stated deliberately, and reset per ROW for the real column count.
   The old `.stat` rule leaked a border here with a `:nth-child(odd)` reset tuned
   for four columns, which drew a stray line at the start of the second row. */
.stat-cell { padding: 0.2rem 1.6rem; border-left: 1px solid rgba(255, 255, 255, 0.14); }
.stat-cell:nth-child(3n + 1) { border-left: 0; }
.stat-num {
  font-family: var(--font-mono);
  font-size: clamp(2.3rem, 4.4vw, 3.3rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--white);
}
/* The + and % stay cyan — on navy that measures 6.9:1, so it is legible type
   rather than decoration. The amber ink used on light grounds would go muddy. */
.stat-num sup { color: var(--cyan); font-size: 0.48em; top: -0.78em; margin-left: 0.04em; }
.stat-note {
  margin: 0.85rem auto 0;
  max-width: 24ch;
  font-size: 0.97rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.74);
}
@media (max-width: 860px) {
  .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.6rem 0; }
  .stat-cell:nth-child(3n + 1) { border-left: 1px solid rgba(255, 255, 255, 0.14); }
  .stat-cell:nth-child(2n + 1) { border-left: 0; }
}
@media (max-width: 480px) {
  .stat-grid { grid-template-columns: 1fr; gap: 2.2rem; }
  /* Every selector that could still be setting a border has to be named here.
     A bare `.stat-cell` (0,1,0) loses to the `:nth-child(3n + 1)` rule (0,2,0)
     in the 860px block above, which left a divider on cell 4 in one column. */
  .stat-cell,
  .stat-cell:nth-child(2n + 1),
  .stat-cell:nth-child(3n + 1) { border-left: 0; padding-inline: 0; }
}

/* The dark treatment of the comparison bars lived here. The owner moved the
   block out of the navy band and onto white, so all of it is deleted: the base
   `.bench-*` rules were the light-ground versions all along, and leaving these
   would have meant two treatments for one component and no page using this one. */




@media (max-width: 760px) {
  /* One card at a time below the three-up breakpoint — with two there is no
     middle, and the whole design is built around a middle. */
  /* No scale here: the card is already full width, so growing it would push past
     both edges of the clip. The single card is self-evidently the focus. */
  @keyframes wd-pop-flat {
    0%   { box-shadow: 0 0 0 0 rgba(79, 198, 222, 0.5); }
    55%  { box-shadow: 0 0 0 8px rgba(79, 198, 222, 0.15); }
    100% { box-shadow: 0 22px 46px -26px rgba(8, 42, 69, 0.55); }
  }
  /* Swipe: let the browser own vertical scrolling, we take the horizontal. */
}

/* ---------- 23. What we do — coverflow carousel ----------
   Owner's reference: cards in perspective, the middle one square to the viewer
   and the neighbours rotated away into the distance, on a dark ground. Auto-runs,
   and DRAGGABLE with a mouse as well as a finger.

   Positioning is absolute + 3D transform per card, driven from JS by each card's
   signed distance from the active index. That distance is computed modulo the
   card count, so the ring is infinite with no clones and no DOM rotation — the
   previous version moved nodes around, which cannot express "two to the left" in
   a way perspective needs.

   Each card carries a PHOTO of the thing it describes, darkened. The darkening is
   two layers, not one: a flat wash to knock the whole picture back, and a bottom
   gradient under the text specifically. One flat wash strong enough for white
   text over a bright sky would have turned the night shot into a black rectangle. */
.cf {
  position: relative;
  /* The dark ground the reference has, in our navy. */
  background: var(--navy-deep);
  border-radius: 18px;
  overflow: hidden;
  padding: clamp(2rem, 4vw, 3.2rem) 0 clamp(1.4rem, 2vw, 2rem);
}
.cf::before {
  /* Same rising glow as the stats band, so the two dark sections belong together */
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 60%;
  background: linear-gradient(to top, rgba(31, 122, 156, 0.28), transparent);
  pointer-events: none;
}
.cf-stage {
  position: relative;
  height: 29rem;
  perspective: 1500px;
  /* The horizontal axis is ours; vertical page scrolling stays the browser's. */
  touch-action: pan-y;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.cf-stage.is-dragging { cursor: grabbing; }
.cf-stage * { -webkit-user-drag: none; }

.cf-card {
  position: absolute;
  top: 0;
  left: 50%;
  width: clamp(15rem, 23vw, 20rem);
  height: 100%;
  margin-left: calc(clamp(15rem, 23vw, 20rem) / -2);
  border-radius: 16px;
  overflow: hidden;
  transform-style: preserve-3d;
  /* JS writes transform, opacity and z-index. Only the visual settle is animated
     here, and the class below turns it off while a finger or mouse is down so the
     card tracks the pointer instead of lagging 450ms behind it. */
  transition: transform 0.45s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.45s ease;
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.75);
  background: var(--navy);
}
.cf-stage.is-dragging .cf-card { transition: none; }

.cf-card img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* Layer one, gentle: enough to sit type over, not enough to lose the picture.
     0.62 was too far — the night shot went black. */
  filter: brightness(0.82) saturate(0.95);
}
.cf-card::after {
  /* Layer two: a stronger gradient under the text only. Measured, not guessed —
     white on the darkest stop is 13:1 and on the lightest part of the text area
     still clears 4.5. */
  content: "";
  position: absolute; inset: 0;
  /* Layer two: the weight sits at the bottom, under the words, and the top half
     of the photograph is left alone. Tuned against MEASURED contrast — white type
     lands between 10:1 and 13:1 on all eight photographs at the worst pixel, so
     there is no case for darkening further. An earlier, heavier ramp was written
     to chase a 3.5:1 reading that turned out to be the measurement sampling
     anti-aliased glyph edges rather than the backdrop. */
  background: linear-gradient(to top,
    rgba(4, 22, 38, 0.95) 0%,
    rgba(4, 22, 38, 0.86) 24%,
    rgba(4, 22, 38, 0.46) 50%,
    rgba(4, 22, 38, 0.08) 74%,
    rgba(4, 22, 38, 0.00) 90%);
}

.cf-body {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 1;
  padding: 1.5rem 1.4rem 1.6rem;
  color: var(--white);
}
.cf-num {
  font-family: var(--font-mono);
  font-size: 0.8rem; font-weight: 600;
  color: var(--cyan);
  letter-spacing: 0.08em;
}
.cf-title {
  margin: 0.4rem 0 0;
  font-family: var(--font-head);
  font-size: 1.14rem; font-weight: 650; line-height: 1.3;
  color: var(--white);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.5);
}
/* Only the card facing the viewer carries its description; the rotated ones keep
   the heading, as the owner asked for on the previous version. */
.cf-desc {
  display: grid; grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.45s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.3s ease;
}
.cf-card.is-active .cf-desc { grid-template-rows: 1fr; opacity: 1; }
.cf-desc > div { overflow: hidden; min-height: 0; }
.cf-desc p {
  margin: 0.7rem 0 0;
  font-size: 0.9rem; line-height: 1.6;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
}

.cf-dots { display: flex; justify-content: center; gap: 0.5rem; margin-top: 1.5rem; }
.cf-dot {
  width: 36px; height: 44px; padding: 0; border: 0; background: none;
  cursor: pointer; display: grid; place-items: center;
  position: relative;
}
/* Same trick as .flow-node: the drawn dot stays 7px, the HIT AREA grows to
   44px. 36px buttons + 8px gap = a 44px pitch, the widest that fits eight
   dots on a 390px screen; the ::after fills the gap so targets meet exactly. */
.cf-dot::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}
.cf-dot::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transition: background 0.2s ease, transform 0.2s ease;
}
.cf-dot[aria-current="true"]::before { background: var(--cyan); transform: scale(1.6); }
.cf-dot:focus-visible { outline: 2px solid var(--cyan); outline-offset: -4px; border-radius: 6px; }

@media (max-width: 700px) {
  .cf-stage { height: 24rem; perspective: 900px; }
  .cf-card { width: min(76vw, 17rem); margin-left: calc(min(76vw, 17rem) / -2); }
}
@media (prefers-reduced-motion: reduce) {
  .cf-card, .cf-desc { transition: none; }
}

.cf-track { list-style: none; margin: 0; padding: 0; }
