/* BVD Travel Centres — brand tokens
   Navy per Brand Foundation v7.0 §11 (official guideline, 2026-07-21). The
   WARM palette deliberately departs from §11 and follows bvdgroup.com instead
   — see the note on --bvd-gold below and DESIGN-UPDATES.md §4.
   Spelling CONFIRMED Canadian (Dan, 2026-07-28): "BVD Travel Centre" for a
   single site, "BVD Travel Centres" for the brand — copy, URLs, meta, schema,
   alt text. Never "Center/Centers". Domain: bvdtravelcentres.ca. */
:root {
  --bvd-navy: #2E3192;
  --bvd-navy-dark: #21245e;
  --white: #ffffff;

  /* WARM FAMILY — realigned to bvdgroup.com 2026-07-29 (Dan).
     The guideline's Lemon #FDF13B, Volt Green #E6DD26 and Highways Shell
     #FEFFEB are RETIRED. They appear nowhere on the group's live site, and
     the lemon in particular read as highlighter next to the navy. The group's
     actual warm family, measured off bvdgroup.com, is gold + amber + cream:
       --bvd-gold  = the parent's --bvd-gold, our accent
       --bvd-amber = the parent's --bs-warning, eyebrows and badges
     This is a deliberate deviation from Brand Foundation v7.0 §11, which
     names all three retired swatches. See DESIGN-UPDATES.md §4.
     ⚠️ Gold and amber are FILL colours, never text on light grounds:
     gold on white is 2.02:1 and amber 1.78:1. Dark text on them instead
     (ink on gold 9.24:1, ink on amber 10.5:1, navy on gold 5.27:1). */
  --bvd-gold: #F4A71E;
  --bvd-amber: #FDB52A;

  /* Neutral ramp adopted from MPP's client-approved strategy deck
     (05-working/strat-presentation/deck.html), which shares its system with
     bvdgroup.com. Re-verified 2026-07-29 against the live parent site: --ink,
     --ink-soft and --surface are byte-identical to its --bs-dark,
     --bs-gray-800 and --bs-gray. Nothing to reconcile. */
  --ink: #0A1425;        /* = parent --bs-dark */
  --ink-soft: #64728F;   /* = parent --bs-gray-800; 4.83:1 on white, AA */
  --border: #E4E8EF;
  --surface: #F7F8F9;    /* = parent --bs-gray; cool section tint */
  --cream: #FEF6EC;      /* warm section tint — replaces the old shell */
  --cream-deep: #FDEABA; /* soft badge fill; ink on it is 15.7:1 */

  /* Type per Brand Foundation v7.0 §11: Poppins (headlines), Circular Std
     (body), Rufina (logo only).

     Headlines are Poppins as specified — it also matches the lettering in the
     retail lockup, so the type stays in step with the logo above it.

     Body is Plus Jakarta Sans: Circular Std is still unlicensed, and PJS is
     both a far closer match for it (geometric humanist, same genre) and the
     face used by bvdgroup.com AND MPP's client-approved strategy deck. Poppins
     is a display face and scanned poorly at body sizes. Swap --font-body to
     'Circular Std' if the licence lands; nothing else needs to change.
     Never substitute Inter or a bare system stack — both are off-brand. */
  --font-head: 'Poppins', system-ui, sans-serif;
  --font-body: 'Plus Jakarta Sans', system-ui, sans-serif;

  /* Form language from bvdgroup.com — 12px radii, coloured navy shadows
     rather than grey, and a short shared easing. This is what makes the
     parent site feel designed; see DESIGN-UPDATES.md §5. */
  --radius: 12px;
  --radius-lg: 20px;
  /* Pill radius as a token rather than a literal 999px in seven places. It was
     introduced so the square-edge review toggle could flatten every rounded
     surface from one switch; that toggle is gone (rounded won, 2026-07-31) but
     the token stays — it is a genuine token, and the indirection is still worth
     having. Dot indicators keep 50% — they are circles, not pills. */
  --radius-pill: 999px;
  --max-width: 1280px;   /* parent runs 1350; 1280 keeps our narrower measures honest */

  /* Section rhythm measured off bvdgroup.com: 120px desktop, 60px mobile.
     Ours was a flat 64px, which is the main reason the site read denser than
     the parent. 8.33vw is 120px at a 1440 viewport exactly, and the floor
     holds the parent's 60px on mobile — so both endpoints match rather than
     merely approximating. Don't lower the vw term: at 7vw the cap is only
     reached at 1714px and desktop quietly lands ~20px short. */
  --section-y: clamp(3.75rem, 8.33vw, 7.5rem);

  --shadow-navy: 0 6px 16px rgba(46, 49, 146, 0.25);
  --shadow-navy-lift: 0 10px 22px rgba(46, 49, 146, 0.32);
  --shadow-navy-press: 0 4px 12px rgba(46, 49, 146, 0.22);
  --shadow-soft: 4px 4px 36px rgba(192, 192, 192, 0.16),
                 -4px -4px 36px rgba(192, 192, 192, 0.12);
  --ease: 0.18s ease;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Keyboard focus was previously invisible everywhere. */
:focus-visible {
  outline: 3px solid var(--bvd-gold);
  outline-offset: 2px;
  border-radius: 2px;
}
.hero :focus-visible, .manifesto :focus-visible, .site-footer :focus-visible {
  outline-color: var(--bvd-amber);
}

/* 18px/1.6. The parent runs 20px/1.6 — we sit one step below it because our
   text columns are narrower (34ch manifesto, 760px location pages) and 20px
   in a 34ch measure gives too few words per line. The 1.6 leading matches. */
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.125rem;
  color: var(--ink);
  background: var(--white);
  line-height: 1.6;
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 700;
  color: var(--bvd-navy);
  line-height: 1.15;
  margin: 0 0 0.5em;
}

/* Signage-first (§11): with no photography until ~September the type carries
   the design, so the display sizes are deliberately large and tightly set. */
h1 {
  font-size: clamp(2.5rem, 6vw, 4.5rem);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.02em;
}
h2 {
  font-size: clamp(1.75rem, 3.5vw, 2.75rem);
  letter-spacing: -0.015em;
}
h3 { font-size: 1.25rem; letter-spacing: -0.01em; }

p { margin: 0 0 1em; color: var(--ink-soft); }

a { color: var(--bvd-navy); }

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* ---------- Header ---------- */
/* Soft dual shadow rather than a hairline border, per bvdgroup.com's
   .nk-header-main. Reads as a floating surface instead of a ruled edge. */
.site-header {
  background: var(--white);
  box-shadow: var(--shadow-soft);
  position: sticky;
  top: 0;
  z-index: 100;
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}

.brand-lockup {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
}

/* The approved retail lockup is a single indivisible asset: BVD wordmark +
   flame maple leaf + TRAVEL CENTRE. It is never rebuilt from the corporate
   mark plus HTML text — that is a recoloured/reconstructed logo, which the
   brand guideline (v7.0 §11) prohibits outright. Source:
   "2. BVD Travel Centre - Logo  (1).pdf".

   HEIGHT IS SET FOR THE HORIZONTAL LOCKUP (1394×176, ratio 7.92). The stacked
   version this replaced was ratio 2.39, so the same 46px height that gave a
   110px-wide mark now gives 364px — height is not interchangeable between the
   two orientations. At 32px the mark measures ~253px, which leaves the three
   nav labels room on one row down to the 640px wrap point. If you ever swap
   back to the stacked asset, put the height back up. */
.brand-lockup img { height: 32px; width: auto; display: block; }

.main-nav { display: flex; gap: 1.75rem; }

.main-nav a {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
  color: var(--ink);
}

.main-nav a:hover, .main-nav a[aria-current="page"] { color: var(--bvd-navy); }

/* ---------- Buttons ---------- */
/* Button system adopted from bvdgroup.com's .btn-bvd — see DESIGN-UPDATES.md §5. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0.85rem 1.4rem;
  border: 0;
  border-radius: var(--radius);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease), box-shadow var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

/* Primary is navy on light grounds, matching the parent site. White on navy
   is 12.4:1, which retires the contrast problem the orange fill had. */
.btn-primary {
  background: var(--bvd-navy);
  color: var(--white);
  box-shadow: var(--shadow-navy);
}
.btn-primary:hover { background: #262986; box-shadow: var(--shadow-navy-lift); }
.btn-primary:active { box-shadow: var(--shadow-navy-press); }

/* Accent, for warm CTAs and for navy grounds where a navy fill would vanish.
   Navy-on-gold = 5.27:1 (AA). White-on-gold is 2.02:1 and FAILS — never put
   white text on this fill, which is the mistake bvdgroup.com's own eyebrow
   pill makes (white on amber, 1.78:1). Hover LIGHTENS to amber (5.97:1);
   darkening would drop the navy text toward the AA floor. */
.btn-accent {
  background: var(--bvd-gold);
  color: var(--bvd-navy);
  box-shadow: 0 6px 16px rgba(244, 167, 30, 0.3);
}
.btn-accent:hover { background: var(--bvd-amber); box-shadow: 0 10px 22px rgba(244, 167, 30, 0.38); }

/* Ghost — softer than the old 2px solid outline. */
.btn-outline {
  background: var(--white);
  color: var(--bvd-navy);
  border: 1px solid rgba(46, 49, 146, 0.3);
}
.btn-outline:hover { background: rgba(46, 49, 146, 0.06); box-shadow: 0 8px 18px rgba(46, 49, 146, 0.16); }

/* ---------- Hero ---------- */
.hero {
  background: linear-gradient(160deg, var(--bvd-navy) 0%, var(--bvd-navy-dark) 100%);
  color: var(--white);
  padding: 5rem 0 4.5rem;
}

.hero .container { max-width: 780px; text-align: center; }

/* Eyebrow pill, adopted from bvdgroup.com's .nk-section-subtitle.bg-warning:
   amber fill, full pill, 14px/600 uppercase, 8px 24px padding — the parent's
   spec exactly, with ONE correction. The parent sets white text on the amber
   (1.78:1, a clear AA failure at this size); ours is ink at 10.5:1. */
/* The gold eyebrow pill, shared by hero and section headers. Extended off the
   hero 2026-07-30 to match bvdgroup.com, which runs this label above most
   section headings rather than only at the top of the page.

   ⚠️ Amber is a FILL here, never the text colour — ink on amber is 9.1:1,
   amber as text on white would be 1.78:1. The parent's own pill gets this
   wrong (white on amber); ours must not follow it. Because the pill is opaque
   it carries that contrast onto any ground, which is why the same class works
   unchanged on white, on --surface and on the navy locations band. */
.hero-eyebrow,
.section-eyebrow {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--bvd-amber);
  color: var(--ink);
  font-size: 0.875rem;
  line-height: 1.2;
  padding: 0.5rem 1.5rem;
  border-radius: var(--radius-pill);
  margin: 0 0 1.25rem;
}

