/* ==========================================================================
   Colonial Calibrations — stylesheet
   Plain CSS, no build step. Edit directly.

   Contents:
     1.  Design tokens
     2.  Base elements
     3.  Accessibility helpers
     4.  Top bar + header + navigation
     5.  Buttons
     6.  Hero
     7.  Proof strip
     8.  Section shell + grids
     8b. Long-form prose
     9.  Cards
     10. Split (text beside image)
     11. Systems grid + mission panel
     12. Table
     13. Resources + FAQ
     14. Service catalogue (services.html)
     15. Forms
     16. Footer
     17. Scroll reveal
     18. Responsive
     19. Motion preferences
   ========================================================================== */


/* 1. Design tokens ========================================================= */

:root {
  /* Navy in four steps rather than one. The single --navy the site started with
     meant every dark surface was the same flat colour; the steps are what let a
     card sit visibly on top of a section, and a section on top of the page. */
  --navy-900: #04101d;
  --navy-800: #07182b;
  --navy-700: #0c2340;
  --navy-600: #143254;

  --red:      #c7202f;
  --red-700:  #a3131f;
  /* Same hue as --red (355deg) at full saturation rather than a lightened
     tint. Lightening alone drifts toward pink; raising saturation instead keeps
     it reading as the brand red while clearing 4.5:1 on the navy grounds. */
  --red-400:  #ff1a2e;

  --silver:   #c9d1dc;
  --light:    #f4f7fb;
  --ink:      #0d1c2e;
  --muted:    #55677d;
  --muted-dk: #9fb1c6;   /* muted text on navy */

  --border:   #dfe6ee;
  --border-dk: rgba(255, 255, 255, .13);

  --radius-sm: 8px;
  --radius:    14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  /* Three shadow tiers. Tinted navy rather than black — a neutral grey shadow
     over a navy-and-white palette reads as dirt on the screen. */
  --shadow-1: 0 1px 2px rgba(7, 24, 43, .06), 0 4px 14px rgba(7, 24, 43, .05);
  --shadow-2: 0 2px 6px rgba(7, 24, 43, .07), 0 16px 34px rgba(7, 24, 43, .10);
  --shadow-3: 0 30px 70px rgba(4, 16, 29, .45);

  /* Wordmark keeps the serif; everything else is set in the UI sans. The mix is
     deliberate — a serif logo against sans headings is what stops the page
     reading like a 2010 template. */
  --font-brand: Georgia, 'Times New Roman', serif;
  --font-ui: 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;

  /* Two widths, and only two. Every full-width row of components sits on
     --maxw so their left and right edges line up all the way down the page;
     anything that is a paragraph to be read — section intros, the FAQ — sits
     on the narrower --maxw-text, which reads as a deliberate measure rather
     than as another stray edge. Adding a third width is what made the page
     ragged before: rows were variously 1440, 1180 and 1080 wide. */
  --maxw: 1280px;
  --maxw-text: 800px;

  /* Sticky header height, used to offset anchored sections so their headings
     don't slide underneath it when jumped to. */
  --header-offset: 118px;
}


/* 2. Base elements ========================================================= */

* {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  font-family: var(--font-ui);
  color: var(--ink);
  background: #fff;
  line-height: 1.6;
  /* Turns on the font's real kerning and ligatures. Free legibility on the
     large uppercase headings, where bad letter fitting is most visible. */
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--font-ui);
  font-weight: 800;
  /* Big type set at default tracking looks loose. Negative tracking on the
     headings is most of what makes the page feel current. */
  letter-spacing: -.02em;
  line-height: 1.12;
}

.brand-title {
  font-family: var(--font-brand);
  letter-spacing: .01em;
}

img {
  max-width: 100%;
}

p {
  margin: 0 0 16px;
}

section[id] {
  scroll-margin-top: var(--header-offset);
}

/* Small uppercase label above a heading. Gives each section a second line of
   hierarchy without another heading level. */
.eyebrow {
  margin: 0 0 12px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--red);
}

.dark .eyebrow,
.hero .eyebrow {
  color: var(--red-400);
}


/* 3. Accessibility helpers ================================================= */

/* Hides text on screen while leaving it readable by screen readers. Use it on
   any control whose only visible content is an icon. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Jump link for keyboard users; only visible once focused. */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 100;
  padding: 12px 18px;
  background: var(--red);
  color: #fff;
  font-weight: 800;
  text-decoration: none;
}

.skip-link:focus {
  left: 8px;
  top: 8px;
}

/* One consistent focus ring across every interactive element. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 3px;
  border-radius: 3px;
}

/* On navy backgrounds the red ring is low contrast — go light instead. */
.dark a:focus-visible,
.dark button:focus-visible,
.dark input:focus-visible,
.dark textarea:focus-visible,
.hero a:focus-visible,
.footer a:focus-visible {
  outline-color: #fff;
}


/* 4. Top bar + header + navigation ========================================= */

.topbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 4px 18px;
  background: var(--navy-900);
  color: #dbe4ee;
  text-align: center;
  padding: 9px 16px;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: .04em;
}

.topbar-call {
  color: #fff;
  font-weight: 800;
  white-space: nowrap;
  text-decoration-color: var(--red);
  text-underline-offset: 3px;
}

.topbar-call:hover {
  color: var(--silver);
}

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 5vw;
  background: rgba(255, 255, 255, .88);
  /* Frosted header. Browsers without backdrop-filter keep the .88 white, which
     is opaque enough to stay readable over anything scrolling beneath it. */
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  position: sticky;
  top: 0;
  z-index: 10;
  transition: box-shadow .22s ease, border-color .22s ease;
}

/* js/main.js adds .is-stuck once the page has scrolled past the top bar, so
   the header is a flat part of the page at rest and a floating bar in use.

   It changes only paint — shadow and border, never height. A sticky element
   stays in normal flow, so shrinking the header on scroll shortens the
   document and yanks everything below it upward at the first scrolled pixel.
   An earlier version tightened the padding and the logo here and jolted the
   page 15px on desktop and 12px on mobile. Keep every property in this rule
   layout-neutral. */