/* Slightly tighter than the hero's, because a section heading follows it
   immediately rather than a display-scale h1. Colour and size are re-asserted
   near the end of this file — see the note there. */
.section-eyebrow {
  padding: 0.4rem 1.15rem;
  margin-bottom: 0.9rem;
}

.hero h1 { color: var(--white); margin-bottom: 0.6em; }

.hero .triad {
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--cream);
  font-size: 1.05rem;
  margin-bottom: 2rem;
}

.hero-actions {
  display: flex;
  gap: 1rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 1.5rem;
}

/* ---------- Hero buttons ----------
   The home hero used to be dark navy and scoped its own gold/translucent
   button pair here. It is a LIGHT ground as of 2026-07-30, so that treatment
   moved into .hero--home and inverted back to the site defaults (navy fill,
   navy outline) — see the note there.

   Nothing else needs a hero-scoped button rule: no other hero on the site has
   buttons in it (checked across faq, locator, privacy and the 72 generated
   location pages), so the old `.hero .btn-*` block was dead and is gone rather
   than left behind with a rationale describing a scrim that no longer exists.
   If a future hero gains a button on a DARK ground, scope it to that hero and
   measure the label against the composited backdrop, not against the token. */


/* ---------- Home hero (image-led, panelled copy) ----------
   Third iteration, and the reasoning matters because two obvious approaches
   were tried and measured first.

     1. Full-bleed photo + full-image scrim (the original, dark dusk art).
        Worked only because forecourt-dusk.jpg was nearly black in the lower
        left. The daylight aerial that replaced it is bright corner to corner,
        and the same 0.93→0.30 scrim rendered it grey and murky.
     2. Copy above, photo as a separate panel below. Contrast became trivial,
        but it split the hero into two blocks and left a hard seam where the
        image met the next section.

   This is the resolution: the photo runs full-bleed and UNSCRIMMED, and the
   mask is scoped to a container around the copy instead of the whole image.
   The picture stays bright everywhere it is not carrying type, and contrast is
   a property of the panel rather than of the art.

   ⚠️ 0.82 is measured, not chosen by eye. Sampled the worst pixel under the
   copy block at 1280x640 and 1440x680, composited navy at each alpha, then
   measured: white 10.73:1, cream 10.01:1. At 0.74 it falls to 7.99/7.46 —
   still AA, but the margin stops being comfortable. Do not lighten it without
   re-running that measurement, and re-run it if the art changes.
   The backdrop blur is decorative: contrast holds on the alpha alone, so
   browsers without backdrop-filter lose nothing that matters. */
.hero--home {
  position: relative;
  padding: 0;
  background-color: var(--ink);
  background-image: url("../assets/img/forecourt-aerial.jpg");
  background-size: cover;
  background-position: center 55%;
  color: var(--white);
  isolation: isolate;
}

.hero--home .container {
  max-width: var(--max-width);
  text-align: left;
  min-height: min(76vh, 680px);
  display: flex;
  flex-direction: column;
  /* Cross-axis on a column flex container, so this is the panel's HORIZONTAL
     position: right, per Dan 2026-07-30, which uncovers the store, canopy and
     truck park on the left of the frame. The TEXT inside stays left-aligned —
     ragged-left prose is harder to read and the panel is the thing being
     positioned, not the copy. */
  align-items: flex-end;
  justify-content: center;
  padding-top: 4rem;
  padding-bottom: 4rem;
}

/* The mask. This is the only thing between the type and a bright photograph,
   so it is the one value in this block that is load-bearing.

   ⚠️ THE BINDING CONSTRAINT IS THE WHITE CANOPY ROOF, not the panel position.
   The brightest pixel under the copy is blown-out white (255,255,255) at the
   canopy, and it falls under the panel whether the panel is left or right
   aligned — so moving the panel does NOT buy transparency, and alignment and
   alpha are independent decisions. Measured against that pixel:

     alpha   h1 white   triad cream   lede
     0.62      5.19        4.84       4.45   <- lede fails AA
     0.70      6.90        6.44       6.06
     0.82     10.73       10.01       8.74

   0.70 lets roughly 30% of the photograph through (was 18% at 0.82) and still
   leaves every element clear of 4.5:1 with margin. The LEDE is the weak link,
   not the headline — it is the smallest text and it is itself translucent, so
   check it first if this is ever lowered again. Do not go below 0.66. */
.hero-copy {
  background: rgba(10, 20, 37, 0.7);
  -webkit-backdrop-filter: blur(12px) saturate(1.15);
  backdrop-filter: blur(12px) saturate(1.15);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-lg);
  padding: clamp(1.6rem, 3vw, 2.75rem);
  box-shadow: 0 24px 60px rgba(10, 20, 37, 0.4);
}

.hero--home h1 { color: var(--white); }
.hero--home .triad { color: var(--cream); }
.hero--home .hero-lede { color: rgba(255, 255, 255, 0.92); }

/* Dark-ground buttons, back after one iteration on a light hero. Gold is
   opaque so it carries its contrast regardless of what the photo does behind
   the panel; the outline button's translucent navy sits on the panel, not on
   bare photo, so its backdrop is the measured 0.82 mask.
     .btn-primary  ink on gold      9.12:1, hover amber 10.37:1
     .btn-outline  white on #2d2f94 over the panel */
.hero--home .btn-primary {
  background: var(--bvd-gold);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.25);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.hero--home .btn-primary:hover {
  background: var(--bvd-amber);
  border-color: rgba(255, 255, 255, 0.45);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.3);
}
.hero--home .btn-outline {
  background: rgba(45, 47, 148, 0.68);
  color: var(--white);
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.hero--home .btn-outline:hover {
  background: #2d2f94;
  color: var(--white);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.3);
}

/* Below 900px the panel takes the full column: at phone widths there is no
   "beside the panel" left to protect, and a narrow inset panel over a busy
   aerial reads as clutter. The shorter min-height stops the hero eating the
   whole first screen on a phone. */
@media (max-width: 900px) {
  .hero--home { background-position: center 58%; }
  /* Full column on a phone: there is no "beside the panel" left to uncover,
     so right-alignment would only produce an odd inset. */
  .hero--home .container { align-items: stretch; }
  .hero--home .container {
    min-height: min(66vh, 560px);
    padding-top: 3rem;
    padding-bottom: 3rem;
  }
}

.hero-copy { max-width: 41rem; }

/* "Built from the road." is the tagline; breaking it after "the" left a widow
   and split the line off its own rhythm. Held to one line, which needs both a
   wider copy column (41rem couldn't fit it at the global h1 scale) and a
   vw-driven size so nowrap survives down to 375px. .hero-lede keeps its own
   34rem cap, so the body measure is unaffected by the wider column. */
/* "Built from the road." is the tagline, and the two breakpoints want opposite
   things from it.

   MOBILE: size first, line count second. Holding one line here meant dropping
   to ~28px, which made the tagline smaller than the lede underneath it — so it
   wraps to two lines and runs LARGER than the old 2.5rem floor instead. 12vw
   reaches the 4.5rem cap at 600px.

   DESKTOP (≥800px): one line at full display size. Measured with Poppins
   loaded, the line is 688px against 752px available at 800px and 832px above
   1024px, so there is real slack rather than a millimetre fit — nowrap has no
   safety valve, and an overflowing headline takes the whole page's horizontal
   scroll with it. 800px is also where the sign-up card goes two-column, so the
   layout changes character in one place rather than two.

   The wider copy column is what makes the one-line version possible at all
   (41rem could not fit it); .hero-lede keeps its own 34rem cap, so the body
   measure is unchanged. */
/* 52rem was the width that let the nowrap h1 fit when .hero-copy was a bare
   block. It is a PANEL now, so its horizontal padding (up to 2.75rem a side)
   comes out of the content box — 52rem would leave the headline ~90px short
   and overflow it. Widened to cover the padding; the h1's own measured width
   is unchanged. */
/* 51rem, down from 59rem: the panel was covering roughly three quarters of the
   frame. This is the narrowest it goes while the nowrap headline still has
   slack — 689px of line in 739px of content box at 1280, and 763px at the
   880px breakpoint. Narrower means letting the headline wrap. */
.hero--home .hero-copy { max-width: min(51rem, 100%); }
.hero--home h1 {
  margin-bottom: 0.35em;
  font-size: clamp(2.75rem, 12vw, 4.5rem);
}