.header.is-stuck {
  border-bottom-color: var(--border);
  box-shadow: 0 6px 26px rgba(7, 24, 43, .10);
}

.logo-wrap {
  display: flex;
  gap: 14px;
  align-items: center;
}

/* EVERY page wraps the header logo in a link back to /, the home page
   included — its logo used to be a bare <img>. Keep it that way. The two
   shapes do not measure the same, and each difference between them showed up
   as the header visibly changing as you moved from one page to another.

   display:block was the first one. An inline <img> inside the link sits on a
   text baseline and reserves descender space beneath itself, which made the
   linked headers 7px taller than the home page's. Blockifying the image
   removes the line box.

   flex:none is the second, on the horizontal axis, and it was the worse of
   the two. The global img { max-width: 100% } above means the image adds
   nothing to the min-content width of the link around it, so the link — a
   flex item — was free to shrink to almost nothing once the header ran out
   of room. On a 390px phone the logo measured 35px on /services against 54px
   on /, and at 360px it collapsed to 8px. Pinning the link means the header
   can never borrow width from the logo.

   Do not put flex:none on the image as well. A bare <img> flex item reports a
   LARGER intrinsic width than a link wrapping the same image does, and that
   was enough to wrap the home page's header onto a second row between 360 and
   430px while every other page stayed on one. Uniform markup is what keeps
   the pages in step; this rule only stops the header squeezing the logo. */
.logo-wrap > a {
  display: block;
  flex: none;
}

.logo-wrap img {
  display: block;
  width: 72px;
  height: 72px;
  object-fit: contain;
}

.brand-title {
  font-size: 28px;
  line-height: .92;
  color: var(--navy-800);
}

.brand-sub {
  font-size: 11px;
  color: var(--red);
  font-weight: 800;
  letter-spacing: .1em;
}

.nav {
  display: flex;
  gap: 6px;
  font-weight: 700;
  font-size: 14px;
}

.nav a {
  position: relative;
  text-decoration: none;
  color: var(--ink);
  padding: 8px 12px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  transition: color .18s ease, background .18s ease;
}

.nav a:hover {
  color: var(--red);
  background: rgba(199, 32, 47, .07);
}

/* Marks the page you are already on. aria-current carries the same fact to a
   screen reader, so the pill is not the only signal. */
.nav a[aria-current='page'] {
  color: var(--red);
  background: rgba(199, 32, 47, .10);
}

/* Hamburger — hidden until the mobile breakpoint (section 18). */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 44px;
  padding: 0;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

/* Three bars drawn from one element: the middle bar is the element itself,
   the outer two are its pseudo-elements. Open turns it into an X. */
.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  display: block;
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: var(--navy-800);
  transition: transform .2s ease, opacity .2s ease;
}

.nav-toggle-bars {
  position: relative;
}

.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: '';
  position: absolute;
  left: 0;
}

.nav-toggle-bars::before { top: -7px; }
.nav-toggle-bars::after  { top:  7px; }

.nav-open .nav-toggle-bars {
  background: transparent;
}

.nav-open .nav-toggle-bars::before {
  transform: translateY(7px) rotate(45deg);
}

.nav-open .nav-toggle-bars::after {
  transform: translateY(-7px) rotate(-45deg);
}


/* 5. Buttons =============================================================== */

.cta {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--navy-800);
  color: #fff;
  padding: 13px 22px;
  border-radius: var(--radius-pill);
  text-decoration: none;
  font-family: inherit;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .01em;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease, background .16s ease;
}

.cta:hover {
  background: var(--navy-700);
  transform: translateY(-2px);
  box-shadow: var(--shadow-2);
}

.cta.red {
  background: var(--red);
}

.cta.red:hover {
  background: var(--red-700);
  box-shadow: 0 12px 26px rgba(199, 32, 47, .32);
}

/* Outlined variant — for the secondary action sitting beside a solid button.
   On navy it borrows the surrounding white; on light backgrounds, the navy. */
.cta.ghost {
  background: transparent;
  color: var(--navy-800);
  border-color: rgba(7, 24, 43, .28);
}

.cta.ghost:hover {
  background: rgba(7, 24, 43, .06);
  color: var(--navy-800);
}

.hero .cta.ghost,
.dark .cta.ghost {
  color: #fff;
  border-color: rgba(255, 255, 255, .42);
}

.hero .cta.ghost:hover,
.dark .cta.ghost:hover {
  background: rgba(255, 255, 255, .12);
  color: #fff;
}

/* Click-to-call button in the sticky header. Mobile only — see section 18.
   At desktop widths the nav plus "Schedule Service" already fill the header,
   and the phone number sits in the top bar, so a third button there only
   crowds the navigation. */
.cta.call {
  display: none;
  background: #fff;
  color: var(--navy-800);
  border: 2px solid var(--navy-800);
  white-space: nowrap;
}

.cta.call:hover {
  background: var(--navy-800);
  color: #fff;
}

/* An inline SVG rather than the telephone character (U+260E) that used to
   sit here. iOS and Android both substitute a full-colour emoji for that
   glyph, which put a red cartoon handset inside an otherwise navy-and-white
   button — and only on phones, which is exactly where the button shows. The
   SVG inherits currentColor instead, so the icon inverts with the button on
   hover the same way the label does. */
.call-icon {
  width: 17px;
  height: 17px;
  flex: none;
}


/* 6. Hero ================================================================== */

/* Headline on the left, a framed photograph of the van on the right — the same
   arrangement as the printed material. The photo is a real <img> in the markup
   rather than a CSS background: the browser starts fetching it while it is
   still parsing the <head>, and it can pick the right size for the screen.
   That is also why there are no <link rel="preload"> tags for it any more. */
.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(38px, 4.5vw, 74px) 5vw clamp(44px, 5.5vw, 84px);
  background: var(--navy-900);
  color: #fff;
}

/* Two soft light sources — cool from the top left, a red bloom bottom right —
   so the navy has some depth instead of reading as a flat fill. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(900px 460px at 8% 12%,  rgba(24, 74, 126, .70), transparent 68%),
    radial-gradient(760px 420px at 92% 96%, rgba(199, 32, 47, .26), transparent 70%);
  pointer-events: none;
}

/* Faint engineering grid, faded out towards the edges so it never draws a hard
   line. Purely decorative; masked away entirely where masks are unsupported. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 62px 62px;
  -webkit-mask-image: radial-gradient(70% 70% at 30% 40%, #000, transparent 100%);
          mask-image: radial-gradient(70% 70% at 30% 40%, #000, transparent 100%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 520px);
  align-items: center;
  gap: clamp(30px, 4vw, 66px);
  max-width: var(--maxw);
  margin: 0 auto;
}

.hero-content {
  max-width: 700px;
}

/* Scales smoothly instead of snapping at the breakpoint. */
.hero h1 {
  font-size: clamp(36px, 4.6vw, 62px);
  line-height: 1.02;
  letter-spacing: -.03em;
  text-transform: uppercase;
  margin: 0;
}

/* Second line of the headline: still inside the <h1> so the full sentence —
   service plus towns — stays one heading for search, but set lighter and
   smaller so it reads as a subtitle. */
.hero-sub {
  display: block;
  font-weight: 500;
  font-size: clamp(17px, 1.9vw, 24px);
  line-height: 1.3;
  letter-spacing: -.01em;
  text-transform: none;
  color: var(--silver);
  margin-top: 14px;
}

/* .redline is centred by default (section 8). In the hero it sits under a
   left-aligned headline, so it has to start at the left edge — hence the
   two-class selector, which outranks the shared rule further down the file. */
.hero .hero-rule {
  margin: 22px 0;
}

.hero p {
  font-size: clamp(15.5px, 1.4vw, 18px);
  max-width: 560px;
  color: #d5e0ec;
}

.hero-actions {
  display: flex;
  gap: 12px;
  margin-top: 26px;
  flex-wrap: wrap;
}

/* --- the photograph ---------------------------------------------------- */

.hero-media {
  position: relative;
}

/* --- the photograph ------------------------------------------------------ */

/* Both pages use this. Three things were making the old arrangement look
   cheap — a seal breaking out of the corner, a status pill on the picture and
   a 42px red band across the van's wheels — and they are fixed in this order:

   1. Nothing sits on the picture any more. The seal and the status pill both
      needed a dark scrim underneath to stay legible, and that scrim was
      draining the contrast out of the top of the photograph.
   2. The frame is capped at 440px. hero-van.jpg is 1040x584 and there is no
      larger original, so this guarantees the browser is always scaling the
      file DOWN — 440px asks for 880 device pixels on a 2x screen out of 1040
      available. Downsampling averages neighbouring pixels together, which is
      what actually removes the graininess; no filter can do that.
   3. An inset vignette (::before) darkens the corners, where the bay behind
      the van is both noisiest and least interesting, so the eye goes to the
      white bodywork and the lettering on it.

   The markup for this lives in BOTH index.html and services.html, and so do
   the #img-crisp and #img-navy filters it references — an SVG filter only
   resolves inside its own document, so each page carries a copy. Change one,
   change the other. */
.hero-photo {
  position: relative;
  max-width: 440px;
  margin-left: auto;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--navy-800);
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: var(--shadow-3);
}

/* aspect-ratio matches the source exactly, so object-fit never has anything to
   crop. #img-crisp is the unsharp mask defined in the SVG sprite in the markup.
   It is deliberately mild: sharpening amplifies whatever noise is already
   there, so past about this strength it trades blur for grain, which on this
   file is a bad trade. */
.hero-photo img {
  display: block;
  width: 100%;
  aspect-ratio: 1040 / 584;
  height: auto;
  object-fit: cover;
  filter: url(#img-crisp) contrast(1.06) saturate(1.05);
}

/* Inset vignette — see point 3 above. Clear through the middle third where the
   van is, closing down towards the corners. */
.hero-photo::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: radial-gradient(118% 96% at 50% 44%, transparent 40%, rgba(4, 16, 29, .58) 100%);
  pointer-events: none;
}

/* The red diagonal from the printed material, kept as a slim edge along the
   bottom of the frame. The band it replaces was 42px tall and cut straight
   across the van's wheels; at this height it reads as a brand rule under the
   picture rather than a band over it. */
.hero-photo::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  height: 9px;
  background: linear-gradient(90deg, var(--red-700), var(--red));
  clip-path: polygon(0 100%, 0 40%, 100% 0, 100% 100%);
  pointer-events: none;
}

/* Three supporting photographs under the van. They do two jobs: they put real
   photography on the page next to a picture that is not one, and they give the
   column enough height to sit level with the headline — which is what the
   caption line that used to be here was doing, less well.

   The navy duotone (#img-navy, defined in the sprite) is the whole trick. Left
   in colour these would be three unrelated stock photographs fighting the van
   for attention; run through one colour ramp they become a single band of
   brand-coloured texture, and the van stays the only saturated object in the
   hero. */
.hero-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  max-width: 440px;
  margin: 12px 0 0 auto;
}

.hero-strip img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255, 255, 255, .12);
  filter: url(#img-navy);
}


/* 7. Proof strip =========================================================== */

/* The four claims that ran along the bottom of the printed sheet. Sits
   immediately under the hero and carries the eye into the page.

   The navy band stays full-bleed so it reads as a continuation of the hero
   above it, but the items inside are pushed onto the same column as every
   other row — the max() resolves to the 5vw page gutter until the viewport is
   wide enough for --maxw, and to the centring margin after that. Capping the
   band itself instead would leave white slivers beside a navy hero. */
.proof {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  padding: 0 max(5vw, calc((100% - var(--maxw)) / 2));
  background: var(--navy-800);
  color: #fff;
}

.proof-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 22px clamp(16px, 1.8vw, 26px);
  border-right: 1px solid var(--border-dk);
}