/* Was 800px, from when .hero-copy was a bare block and the line measured 688px
   against 752px of column. The copy is a PANEL now and its padding comes out of
   that column, so at 800px the same 688px line has only 686px to sit in and
   overflows by 2px. Raised until there is real slack again: at 880px the line
   has ~70px spare. Between 800 and 880 it wraps to two lines, which is what it
   already did below 800. nowrap has no safety valve — an overflowing headline
   takes the page's horizontal scroll with it — so this wants margin, not a fit. */
@media (min-width: 880px) {
  .hero--home h1 { white-space: nowrap; }
}

.hero--home .triad {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  margin-bottom: 1.25rem;
}

/* Colour is set with the other light-ground type further up. It was
   rgba(255,255,255,0.82) here — a leftover from the dark hero, and because this
   block sits LATER at equal specificity it silently won and rendered the lede
   white on cream at 1.07:1, i.e. invisible. Do not reintroduce a colour here. */
.hero--home .hero-lede {
  font-size: 1.05rem;
  max-width: 34rem;
}

.hero--home .hero-actions { justify-content: flex-start; }

/* One orchestrated entrance rather than scattered micro-interactions. */
@media (prefers-reduced-motion: no-preference) {
  .hero-copy > * {
    animation: rise 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
  }
  .hero-copy > :nth-child(1) { animation-delay: 0.05s; }
  .hero-copy > :nth-child(2) { animation-delay: 0.14s; }
  .hero-copy > :nth-child(3) { animation-delay: 0.23s; }
  .hero-copy > :nth-child(4) { animation-delay: 0.32s; }
  .hero-copy > :nth-child(5) { animation-delay: 0.41s; }
}

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

/* ---------- Sections ---------- */
section { padding: var(--section-y) 0; }

/* ---------- Signup section (image-led, dark) ----------
   Was a flat --cream tint. That put it directly under the light-grey locations
   band, and light-on-light gave the page no step at all between the two — the
   luminance delta was 0.007, so the boundary was carried almost entirely by a
   cool-to-warm hue shift. Dan called it on sight, 2026-07-30.

   This reuses forecourt-dusk.jpg, the previous home hero, which went unused
   when the daylight aerial replaced it. It is the right image for this slot:
   already web-optimised at 1376x768/124KB, already approved, and its dusk
   palette reads as a different time of day from the two daylight renders
   above rather than as a third helping of the same photograph.

   ⚠️ NO TEXT SITS ON THE PHOTO HERE. Everything lives inside the opaque white
   .signup card, so the scrim is compositional, not an accessibility control —
   unlike the hero panel's 0.70, this number is not load-bearing and can be
   moved on taste. If copy is ever added directly onto this section, that stops
   being true and it has to be measured like the hero was.

   This class is also emitted by signup_block() in the generator, so all 72
   location pages pick it up. That is deliberate: it is the same block and it
   should not look like two different components. The background URL is
   relative to THIS stylesheet, not to the page, so it resolves the same at
   every directory depth. */
.section-alt {
  position: relative;
  background-color: var(--ink);
  background-image: url("../assets/img/forecourt-dusk.jpg");
  background-size: cover;
  background-position: center 62%;
  isolation: isolate;
}

.section-alt::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* The render's mean luminance is 95/255, so it is already a dark image; this
     only deepens it enough that the white card reads as lifted off the page
     rather than floating on a busy photo. */
  background: linear-gradient(
    180deg,
    rgba(10, 20, 37, 0.82) 0%,
    rgba(10, 20, 37, 0.72) 45%,
    rgba(10, 20, 37, 0.8) 100%
  );
}

/* --shadow-soft is a pale grey glow built for tinted light sections; on a dark
   photo it is invisible and the card loses its edge. */
.section-alt .signup {
  box-shadow: 0 26px 60px rgba(0, 0, 0, 0.45);
}

/* Neutral ground for card sections. --shadow-soft is a pale grey glow, so a
   white card on a white section reads as no card at all — anything using
   .card needs a tinted surface underneath it or the elevation is wasted. */
.section-surface { background: var(--surface); }

/* ---------- Cards ---------- */
/* DESIGN-UPDATES.md §9 step 5. One card primitive, two variants:
   .card is a static panel; .card-link is the interactive version. The lift is
   deliberately NOT on the base class — a hover lift on a block you cannot
   click is a false affordance, and most cards on this site are read-only. */
.card {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 1.75rem;
}

.card-link {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: transform var(--ease), box-shadow var(--ease);
}
.card-link:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(46, 49, 146, 0.14);
}
.card-link:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .card-link:hover { transform: none; }
}


/* Pillars — asymmetric split rather than three equal centred cards, which is
   the layout that made the page read as a template. The navy `.icon-dot`
   placeholders are retired: a bare circle reads as a missing icon. Numerals
   carry the same rhythm and are finished work. */
.split {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: 3rem 4rem;
  align-items: start;
}

.split-lead h2 { margin-bottom: 0.6rem; }
.split-lead p { font-size: 1.1rem; max-width: 26rem; }

.value-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.value-item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 1.5rem;
  padding: 1.75rem 0;
  border-top: 1px solid var(--border);
}
.value-item:last-child { border-bottom: 1px solid var(--border); }

/* Navy, not gold. Gold on white is 2.02:1 and the old orange was 2.25:1 —
   both illegible. These numerals are aria-hidden, so they are not an AA
   failure in the strict sense, but "decorative" is not a licence to be
   unreadable. Warm accent stays on fills and rules, where it works. */
.value-num {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--bvd-navy);
  letter-spacing: 0.06em;
  padding-top: 0.2rem;
}

.value-item h3 { margin-bottom: 0.35rem; }
.value-item p { margin-bottom: 0; }

@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; gap: 2rem; }
}

/* Manifesto — the strongest copy on the site, so it gets display treatment.
   §11: with photography scarce, the type IS the design. road.jpg sits behind
   at low opacity for atmosphere; the navy base carries the contrast so the
   texture can never push text below AA. */
.manifesto {
  position: relative;
  background: var(--bvd-navy);
  color: var(--cream);
  overflow: hidden;
  isolation: isolate;
  padding: 6rem 0;
}

/* Kept low: at 0.16 the road read as a photograph and lifted the navy paler
   than the footer, which broke the page's depth order. At 0.09 with a dark
   wash over it, it reads as texture and the navy stays rich. */
.manifesto::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(rgba(10, 20, 37, 0.55), rgba(10, 20, 37, 0.55)),
    url("../assets/img/road.jpg");
  background-size: cover;
  background-position: center;
  opacity: 0.28;
}

.manifesto .container { max-width: 860px; }

.manifesto p {
  color: var(--cream);
  font-size: clamp(1.15rem, 2.1vw, 1.6rem);
  line-height: 1.45;
  margin-bottom: 1.1em;
  max-width: 34ch;
}
.manifesto p:nth-of-type(2) { max-width: 46ch; }