/* The first and last items sit against the content edge, so their outer
   padding would push their text off that column. */
.proof-item:first-child { padding-left: 0; }
.proof-item:last-child  { padding-right: 0; border-right: 0; }

/* White rather than the pale pink these were: on navy the pink read as a
   fourth icon style and as a washed-out red rather than as a deliberate one. */
.proof-item svg {
  flex: none;
  width: 30px;
  height: 30px;
  color: #fff;
}

.proof-item strong {
  display: block;
  font-size: 14px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.proof-item span {
  display: block;
  font-size: 13px;
  color: var(--muted-dk);
}


/* 8. Section shell + grids ================================================= */

.section {
  padding: clamp(52px, 6.5vw, 92px) 5vw;
}

.section.light {
  background: var(--light);
}

.section.dark {
  position: relative;
  background: var(--navy-800);
  color: #fff;
}

/* Same soft bloom as the hero, so the dark bands read as the same material. */
.section.dark::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(900px 420px at 82% 8%, rgba(24, 74, 126, .55), transparent 70%);
  pointer-events: none;
}

.section.dark > * {
  position: relative;
  z-index: 1;
}

.section-head {
  max-width: var(--maxw-text);
  margin: 0 auto 44px;
  text-align: center;
}

/* h1 is included for the pages whose section heading IS the page heading
   (thanks.html, 404.html); index.html uses h2 here because its h1 is the hero. */
.section-head h1,
.section-head h2 {
  font-size: clamp(28px, 3.6vw, 44px);
  margin: 0 0 10px;
}

.section-head p {
  color: var(--muted);
  font-size: 17px;
}

.dark .section-head p {
  color: var(--muted-dk);
}

.redline {
  width: 66px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--red), var(--red-700));
  margin: 16px auto;
}

.grid {
  display: grid;
  gap: 22px;
  max-width: var(--maxw);
  margin: 0 auto;
}

/* One line of copy introducing a CTA pair mid-page, where the section already
   had its intro at the top and the reader needs a reason to act now. */
.section-cta {
  max-width: var(--maxw-text);
  margin: 34px auto 0;
  text-align: center;
  font-weight: 700;
  font-size: 17px;
}

.section-cta + .section-more {
  margin-top: 16px;
}

/* "See all …" buttons under a grid of highlights, or under a section intro. */
.section-more {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin: 38px 0 0;
}

.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }


/* 8b. Long-form prose ====================================================== */

/* Everything else on this site is a component in a centred, symmetrical layout.
   A policy page is the one thing that is genuinely just text to be read start
   to finish, so it gets a left-aligned column on --maxw-text — the same measure
   as the section intros, which is why it lines up with them rather than
   introducing a fourth width. Used by privacy.html. */
.prose {
  max-width: var(--maxw-text);
  margin: 0 auto;
}

/* The policy is written as short run-in clauses rather than headed sections —
   a page this brief does not need nine headings, and run-in terms are the
   normal form for a document of this kind. The term is the only emphasis in
   the paragraph, so it can carry a little tracking without competing. */
.prose .term {
  color: var(--ink);
  letter-spacing: .005em;
}

.prose p {
  margin: 0 0 18px;
}

/* Effective date, under the page title. */
.prose .stamp {
  font-size: 14px;
  color: var(--muted);
  margin: 0 0 34px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

/* Kept for a policy that grows a headed section or a list later; the base
   heading rules in section 2 set no size, so an unstyled h2 here would come
   out at the browser default. */
.prose h2 {
  font-size: clamp(21px, 2.6vw, 26px);
  margin: 44px 0 12px;
}

.prose ul {
  margin: 0 0 18px;
  padding-left: 22px;
}

.prose li {
  margin-bottom: 8px;
}


/* 9. Cards ================================================================= */

.card {
  position: relative;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: var(--shadow-1);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;

  /* A grid item refuses by default to get narrower than its longest
     unbreakable word. The contact email is long enough to push the whole page
     sideways on a 320px phone, so let the card shrink and let the address wrap. */
  min-width: 0;
  overflow-wrap: break-word;
}

/* Red rule that draws itself across the top of the card on hover. Cheaper and
   quieter than changing the whole border colour. */
.grid:not(.contact-grid) > .card::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  height: 3px;
  width: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--red);
  transition: width .28s ease;
}

.grid:not(.contact-grid) > .card:hover::after {
  width: 100%;
}

/* Lift only the cards that lead somewhere or present a service — not the
   plain content cards in the contact section. */
.grid:not(.contact-grid) > .card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-2);
  border-color: #c9d5e3;
}

/* The contact details are far shorter than the form beside them. Stretching
   both to the same height left a tall empty box; let each size to its content,
   and give the form the larger share of the row so the gap under the short
   card is less of a void. */
#contact .contact-grid {
  align-items: start;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
}

.dark .card {
  background: rgba(255, 255, 255, .05);
  color: #dce6f1;
  border-color: var(--border-dk);
  box-shadow: none;
}

.dark .grid:not(.contact-grid) > .card:hover {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .24);
}

/* Icon tile — the navy rounded square used throughout the printed material.
   Shared by the home page cards and the services catalogue. */
.icon-tile {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  margin-bottom: 18px;
  border-radius: var(--radius-sm);
  background: linear-gradient(160deg, var(--navy-700), var(--navy-900));
  color: #fff;
  box-shadow: 0 8px 18px rgba(7, 24, 43, .22);
}

.icon-tile svg {
  width: 28px;
  height: 28px;
}

.dark .icon-tile {
  background: var(--navy-600);
  box-shadow: none;
}

.card :is(h2, h3) {
  margin: 0 0 10px;
  font-size: 19px;
  color: var(--navy-800);
}

/* :not([class]) matters. This rule is for plain body copy inside a card, but
   without the guard it also outranks .field, .form-status and .contact-note —
   each of which sets its own margin and each of which silently lost it,
   collapsing the contact form's field spacing to zero. */
.card p:not([class]) {
  margin: 0;
  color: var(--muted);
  font-size: 15.5px;
}

.dark .card :is(h2, h3) {
  color: #fff;
}

.dark .card p:not([class]) {
  color: var(--muted-dk);
}

/* Links on navy would otherwise fall back to the browser's default dark blue,
   which is unreadable against this background. */
.dark a {
  color: #fff;
  text-decoration-color: var(--red);
  text-underline-offset: 3px;
}

.dark a:hover {
  color: var(--silver);
}

/* except the buttons, which carry their own colour */
.dark a.cta {
  color: #fff;
  text-decoration: none;
}


/* 10. Split (text beside image) ============================================ */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 46px;
  align-items: center;
  max-width: var(--maxw);
  margin: 0 auto;
}

.split h2 {
  font-size: clamp(24px, 2.6vw, 34px);
  margin: 0 0 14px;
}

.split img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

/* Red tick bullets, reused wherever a list of claims appears. */
.split ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.split ul li {
  position: relative;
  padding-left: 32px;
  color: var(--muted);
}

.split ul li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--red) center / 10px 10px no-repeat;
  /* Inline tick, so this needs no sprite reference and works in any context. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 12.6 9.4 17.5 19.5 6.6'/%3E%3C/svg%3E");
}


/* 11. Systems grid + mission panel ========================================= */

/* The row of system icons from the printed sheet — AEB, lane keep, adaptive
   cruise and the rest. Line-art icons on tiles, three or four to a row. */
/* Five to a row, so the ten entries make two even rows. auto-fit was tried
   first and gave a ragged 7 + 3 at desktop width. */
.systems {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 14px;
  max-width: var(--maxw);
  margin: 0 auto;
}

.system {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  padding: 24px 14px;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--navy-800);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}

.system:hover {
  transform: translateY(-3px);
  border-color: #c9d5e3;
  box-shadow: var(--shadow-2);
}

/* Bare line icon, navy — the second of the two icon treatments on the site.
   A filled navy tile (.icon-tile / .svc-icon) marks something you do; a bare
   line icon marks a category being labelled. These were red, which made a
   third treatment and spent the accent colour on decoration — red is doing
   real work on the eyebrows, rules, ticks and buttons. */
.system svg {
  width: 40px;
  height: 40px;
  color: var(--navy-700);
}

.dark .system {
  background: rgba(255, 255, 255, .05);
  border-color: var(--border-dk);
  color: #fff;
}

.dark .system svg {
  color: #fff;
}

.dark .system:hover {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .24);
  box-shadow: none;
}

/* Mission statement beside the four commitments — the "OUR MISSION" block. */
.mission {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: clamp(28px, 4vw, 60px);
  align-items: center;
  max-width: var(--maxw);
  margin: 0 auto;
}

.mission h2 {
  font-size: clamp(26px, 3.2vw, 40px);
  text-transform: uppercase;
  margin: 0 0 18px;
}

.mission .lead {
  font-size: 18px;
  color: #dce6f1;
}

/* The emphasised word in the two closing headings. Two values, because these
   blocks now appear on both grounds: #c7202f is 5.71:1 on white but only
   3.13:1 on navy, which fails AA for normal text. --red-400 is 4.63:1 on navy
   but would be too light to read on white. */
.accent {
  color: var(--red);
}

.dark .accent {
  color: var(--red-400);
}