.manifesto .lockup-close {
  font-family: var(--font-head);
  font-weight: 800;
  color: var(--white);
  font-size: clamp(1.75rem, 4.5vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  max-width: none;
  margin-top: 2.5rem;
  padding-top: 2.5rem;
  border-top: 2px solid rgba(255, 255, 255, 0.18);
}

.manifesto .lockup-tag {
  display: block;
  font-size: 0.5em;
  font-weight: 600;
  font-style: italic;
  letter-spacing: -0.01em;
  color: var(--cream);
  margin-top: 0.4em;
}

/* ---------- Locations band (image-led, light) ----------
   forecourt-commuter.jpg, added 2026-07-30.

   ⚠️ It is a PANEL, not a full-bleed background, and that was measured into
   rather than chosen. The first attempt put the photo behind the whole band
   the way the hero does. Two things killed it. The render is 3:2 and the band
   is ~4:1, so `cover` threw away 65% of the frame and left an anonymous slice
   of canopy underside with the branded fascia cropped off the top. And the
   image is backlit, bright corner to corner, so the scrim needed for AA
   (0.93→0.74) reduced what survived the crop to a dark smear. Scrim strong
   enough for type and photo bright enough to be worth showing were not both
   available from that layout.

   THE GROUND WENT LIGHT ON 2026-07-30, completing the P1 rebalance. It was
   navy, which put it directly under the navy .manifesto and made two dark
   bands back to back — the page read navy-dominant, which is the thing the
   feedback asked to invert. Navy is now punctuation: the manifesto and the
   footer, and nothing else. An earlier note here argued the two dark bands
   were fine because they were "different in kind"; that was a workaround for
   a problem this change removes, and it is gone.

   The source render is 2528x1696, cropped to drop the right ~10% (a garbled
   AI artefact sign) and the top 10% of empty sky. The crop is baked into the
   JPG, not done in CSS, so the artefact cannot reappear at some viewport that
   reveals more of the right edge. */
.locations-band {
  background: var(--surface);
  color: var(--ink);
}

.locations-band .container {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
}

.locations-band h2 { margin-bottom: 0.3rem; color: var(--ink); }
/* NOT --ink-soft: it measures 4.54:1 on --surface, i.e. AA by a hair for body
   text. #52607C is the same tone darkened to 5.94:1, the value the hero lede
   uses for the same reason. */
.locations-band p { margin-bottom: 0; color: #52607C; }

.band-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.75rem;
}
.band-copy > :first-child { margin-bottom: 0; }

/* The stat and the CTA used to sit on the same row as the heading, spread by
   space-between. In a two-column band that row has half the width, so they
   stack under the copy instead and the stat picks up a rule to stop it reading
   as a third paragraph. */
/* The copy column used to end in a large gold "72" stat with a rule above it.
   The count is gone (see index.html), so the rule moved onto the detail line
   that replaced it — the column still needs something to stop the button
   floating free of the heading, and a hairline does that without asserting a
   number that goes stale. */
.locations-band p.band-detail {
  color: #52607C;
  max-width: 34ch;
  padding-top: 1.5rem;
  border-top: 2px solid var(--border);
  align-self: stretch;
}

.locations-band p.band-provinces {
  /* Navy rather than the body tone: it is a list of place names, and on a light
     ground navy reads as brand colour where cream-at-0.85 just read as faded.
     10.02:1 on --surface. */
  color: var(--bvd-navy);
  font-size: 0.95rem;
}

/* The band's CTA takes the site default again — solid navy fill, white label,
   10.66:1. It carried the hero's gold treatment while the band was navy, for
   the same reason the hero does: a navy fill on a navy ground is a
   button-shaped hole. On the light ground that reverses, and gold on
   --surface would be a pale button on a pale section. No override needed. */

/* 3:2 matches the crop, so object-fit has nothing to trim at desktop widths
   and the canopy stays whole. The frame is what makes this read as "a
   photograph we are showing you" rather than "a background we put type on". */
.band-media {
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(10, 20, 37, 0.18);
}
.band-media img {
  display: block;
  width: 100%;
  /* height:auto is load-bearing, not tidiness. The width/height attributes on
     the <img> are presentational hints that compute to a real used height, and
     a used height beats aspect-ratio — without this the image renders at its
     full 1080px intrinsic height and the band grows to ~1300px tall. */
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

/* Single column below 880px. The photo goes SECOND in source and stays second
   here: the heading is what tells you what the section is, and leading a
   stacked band with an untitled picture makes the reader scroll to find out.
   On the desktop grid it is the right-hand column, so source order and visual
   order agree at both sizes and the tab order needs no fixing. */
@media (max-width: 880px) {
  .locations-band .container { grid-template-columns: 1fr; }
  .band-media img { aspect-ratio: 16 / 9; }
}

/* Transition reassurance banner */
.transition-banner {
  background: var(--bvd-navy);
  color: var(--white);
  text-align: center;
  padding: 0.85rem 1rem;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.95rem;
}
.transition-banner a { color: var(--bvd-amber); text-decoration: underline; }

/* ---------- Email capture ---------- */
/* Elevation instead of a hairline border — the border was doing the job of a
   shadow and reading as a wireframe. Sits on --cream, so the soft glow
   registers. */
.signup {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 2.5rem;
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
}

/* The flex row is .signup-row, NOT .signup-form. The consent checkbox has to
   live INSIDE the form to be submitted and validated (it used to be a sibling
   of it, so OPT_IN was never posted at all), and a checkbox block cannot sit
   in the same nowrap flex row as the email + button. So the form is a plain
   block and the row inside it does the layout. */
.signup-form { margin-top: 1.5rem; }

.signup-row {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  justify-content: center;
}

.signup-form input[type="email"] {
  flex: 1 1 260px;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 1rem;
}

.btn[disabled], .btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
/* Disabled must not lift or recolour on hover, or it reads as clickable. */
.btn[disabled]:hover, .btn:disabled:hover {
  transform: none;
  box-shadow: var(--shadow-navy);
}
.btn-primary[disabled]:hover { background: var(--bvd-navy); }
.btn-accent[disabled]:hover { background: var(--bvd-gold); }

.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;
}

.consent-line {
  font-size: 0.8rem;
  color: var(--ink-soft);
  margin-top: 1rem;
  text-align: left;
}

.consent-line label { display: flex; gap: 0.5rem; align-items: flex-start; }

/* Brevo's bot honeypot. Their endpoint requires the field to be present and
   posted EMPTY — a filled value means the submission is silently discarded as
   a bot, so this must never be reachable by a real person or an autofill.
   Not `display:none`/`hidden`: some password managers skip those and some
   bots specifically look for them. Off-canvas, zero-size, untabbable. */
.signup-hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px !important;
  height: 1px !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

/* Post-submit confirmation. Replaces the form in place — the alternative was a
   redirect to a thank-you page we do not have, or Brevo's own hosted page,
   which is off-brand and drops the visitor out of the site. */
.signup-thanks {
  margin-top: 1.5rem;
  padding: 1.25rem 1.5rem;
  background: var(--cream);
  border-left: 4px solid var(--bvd-gold);
  border-radius: var(--radius);
  text-align: left;
}
.signup-thanks h3 {
  font-family: var(--font-head);
  font-size: 1.15rem;
  color: var(--bvd-navy);
  margin: 0 0 0.35em;
}
.signup-thanks p { font-size: 0.9rem; color: var(--ink); margin: 0; }
.signup-thanks[hidden] { display: none; }
/* Focus lands here after submit so the outcome is announced, but it is not a
   persistent tab stop — hence tabindex="-1" and no visible ring. */
.signup-thanks:focus { outline: none; }

.form-note {
  font-size: 0.75rem;
  color: var(--ink-soft);
  border: 1px dashed var(--border);
  padding: 0.75rem 1rem;
  border-radius: var(--radius);
  margin-top: 1rem;
}

/* Desktop: the card goes side-by-side and takes the full container width.
   Stacked and centred at 560px it was a narrow column adrift in a wide band,
   which read as an afterthought on the one conversion point this launch has.
   Pitch on the left, the whole form on the right.

   Mobile keeps the stacked centred card unchanged — a two-column form at
   375px is worse than a centred one.

   This block must stay AFTER the base .signup rules: media queries add no
   specificity, so placing it earlier would let the unprefixed declarations
   win on source order. */
@media (min-width: 800px) {
  .signup {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    align-items: center;
    gap: 3.5rem;
    max-width: none;
    padding: 3rem 3.5rem;
    text-align: left;
  }
  .signup-copy h2 { margin-bottom: 0.4em; }
  .signup-copy p:last-child { margin-bottom: 0; }
  .signup-form { margin-top: 0; }
  .signup-row { flex-wrap: nowrap; justify-content: flex-start; }
  .signup-form input[type="email"] { flex: 1 1 auto; }
}

/* ---------- Store locator ---------- */
/* Behaviour is js/locator.js; data is data/locations.json from the generator.
   Ported from MPP's store-locator widget, minus its runtime theming layer —
   this one inherits the site's tokens instead of shipping ~40 appearance
   fields. See DESIGN-UPDATES.md §12. */

/* The locator's hero is a label, not a statement — on a page whose whole job
   is the controls below it, a 4.5rem display h1 pushes the search box under
   the fold. Scoped to this page so the 72 location pages keep the full
   signage-scale h1. */
.hero--slim { padding: 2.5rem 0; }
.hero--slim h1 { font-size: clamp(1.9rem, 3.4vw, 2.75rem); margin-bottom: 0.4em; }
.hero--slim .triad { font-size: 0.95rem; margin-bottom: 0; }
.hero--slim .hero-eyebrow { margin-bottom: 1rem; }

/* This page is a tool, not a document, so the chrome above and below the pane
   is cut to what the brand needs and no more — every pixel spent here is a
   pixel the map doesn't get. The double class also settles a collision: the
   hero carries both --slim (2.5rem) and --compact (3.5rem), which are equal
   specificity, so --compact was quietly winning on source order and the slim
   padding never applied. */
.hero--slim.hero--compact { padding: 2rem 0 1.75rem; }

/* FAQ hero. The h1 is held to one line: "New name. Same road." is two beats
   and breaking it after "New name." reads as a stutter. nowrap needs a size
   that fits the narrowest viewport too, hence the vw-driven clamp rather than
   the global h1 scale — verified no overflow at 375px. */
.hero--faq { padding: 3rem 0 2.5rem; }
.hero--faq h1 {
  white-space: nowrap;
  font-size: clamp(1.55rem, 5.2vw, 4rem);
}
/* ---------- Store locator (v2 — bvdgroup.com/locations) ----------
   Behaviour is js/locator.js; data is data/locations.json from the generator.

   Rebuilt 2026-07-31 to match the parent's location finder. The v1 design (a
   sidebar of cards beside a bounded map) is retained verbatim at
   css/locator-legacy.css + js/locator-legacy.js + locator-legacy.html — it is
   unlinked reference material, not a second locator.

   What changed, and why the class prefix moved from .loc- to .lc-: this is no
   longer a document with a map in it. It is an app pane. The map IS the page,
   edge to edge, and everything else floats on top of it — search bar, filter
   modal, results list, detail drawer. A rename rather than a rewrite in place
   means the two stylesheets can coexist without a specificity fight, and it
   keeps `git grep lc-` honest about which locator a rule belongs to.

   Three things carried over from v1 because they were right:
     - .loc-map-pending (shared, in this file above) is still the fail-closed
       panel, so a missing Maps key degrades to a labelled box rather than a
       blank one.
     - Open now stays ON the surface rather than inside the filter modal. It is
       the one filter people reach for without going looking, and burying it
       would hide the fact that it is on by default.
     - The /locations/ index is still the no-JS path, linked from <noscript>
       and from the empty state.
   ------------------------------------------------------------------------- */

/* The pane breaks out of .container: the parent's map runs to the window edge
   and the floating controls are positioned against that edge, not against a
   1280px measure. --lc-h is measured in locator.js (viewport minus the header
   above it) for the same reason v1 measured --loc-h: CSS cannot see the
   element's own document offset.

   The calc() fallback covers first paint and the no-JS frame. Header is ~4.5rem
   and there is no hero on this page, so 5rem clears it. It must stand on its
   own — if it doesn't, a stale or blocked locator.js shows up as a map of the
   wrong height and nothing else. */
.lc {
  position: relative;
  height: var(--lc-h, calc(100dvh - 5rem));
  min-height: 520px;
  background: var(--surface);
  overflow: hidden;
  /* Contains the absolutely-positioned chrome below, and clips the drawer's
     slide-in transform so it cannot paint over the footer. */
  isolation: isolate;
}

.lc-map { position: absolute; inset: 0; }

/* Everything that floats over the map sits in one stacking band above it, so
   the map's own Google controls (zoom, attribution) can never land on top of
   our chrome. Google's UI runs at z-index 0-1000000 internally but only within
   the map container, which is why one level here is enough. */
.lc-float { position: absolute; z-index: 2; }

/* --- floating search bar --- */
/* Top-centre, matching the parent. It is a single pill holding the query, a
   divider and the two controls that open state rather than change it. */
.lc-bar {
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  width: min(46rem, calc(100% - 2rem));
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0.3rem 0.3rem 0.9rem;
  background: var(--white);
  border-radius: var(--radius-pill);
  box-shadow: 0 6px 24px rgba(10, 20, 37, 0.16);
}

.lc-bar .ic-search { width: 1.15rem; height: 1.15rem; flex: none; color: var(--ink-soft); }

.lc-bar input[type="search"] {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0.6rem 0.5rem;
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
}
.lc-bar input[type="search"]:focus { outline: none; }
.lc-bar input[type="search"]::placeholder { color: var(--ink-soft); }
/* The browser's own clear affordance duplicates .lc-clear and lands in a
   different place in each engine. */
.lc-bar input[type="search"]::-webkit-search-cancel-button { display: none; }

.lc-clear {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
}
.lc-clear:hover { background: var(--surface); color: var(--ink); }
.lc-clear .ic { width: 0.9rem; height: 0.9rem; }
.lc-clear[hidden] { display: none; }

.lc-bar-sep {
  flex: none;
  width: 1px;
  align-self: stretch;
  margin: 0.35rem 0.3rem;
  background: var(--border);
}

/* Filters button. Carries a count so applied filters are never invisible while
   the modal is shut — same contract as v1's disclosure toggle. */
.lc-filterbtn {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 0.9rem;
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
  transition: background var(--ease);
}
.lc-filterbtn:hover { background: var(--surface); }
.lc-filterbtn .ic { width: 1.05rem; height: 1.05rem; color: var(--bvd-navy); }

.lc-n {
  display: inline-grid;
  place-items: center;
  min-width: 1.3rem;
  height: 1.3rem;
  padding: 0 0.35rem;
  border-radius: var(--radius-pill);
  background: var(--bvd-navy);
  color: var(--white);
  font-size: 0.72rem;
  line-height: 1;
}
.lc-n[hidden] { display: none; }

/* --- top-left / bottom-right map buttons --- */
/* The parent puts "Plan a trip" top-left and a locate control bottom-right.
   Trip planning is out of scope (it needs the Directions API and would change
   what privacy.html says about geolocation staying in the browser), so the
   top-left slot carries the list toggle instead — the one control the parent
   does not have and we do. */
.lc-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.6rem 0.9rem;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--white);
  box-shadow: 0 4px 16px rgba(10, 20, 37, 0.16);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
  transition: box-shadow var(--ease), background var(--ease);
}
.lc-btn:hover { box-shadow: 0 6px 20px rgba(10, 20, 37, 0.22); }
.lc-btn .ic { width: 1.05rem; height: 1.05rem; color: var(--bvd-navy); }
.lc-btn[aria-pressed="true"] {
  background: var(--bvd-navy);
  color: var(--white);
}
.lc-btn[aria-pressed="true"] .ic { color: var(--white); }
.lc-btn:disabled { opacity: 0.6; cursor: default; }