.mission .tagline,
.standard .tagline {
  margin: 16px 0 0;
  font-weight: 800;
  font-size: 14px;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* The mission band is on navy, the services one on light — so the colour comes
   from the context rather than being baked into the tagline. */
.dark .tagline {
  color: #fff;
}

/* .section-more centres its buttons and clears 38px above them, which is right
   beneath a centred section heading but leaves the button stranded in the
   middle of this block's left-aligned column.

   The margin is larger than the gap above the tagline on purpose: the tagline
   closes the sentence above it, the button is a separate action, and when both
   gaps were about the same the button looked stuck to the tagline. */
.mission .section-more,
.standard .section-more {
  justify-content: flex-start;
  margin-top: 34px;
}


/* 12. Table ================================================================ */

/* Wrapper lets a wide table scroll on its own instead of forcing the whole
   page to scroll sideways on small screens. */
/* The table is wider than a phone and scrolls sideways. Nothing about a
   clipped table says "swipe me", though, and at 390px the third column is off
   the screen entirely — so the wrapper carries a shadow on whichever edge has
   more table behind it.

   The shadow cannot be part of the wrapper's background, which is where the
   usual pure-CSS version of this puts it: the header row has an opaque navy
   background of its own and would paint straight over it. It is an overlay
   instead, and that is the whole reason the scrolling moved down to an inner
   .table-scroll — an overlay pinned to a box that is itself scrolling would
   slide away with the content. */
.table-wrap {
  position: relative;
  overflow: hidden;
  max-width: var(--maxw);
  margin: 0 auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  box-shadow: var(--shadow-1);
}

.table-scroll {
  overflow-x: auto;
}

/* A shadow rather than a fade to white, because it has to cross both the navy
   header row and the white body rows and stay legible over each. */
.table-wrap::before,
.table-wrap::after {
  content: '';
  position: absolute;
  top: 0;
  width: 42px;
  height: 100%;
  opacity: 0;
  transition: opacity .22s ease;
  pointer-events: none;
}

.table-wrap::before {
  left: 0;
  background: linear-gradient(to left, rgba(7, 24, 43, 0), rgba(7, 24, 43, .17));
}

.table-wrap::after {
  right: 0;
  background: linear-gradient(to right, rgba(7, 24, 43, 0), rgba(7, 24, 43, .17));
}

/* main.js adds these as the table is scrolled. If scripting is off neither
   ever appears, which is the right way round to fail: a shadow sitting
   permanently on a table that does not scroll reads as a rendering fault. */
.table-wrap.can-left::before,
.table-wrap.can-right::after {
  opacity: 1;
}

.table {
  width: 100%;
  border-collapse: collapse;
  min-width: 640px;
}

.table th {
  background: var(--navy-800);
  color: #fff;
  font-size: 13px;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.table th,
.table td {
  padding: 16px 18px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.table td {
  color: var(--muted);
  font-size: 15.5px;
}

/* The repair event is the column people scan down, so it stays full-strength
   navy while the two explanatory columns sit back. */
.table td:first-child {
  color: var(--navy-800);
  font-weight: 700;
}

.table tbody tr:last-child td {
  border-bottom: 0;
}

.table tbody tr:hover td {
  background: var(--light);
}


/* 13. Resources + FAQ ====================================================== */

/* Pins each card's link to the bottom so the whole row of links lines up even
   when one description runs longer than its neighbour's. */
.resource {
  display: flex;
  flex-direction: column;
}

.resource a {
  color: var(--red);
  font-weight: 800;
  margin-top: auto;
  align-self: flex-start;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: border-color .18s ease;
}

.resource a::after {
  content: ' \2192';
}

.resource a:hover {
  border-bottom-color: var(--red);
}

.faq-list {
  max-width: var(--maxw-text);
  margin: 0 auto;
}

.faq details {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin: 0 0 12px;
  padding: 4px 22px;
  box-shadow: var(--shadow-1);
  transition: border-color .18s ease, box-shadow .18s ease;
}

.faq details[open] {
  border-color: #c9d5e3;
  box-shadow: var(--shadow-2);
}

/* Replaces the browser's default disclosure triangle with a +/- control on the
   right, matching how the printed material set its question list out. */
.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 0;
  font-weight: 700;
  font-size: 16.5px;
  color: var(--navy-800);
  cursor: pointer;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: '';
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background:
    /* horizontal bar of the plus */
    linear-gradient(var(--red), var(--red)) center / 11px 2.5px no-repeat,
    /* vertical bar — rotated away when the answer opens */
    linear-gradient(var(--red), var(--red)) center / 2.5px 11px no-repeat,
    rgba(199, 32, 47, .10);
  transition: background-size .22s ease;
}

/* Collapses the vertical bar, leaving a minus. Only two sizes are listed
   because only the two gradients are background *images* — the rgba fill is
   the background-color and takes no size. */
.faq details[open] summary::after {
  background-size: 11px 2.5px, 0 0;
}

.faq details p {
  margin: 0 0 20px;
  padding-right: 42px;
  color: var(--muted);
}


/* 14. Service catalogue (services.html) ==================================== */

/* Holds the <symbol> definitions the icons draw from. It has to stay in the
   document (an external file breaks <use> in some browsers) but must take up
   no room and paint nothing. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* The catalogue is a long list, not a set of cards: at this length, boxes
   around every entry turn into visual noise. Each row is an icon tile, a name
   and a line of detail, separated by hairlines. */
.svc-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 clamp(24px, 4vw, 56px);
  max-width: var(--maxw);
  margin: 0 auto;
}

.svc {
  display: flex;
  gap: 18px;
  padding: 22px 0;
  border-bottom: 1px solid var(--border);
}

.dark .svc {
  border-bottom-color: var(--border-dk);
}

/* Last row of each column has nothing under it to separate from. Works for the
   odd-count case too: the final item is alone on its row. */
.svc:nth-last-child(-n + 2) {
  border-bottom: 0;
}

.svc-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  border-radius: var(--radius-sm);
  background: linear-gradient(160deg, var(--navy-700), var(--navy-900));
  color: #fff;
  box-shadow: 0 6px 14px rgba(7, 24, 43, .2);
}

.dark .svc-icon {
  background: var(--navy-600);
  box-shadow: none;
}

.svc-icon svg {
  width: 26px;
  height: 26px;
}

.svc h3 {
  margin: 0 0 5px;
  font-size: 15px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--navy-800);
}

.dark .svc h3 {
  color: #fff;
}

.svc p {
  margin: 0;
  font-size: 15px;
  color: var(--muted);
}

.dark .svc p {
  color: var(--muted-dk);
}

/* Red tick bullets — used for the additional-services list and the closing
   commitments band. */
.checklist {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: var(--maxw);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px clamp(24px, 4vw, 56px);
}

.checklist li {
  display: flex;
  gap: 13px;
}

.checklist .tick {
  flex: none;
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  margin-top: 4px;
  border-radius: 50%;
  background: var(--red);
  color: #fff;
}

.checklist .tick svg {
  width: 12px;
  height: 12px;
}

.checklist strong {
  display: block;
  color: var(--navy-800);
}

.dark .checklist strong {
  color: #fff;
}

.checklist p {
  margin: 3px 0 0;
  font-size: 15px;
  color: var(--muted);
}

/* The margin:0 auto above centres the checklist when it stands alone inside a
   section, which is what it does on services.html. Inside .mission and
   .standard it is a GRID ITEM, and auto side margins make a grid item
   shrink-wrap to its content rather than fill its track — so at narrow widths,
   where the list drops to one column, it collapsed to the width of its longest
   entry and floated ~250px away from the text above it. */
.mission .checklist,
.standard .checklist {
  margin: 0;
  max-width: none;
}

.dark .checklist p {
  color: var(--muted-dk);
}

/* Who we serve — icon over a short label, evenly spread. */
.serve {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 16px;
  max-width: var(--maxw);
  margin: 0 auto;
  text-align: center;
}

.serve-item {
  padding: 26px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}

.serve-item:hover {
  transform: translateY(-3px);
  border-color: #c9d5e3;
  box-shadow: var(--shadow-2);
}

.serve-item svg {
  width: 46px;
  height: 46px;
  color: var(--navy-800);
}

.dark .serve-item {
  background: rgba(255, 255, 255, .05);
  border-color: var(--border-dk);
}

.dark .serve-item svg {
  color: #fff;
}

.serve-item span {
  display: block;
  margin-top: 12px;
  font-weight: 800;
  font-size: 13px;
  letter-spacing: .05em;
  text-transform: uppercase;
}

/* Closing band: heading on the left, commitments on the right. */
.standard {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  max-width: var(--maxw);
  margin: 0 auto;
}