/* One flex cluster rather than two independently positioned buttons. Absolute
   left offsets have to be re-guessed every time a label or an icon changes
   width, and the two rendered at different heights (36px vs 33px) because only
   one of them carried a glyph. Open now rides beside the list toggle rather
   than inside the filter modal: it is on by default (OPEN_NOW_DEFAULT in
   locator.js), and a default that removes pins has to be visible or it reads
   as missing data. */
.lc-topleft {
  top: 1rem;
  left: 1rem;
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
}

.lc-locate { bottom: 1.25rem; right: 1rem; }

/* --- results list --- */
/* A floating panel, not a grid column: the map is full-bleed underneath it, so
   the list overlays rather than displaces. Collapsible, because the parent's
   design is map-first and the list is our addition — but retained, because it
   is the only keyboard-navigable path through the results and the only thing
   that still works if the map fails to load. */
.lc-list {
  top: 4.75rem;
  left: 1rem;
  bottom: 1.25rem;
  width: 22.5rem;
  max-width: calc(100% - 2rem);
  display: flex;
  flex-direction: column;
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 30px rgba(10, 20, 37, 0.18);
  overflow: hidden;
  transition: opacity var(--ease), transform var(--ease);
}
.lc-list[hidden] { display: none; }
/* Animating out rather than snapping: the panel covers a quarter of the map,
   and a hard disappearance reads as a repaint fault. */
.lc-list.is-closing { opacity: 0; transform: translateX(-0.75rem); }

.lc-list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border);
}

/* The live filtered count. Computed from the data at runtime, so it cannot go
   stale the way a hard-coded network total would — which is why this one is
   permitted where "72 locations" in prose is not. */
.lc-count {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ink);
}
.lc-count span { font-weight: 400; color: var(--ink-soft); }

.lc-results {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  margin: 0;
  padding: 0.5rem;
  list-style: none;
}

.lc-card {
  padding: 0.75rem 0.8rem;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background var(--ease);
}
.lc-card + .lc-card { border-top: 1px solid var(--border); border-radius: 0; }
.lc-card:hover { background: var(--surface); }
.lc-card.is-active {
  background: var(--cream);
  border-top-color: transparent;
  border-radius: var(--radius);
}
.lc-card.is-active + .lc-card { border-top-color: transparent; }

.lc-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}
.lc-card-head h3 {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.3;
  min-width: 0;
}
.lc-dist { flex: none; font-size: 0.78rem; font-weight: 600; color: var(--bvd-navy); }

.lc-card-sub {
  margin: 0.15rem 0 0;
  font-size: 0.8rem;
  /* Not --ink-soft: 4.83:1 is AA by a hair and this is the smallest text in a
     panel that floats over map tiles. Same substitution and reason as the hero
     lede and the locations band. */
  color: #52607C;
}

.lc-card-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.4rem; }

/* Directions on the card itself. Left-aligned and shrink-to-fit rather than
   full width: the card is already clickable everywhere else, so a button
   spanning it would read as the card's primary action when it is the
   secondary one. The navy glow is dropped for the same reason it is dropped
   in the drawer — at this size it reads as a smudge, not as depth. */
/* Two actions since 2026-08-01, so this has to wrap: the list panel is ~330px
   of usable width and the pair comes to roughly 250px, which fits at desktop
   but not once a long site name forces the panel narrower. flex-wrap lets the
   second button drop to its own line instead of overflowing the card. */
.lc-card-actions {
  margin-top: 0.55rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.lc-card-actions .btn {
  padding: 0.35rem 0.65rem;
  font-size: 0.78rem;
  box-shadow: none;
}
.lc-card-actions .btn:hover { box-shadow: none; }
.lc-card-actions .ic { width: 0.9rem; height: 0.9rem; }

.lc-empty { padding: 1.5rem 1rem; font-size: 0.9rem; color: var(--ink-soft); }
.lc-empty p { margin: 0; }

/* --- status badges (shared by card, drawer and legend) --- */
.lc-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
.lc-badge::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: currentColor;
}
.lc-badge--open { background: #E8F5EC; color: #1B6B38; }     /* 6.34:1 */
.lc-badge--shut { background: var(--surface); color: #52607C; }
.lc-badge--soon { background: var(--cream-deep); color: var(--ink); }
.lc-badge--closed { background: var(--surface); color: #52607C; }

/* A plain fact chip: amenity names on a card, fuel grades in the drawer. No
   dot, no colour coding — these are listings, not statuses. */
.lc-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.15rem 0.45rem;
  border-radius: var(--radius-pill);
  background: var(--surface);
  font-size: 0.72rem;
  color: #52607C;
}
.lc-tag .ic { width: 0.85rem; height: 0.85rem; }

/* --- legend --- */
/* The parent's legend names its three networks. Ours is one network, so the
   legend does the job that is actually useful here: telling you that a slate
   pin is not a working site. That distinction is otherwise invisible on the
   map, and it is the only cue that a pin is Coming Soon. */
.lc-legend {
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.5rem 0.9rem;
  background: var(--white);
  border-radius: var(--radius-pill);
  box-shadow: 0 4px 16px rgba(10, 20, 37, 0.16);
  font-size: 0.78rem;
  color: var(--ink);
  white-space: nowrap;
}
.lc-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
.lc-legend i {
  width: 1.15rem;
  height: 1rem;
  border-radius: 0;
  background: url("../assets/img/map-marker.png?v=20260801c") center/contain no-repeat;
}
.lc-legend i.is-soon {
  background-image: url("../assets/img/map-marker-muted.png?v=20260801c");
}

/* --- detail drawer --- */
/* Bottom sheet, full width, matching the parent. It replaces v1's map
   InfoWindow: a bubble that size could not hold hours, parking, amenities and
   fuel without scrolling, and the parent solves it by moving the detail out of
   the map entirely. .loc-pin* and the .gm-style bubble overrides went to
   locator-legacy.css with the rest of v1 — nothing here opens an InfoWindow. */
.lc-drawer {
  /* ⚠️ Explicit, not inherited from .lc-float — the drawer is not a .lc-float
     (it sits above that band on z-index 3, not 2). Without this the offsets
     below are inert, the drawer lands in normal flow at the end of the
     section, and it paints 87px past the pane and over the footer. */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  /* 55%, not 78%. The drawer is as tall as its content and the content varies
     — six fact columns wrap to two rows on a 1280 window. At 78% a full drawer
     on a 720px-tall screen left the list panel 68px of usable height, which is
     one and a half cards. The drawer scrolls internally past this, which is
     the right trade: the list is a navigation surface and has to stay usable,
     the drawer is a detail surface and can scroll. */
  max-height: 55%;
  overflow-y: auto;
  background: var(--white);
  border-top: 1px solid var(--border);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: 0 -8px 30px rgba(10, 20, 37, 0.18);
  transform: translateY(0);
  transition: transform var(--ease);
}
.lc-drawer[hidden] { display: none; }
.lc-drawer.is-entering { transform: translateY(100%); }

.lc-drawer-inner { padding: 1.1rem 1.25rem 1.35rem; }

.lc-drawer-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.lc-drawer-id { min-width: 0; }

.lc-drawer-name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.15rem;
  font-size: 1.15rem;
  line-height: 1.2;
  /* Uppercase, as the parent sets it. The site names are short proper nouns
     ("Acheson", "Rosslyn") and read as signage at this size. */
  text-transform: uppercase;
  letter-spacing: 0.01em;
}
.lc-drawer-name .ic { width: 1.15rem; height: 1.15rem; flex: none; color: var(--bvd-navy); }

.lc-drawer-meta {
  margin: 0 0 0.4rem;
  font-size: 0.8rem;
  color: #52607C;
}

.lc-drawer-addr {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  color: var(--ink);
}
.lc-drawer-addr .ic { width: 0.95rem; height: 0.95rem; flex: none; color: var(--ink-soft); }

/* Copy-address button. Confirms in place by swapping to a check — the parent
   does the same, and a toast for a one-field copy is more chrome than the
   action deserves. */
.lc-copy {
  display: inline-grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
}
.lc-copy:hover { background: var(--surface); color: var(--ink); }
/* currentColor for the same reason as .lc-chip .ic — the success state swaps
   in a check glyph and turns the button green, and an explicitly-navy icon
   would ignore that and leave the confirmation looking unchanged. */
.lc-copy .ic { width: 0.9rem; height: 0.9rem; color: currentColor; }
.lc-copy.is-done { color: #1B6B38; }

.lc-drawer-actions { display: flex; align-items: flex-start; gap: 0.4rem; flex-wrap: wrap; }
.lc-drawer-actions .btn { box-shadow: none; }
.lc-drawer-actions .btn:hover { box-shadow: none; }

.lc-close {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  flex: none;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--white);
  color: var(--ink);
  cursor: pointer;
}
.lc-close:hover { background: var(--surface); }
.lc-close .ic { width: 1rem; height: 1rem; }

/* Fact columns. auto-fit rather than a fixed count: the drawer is full-window
   width and the number of populated columns varies by site (a cardlock with no
   restaurant has three, a travel centre with food has five). */
.lc-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1.25rem 1.5rem;
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--border);
}

.lc-fact h4 {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.6rem;
  font-family: var(--font-head);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.lc-fact h4 .ic { width: 0.9rem; height: 0.9rem; color: var(--bvd-navy); }

.lc-fact ul { margin: 0; padding: 0; list-style: none; }
.lc-fact li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  line-height: 1.9;
  color: var(--ink);
}
.lc-fact li .ic { width: 1rem; height: 1rem; flex: none; color: var(--ink-soft); }
.lc-fact .lc-val { margin-left: auto; font-weight: 600; }

/* Hours table. Today is bolded rather than coloured — the drawer already
   carries a coloured open/closed badge two lines up, and a second colour cue
   for a different meaning competes with it. */
.lc-hours { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.lc-hours th, .lc-hours td { padding: 0.18rem 0; text-align: left; font-weight: 400; }
.lc-hours th { color: #52607C; }
.lc-hours td { text-align: right; color: var(--ink); }
.lc-hours .is-today th, .lc-hours .is-today td { font-weight: 700; color: var(--ink); }

/* Coming-soon tenants are badged. An unbadged name has to mean "you can eat
   here now" — §7.4 permits amenity listings, not promotion, and a name with no
   qualifier on a site with no kitchen would be both wrong and promotional. */
.lc-soon {
  margin-left: 0.3rem;
  padding: 0.05rem 0.35rem;
  border-radius: var(--radius-pill);
  background: var(--cream-deep);
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--ink);
}

/* --- filter modal --- */
.lc-modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgba(10, 20, 37, 0.45);
}
.lc-modal[hidden] { display: none; }

.lc-modal-box {
  display: flex;
  flex-direction: column;
  width: min(30rem, 100%);
  max-height: min(40rem, 100%);
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px rgba(10, 20, 37, 0.35);
  overflow: hidden;
}

.lc-modal-head,
.lc-modal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.9rem 1.1rem;
}
.lc-modal-head { border-bottom: 1px solid var(--border); }
.lc-modal-foot { border-top: 1px solid var(--border); background: var(--surface); }

.lc-modal-head h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  font-size: 1.05rem;
}
.lc-modal-head h2 .ic { width: 1.1rem; height: 1.1rem; color: var(--bvd-navy); }

.lc-modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 1.1rem; }

.lc-group + .lc-group { margin-top: 1.25rem; }

/* Heading row for a group that carries a control beside its title (only
   Restaurants, so far). Baseline alignment would hang the chip off the
   heading's baseline and sit it too low against an uppercase 0.72rem label;
   centring reads level. */
.lc-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.55rem;
}
.lc-group-head h3 { margin-bottom: 0; }

.lc-group h3 {
  margin: 0 0 0.55rem;
  font-family: var(--font-head);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.lc-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.lc-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--white);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.82rem;
  line-height: 1.2;
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--ease), background var(--ease);
}
.lc-chip:hover { border-color: var(--bvd-navy); }
/* ⚠️ `color: currentColor` is load-bearing, not redundant. The global `.ic`
   rule sets an explicit `color: var(--bvd-navy)`, and an explicit declaration
   on the icon always beats colour inherited from the pressed button — so
   without this the glyph stayed navy on the navy pressed fill and vanished.
   Specificity and source order don't enter into it; inheritance simply loses
   to any declaration. Same fix `.btn .ic` already carries.
   Any new locator control that puts an icon on a coloured fill needs this. */
.lc-chip .ic { width: 1rem; height: 1rem; color: currentColor; }
/* Navy fill / white text, not gold: gold on white is 2.02:1 and gold as a fill
   under white text is no better. Ink or navy goes ON gold, never the reverse. */
.lc-chip[aria-pressed="true"] {
  background: var(--bvd-navy);
  border-color: var(--bvd-navy);
  color: var(--white);
}

/* Chip sized to sit on a heading row without out-weighing the heading. */
.lc-chip--sm { padding: 0.3rem 0.6rem; font-size: 0.78rem; }
.lc-chip--sm .ic { width: 0.9rem; height: 0.9rem; }

.lc-field { display: flex; flex-direction: column; gap: 0.35rem; }
.lc-field + .lc-field { margin-top: 0.6rem; }
.lc-group input[type="text"],
.lc-group select {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--white);
  font: inherit;
  font-size: 0.88rem;
  color: var(--ink);
}
.lc-group input[type="text"]:focus,
.lc-group select:focus { outline: 2px solid var(--bvd-navy); outline-offset: 1px; }

/* Restaurant brands. A checkbox list rather than chips: thirteen brand names
   wrap to five ragged rows as pills, and the parent uses a scrolling list for
   the same reason. */
.lc-brands {
  max-height: 11rem;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.3rem;
}
.lc-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius);
  font-size: 0.85rem;
  cursor: pointer;
}
.lc-brand:hover { background: var(--surface); }
.lc-brand input { accent-color: var(--bvd-navy); }

/* Tenant logo. A FIXED BOX with `contain`, not a height — the thirteen marks
   run from near-square (A&W, Tim Hortons) to very wide (Little Caesars
   Express, Ranchers Sports Bar & Grill), and setting only a height would let
   the wide ones push the brand name off the row. contain letterboxes inside
   the box instead, so every row lines up whatever lands in it. */
.lc-brand-logo {
  width: 2.25rem;
  height: 1.5rem;
  flex: none;
  object-fit: contain;
  object-position: center;
}
.lc-brand .lc-brand-n { margin-left: auto; font-size: 0.75rem; color: var(--ink-soft); }

.lc-modal-foot .lc-count { font-size: 0.85rem; }
.lc-reset {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.7rem;
  border: 0;
  border-radius: var(--radius);
  background: none;
  font: inherit;
  font-size: 0.85rem;
  /* Not --ink-soft: measured 4.54:1 on the modal foot's --surface ground, AA
     by a hair. Same substitution and same reason as the hero lede, the
     locations band and .lc-card-sub. #52607C measures 5.94:1 here. */
  color: #52607C;
  cursor: pointer;
  text-decoration: underline;
}
.lc-reset:hover { color: var(--ink); }

/* --- states --- */
.lc-loading {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  z-index: 4;
  background: var(--surface);
  color: var(--ink-soft);
}
.lc-loading[hidden] { display: none; }

/* Transition notice. No date and no count: both go stale on their own clock,
   and "opening August 1" is wrong from August 2. Points at the FAQ, which is
   where the approved answers live.

   ⚠️ Bottom-CENTRE, stacked above the legend, not bottom-left. The left edge
   from 4.75rem down to 1.25rem belongs to .lc-list, and a bottom-left notice
   sits underneath it — measured overlap at 1280x720. The centre column is the
   only bottom region both panels leave clear. */