.standard h2 {
  font-size: clamp(26px, 3.4vw, 40px);
  text-transform: uppercase;
  margin: 0;
}


/* Contact details as a description list — the labels are names for the values
   beside them, which is what <dl> is for, and it lets the label column stay a
   fixed width so the values line up. */
.contact-list {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px 18px;
  margin: 0;
}

.contact-list dt {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted-dk);
  padding-top: 2px;
  white-space: nowrap;
}

.contact-list dd {
  margin: 0;
  overflow-wrap: break-word;
}

/* The dot marks the one line on the card that is a promise rather than a
   fact, so it reads as a live commitment rather than another detail. */
.contact-note {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 22px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--border-dk);
  font-weight: 700;
  font-size: 14px;
}

.contact-note .dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #37d67a;
}


/* 15. Forms ================================================================ */

/* One field: its label sits above the input and stays put while typing. */
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 0 16px;
}

.field label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .03em;
}

.field .optional {
  font-weight: 400;
  opacity: .75;
}

.form-field {
  width: 100%;
  padding: 13px 15px;
  font-family: inherit;
  font-size: 16px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color .16s ease, box-shadow .16s ease;
}

.form-field:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(199, 32, 47, .14);
}

textarea.form-field {
  height: 110px;
  resize: vertical;
}

form button[type="submit"] {
  margin-top: 8px;
}

/* #7c8b9c was only 3.48:1 against the white field and failed WCAG AA. This is
   5.45:1. Placeholders are examples, not labels, but must still be readable. */
.form-field::placeholder {
  color: #5c6b7d;
  opacity: 1;
}

/* A <select> renders with the platform's own arrow and, on some platforms, its
   own background — both of which ignore the field styling above. */
select.form-field {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 42px;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2355677d' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 16px 16px;
}

/* Only after the field has been interacted with — :invalid alone paints every
   required field red before the visitor has typed a character. */
.form-field:user-invalid {
  border-color: #d13b48;
  box-shadow: 0 0 0 3px rgba(209, 59, 72, .18);
}

/* Submit feedback. Empty and present from first paint; see the markup comment.

   Two sets of colours, because the form appears on a navy card on index.html
   and a white one on services.html. Neither set is var(--red): on the navy
   card #c7202f measures about 3.5:1 and fails AA for text, and the tints that
   fix that would in turn be unreadable on white. The navy error tint is the
   brand hue at full saturation — as close to --red-400 as 14px text can sit on
   the .dark card ground (4.62:1) without dropping under AA. */
.form-status {
  margin: 0 0 14px;
  font-size: 14px;
  font-weight: 700;
  line-height: 1.45;
}