.lc-notice {
  bottom: 4.25rem;
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(24rem, calc(100% - 2rem));
  padding: 0.6rem 0.85rem;
  background: var(--white);
  border-left: 3px solid var(--bvd-gold);
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgba(10, 20, 37, 0.16);
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--ink);
}
.lc-notice a { color: var(--bvd-navy); }

/* Drawer-open state. The drawer is a bottom sheet the width of the window, so
   everything anchored to the bottom edge ends up behind it. --lc-drawer-h is
   the drawer's measured height, published by locator.js when it opens: the
   height depends on the site (a cardlock with no restaurant has three fact
   columns, a travel centre with food has six, and they wrap differently at
   every width), so it cannot be a constant here.

   The legend and the notice are hidden rather than moved. Both are ambient
   orientation for someone scanning the map, and neither is relevant once a
   specific site is open — the drawer states that site's status in words. */
.lc.is-detail .lc-list,
.lc.is-detail .lc-locate { bottom: calc(var(--lc-drawer-h, 0px) + 0.75rem); }
/* A floor, so a tall drawer on a short window cannot squash the list out of
   existence. Past this the drawer simply overlaps it — which is ordinary
   bottom-sheet behaviour, and the list still scrolls inside what is visible. */
.lc.is-detail .lc-list { min-height: 11rem; }
.lc.is-detail .lc-legend,
.lc.is-detail .lc-notice { display: none; }

/* --- responsive --- */
/* Below 900px the floating panels stop floating. The list becomes a bottom
   sheet the map can be seen above, which is the pattern Dan set on the v1
   locator (map first, bounded list) carried into this design. */
@media (max-width: 900px) {
  .lc { height: var(--lc-h, calc(100dvh - 4rem)); }

  .lc-bar { top: 0.75rem; width: calc(100% - 1.5rem); padding-left: 0.75rem; }
  .lc-bar .lc-filterbtn span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

  /* Both control clusters move to the row under the search bar rather than to
     the bottom edge. The bottom half of the pane is the list sheet, and it
     opens and closes — anything anchored down there is either under the sheet
     or floating in the middle of the map depending on state, which is worse
     than a fixed row. Near me loses its label here: at 375px the three
     controls plus their margins come to 317px with it and 249px without. */
  .lc-topleft { top: 4.5rem; bottom: auto; left: 0.75rem; }
  .lc-locate { top: 4.5rem; bottom: auto; right: 0.75rem; }
  .lc-locate span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .lc-locate .ic { margin: 0; }

  .lc-list {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    max-width: none;
    height: 55%;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
  .lc-list.is-closing { transform: translateY(0.75rem); }

  /* The legend and the notice both compete with the bottom sheet for the same
     edge, and neither is load-bearing on a phone: the drawer states a site's
     status in words, and the notice is one tap away in the nav. */
  .lc-legend, .lc-notice { display: none; }

  /* ⚠️ The desktop is-detail rules must be undone here, not inherited. There
     the list is anchored top-and-bottom and shrinks to clear the drawer; here
     it has a fixed height:55% and bottom:0, so raising its `bottom` by the
     drawer's height slides the whole sheet off the top of the pane — measured
     at y:-160 on a 375x812 screen. On a phone the drawer is a full-screen
     detail view: the sheet stays where it is and the drawer covers it. */
  .lc.is-detail .lc-list { bottom: 0; min-height: 0; }
  .lc.is-detail .lc-locate { bottom: auto; }
  /* And the map controls step aside rather than sitting behind the drawer,
     which starts above them at this width. */
  .lc.is-detail .lc-topleft,
  .lc.is-detail .lc-locate { display: none; }

  .lc-drawer { max-height: 85%; }
  .lc-drawer-inner { padding: 1rem; }
  .lc-drawer-top { flex-direction: column; gap: 0.75rem; }
  .lc-drawer-actions { width: 100%; }
  .lc-drawer-actions .btn { flex: 1 1 auto; justify-content: center; }
  .lc-facts { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .lc-list, .lc-drawer { transition: none; }
}

/* ---------- Province landing pages ----------
   Six pages at /locations/<code>/, added 2026-08-01. Strictly an SEO/AEO
   surface: they exist so "BVD travel centres in Alberta" has a page to match
   and so answer engines have a province-scoped entity to cite rather than
   inferring one from 72 stations. Amenity variants were deliberately skipped
   (Dan) — six pages of real content beats thirty-six of padding, and thin
   near-duplicate sets are an SEO liability. */
.prov-cities {
  margin: 0 0 1.5rem;
  max-width: 46rem;
  /* Not --ink-soft: 4.83:1 on white is AA by a hair. Same substitution used
     for the hero lede, the locations band and the breadcrumbs. */
  color: #52607C;
}

/* The address under the site name on a province page row. The name alone
   ("Calgary 7 Ave") does not tell a driver which side of town it is on. */
.loc-addr-line {
  display: block;
  width: 100%;
  margin-top: 0.15rem;
  font-size: 0.85rem;
  color: #52607C;
}

.prov-back { margin: 1.75rem 0 0; }

/* The province heading on /locations/ is a link now. Keep it reading as a
   heading, not as body-copy link text. */
.province-head a { color: inherit; text-decoration: none; }
.province-head a:hover { color: var(--bvd-navy); text-decoration: underline; }

/* ---------- Breadcrumbs (generated pages) ----------
   Added 2026-07-31 with BreadcrumbList schema. The two go together on purpose:
   structured data describing a path the page does not actually offer is both an
   SEO risk and a dead end for a visitor three levels deep. Location pages had
   neither until now.

   Sits between the header and the hero, on the page ground rather than in a
   band of its own — it is orientation, not a section. */
.crumbs {
  padding: 0.85rem 0 0;
  font-size: 0.82rem;
}
.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.15rem 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.crumbs li { display: inline-flex; align-items: center; gap: 0.4rem; }
/* Separator on the ::before of every item except the first, so the markup
   carries no presentational characters for a screen reader to read out. */
.crumbs li + li::before {
  content: "›";
  color: var(--border);
  font-size: 1.05em;
  line-height: 1;
}
/* Not --ink-soft: 4.83:1 is AA by a hair and these are the smallest links on
   the page. Same substitution as the hero lede and the locations band. */
.crumbs a { color: #52607C; text-decoration: none; }
.crumbs a:hover { color: var(--bvd-navy); text-decoration: underline; }
.crumbs [aria-current="page"] { color: var(--ink); font-weight: 600; }

@media (max-width: 640px) {
  .crumbs { font-size: 0.78rem; }
}

/* ---------- Location page map ---------- */
/* Behaviour is js/location-map.js, key is js/maps.js — shared with the
   locator so there is one place to drop the real key. Reuses .loc-map-pending
   for the no-key state rather than defining a second panel.

   The map was its own full-width section below the facts. It is now the left
   column of .site-layout — see the layout block further down. */

/* Shared no-key panel. Three consumers now: js/location-map.js on the 72
   location pages, js/locator.js in the locator's map slot, and the legacy
   locator. It lives in the shared sheet rather than with any one of them —
   it used to sit in the locator block, which is why it stayed behind when
   that block moved out to css/locator-legacy.css. */
.loc-map-pending {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 2rem;
  text-align: center;
  color: var(--ink-soft);
}
.loc-map-pending .ic { width: 2.5rem; height: 2.5rem; color: var(--border); }
.loc-map-pending p { margin: 0; max-width: 26rem; font-size: 0.9rem; }
.loc-map-pending strong { color: var(--ink); }

/* Matched to .detail-block h2 rather than left as a 1.25rem heading: it now
   sits level with the ADDRESS and HOURS card heads, and three sizes of heading
   across one row read as an accident. */
.map-head {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  border-bottom: 2px solid var(--bvd-gold);
  padding-bottom: 0.5rem;
  margin-bottom: 1rem;
}
.site-map {
  height: clamp(260px, 38vh, 420px);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
  background: var(--white);
}

/* ---------- Legal / long-form document ---------- */
/* The only long-form prose page on the site. The global h2 runs to 2.75rem,
   which is a display size — used as a section heading every few paragraphs it
   reads as a series of page titles. Scoped down here rather than globally,
   because the display scale is right everywhere else. */
.legal-section { padding-top: clamp(2rem, 4vw, 3.25rem); }

.legal-doc {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: clamp(1.75rem, 4vw, 3rem);
}

.legal-doc h2 {
  font-size: 1.35rem;
  letter-spacing: -0.01em;
  margin-top: 2.25rem;
  margin-bottom: 0.6rem;
}
.legal-doc > :first-child { margin-top: 0; }
.legal-doc h3 {
  font-size: 1.02rem;
  color: var(--ink);
  margin-top: 1.5rem;
  margin-bottom: 0.4rem;
}
/* 68ch keeps the measure readable at the 760px container. */
.legal-doc p { max-width: 68ch; }
.legal-doc > :last-child { margin-bottom: 0; }

.legal-list {
  margin: 0 0 1em;
  padding-left: 1.25rem;
  max-width: 68ch;
  color: var(--ink-soft);
}
.legal-list li { margin-bottom: 0.5rem; }

/* Draft notice. Amber fill with ink on it — gold and amber are never text on a
   light ground (2.02:1 and 1.78:1), so the tint carries the emphasis and the
   text stays ink. Remove this rule when the draft flag comes off the page. */
.legal-flag {
  background: var(--cream);
  border-left: 3px solid var(--bvd-gold);
  padding: 0.9rem 1.1rem;
  border-radius: var(--radius);
  color: var(--ink);
}

.legal-meta {
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

/* ---------- FAQ ---------- */
/* Card per question rather than a ruled list: the answers are long and
   several run to two paragraphs, so a hairline between them wasn't enough
   separation to scan. Not interactive, so no lift. */
.faq-list { display: grid; gap: 1rem; }

/* Two-up from 900px. Six single-column cards on a wide screen was a long thin
   ribbon with the last two below the fold for no reason — these are short,
   independent Q&As with no reading order, which is exactly the content that
   suits columns. align-items:start so a short answer doesn't inherit the
   height of a tall neighbour. */
@media (min-width: 900px) {
  .faq-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
    align-items: start;
  }
}

/* The hero already carries generous padding; a full section's worth on top of
   it put the first question most of a screen down on a page people arrive at
   with one specific question. */
.faq-section { padding-top: 2.5rem; }

.faq-item {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 1.75rem 2rem;
}
.faq-item h3 { margin-bottom: 0.5rem; }
.faq-item p:last-child { margin-bottom: 0; }

/* ---------- Locator placeholder ---------- */
.locator-placeholder {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 3rem 2rem;
  text-align: center;
  color: var(--ink-soft);
  background: var(--white);
}

/* ---------- Location pages (generated — see build/generate_locations.py) ---------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--bvd-amber);
  color: var(--ink);
  font-family: var(--font-head);
  font-weight: 700;
  padding: 0.75rem 1.25rem;
  z-index: 200;
}
.skip-link:focus { left: 0; }

.hero--compact { padding: 3.5rem 0; }
.container--narrow { max-width: 760px; }

.h1-sub {
  display: block;
  font-size: 0.55em;
  font-weight: 600;
  color: var(--bvd-amber);
  margin-top: 0.35em;
}

.status-badge {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius-pill);
  margin-bottom: 1rem;
}
.badge-soon { background: var(--cream-deep); color: var(--ink); }
.badge-closed { background: var(--surface); color: var(--ink-soft); border: 1px solid var(--border); }

.inline-badge {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink-soft);
  border: 1px solid var(--border);
  vertical-align: middle;
}

/* ---------- Icons ---------- */
/* Hand-authored inline SVG subset (see ICONS in build/generate_locations.py).
   Stroke, not the parent's Font Awesome solid — no CDN, and stroke stays
   legible at list size. Sized in em so they track whatever text they sit in,
   and always aria-hidden beside a real label. */
.ic {
  width: 1.35em;
  height: 1.35em;
  flex: none;
  color: var(--bvd-navy);
  vertical-align: -0.28em;
}
.btn .ic { width: 1.05em; height: 1.05em; color: currentColor; }

/* ---------- Location page detail layout ---------- */
/* Map on the left, facts on the right in two columns; map on top on mobile.
   The map is FIRST in source (see the generator), so the mobile stack is just
   source order and desktop is a plain two-column grid — no `order` juggling,
   which would have desynced the visual order from the tab order.

   This section used to sit in .container--narrow (760px). Two columns of cards
   beside a map does not fit in 760px, so it runs in the full 1280px container
   and the two-column split only switches on once there is room for it. */
.section-detail {
  /* section's default --section-y tops out at 120px, which under an already
     generous hero left the facts stranded well down the page. */
  padding-top: clamp(2rem, 4vw, 3.25rem);
}

.site-layout {
  display: grid;
  gap: 1.25rem;
}

/* Detail blocks are cards. They were bare stacked text on white, which is
   the layout that made 72 pages read as a data dump. They carry their own
   padding, so the grid gap tightens.

   Two columns, explicitly — not auto-fit. auto-fit went to three across the
   mid range, which is both narrower than asked for and a third column that
   only ever holds one card on most sites. */
.site-detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  align-content: start;
}

@media (min-width: 640px) {
  .site-detail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 1080px is where a ~480px map column and two ~330px card columns both clear
   their minimums; below it the map goes full width on top and the cards stay
   two-up on their own. */
@media (min-width: 1080px) {
  .site-layout {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  }
  /* Flex column so the map panel absorbs the leftover height and its bottom
     edge lands level with the last card, instead of the clamp leaving a ragged
     gap under it. min-height is the floor for sparse sites — a Coming Soon page
     has two cards, not four. */
  .site-map-col { display: flex; flex-direction: column; }
  .site-map-col .site-map { flex: 1 1 auto; height: auto; min-height: 340px; }
}

.detail-block {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 1.75rem;
}

.detail-block h2 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-soft);
  border-bottom: 2px solid var(--bvd-gold);
  padding-bottom: 0.5rem;
  margin-bottom: 1rem;
}
.detail-block > :last-child { margin-bottom: 0; }

.detail-block address {
  font-style: normal;
  font-size: 1.05rem;
  line-height: 1.6;
  /* The Get directions button sat directly against the postal code line, so the
     address and the control read as one clump. The button is a separate action
     and needs to look like one. */
  margin-bottom: 1.25rem;
}

.btn-sm { padding: 0.55rem 1.1rem; font-size: 0.85rem; }

.fact-list { list-style: none; margin: 0; padding: 0; }
.fact-list li {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--border);
  color: var(--ink-soft);
  font-size: 0.95rem;
}
.fact-list li:last-child { border-bottom: 0; }

.hours-table { width: 100%; border-collapse: collapse; }
.hours-table th, .hours-table td {
  text-align: left;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
  font-weight: 400;
  color: var(--ink-soft);
}
.hours-table th { font-weight: 600; color: var(--ink); padding-right: 1rem; }
.hours-table tr:last-child th, .hours-table tr:last-child td { border-bottom: 0; }

.province-head {
  font-size: 1.1rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-top: 2.5rem;
  padding-bottom: 0.5rem;
  border-bottom: 2px solid var(--bvd-gold);
}
.province-head:first-child { margin-top: 0; }
.province-head span { color: var(--ink-soft); font-weight: 600; }

/* Was a two-column ruled list. Now a card grid: these are the only links on
   the page and the whole card is the hit target, which matters most on
   mobile where a 0.45rem-tall text row is a poor tap target. Auto-fit rather
   than a fixed count, so Ontario's 28 and New Brunswick's 2 both look
   deliberate. */
.location-index {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 0.85rem;
}

.location-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 1.1rem 1.25rem;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
}
.location-card .loc-name {
  font-family: var(--font-head);
  font-weight: 600;
  color: var(--bvd-navy);
}
/* Arrow is decorative and grows on hover — the only motion cue that the card
   is a link, since the lift alone is easy to miss on a dense grid. */
.location-card .loc-name::after {
  content: " →";
  color: var(--bvd-gold);
  transition: opacity var(--ease);
  opacity: 0.55;
}
.location-card:hover .loc-name::after { opacity: 1; }
/* Grid rows are equal height, so a badge on one card leaves dead space at the
   foot of its neighbours. Anchoring the badge to the bottom makes that space
   read as a consistent baseline rather than as a card that failed to fill. */
.location-card .inline-badge { margin-top: auto; }

/* ---------- Footer ---------- */
.site-footer {
  background: var(--bvd-navy-dark);
  color: var(--cream);
  padding: 2.5rem 0;
  font-size: 0.85rem;
}

.site-footer .container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}

.site-footer a { color: var(--cream); }
.site-footer .footer-links { display: flex; gap: 1.5rem; }

/* The reversed lockup finally has a home — the footer was the one dark
   surface carrying no brand mark at all. */
.footer-brand { display: flex; flex-direction: column; gap: 1rem; align-items: flex-start; }
/* Reversed horizontal lockup on the navy footer. Same ratio caveat as the
   header — 42px on the stacked asset was ~100px wide; on this one it would be
   333px. 34px lands at ~269px, which still fits the 375px viewport once the
   footer centres. */
.footer-brand img { height: 34px; width: auto; display: block; }

@media (max-width: 640px) {
  .footer-brand { align-items: center; }
}

@media (max-width: 640px) {
  /* The lockup plus three nav labels is wider than the viewport below 640, so
     the header row wraps rather than hiding links: there is no burger menu, and
     three items is not enough to need one. The horizontal lockup makes this
     more necessary, not less — it is ~253px on its own at the desktop height. */
  .site-header .container {
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.6rem;
    padding-top: 0.7rem;
    padding-bottom: 0.7rem;
  }
  /* 26px → ~206px wide, comfortably inside a 375px viewport once padding is
     taken off. This has to stay BELOW the desktop 32px: the old value here was
     38px, which was a reduction from the stacked lockup's 46px but would now be
     an enlargement. */
  .brand-lockup img { height: 26px; }
  .main-nav { gap: 1.1rem; flex-wrap: wrap; justify-content: center; }
  .main-nav a { font-size: 0.85rem; }
  .site-footer .container { flex-direction: column; text-align: center; }
}

/* ---------- Section eyebrow, colour + size (deliberately last) ----------
   .section-eyebrow is (0,1,0) and loses to every element-qualified section
   rule it lands inside. Both losses were real and shipped-adjacent:

     .locations-band p  (0,1,1) -> cream on the amber pill, ~1.7:1. That is
       precisely the mistake the brand notes call out on bvdgroup.com's own
       eyebrow (white on amber) and say ours must not repeat.
     .split-lead p      (0,1,1) -> 17.6px instead of 12.8px, so the pill on the
       values section rendered 40% larger than the other two.

   Qualifying to p.section-eyebrow (0,1,1) only TIES those, so this block also
   has to sit after them in source order. Hence: last, on purpose. Adding a new
   section that styles its own <p> will re-break it unless that rule is above
   this one — if a pill ever looks wrong, check specificity here first.

   Ink on amber is 9.1:1. Amber is a fill and never a text colour. */
p.section-eyebrow {
  color: var(--ink);
  font-size: 0.8rem;
}