.form-status:empty      { display: none; }
.form-status.is-pending { color: var(--muted); }
.form-status.is-error   { color: #a3121e; }
.form-status.is-success { color: #12653c; }

.dark .form-status.is-pending { color: var(--muted-dk); }
.dark .form-status.is-error   { color: #ff4253; }
.dark .form-status.is-success { color: #7ee2a8; }

.cta[disabled] {
  opacity: .62;
  cursor: default;
}

.cta[disabled]:hover {
  transform: none;
  box-shadow: none;
}

/* Replaces the form in place on a successful send. */
.form-success :is(h2, h3) {
  margin: 16px 0 10px;
}

/* Green rather than the usual navy tile: colour is not the only signal here
   (the tick icon and the words "Request received" carry it too), but on a
   success panel it should not look identical to every other icon on the page. */
.form-success .icon-tile {
  margin-bottom: 0;
  background: #1f7a4d;
  box-shadow: none;
}

.form-success:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 6px;
}

.dark .form-success:focus-visible {
  outline-color: #fff;
}

/* Spam honeypot — hidden from people, left in the DOM for bots to fill in. */
.hp {
  position: absolute;
  left: -9999px;
  opacity: 0;
}


/* 16. Footer =============================================================== */

.footer {
  background: var(--navy-900);
  color: #cfdae7;
  padding: 56px 5vw 34px;
}

.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 34px;
  max-width: var(--maxw);
  margin: 0 auto;
}

.footer h3 {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 16px;
}

.footer p {
  font-size: 14.5px;
  color: var(--muted-dk);
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.footer-brand img {
  width: 52px;
  height: 52px;
}

.footer-brand .brand-title {
  font-size: 21px;
  line-height: 1;
  color: #fff;
}

.footer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 14.5px;
}

.footer-list li + li {
  margin-top: 9px;
}

/* Underlined at rest, not just on hover. Without this a footer link is
   indistinguishable from the plain text beside it. */
.footer a {
  color: #dce6f1;
  text-decoration: underline;
  text-decoration-color: rgba(255, 255, 255, .28);
  text-underline-offset: 3px;
}

.footer a:hover {
  color: #fff;
  text-decoration-color: var(--red);
}

.fine {
  border-top: 1px solid rgba(255, 255, 255, .13);
  max-width: var(--maxw);
  margin: 38px auto 0;
  padding-top: 20px;
  color: #8ea1b6;
  font-size: 12.5px;
}


/* 17. Scroll reveal ======================================================== */

/* js/main.js adds .reveal to each section and then .is-in as it scrolls into
   view. The class is applied from JavaScript rather than written into the HTML
   on purpose: if the script fails to load, nothing was ever hidden. */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .6s ease, transform .6s cubic-bezier(.22, .8, .3, 1);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}


/* 18. Responsive =========================================================== */

/* Several breakpoints, because the header runs out of room before the content
   does:
     1250px  the header tightens its spacing to stay on one row
     1100px  the nav collapses behind the hamburger, and the hero stacks
      900px  the content grids drop to a single column
      560px  narrow-phone trims                                              */


/* Laptops from the hamburger breakpoint to roughly 1250px: the five nav labels
   plus the logo and the Schedule button do not quite fit at full size, which
   used to wrap every label onto two lines and make the header taller.
   Tightening the spacing keeps it to a single row. */
@media (min-width: 1100px) and (max-width: 1250px) {

  .header {
    padding-left: 3vw;
    padding-right: 3vw;
  }

  .nav {
    gap: 2px;
    font-size: 13px;
  }

  .nav a {
    padding: 8px 9px;
  }

  .logo-wrap img {
    width: 60px;
    height: 60px;
  }

  .brand-title {
    font-size: 24px;
  }

  /* Keeps the tagline on one line at the bottom of this range. */
  .brand-sub {
    font-size: 10px;
    letter-spacing: .05em;
  }
}


/* Header -> hamburger. This happens at 1100px rather than 900px because five
   nav labels, the logo and a button genuinely do not fit below about 1100px;
   trying to keep them meant a cramped two-row header on small laptops and
   landscape tablets. The content grids stay wide until 900px (next block). */
@media (max-width: 1099px) {

  .header {
    flex-wrap: wrap;
    gap: 14px;
  }

  .logo-wrap {
    flex: 1;
    gap: 10px;
  }

  /* Smaller logo frees up the width the call button needs. */
  .logo-wrap img {
    width: 54px;
    height: 54px;
  }

  .brand-title {
    font-size: 23px;
  }

  .brand-sub {
    font-size: 10px;
  }

  .nav-toggle {
    display: inline-flex;
  }

  /* Here the call button appears and then never hides: the top bar carrying the
     number scrolls away, the nav is behind the hamburger, and tapping to call is
     the main thing a visitor holding a phone wants. */
  .cta.call {
    display: inline-flex;
    padding: 11px 16px;
    font-size: 15px;
    gap: 7px;
  }

  /* Nav and the Schedule button collapse behind the hamburger, and drop onto
     their own full-width rows when opened. */
  .nav {
    display: none;
    order: 3;
    flex-basis: 100%;
    flex-direction: column;
    gap: 0;
    font-size: 16px;
  }

  .nav a {
    padding: 14px 6px;
    border-radius: 0;
    border-top: 1px solid var(--border);
  }

  .cta.schedule {
    display: none;
    order: 4;
    flex-basis: 100%;
    justify-content: center;
  }

  .header.nav-open .nav {
    display: flex;
  }

  .header.nav-open .cta.schedule {
    display: inline-flex;
  }

  /* Below this the headline and the photograph cannot both hold their width,
     so the photo moves underneath. */
  .hero-inner {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero-content {
    max-width: none;
  }

  .hero-photo,
  .hero-strip {
    margin-inline: auto;
  }

  .proof {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .proof-item {
    padding-left: 0;
  }

  .proof-item:nth-child(2n) {
    border-right: 0;
  }

  .proof-item:nth-child(-n + 2) {
    border-bottom: 1px solid var(--border-dk);
  }

  /* Four columns of heading-plus-paragraph measure about 190px here, which is
     too narrow to read. They go to two now rather than surviving to 900px and
     then dropping straight to one full-width card. */
  .cols-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .serve {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* Ten systems: five per row or two, never three — three leaves a single
     orphan tile alone on a fourth row. */
  .systems {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}


/* Content grids -> single column. .cols-4 is deliberately absent: it went to
   two columns at 1100px and stays there until 560px, because a four-up row of
   short service cards reads better as a 2x2 block than as four full-width
   bands you have to scroll past one at a time. */
@media (max-width: 900px) {

  /* .cols-3 goes to two, not one: a single 790px-wide card holding an icon and
     two lines of text reads as an empty band. It reaches one column at 560px
     with .cols-4, below. */
  .cols-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cols-2,
  .split,
  .mission,
  .svc-list,
  .checklist,
  .standard {
    grid-template-columns: 1fr;
  }

  /* The rule above cannot reach the contact row on index.html and
     services.html. Section 8 splits that row .82fr/1.18fr with the selector
     '#contact .contact-grid', and an id outranks any plain class at every
     width — so the row stayed two columns all the way down to 320px. At that
     size the 'Service area' term (white-space: nowrap) took most of the card
     and the phone number and email address beside it wrapped one character
     per line. Repeating the selector here is deliberate: it has to match that
     specificity to win. If you ever drop the id from the section 8 rule, drop
     this one with it. 404.html and thanks.html are not affected — their
     contact rows are two peer cards, outside any #contact section. */
  #contact .contact-grid {
    grid-template-columns: 1fr;
  }

  /* The diagram is the point of the education section, so it leads on a phone
     rather than sitting below a screenful of text. */
  .split img {
    order: -1;
  }

  /* In one column only the very last row has nothing beneath it, so the
     second-to-last gets its hairline back. (The two-column rule in section 14
     assumes an even number of entries — keep the lists even.) */
  .svc:nth-last-child(2) {
    border-bottom: 1px solid var(--border);
  }

  .dark .svc:nth-last-child(2) {
    border-bottom-color: var(--border-dk);
  }

  .footer-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

}


/* Narrow phones: the header has to hold the logo, the call button and the
   hamburger, so the tagline steps aside and the call button loses its number
   (the aria-label keeps it spoken in full for screen readers). */
@media (max-width: 560px) {

  .brand-sub {
    display: none;
  }

  .call-label {
    display: none;
  }

  .cta.call::after {
    content: 'Call';
  }

  .proof {
    grid-template-columns: 1fr;
  }

  .proof-item {
    border-right: 0;
    border-bottom: 1px solid var(--border-dk);
  }

  .proof-item:last-child {
    border-bottom: 0;
  }

  .cols-3,
  .cols-4,
  .serve,
  .footer-grid {
    grid-template-columns: 1fr;
  }

  .hero-actions .cta {
    flex: 1 1 100%;
    justify-content: center;
  }
}


/* 19. Motion preferences =================================================== */

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }

  .cta:hover,
  .system:hover,
  .serve-item:hover,
  .grid:not(.contact-grid) > .card:hover {
    transform: none;
  }

  /* Never leave content stuck invisible if the reveal transition is disabled
     before the observer has run. */
  .reveal {
    opacity: 1;
    transform: none;
  }
}
