/* ==========================================================================
   First Choice Logistics — Public site styles
   Depends on tokens.css. Mobile-first.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* clip, not hidden: `hidden` here silently breaks position:sticky */
  overflow-x: clip;
}

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

img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: var(--navy-700); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--gold-600); }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { padding: 0; list-style: none; }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--navy); color: #fff; padding: 12px 20px;
  border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; color: #fff; }

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

/* --- Typography ----------------------------------------------------------- */
h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  line-height: 1.15;
  font-weight: var(--fw-bold);
  color: var(--navy);
  text-wrap: balance;
}
h1 { font-size: clamp(2rem, 1.4rem + 3vw, 3.5rem); letter-spacing: -.02em; }
h2 { font-size: clamp(1.6rem, 1.2rem + 2vw, 2.5rem); letter-spacing: -.015em; }
h3 { font-size: clamp(1.2rem, 1.05rem + .8vw, 1.6rem); }
h4 { font-size: 1.125rem; }
p  { text-wrap: pretty; }
.lead { font-size: clamp(1.05rem, 1rem + .4vw, 1.25rem); color: var(--ink-muted); }
.eyebrow {
  font-family: var(--font-display); font-size: .8rem; font-weight: var(--fw-bold);
  letter-spacing: .12em; text-transform: uppercase; color: var(--gold-600);
  margin-bottom: var(--space-1);
}

/* --- Layout --------------------------------------------------------------- */
.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--space-3); }
.container--narrow { max-width: var(--maxw-narrow); }
.section { padding-block: var(--space-8); }
.section--tight { padding-block: var(--space-6); }
.section--alt { background: var(--surface-alt); }

/* --- Borders on tinted ground ----------------------------------------------
   Deepening --surface-alt had a side effect worth naming: the default border
   (#E2E8F0) was chosen against white, where it reads at about 1.18:1. On the new
   #E7EEF7 tint it drops to 1.05:1 — effectively invisible, so cards and rules
   inside tinted sections lost their outline entirely.

   Scoped rather than global: --border still works on the white sections, and
   changing it everywhere would darken outlines across the admin, portal and
   cabinet too, which are not in scope for this pass. */
.section--alt .card,
.section--alt .feature-card,
.section--alt .dest-card,
.section--alt .map-legend a,
.section--alt .service-card { border-color: #A8B6C8; }
.section--alt hr,
.stats-band,
.carrier-band { border-color: #A8B6C8; }

/* --- Section texture -------------------------------------------------------
   A very low-contrast diagonal weave over the tinted ground. This exists so the
   "Why Ship With Us" block reads as a distinct surface from the statistics strip
   directly above it, which shares the same tint. Pure CSS on purpose: a
   photographic background here would be the page's fourth photographic surface
   (after the hero and the service cards) and would need its own contrast
   testing, its own asset weight, and its own art direction for what is meant to
   be quiet texture. Roughly 2% contrast — it should read as paper, not pattern. */
.section--texture {
  background-image:
    repeating-linear-gradient(135deg,
      rgba(4,35,86,.016) 0 1px,
      transparent 1px 11px);
}

/* --- Statistics strip ------------------------------------------------------
   Hairlines top and bottom. These do most of the work of separating the strip
   from the tinted section beneath it, which carries the same background colour —
   structure separates them, not tone. */
.stats-band {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  position: relative;
}
.stats-band::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold) 30%, var(--gold) 70%, transparent);
  opacity: .5;
}

/* --- Carrier partners band -------------------------------------------------
   Was three ~34px logos at 75% opacity floating in a full-height white section,
   which read as an unfinished block. Now a compact band: a top rule that keeps
   it defined on whichever turn the alternation hands it, larger marks at an
   even optical weight, and much less vertical air. */
.carrier-band {
  border-top: 1px solid var(--border);
  padding-block: var(--space-3);
}
.carrier-band .eyebrow { margin-bottom: var(--space-2); }
.carrier-list {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: var(--space-3) var(--space-8);
  margin: 0; padding: 0; list-style: none;
}
.carrier-list li { display: flex; align-items: center; }
/* Greyscale keeps three unrelated brand palettes from fighting the navy/gold
   scheme. Opacity is .82 rather than the old .75 so the marks are legible
   rather than merely present; colour returns on hover. */
.carrier-logo {
  height: 40px; width: auto; opacity: .82;
  filter: grayscale(1);
  transition: opacity var(--dur-fast) ease, filter var(--dur-fast) ease;
}
.carrier-list li:hover .carrier-logo { opacity: 1; filter: grayscale(0); }
/* Per-mark optical balance. These three logos do not fill their canvases
   equally — MSC is a compact stacked mark, Maersk and ZIM are wide lockups — so
   a single height makes MSC look small and ZIM look oversized.

   MSC also needs its own weight, not just its own size: it is solid black
   artwork, and grayscale() leaves black as black while turning Maersk's blue
   into a mid-grey, so left alone it reads twice as heavy and pulls the eye
   off-centre.

   Balanced with opacity rather than brightness(). brightness() alters the mark's
   own colour values, which the project image brief rules out for third-party
   trademarks ("download the official versions" — recolouring a carrier's mark is
   the same category of problem as redrawing it). Opacity is a uniform display
   treatment that leaves the artwork itself untouched, and full colour returns on
   hover either way. */
.carrier-logo--msc  { height: 46px; opacity: .55; }
.carrier-logo--zim  { height: 40px; }
.carrier-list li:hover .carrier-logo--msc { opacity: 1; }
.carrier-name {
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem;
  color: var(--ink-muted); letter-spacing: .01em;   /* not --ink-faint: 2.63:1 on the tint */
}

/* --- Destination cards -----------------------------------------------------
   A gold left edge that thickens on hover, tying the cards to the network band
   above them. */
.dest-band .map-legend a { border-left: 3px solid var(--gold); }
.dest-band .map-legend a:hover,
.dest-band .map-legend a:focus-visible { border-left-width: 5px; }

.section--navy { background: var(--grad-navy); color: rgba(255,255,255,.88); }
.section--navy h1, .section--navy h2, .section--navy h3 { color: #fff; }
.section--navy .eyebrow { color: var(--gold-400); }
.section--navy .lead { color: rgba(255,255,255,.78); }

.section-head { max-width: 680px; margin-bottom: var(--space-5); }
.section-head--center { margin-inline: auto; text-align: center; }

.grid { display: grid; gap: var(--space-3); }
.grid-2, .grid-3, .grid-4 { display: grid; gap: var(--space-3); }

@media (min-width: 768px) {
  .section { padding-block: var(--space-12); }
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

/* --- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 14px 26px; border: 1px solid transparent; border-radius: var(--radius-pill);
  font-family: var(--font-display); font-weight: var(--fw-semi); font-size: .975rem;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) ease;
  min-height: 48px; /* touch target */
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--gold { background: var(--grad-gold); color: var(--navy-900); box-shadow: var(--shadow-gold); }
.btn--gold:hover { color: var(--navy-900); box-shadow: 0 12px 30px rgba(197,151,51,.4); }
.btn--navy { background: var(--navy); color: #fff; }
.btn--navy:hover { background: var(--navy-700); color: #fff; }
.btn--ghost { background: transparent; border-color: var(--border-strong); color: var(--navy); }
.btn--ghost:hover { border-color: var(--navy); background: var(--surface-alt); color: var(--navy); }
.btn--ghost-light { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.35); color: #fff; }
.btn--ghost-light:hover { background: rgba(255,255,255,.16); color: #fff; }
.btn--sm { padding: 9px 18px; font-size: .875rem; min-height: 40px; }
.btn--block { width: 100%; }

/* --- Cards & glass -------------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-3); box-shadow: var(--shadow-sm);
}

/* Glass: nav, cards, modals, tooltips ONLY. Never hero backgrounds. */
.glass {
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .glass {
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(140%);
    backdrop-filter: blur(var(--glass-blur)) saturate(140%);
    background: rgba(255,255,255,.62);
  }
}
.glass--dark {
  background: var(--glass-dark-bg); border-color: var(--glass-dark-brd); color: #fff;
}

.feature-card { position: relative; padding: var(--space-4) var(--space-3); height: 100%; }
.feature-card__icon {
  width: 52px; height: 52px; display: grid; place-items: center;
  border-radius: var(--radius-sm); background: rgba(197,151,51,.12);
  color: var(--gold-600); margin-bottom: var(--space-2);
}
.feature-card h3 { margin-bottom: 8px; }
.feature-card p { color: var(--ink-muted); font-size: .95rem; }

/* --- Top information bar ---------------------------------------------------
   The thin strip above the nav carrying the two numbers, WhatsApp, email and
   Track Shipment. Desktop only — hidden below 1024px, where the bottom tab bar
   and the floating WhatsApp button already cover the same ground and six items
   would wrap to three lines.

   HOW THE HERO CONFLICT IS SOLVED
   The site's own comment in header.php explains that a bar which scrolls away
   above a FIXED header leaves the header covering the content. True — so the
   bar is not left to scroll independently. .header-stack wraps the bar and the
   nav and IS the fixed element, so the two move as one. On scroll the bar
   collapses its own height to zero and the nav slides up to meet the top of
   the window. Nothing is ever covered, and the hero photo stays full-bleed
   behind a transparent header.
--------------------------------------------------------------------------- */
.topbar { display: none; }

@media (min-width: 1024px) {
  .topbar {
    display: block;
    background: var(--navy-900);
    color: rgba(255,255,255,.86);
    font-size: .78rem;
    line-height: 1;
    /* Height is animated, not display, so the collapse can be smooth and so
       the nav below it has something continuous to slide against. */
    height: 34px;
    overflow: hidden;
    transition: height .28s ease, opacity .22s ease;
  }
  .topbar__inner {
    display: flex; align-items: center; justify-content: center;
    gap: 18px; height: 34px;
  }
  .topbar__item {
    display: inline-flex; align-items: center; gap: 6px;
    color: rgba(255,255,255,.86); text-decoration: none; white-space: nowrap;
    transition: color .15s ease;
  }
  .topbar__item strong { color: #fff; font-weight: var(--fw-semi); }
  .topbar__item:hover,
  .topbar__item:hover strong { color: var(--gold-400); }
  .topbar__item svg { flex-shrink: 0; opacity: .8; }
  .topbar__label { opacity: .75; }
  .topbar__sep { width: 1px; height: 13px; background: rgba(255,255,255,.2); flex-shrink: 0; }

  /* Narrower desktops: drop the "Toll-free" word before dropping any link. */
  @media (max-width: 1200px) {
    .topbar__inner { gap: 13px; }
    .topbar__label { display: none; }
  }
}

/* The stack. On inner pages this is a plain sticky block and the header inside
   it behaves exactly as it did before the bar existed. */
.header-stack { position: sticky; top: 0; z-index: var(--z-nav); }

@media (min-width: 1024px) {
  /* On the homepage the STACK is fixed, not the header — that is the whole
     trick. The header inside it reverts to static so it cannot fix itself
     separately and leave the bar behind. */
  .header-stack--overlay { position: fixed; left: 0; right: 0; top: 0; }
  .header-stack--overlay .site-header--overlay { position: static; }

  /* Scrolled: the bar folds away and the nav takes the top of the window. */
  .header-stack--overlay.is-scrolled .topbar { height: 0; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .topbar { transition: none; }
}

/* --- Header / nav --------------------------------------------------------- */
.announcement {
  background: var(--navy-900); color: rgba(255,255,255,.9);
  text-align: center; font-size: .875rem; padding: 8px var(--space-2);
}
.announcement a { color: var(--gold-400); }

.site-header {
  position: sticky; top: 0; z-index: var(--z-nav);
  background: rgba(255,255,255,.86);
  border-bottom: 1px solid var(--border);
}
@supports (backdrop-filter: blur(1px)) {
  .site-header { -webkit-backdrop-filter: blur(12px) saturate(150%); backdrop-filter: blur(12px) saturate(150%); }
}
.site-header__inner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  min-height: var(--nav-h-mobile);
}
/* Logo sized generously: with the tagline removed from this lockup, the
   shield and wordmark can fill the header height and stay clearly legible. */
.brand { display: flex; align-items: center; flex-shrink: 0; }
.brand img { height: 44px; width: auto; }

.nav { display: none; }
.nav ul { display: flex; align-items: center; gap: var(--space-3); }
.nav a {
  font-family: var(--font-display); font-weight: var(--fw-medium); font-size: .95rem;
  color: var(--navy); text-decoration: none; padding: 6px 2px;
  border-bottom: 2px solid transparent;
}
.nav a:hover, .nav a.is-active { color: var(--gold-600); border-bottom-color: var(--gold); }

.header-actions { display: flex; align-items: center; gap: var(--space-2); }
.header-phone {
  display: none; align-items: center; gap: 8px;
  font-family: var(--font-display); font-weight: var(--fw-semi); color: var(--navy);
  text-decoration: none; font-size: .95rem;
}
.header-phone:hover { color: var(--gold-600); }

/* --- Header actions: WhatsApp · Get a Quote · Call -------------------------
   Three buttons that must stay legible across the header's THREE surfaces:
   transparent over the hero photo, dark navy once scrolled, and solid white on
   every inner page. Anything whose contrast depends on the background fails on
   one of them, so both new buttons carry their own fill.

   Sizing is pinned to the gold .btn--sm beside them: 38px tall, so the row
   reads as one set of controls rather than three unrelated objects. */
.header-wa,
.header-call {
  align-items: center; justify-content: center;
  height: 38px; border-radius: 999px;
  text-decoration: none; white-space: nowrap;
  font-family: var(--font-display); font-weight: var(--fw-semi); font-size: .82rem;
  transition: transform .15s ease, box-shadow .15s ease,
              background-color .15s ease, border-color .15s ease;
}
.header-wa:hover,
.header-call:hover { transform: translateY(-1px); }

/* WhatsApp — brand green, icon only. The one place a non-brand colour is
   allowed, because the green IS the recognition. */
.header-wa {
  width: 38px; padding: 0;
  background: #25D366; color: #fff;
  box-shadow: 0 4px 12px rgba(37,211,102,.30);
}
.header-wa:hover { background: #1EBE5A; color: #fff; box-shadow: 0 7px 18px rgba(37,211,102,.38); }

/* Call — navy fill, gold hairline, white label. Navy keeps the palette to the
   two brand colours; the gold border is what stops it vanishing into the navy
   header, and it echoes the gold CTA next to it rather than adding a fourth
   colour. */
.header-call {
  display: none;                       /* .hide-on-mobile turns it on at 1024px */
  gap: 7px; padding: 0 15px;
  background: var(--navy); color: #fff;
  border: 1.5px solid var(--gold);
}
.header-call svg { flex-shrink: 0; }
.header-call:hover { background: var(--navy-700); color: #fff; border-color: var(--gold-400); }
.header-call:focus-visible,
.header-wa:focus-visible { outline: 3px solid var(--gold-400); outline-offset: 3px; }

/* On a solid white header the navy fill already separates itself, so the gold
   is softened — a full-strength gold hairline on white reads as a warning. */
.site-header:not(.site-header--overlay) .header-call { border-color: rgba(197,151,51,.55); }

/* Over the photo, and on the scrolled navy bar, the button sits on a dark
   surface: lift the fill slightly so it does not merge with the header. */
.site-header--overlay .header-call { background: rgba(4,35,86,.72); }
.site-header--overlay.is-scrolled .header-call { background: rgba(2,22,52,.55); }

@media (prefers-reduced-motion: reduce) {
  .header-wa, .header-call { transition: none; }
  .header-wa:hover, .header-call:hover { transform: none; }
}

.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: transparent; cursor: pointer; color: var(--navy);
}
.mobile-nav {
  display: none; border-top: 1px solid var(--border); background: var(--surface);
  padding: var(--space-2) 0 var(--space-3);
}
.mobile-nav.is-open { display: block; }
.mobile-nav a {
  display: block; padding: 14px var(--space-3); font-family: var(--font-display);
  font-weight: var(--fw-medium); color: var(--navy); text-decoration: none;
  border-bottom: 1px solid var(--border);
}
.mobile-nav a.is-active { color: var(--gold-600); }
.mobile-nav__cta { padding: var(--space-2) var(--space-3) 0; }

@media (min-width: 1024px) {
  .site-header__inner { min-height: var(--nav-h); }
  .brand img { height: 56px; }
  .nav { display: block; }
  .header-phone { display: inline-flex; }
  .nav-toggle, .mobile-nav { display: none !important; }
}

/* --- Mobile bottom tab bar -------------------------------------------------
   The mobile primary navigation. Mobile-first: it is present by default and
   switched off at 1024px — not 768px. 1024 is where `.nav` becomes visible
   (see the header block above); hiding the bar at 768 would leave every
   viewport from 768 to 1023px with no navigation at all now that the hamburger
   is gone.

   Deliberately static — no hide-on-scroll. A bar that comes and goes forces the
   user to scroll the wrong way to get their navigation back, and on a haulage
   site the quote action must be reachable at any scroll position.

   White ground rather than navy: the header is white, and a navy slab pinned to
   the bottom of every page would read as a second, competing brand surface. It
   also lets the gold centre button carry the only strong colour on the bar.  */
:root {
  /* Bar height excluding the safe-area inset. Three separate rules depend on
     this number (the bar, the page's bottom padding, the cookie banner's
     offset), so it lives in one place. Not in tokens.css because that file is
     partly overwritten at runtime from web_settings. */
  --tabbar-h: 62px;

  /* The height anything BELOW the bar has to clear, which is not the same
     number. --tabbar-h is a min-height; the real rendered height is whatever
     the tallest column needs, and the centre column carries a 56px button
     above its label. In English every label is one line and the bar renders at
     63px. In Ukrainian "Отримати розрахунок" takes two lines and the bar
     renders at 67.4px — measured, not estimated. Page padding and the cookie
     banner key off this worst case so they are correct in both languages;
     over-reserving four pixels in English costs nothing and is invisible,
     whereas under-reserving in Ukrainian tucks live content behind the bar. */
  --tabbar-clear: 68px;
}

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-nav);
  /* minmax(0, 1fr), not a bare 1fr. A bare 1fr floors at the column's
     min-content width, so one long label steals width from the other four:
     in Ukrainian "Отримати розрахунок" measured 117px and dragged the grid to
     68/68/117/68/68 instead of five equal fifths, throwing the raised gold
     button off the centre of the bar. minmax(0, …) lets the column go narrower
     than its content and keeps the five slots identical in every language. */
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  background: var(--surface);
  border-top: 1px solid var(--border);
  /* Upward shadow so content scrolling underneath is visibly behind the bar. */
  box-shadow: 0 -2px 12px rgba(4, 35, 86, .08);
  /* Without this the bar sits under the iOS home indicator and the bottom row
     of tabs cannot be tapped. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.tabbar__item,
.tabbar__cta {
  display: flex; flex-direction: column; align-items: center;
  /* Bottom-aligned so every label sits on one line across the five columns.
     It also means the centre button, being taller than the bar, overflows
     upward on its own — no negative margin needed to raise it. */
  justify-content: flex-end;
  gap: 4px; min-height: var(--tabbar-h); padding-bottom: 8px;
  color: var(--navy); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  /* Lets the flex item shrink below its content width inside the grid column.
     Without it the label's min-content width re-imposes the same floor that
     minmax(0, 1fr) was added to remove. Kept off `overflow` on this element
     deliberately — .tabbar__cta raises its button with a negative top margin,
     and clipping here would cut the raised button off. */
  min-width: 0;
}
.tabbar__icon { color: var(--ink-muted); }
.tabbar__label {
  font-family: var(--font-display); font-weight: var(--fw-medium);
  font-size: .6875rem; line-height: 1.2; letter-spacing: .01em;
  color: var(--ink-muted);
  /* WRAPPING, NOT NOWRAP. This was white-space: nowrap, sized against
     "Get a Quote" at ~62px in a 64px column. Ukrainian breaks that assumption:
     "Отримати розрахунок" is ~117px on one line and cannot be shrunk onto one
     line at any font size a thumb can read (it would need ~7px type).
     Allowing the break puts it on two lines of ~50px and ~62px, both of which
     clear a 78px column at 390px.

     The bar does NOT get taller as a result. The item's min-height is
     var(--tabbar-h) and its content — 22px icon + 4px gap + one 13px line +
     8px padding — only came to ~47px, so the second line is absorbed by slack
     that was already there. Verified at 390/430/768/1280: bar stays 63px in
     both languages, so body's padding-bottom and the cookie banner's offset
     (both keyed to --tabbar-h) stay correct.

     text-align centres the second line under the first; overflow-wrap is the
     last resort for a long single word on a very narrow phone. */
  white-space: normal; text-align: center;
  overflow-wrap: anywhere; hyphens: none;
  /* Never wider than its own column, so a label can never be the thing that
     puts a horizontal scrollbar on the page. */
  max-width: 100%; overflow: hidden;
}

/* Active tab: colour on both icon and label, plus a weight change and a gold
   rule along the bar's top edge, so the state is not carried by hue alone. */
.tabbar__item.is-active { color: var(--gold-600); position: relative; }
.tabbar__item.is-active .tabbar__icon,
.tabbar__item.is-active .tabbar__label { color: var(--gold-600); }
.tabbar__item.is-active .tabbar__label { font-weight: var(--fw-bold); }
.tabbar__item.is-active::before {
  content: ""; position: absolute; top: -1px; left: 22%; right: 22%;
  height: 2px; background: var(--gold); border-radius: var(--radius-pill);
}

/* Centre action, raised out of the bar like a native FAB. */
.tabbar__cta-btn {
  display: grid; place-items: center;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--grad-gold); color: var(--navy-900);
  box-shadow: var(--shadow-gold);
  /* Ring in the bar's own ground colour, so the button reads as sitting in
     front of the bar rather than notched into it. */
  border: 3px solid var(--surface);
  /* Raises the button above the bar's top edge. It has to be a negative margin
     rather than free overflow: grid rows size to their content, so without it
     the tall centre column drags the whole bar up to ~82px. */
  margin-top: -28px;
}
.tabbar__cta .tabbar__label { color: var(--navy); font-weight: var(--fw-semi); }

/* Press feedback only — opacity and transform, nothing that reflows. */
.tabbar__item:active { opacity: .6; }
.tabbar__cta:active .tabbar__cta-btn { transform: scale(.94); }

/* Content sits behind a fixed bar, so the last rows of the footer would
   otherwise be unreachable. Padding goes on <body>: the bar is a sibling of
   <main> and the footer, so padding either one alone would leave the other
   underneath it. */
body { padding-bottom: calc(var(--tabbar-clear) + env(safe-area-inset-bottom, 0px)); }

/* Tablet and small laptop windows still get the bar, but stretched to five
   full-width columns it reads as an accident rather than a design, so the row
   is capped and centred while the bar itself stays edge to edge. */
@media (min-width: 768px) {
  .tabbar { grid-template-columns: repeat(5, minmax(0, 104px)); justify-content: center; }
}

/* The header's own quote button, duplicated by the tab bar's centre action
   below 1024px — the same boundary at which the desktop nav and header phone
   appear. Declared before the media query: same specificity, so source order
   decides. Restored to inline-flex rather than `revert` because the only
   element carrying the class is a .btn. */
.hide-on-mobile { display: none; }

@media (min-width: 1024px) {
  .tabbar { display: none; }
  body { padding-bottom: 0; }
  .hide-on-mobile { display: inline-flex; }
}

/* --- Overlay navigation --------------------------------------------------- */
/*
   Opt-in only: applied when a page sets $heroOverlay before including the
   header, which it should only do when the page opens with a dark hero. Every
   rule below is scoped to .site-header--overlay, so the default solid header
   used by contact, quote, track, blog and the legal pages is untouched.

   Two states on ONE element — the height, padding and logo size never change
   between them, so nothing shifts when the state flips:
     at top      transparent, sitting over the hero photograph
     scrolled    translucent navy glass
*/
.site-header--overlay {
  position: fixed; left: 0; right: 0; top: 0;
  background: transparent;
  border-bottom-color: transparent;
  /* Only the properties that actually change are transitioned. */
  transition: background-color .28s ease, border-color .28s ease, box-shadow .28s ease;
}
@supports (backdrop-filter: blur(1px)) {
  /* Cancel the blur inherited from .site-header while transparent — blurring
     nothing costs a compositor layer and slightly veils the hero. */
  .site-header--overlay {
    -webkit-backdrop-filter: none; backdrop-filter: none;
    transition: background-color .28s ease, border-color .28s ease,
                box-shadow .28s ease, backdrop-filter .28s ease;
  }
}

/* Light treatment for everything sitting on the photograph. */
.site-header--overlay .nav a,
.site-header--overlay .header-phone { color: #fff; }
.site-header--overlay .nav a:hover,
.site-header--overlay .nav a.is-active,
.site-header--overlay .header-phone:hover { color: var(--gold-400); }
.site-header--overlay .nav a.is-active { border-bottom-color: var(--gold); }
.site-header--overlay .nav-toggle {
  color: #fff; border-color: rgba(255,255,255,.42);
  background: rgba(255,255,255,.08);
}
.site-header--overlay .nav-toggle:hover { background: rgba(255,255,255,.16); }

/* Scrolled: a navy glass surface rather than a white bar. Opacity is kept
   high enough that the navigation stays readable over any photograph or
   section that happens to pass underneath — readability wins over the effect. */
.site-header--overlay.is-scrolled {
  background: rgba(4, 35, 86, .88);
  border-bottom: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 6px 28px rgba(2, 22, 52, .28);
}
@supports (backdrop-filter: blur(1px)) {
  .site-header--overlay.is-scrolled {
    /* With blur available the surface can be a little lighter and still read. */
    background: rgba(4, 35, 86, .78);
    -webkit-backdrop-filter: blur(14px) saturate(160%);
    backdrop-filter: blur(14px) saturate(160%);
  }
}

/* The mobile panel is opaque navy in both states — a translucent full-height
   menu over a photograph is where this kind of design usually becomes
   unreadable. */
.site-header--overlay .mobile-nav {
  background: var(--navy-900);
  border-top-color: rgba(255,255,255,.12);
}
.site-header--overlay .mobile-nav a {
  color: #fff; border-bottom-color: rgba(255,255,255,.10);
}
.site-header--overlay .mobile-nav a.is-active { color: var(--gold-400); }

/* Keyboard focus must stay obvious against the photograph. */
.site-header--overlay a:focus-visible,
.site-header--overlay button:focus-visible {
  outline: 2px solid var(--gold-400); outline-offset: 3px; border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .site-header--overlay { transition: none; }
}

/* --- Hero ----------------------------------------------------------------- */
/* No backdrop-filter here by design — depth comes from gradient + glow. */
.hero {
  position: relative; overflow: hidden;
  background: var(--grad-navy); color: #fff;
  padding-block: var(--space-8) var(--space-10);
}

/*
   With a fixed overlay header the header no longer occupies space in the flow,
   so the hero photograph starts at the very top of the page and continues
   behind the navigation. The hero's own top padding absorbs the nav height so
   the headline can never sit underneath it.

   This is why the nav height is a token: the padding is derived from it rather
   than being a magic number that drifts if the header ever changes size.

   The flag is a class on <body> set by PHP rather than a :has() selector —
   :has() would silently do nothing on an older browser and drop the headline
   underneath the navigation, which is exactly the failure this is meant to
   prevent.
*/
.has-overlay-nav .hero,
.has-overlay-nav main > .section--navy:first-child,
.has-overlay-nav main > .page-hero:first-child {
  padding-top: calc(var(--nav-h-mobile) + var(--space-8));
}
@media (min-width: 1024px) {
  .has-overlay-nav .hero,
  .has-overlay-nav main > .section--navy:first-child,
  .has-overlay-nav main > .page-hero:first-child {
    padding-top: calc(var(--nav-h) + var(--space-8));
  }
}
.hero::after {
  content: ""; position: absolute; inset: auto -10% -40% auto;
  width: 60vw; height: 60vw; max-width: 700px; max-height: 700px;
  background: radial-gradient(circle, rgba(197,151,51,.22) 0%, transparent 68%);
  pointer-events: none;
}

/* Applied only when a hero photo has actually been supplied.

   The photograph is the hero, not a texture behind a navy panel. The scrim
   below is deliberately light, and that is a measured decision rather than a
   taste one: this image is a dusk port scene whose text area sits at a median
   luminance of 33/255 and a 99th percentile of 69/255. White text on that
   backdrop already clears WCAG AA at about 9.5:1 with NO overlay at all.

   So the navy is doing brand and depth work, not rescue work — a brief navy
   presence at the very left edge that has effectively dissolved by the time
   the eye reaches the middle of the frame. Two gradients in one property, not
   extra elements:
     1. horizontal — the brand edge, fading out entirely by 55%
     2. vertical   — a whisper of shade under the fixed navigation, because
                     the nav band peaks around 80/255 and white nav text
                     should not have to rely on the photo staying dark there

   If this photograph is ever swapped for a brighter one, re-measure before
   assuming these values still hold. */
.hero--photo::before {
  content: ""; position: absolute; inset: 0;
  background-image:
    linear-gradient(95deg,
      rgba(4,35,86,.72) 0%,
      rgba(4,35,86,.50) 8%,
      rgba(4,35,86,.28) 20%,
      rgba(4,35,86,.13) 33%,
      rgba(4,35,86,.04) 45%,
      rgba(4,35,86,0)  55%),
    linear-gradient(180deg,
      rgba(2,22,52,.42) 0%,
      rgba(2,22,52,.12) 14%,
      rgba(2,22,52,0)   26%),
    var(--hero-img);
  background-size: cover;
  background-position: center right;
  pointer-events: none;
}

/* Insurance for the handful of specular highlights in the photograph — port
   floodlights and their reflections reach luminance 195, and a glyph landing
   on one would otherwise lose its edge. A shadow costs nothing visually and
   lets the scrim stay light, which a blanket darkening would not. */
.hero h1, .hero p, .hero .eyebrow, .hero__track label {
  text-shadow: 0 1px 3px rgba(2,22,52,.45);
}

/* Between a small laptop and a full desktop the container is narrower than the
   copy's 620px allowance, so the text runs proportionally further across the
   frame — around 50% at 1280px against 46% at 1920px. The stops are stretched
   to match, so the paragraph and tracking field never reach the bright ship. */
@media (min-width: 1025px) and (max-width: 1280px) {
  .hero--photo::before {
    background-image:
      linear-gradient(95deg,
        rgba(4,35,86,.76) 0%,
        rgba(4,35,86,.62) 12%,
        rgba(4,35,86,.48) 30%,
        rgba(4,35,86,.40) 48%,
        rgba(4,35,86,.36) 62%,
        rgba(4,35,86,.14) 74%,
        rgba(4,35,86,0)   86%),
      linear-gradient(180deg,
        rgba(2,22,52,.42) 0%,
        rgba(2,22,52,.12) 14%,
        rgba(2,22,52,0)   26%),
      var(--hero-img);
  }
}

/* Up to and including tablet landscape the copy spans most of the frame — at
   768px it reaches roughly 84% of the width. A left-to-right gradient is the
   wrong shape for that: the only way to make it work would be to darken almost
   everything, which is the very thing being removed. So below this width the
   scrim turns vertical instead, sitting evenly behind the copy while leaving
   the photograph clearly visible through it. Measured, not assumed: a
   horizontal gradient here bottomed out at 2.7:1 against white text. */
@media (max-width: 1024px) {
  .hero--photo::before {
    background-image:
      linear-gradient(180deg,
        rgba(2,22,52,.62) 0%,
        rgba(2,22,52,.50) 45%,
        rgba(4,35,86,.42) 100%),
      var(--hero-img);
  }
}
/* The hero uses a WIDER container than the rest of the page so the text sits
   closer to the left edge, leaving more of the photograph visible. */
.hero > .container { max-width: var(--maxw-wide); }
.hero__inner { position: relative; z-index: 1; max-width: 620px; }
.hero h1 { color: #fff; margin-bottom: var(--space-2); }
.hero p { color: rgba(255,255,255,.82); font-size: clamp(1.05rem,1rem + .5vw,1.25rem); margin-bottom: var(--space-4); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }

.hero__track { max-width: 480px; }
.hero__track label {
  display: block; font-size: .875rem; color: rgba(255,255,255,.72); margin-bottom: 10px;
}
/* Input and button on one line, matched heights, button never shrinks. */
.hero__track-row { display: flex; gap: 10px; align-items: stretch; }
.hero__track input {
  flex: 1 1 auto; min-width: 0;
  padding: 13px 18px; border-radius: var(--radius-pill);
  border: 1px solid rgba(255,255,255,.32); background: rgba(255,255,255,.12);
  color: #fff; font-size: .95rem; min-height: 48px;
}
.hero__track input::placeholder { color: rgba(255,255,255,.62); }
.hero__track input:focus {
  outline: none; border-color: var(--gold);
  background: rgba(255,255,255,.18); box-shadow: 0 0 0 3px rgba(197,151,51,.25);
}
.hero__track .btn { flex: 0 0 auto; padding-inline: var(--space-4); }
@media (max-width: 479px) {
  .hero__track-row { flex-direction: column; }
  .hero__track .btn { width: 100%; }
}

@media (min-width: 768px) { .hero { padding-block: var(--space-12) var(--space-16); } }

/* --- Trust stats ---------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
.stat {
  text-align: center; padding: var(--space-2) var(--space-1);
  position: relative;
}
/* Hairline separators read as a single band rather than floating numbers */
.stat + .stat::before {
  content: ""; position: absolute; left: 0; top: 20%; bottom: 20%;
  width: 1px; background: var(--border);
}
.stat__value {
  font-family: var(--font-display); font-size: clamp(1.8rem, 1.3rem + 1.8vw, 2.75rem);
  font-weight: var(--fw-black); color: var(--gold); line-height: 1;
}
.stat__label {
  font-size: .875rem; font-weight: var(--fw-semi); color: var(--navy); margin-top: 8px;
}
.stat__note {
  font-size: .78rem; color: var(--ink-muted); margin-top: 4px; line-height: 1.45;
}
.section--navy .stat__label { color: #fff; }
.section--navy .stat__note  { color: rgba(255,255,255,.6); }
.section--navy .stat + .stat::before { background: rgba(255,255,255,.14); }

/* --- Statistics marquee ---------------------------------------------------
   The five trust figures travel slowly right to left on one continuous track.

   Structure: a clipping viewport, a flex track, and one or more copies of the
   statistics group. Only ONE copy exists in the HTML — main.js clones it until
   the track is at least twice the viewport width, then animates the track by
   exactly -50%. Because the second half is identical to the first, the moment
   the animation restarts is the moment the clone is sitting precisely where
   the original began, so there is no seam and no empty gap.

   Everything below is scoped to .stats-marquee, so the .stats grid used
   elsewhere (and here, before JS runs) is untouched.                        */
.stats-band { padding-block: var(--space-5); }   /* was --space-6; tightened */

.stats-marquee {
  overflow: hidden;          /* clips the track — the page never scrolls */
  position: relative;
  /* Soft edges so statistics fade in and out rather than being sliced by a
     hard boundary. Falls back to a plain clip where masking is unsupported. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.stats-marquee__track {
  display: flex;
  width: max-content;
  will-change: transform;
}
/* Inside the track the group stops being a grid and becomes a row, so the
   statistics keep their natural widths instead of being forced into columns. */
.stats-marquee .stats {
  display: flex;
  grid-template-columns: none;   /* neutralises the grid rules below */
  flex: 0 0 auto;
  gap: 0;
}
.stats-marquee .stat {
  flex: 0 0 auto;
  min-width: 220px;
  padding-inline: var(--space-4);
}
/* The hairline separator has to come from the item itself here: the original
   `.stat + .stat` rule would drop the divider between the last item of one
   copy and the first of the next, which is exactly where the loop joins. */
.stats-marquee .stat::before {
  content: ""; position: absolute; left: 0; top: 22%; bottom: 22%;
  width: 1px; background: var(--border);
}

/* Running only once the track has been measured and cloned avoids a flash of
   half-built marquee on load. */
.stats-marquee.is-running .stats-marquee__track {
  animation: stats-scroll var(--marquee-duration, 40s) linear infinite;
}
.stats-marquee.is-running:hover .stats-marquee__track,
.stats-marquee.is-running:focus-within .stats-marquee__track {
  animation-play-state: paused;
}
@keyframes stats-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

@media (min-width: 768px) {
  .stats-marquee .stat { min-width: 260px; padding-inline: var(--space-5); }
}

/* Reduced motion: no clone is created and no animation runs (main.js checks
   the same query), so the statistics simply sit still. The mask is dropped
   too — with nothing moving, faded edges would just look like low contrast. */
@media (prefers-reduced-motion: reduce) {
  .stats-marquee {
    -webkit-mask-image: none; mask-image: none;
    overflow-x: auto;               /* still reachable if it overflows */
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .stats-marquee::-webkit-scrollbar { display: none; }
  .stats-marquee.is-running .stats-marquee__track { animation: none; }
}

/* Column counts that avoid an awkward orphan on the last row */
@media (min-width: 768px)  { .stats { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) {
  .stats--4 { grid-template-columns: repeat(4, 1fr); }
  .stats--5 { grid-template-columns: repeat(5, 1fr); }
  .stats--6 { grid-template-columns: repeat(3, 1fr); }
}
/* On the 2-column mobile layout, drop the separator at each row start */
@media (max-width: 767px) { .stat:nth-child(odd)::before { display: none; } }

/* --- Trade-lane map ------------------------------------------------------- */
.map-wrap { position: relative; }

/* --- Network ribbon --------------------------------------------------------
   The map was previously a dark rounded widget sitting on a white page. It is
   now a light band running the full width of the viewport, so the network
   reads as part of the page rather than a component dropped onto it.

   Full-bleed without horizontal overflow: the figure is pulled to the viewport
   edges using `50% - 50vw`. Note 100vw includes the scrollbar on Windows,
   which is the classic cause of a stray ~15px of horizontal scroll; the page
   already sets overflow-x: clip, which contains that safely.               */
.network-band {
  background:
    linear-gradient(180deg, var(--surface) 0%, var(--surface-alt) 42%, var(--surface) 100%);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  position: relative;
  /* The full-bleed figure inside is sized from 100vw, which on Windows
     includes the scrollbar. html/body already clip the overflow, but
     overflow-x: clip is unsupported before Safari 16 — containing it here as
     well means no browser can ever produce a horizontal page scrollbar. Both
     axes, because overflow-x alone would compute overflow-y to auto and give
     the band its own vertical scrollbar. */
  overflow: hidden;
}
/* A restrained gold hairline marks the top of the ribbon. */
.network-band::before {
  content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold) 30%, var(--gold) 70%, transparent);
  opacity: .55;
}

.map-figure {
  margin: 0;
  position: relative;
  border-radius: 0;
  border: 0;
  box-shadow: none;
  width: calc(100% + 2 * var(--space-3));
  margin-inline: calc(-1 * var(--space-3));
}
/* Scoped to the ribbon on purpose. destinations.php includes the same map
   inside an ordinary .container, where a full-bleed pull would break it out of
   the page grid and sit against the white background with no band behind it.
   There it stays a contained figure. */
@media (min-width: 1240px) {
  .network-band .map-figure { width: 100vw; margin-inline: calc(50% - 50vw); }
}
.trade-map { width: 100%; height: auto; display: block; }

/* --- Landmasses ------------------------------------------------------------
   Navy land on a pale ocean. The coastline is a translucent navy so shapes
   stay crisp against the water without a hard outline. */
.trade-map .land {
  stroke: rgba(4,35,86,.28); stroke-width: 1; stroke-linejoin: round;
}
.trade-map .land--focus { stroke: rgba(197,151,51,.55); stroke-width: 1.3; }

/* --- Shipping lanes --------------------------------------------------------
   Direction of travel is west to east: the dash offset animates negatively,
   pulling the dashes from Canada toward Europe along the path as drawn. */
.trade-map .lane-base {
  fill: none; stroke: rgba(4,35,86,.16); stroke-width: 1.6; stroke-linecap: round;
}
.trade-map .lane {
  fill: none; stroke: var(--gold-600); stroke-width: 2.2; stroke-linecap: round;
  opacity: .9; stroke-dasharray: 6 12;
  animation: laneflow 3.2s linear infinite;
}
.trade-map .lane--inland {
  stroke: rgba(4,35,86,.42); stroke-width: 1.4; stroke-dasharray: 2 6; opacity: .7;
}
@keyframes laneflow { to { stroke-dashoffset: -54; } }

/* The travelling vessel: navy with a white ring, so it stays visible over both
   the pale ocean and the navy landmasses it passes. */
.trade-map .lane-ship {
  fill: var(--navy); stroke: #fff; stroke-width: 1.4;
  filter: drop-shadow(0 1px 3px rgba(4,35,86,.4));
}

/* --- Pins ------------------------------------------------------------------ */
.trade-map .pin { cursor: pointer; }
.trade-map .pin-dot {
  fill: var(--gold); stroke: var(--navy); stroke-width: 1.8;
  transition: r var(--dur-fast) var(--ease-out);
}
.trade-map .pin-pulse {
  fill: none; stroke: var(--gold-600); stroke-width: 1.5;
  transform-origin: center; transform-box: fill-box;
  animation: pinpulse 3.4s ease-out infinite;
}
@keyframes pinpulse {
  0%   { r: 6;  opacity: .7; }
  70%  { r: 20; opacity: 0; }
  100% { r: 20; opacity: 0; }
}
.trade-map .pin:hover .pin-dot,
.trade-map .pin:focus-visible .pin-dot { r: 7.5; }
.trade-map .pin:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* Origin markers: navy core, white ring, deliberately quieter than the gold
   destinations so origin and destination are never confused. */
.trade-map .origin-dot { fill: var(--navy); stroke: #fff; stroke-width: 1.6; }

/* Hairline tying a displaced label back to its pin. Sits under the label's
   white halo, so it appears to stop cleanly at the text rather than run into
   it. */
.trade-map .pin-leader { stroke: rgba(4,35,86,.38); stroke-width: 1; }

/* Labels sit on a pale ocean now, so they carry a white halo via paint-order
   rather than relying on the old dark background for contrast. */
.trade-map .origin-label,
.trade-map .pin-label {
  font-family: var(--font-display); font-weight: var(--fw-semi);
  paint-order: stroke; stroke: rgba(255,255,255,.92); stroke-width: 3.5px;
  stroke-linejoin: round;
}
.trade-map .origin-label { font-size: 12px; fill: var(--navy); opacity: .78; }
.trade-map .pin-label    { font-size: 13.5px; fill: var(--navy); }

/* Hover/focus linking. Dimming is applied to the map as a whole, so a
   destination card below can trigger the same highlight as the pin itself. */
.trade-map.is-focusing .lane,
.trade-map.is-focusing .lane-base,
.trade-map.is-focusing .lane-ship,
.trade-map.is-focusing .pin {
  opacity: .2; transition: opacity .25s ease;
}
.trade-map.is-focusing .is-highlit { opacity: 1; }
.trade-map .is-highlit .pin-dot { r: 7.5; }

.map-caption {
  text-align: center; color: var(--ink-muted); font-size: .9rem;
  padding: var(--space-3) var(--space-3) 0;
  max-width: 60ch; margin-inline: auto;
}

/* --- Destination cards -----------------------------------------------------
   Styled as a continuation of the map rather than a separate module. */
.map-legend { margin-top: var(--space-4); }
.map-legend ul { display: grid; gap: 12px; }
.map-legend a {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 14px var(--space-3); border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border);
  text-decoration: none; color: var(--navy); font-weight: var(--fw-medium);
  transition: border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease,
              transform var(--dur-fast) var(--ease-out);
}
.map-legend a:hover,
.map-legend a:focus-visible {
  border-color: var(--gold); box-shadow: 0 6px 18px rgba(4,35,86,.10);
  transform: translateY(-2px);
}
.map-legend__name { display: flex; align-items: center; gap: 10px; }
.map-legend .transit { font-size: .85rem; color: var(--ink-muted); font-weight: 400; white-space: nowrap; }
@media (min-width: 768px)  { .map-legend ul { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1280px) { .map-legend ul { grid-template-columns: repeat(3, 1fr); } }

/* A small key, included only because origin and destination markers differ. */
.map-key {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
  padding-top: var(--space-3);
  font-size: .82rem; color: var(--ink-muted);
}
.map-key span { display: inline-flex; align-items: center; gap: 8px; }
.map-key i {
  width: 10px; height: 10px; border-radius: 50%; display: inline-block;
  border: 1.5px solid #fff; box-shadow: 0 0 0 1px rgba(4,35,86,.25);
}
.map-key .key-origin { background: var(--navy); }
.map-key .key-dest   { background: var(--gold); }
.map-key .key-route  {
  width: 22px; height: 0; border-radius: 0; box-shadow: none;
  border: 0; border-top: 2px dashed var(--gold-600);
}

/* No min-height. The viewBox is now cropped to the artwork, so the SVG's
   natural aspect ratio already gives the right height; forcing a taller box
   would only make preserveAspectRatio letterbox the dead space back in. */

/* Small screens.
   A 1000-unit wide map drawn into a 390px viewport renders at 0.39 scale, so
   a 17-unit label lands at under 7px — the six Baltic and Black Sea labels
   become an unreadable smudge, which is worse than no labels at all. Below
   768px the map therefore drops its text and works as the illustration of the
   crossing, while the destination list underneath carries every port name and
   transit time as real, tappable, screen-reader-friendly text. The pins keep
   their aria-labels, so nothing is lost to assistive tech either. */
@media (max-width: 767px) {
  .trade-map .origin-label,
  .trade-map .pin-label,
  .trade-map .pin-leader { display: none; }
  .trade-map .pin-dot   { r: 9; stroke-width: 3; }
  .trade-map .origin-dot { r: 6; stroke-width: 2.5; }
  .trade-map .lane      { stroke-width: 4; stroke-dasharray: 10 20; }
  .trade-map .lane-base { stroke-width: 3; }
  .trade-map .lane-ship { r: 6; }
  @keyframes laneflow { to { stroke-dashoffset: -90; } }
  .map-caption { font-size: .85rem; }
}

/* Reduced motion: routes and markers hold still. Every lane stays drawn, so
   the network remains fully readable — only the movement stops. */
@media (prefers-reduced-motion: reduce) {
  .trade-map .lane { animation: none; stroke-dasharray: 6 12; }
  .trade-map .pin-pulse { animation: none; opacity: .35; }
  .trade-map .lane-ship { display: none; }
}

/* --- Steps ---------------------------------------------------------------- */
.steps { counter-reset: step; display: grid; gap: var(--space-3); }
.step { position: relative; padding-left: 64px; }
.step::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 0; top: 0; width: 46px; height: 46px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--grad-gold); color: var(--navy-900);
  font-family: var(--font-display); font-weight: var(--fw-black); font-size: 1.05rem;
}
.step h3 { margin-bottom: 6px; }
.step p { color: var(--ink-muted); font-size: .95rem; }
@media (min-width: 768px) { .steps { grid-template-columns: repeat(2, 1fr); gap: var(--space-5); } }
@media (min-width: 1024px) { .steps { grid-template-columns: repeat(4, 1fr); } .step { padding-left: 0; padding-top: 60px; } .step::before { top: 0; } }

/* --- Destination cards ---------------------------------------------------- */
.dest-card {
  display: flex; flex-direction: column; gap: 10px; height: 100%;
  padding: var(--space-3); border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--border);
  text-decoration: none; color: inherit;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out), border-color var(--dur) ease;
}
.dest-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--gold); color: inherit; }
.dest-card__flag { width: 32px; height: 22px; border-radius: 3px; overflow: hidden; box-shadow: var(--shadow-sm); }
.dest-card__name { font-family: var(--font-display); font-weight: var(--fw-bold); color: var(--navy); font-size: 1.1rem; }
.dest-card__meta { font-size: .875rem; color: var(--ink-muted); }
.dest-card__link { margin-top: auto; font-weight: var(--fw-semi); color: var(--gold-600); font-size: .9rem; }

/* --- Service cards -------------------------------------------------------- */
.service-card {
  display: flex; flex-direction: column; height: 100%;
  padding: 0; overflow: hidden; text-decoration: none; color: inherit;
  transition: transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out),
              border-color var(--dur) ease;
}
.service-card:hover {
  transform: translateY(-4px); box-shadow: var(--shadow);
  border-color: var(--gold); color: inherit;
}
.service-card__media { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--surface-alt); }
.service-card__img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-slow) var(--ease-out); }
.service-card:hover .service-card__img { transform: scale(1.04); }
/* Navy wash so the photos read as one family rather than six unrelated stocks */
.service-card__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(4,35,86,.10) 0%, rgba(4,35,86,.30) 100%);
  pointer-events: none;
}
.service-card__body { padding: var(--space-3); display: flex; flex-direction: column; flex: 1; }
.service-card__body h3 { margin-bottom: 8px; }
.service-card__body p { color: var(--ink-muted); font-size: .95rem; }
/* When a photo is present the icon sits over the join, tying the two together */
.service-card--photo .feature-card__icon { margin-top: -46px; position: relative; z-index: 1;
  background: var(--surface); box-shadow: var(--shadow-sm); }

/* --- Media band (About, Loading Locations, How It Works) ------------------ */
.media-band { border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow); }
.media-band img { width: 100%; height: auto; display: block; }
.media-split { display: grid; gap: var(--space-4); align-items: center; }
@media (min-width: 900px) { .media-split { grid-template-columns: 1fr 1fr; gap: var(--space-6); } }

/* --- Testimonials --------------------------------------------------------- */
.quote-card { padding: var(--space-4) var(--space-3); height: 100%; }
.quote-card blockquote { font-size: 1.05rem; color: var(--ink); margin-bottom: var(--space-2); }
.quote-card blockquote::before { content: "\201C"; font-family: var(--font-display); font-size: 3rem; color: var(--gold); line-height: 0; vertical-align: -.35em; margin-right: 4px; }
.quote-card cite { font-style: normal; font-weight: var(--fw-semi); color: var(--navy); }
.quote-card .loc { font-size: .85rem; color: var(--ink-muted); }

/* --- Forms ---------------------------------------------------------------- */
.form { display: grid; gap: var(--space-2); }
.form-row { display: grid; gap: var(--space-2); }
@media (min-width: 768px) { .form-row--2 { grid-template-columns: repeat(2, 1fr); } }

.field label { display: block; font-weight: var(--fw-medium); font-size: .925rem; margin-bottom: 6px; color: var(--navy); }
.field .req { color: var(--danger); }
.field input, .field select, .field textarea {
  width: 100%; padding: 13px 15px; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); background: var(--surface); min-height: 48px;
  transition: border-color var(--dur-fast) ease, box-shadow var(--dur-fast) ease;
}
.field textarea { min-height: 130px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--navy-500); box-shadow: 0 0 0 3px rgba(26,92,176,.15);
}
.field .hint { font-size: .825rem; color: var(--ink-muted); margin-top: 5px; }
.field--error input, .field--error select, .field--error textarea { border-color: var(--danger); }
.field .err { font-size: .85rem; color: var(--danger); margin-top: 5px; }

.checkbox { display: flex; align-items: flex-start; gap: 10px; font-size: .925rem; }
.checkbox input { width: 20px; height: 20px; min-height: 0; margin-top: 2px; flex-shrink: 0; }

/* Honeypot — hidden from humans, visible to naive bots */
.hp-field { position: absolute !important; left: -9999px; opacity: 0; height: 0; width: 0; }

.alert { padding: 14px var(--space-2); border-radius: var(--radius-sm); font-size: .95rem; border: 1px solid; }
.alert--error   { background: var(--danger-bg);  border-color: rgba(192,57,43,.3);  color: #8C231A; }
.alert--success { background: var(--success-bg); border-color: rgba(30,142,90,.3);  color: #14603C; }
.alert--info    { background: var(--info-bg);    border-color: rgba(26,92,176,.25); color: #14407C; }
.alert--warning { background: var(--warning-bg); border-color: rgba(197,138,21,.3); color: #8A5F0B; }

/* --- Tracking result ------------------------------------------------------ */
.track-timeline { display: grid; gap: 0; margin-top: var(--space-3); }
.track-step { display: grid; grid-template-columns: 32px 1fr; gap: var(--space-2); padding-bottom: var(--space-3); position: relative; }
.track-step:not(:last-child)::before {
  content: ""; position: absolute; left: 15px; top: 26px; bottom: 0; width: 2px; background: var(--border);
}
.track-step__dot {
  width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
  background: var(--surface-alt); border: 2px solid var(--border); z-index: 1; font-size: .8rem;
}
.track-step--done .track-step__dot { background: var(--success); border-color: var(--success); color: #fff; }
.track-step--current .track-step__dot { background: var(--gold); border-color: var(--gold); color: var(--navy-900); }
.track-step--done:not(:last-child)::before { background: var(--success); }
.track-step__label { font-family: var(--font-display); font-weight: var(--fw-semi); color: var(--navy); }
.track-step__meta { font-size: .875rem; color: var(--ink-muted); }

/* --- Footer --------------------------------------------------------------- */
.site-footer { background: var(--navy-900); color: rgba(255,255,255,.72); padding-block: var(--space-8) var(--space-4); }
.site-footer h4 {
  color: #fff; font-size: .8rem; letter-spacing: .1em; text-transform: uppercase;
  margin-bottom: var(--space-2); font-family: var(--font-display);
}
.site-footer a { color: rgba(255,255,255,.72); text-decoration: none; }
.site-footer a:hover { color: var(--gold-400); }
.footer-grid { display: grid; gap: var(--space-5); }
@media (min-width: 768px) { .footer-grid { grid-template-columns: 1.5fr 1fr 1fr 1.2fr; } }

/* Sized to carry similar visual weight to the column headings beside it,
   rather than sitting as a small mark in the corner. */
.footer-brand img {
  width: 100%; max-width: 280px; height: auto;
  margin-bottom: var(--space-3);
}
@media (min-width: 768px) { .footer-brand img { max-width: 260px; } }
@media (min-width: 1280px) { .footer-brand img { max-width: 300px; } }
.footer-brand p { font-size: .925rem; line-height: 1.7; }
.footer-links li { margin-bottom: 10px; font-size: .925rem; }
.footer-contact li { display: flex; gap: 10px; margin-bottom: 12px; font-size: .925rem; }
.footer-contact svg { flex-shrink: 0; margin-top: 3px; color: var(--gold); }
/* "(toll-free)" beside the second number. The li is a flex row, so this sits as
   a third item; the gap is closed to 0 and a normal space used instead, so the
   note reads as part of the number rather than a separate column. Dimmer than
   the .72 body alpha so the number itself keeps the emphasis. */
.footer-contact__note { color: rgba(255,255,255,.5); font-size: .8rem; white-space: nowrap; margin-left: -4px; }

.social-row { display: flex; gap: 10px; margin-top: var(--space-2); }
.social-row a {
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); color: #fff;
  transition: background var(--dur-fast) ease, transform var(--dur-fast) var(--ease-out);
}
.social-row a:hover { background: var(--gold); color: var(--navy-900); transform: translateY(-2px); }

/* Countries-served flag strip */
.flag-strip { margin-top: var(--space-6); padding-top: var(--space-4); border-top: 1px solid rgba(255,255,255,.12); }
.flag-strip__title { font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: rgba(255,255,255,.55); margin-bottom: var(--space-3); text-align: center; }
.flag-strip ul { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2) var(--space-4); }
.flag-strip li { display: flex; align-items: center; gap: 10px; font-size: .9rem; color: rgba(255,255,255,.85); }
.flag-strip .flag {
  width: 30px; height: 20px; border-radius: 3px; overflow: hidden; flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(255,255,255,.18);
}

.footer-bottom {
  margin-top: var(--space-6); padding-top: var(--space-3);
  border-top: 1px solid rgba(255,255,255,.12);
  display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: space-between;
  font-size: .85rem; color: rgba(255,255,255,.5);
}
.footer-bottom ul { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* --- Cookie banner -------------------------------------------------------- */
/* Below 1024px the bottom tab bar owns the bottom of the viewport, so the
   banner is lifted clear of it — sitting on top of the bar would cover the
   navigation, and sitting under it would hide the consent buttons. Kept here
   rather than in the tab bar block so it wins the cascade against the base
   `bottom` above it. */
.cookie-banner {
  position: fixed; left: var(--space-2); right: var(--space-2);
  bottom: calc(var(--tabbar-clear) + env(safe-area-inset-bottom, 0px) + var(--space-2));
  z-index: var(--z-toast); padding: var(--space-3); display: none;
}
.cookie-banner.is-visible { display: block; }
.cookie-banner p { font-size: .9rem; margin-bottom: var(--space-2); }
.cookie-banner__actions { display: flex; flex-wrap: wrap; gap: 10px; }
@media (min-width: 768px) {
  .cookie-banner { left: auto; right: var(--space-3); max-width: 420px; }
}
/* Drops back to the viewport edge only once the tab bar is gone. */
@media (min-width: 1024px) {
  .cookie-banner { bottom: var(--space-3); }
}

/* --- Floating WhatsApp button ---------------------------------------------
   Pinned to the viewport the way the header is, so it stays put through any
   amount of scrolling. Bottom-RIGHT, clearing the mobile tab bar with the same
   --tabbar-clear the cookie banner uses so the two never drift apart.

   z-index sits below --z-toast on purpose: the cookie banner is a legal
   consent control and must never be covered by a chat button. Above --z-nav
   so ordinary page content cannot bury it. */
.wa-float {
  position: fixed;
  right: var(--space-3);
  bottom: calc(var(--tabbar-clear) + env(safe-area-inset-bottom, 0px) + var(--space-3));
  z-index: 300;                       /* > --z-nav (100), < --z-toast (500) */
  width: 58px; height: 58px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: #25D366;                /* WhatsApp brand green, not a site token */
  color: #fff;
  box-shadow: 0 6px 18px rgba(0,0,0,.28);
  transition: transform var(--dur-fast, .15s) ease, box-shadow var(--dur-fast, .15s) ease;
  -webkit-tap-highlight-color: transparent;
}
.wa-float:hover,
.wa-float:focus-visible { transform: scale(1.07); box-shadow: 0 10px 24px rgba(0,0,0,.34); color: #fff; }
.wa-float:active { transform: scale(.96); }
.wa-float:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }
.wa-float__icon { display: grid; place-items: center; line-height: 0; }

/* The "blink": a ring that expands and fades out from under the button.
   It is a sibling behind the icon rather than an animation on the button
   itself, so the button never moves and never shifts what the user is
   about to tap. */
.wa-float__pulse {
  position: absolute; inset: 0;
  border-radius: 50%;
  background: #25D366;
  opacity: .65;
  animation: wa-pulse 2.4s cubic-bezier(.25,.9,.35,1) infinite;
  z-index: -1;
}
@keyframes wa-pulse {
  0%   { transform: scale(1);    opacity: .6; }
  70%  { transform: scale(1.85); opacity: 0;  }
  100% { transform: scale(1.85); opacity: 0;  }
}
/* Once the ring has drawn attention, stop nagging while the user is reading. */
.wa-float:hover .wa-float__pulse { animation-play-state: paused; opacity: 0; }

/* A repeating pulse is exactly what vestibular-motion settings exist to stop.
   The button stays, fully functional; only the animation goes. */
@media (prefers-reduced-motion: reduce) {
  .wa-float__pulse { animation: none; opacity: 0; }
  .wa-float { transition: none; }
  .wa-float:hover, .wa-float:focus-visible { transform: none; }
}

/* Below the tab-bar breakpoint the button is slightly smaller, so it does not
   crowd a phone screen. */
@media (max-width: 480px) {
  .wa-float { width: 52px; height: 52px; right: var(--space-2); }
}

/* --- Utilities ------------------------------------------------------------ */
.text-center { text-align: center; }
.mt-1{margin-top:var(--space-1)} .mt-2{margin-top:var(--space-2)} .mt-3{margin-top:var(--space-3)}
.mt-4{margin-top:var(--space-4)} .mt-6{margin-top:var(--space-6)}
.mb-2{margin-bottom:var(--space-2)} .mb-3{margin-bottom:var(--space-3)} .mb-4{margin-bottom:var(--space-4)}
.muted { color: var(--ink-muted); }
.badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px;
  border-radius: var(--radius-pill); font-size: .8rem; font-weight: var(--fw-semi);
  background: var(--surface-alt); color: var(--ink-muted);
}
.badge--gold { background: rgba(197,151,51,.15); color: var(--gold-600); }
.badge--success { background: var(--success-bg); color: var(--success); }

/* Scroll reveal — opacity/transform only, no layout shift */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
.reveal.is-visible { opacity: 1; transform: none; }

/* --- Member Portal & Dealer Cabinet --------------------------------------- */
/* The signed-in customer areas. Everything here reuses the public tokens on
   purpose: a customer moves between the marketing site and their account, and
   the join should be invisible. Only the chrome and a plain data table are
   new — cards, buttons, badges, alerts and the tracking timeline are shared. */
.portal-body { background: var(--surface-alt); }

.portal-topbar { background: #fff; border-bottom: 1px solid var(--border); }
.portal-topbar__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); min-height: var(--nav-h-mobile); padding-block: 10px; flex-wrap: wrap;
}
.portal-topbar .brand img { width: auto; height: 40px; }
@media (min-width: 768px) {
  .portal-topbar__inner { min-height: var(--nav-h); }
  .portal-topbar .brand img { height: 48px; }
}
.portal-who { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.portal-who__name { font-weight: var(--fw-semi); color: var(--navy); font-size: .925rem; }

/* Horizontal scroll rather than a hamburger: at most eight short links, and a
   menu button would hide the one thing the customer came for. */
.portal-nav { background: var(--navy); }
.portal-nav__inner { display: flex; gap: var(--space-1); overflow-x: auto; scrollbar-width: none; }
.portal-nav__inner::-webkit-scrollbar { display: none; }
.portal-nav a {
  color: rgba(255,255,255,.78); text-decoration: none; white-space: nowrap;
  padding: 14px 14px; font-family: var(--font-display); font-weight: var(--fw-medium);
  font-size: .9rem; border-bottom: 3px solid transparent;
}
.portal-nav a:hover { color: #fff; }
.portal-nav a.is-active { color: #fff; border-bottom-color: var(--gold); }

.portal-head { margin-bottom: var(--space-4); }
.portal-head h1 { font-size: clamp(1.7rem, 1.3rem + 1.6vw, 2.4rem); }
.portal-head .lead { margin-top: 8px; }

/* Data table. The public site had no table style because it had no tables;
   this one stays light so it does not read as the dense admin grid. */
.portal-table-wrap { overflow-x: auto; }
.portal-table { width: 100%; border-collapse: collapse; min-width: 560px; }
.portal-table th, .portal-table td { padding: 14px var(--space-2); text-align: left; vertical-align: top; }
.portal-table thead th {
  font-family: var(--font-display); font-size: .78rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-muted); border-bottom: 1px solid var(--border);
}
.portal-table tbody tr { border-bottom: 1px solid var(--border); }
.portal-table tbody tr:last-child { border-bottom: 0; }
.portal-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.portal-table .mono { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }

/* Label/value pairs — used for shipment and invoice facts. */
.portal-facts { display: grid; gap: var(--space-2); }
@media (min-width: 480px) { .portal-facts { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .portal-facts { grid-template-columns: repeat(3, 1fr); } }
.portal-fact__label {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-muted);
}
.portal-fact__value { font-weight: var(--fw-semi); color: var(--navy); }

.portal-figure { display: flex; flex-direction: column; gap: 4px; }
.portal-figure__value { font-family: var(--font-display); font-size: 1.9rem; font-weight: var(--fw-bold); color: var(--navy); line-height: 1.1; }
.portal-figure__label { font-size: .875rem; color: var(--ink-muted); }

.portal-progress { height: 8px; border-radius: var(--radius-pill); background: var(--surface-sunken); overflow: hidden; }
.portal-progress__fill { height: 100%; background: var(--grad-gold); border-radius: var(--radius-pill); }

/* Never show a bare empty list: this always carries an explanation. */
.portal-empty { text-align: center; padding: var(--space-5) var(--space-2); }
.portal-empty h3 { margin-bottom: 8px; }
.portal-empty p { color: var(--ink-muted); max-width: 46ch; margin-inline: auto; }

.portal-actions { display: flex; flex-wrap: wrap; gap: 10px; }

.portal-footer { background: #fff; border-top: 1px solid var(--border); padding-block: var(--space-4); margin-top: var(--space-8); }
.portal-footer__help p { font-size: .95rem; color: var(--ink-muted); }
.portal-footer__links { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.portal-footer__links a { display: inline-flex; align-items: center; gap: 8px; font-size: .925rem; }
.portal-footer__links svg { color: var(--gold); }
.portal-footer__bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-1);
  margin-top: var(--space-3); padding-top: var(--space-2);
  border-top: 1px solid var(--border); font-size: .85rem; color: var(--ink-faint);
}

/* Sign-in / password reset — one narrow card, nothing else on the page. */
.portal-auth { min-height: 100vh; display: grid; place-items: center; padding: var(--space-3); background: var(--surface-alt); }
.portal-auth__card { width: 100%; max-width: 440px; padding: var(--space-4); }
.portal-auth__card .brand img { height: 48px; width: auto; margin: 0 auto var(--space-3); }

/* Numbered / bulleted explanations. Used wherever a page has to say what
   happens next, which in the customer areas is nearly everywhere. */
.portal-steps { display: grid; gap: 10px; padding-left: 1.2em; }
.portal-steps li { font-size: .95rem; color: var(--ink-muted); }
.portal-steps strong { color: var(--navy); }

/* Filter chips — a row of links, not a <select>, so the current filter is
   visible without opening anything. */
.portal-filters { display: flex; flex-wrap: wrap; gap: 8px; }
.portal-chip {
  display: inline-flex; align-items: center; padding: 7px 14px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: #fff; color: var(--ink-muted);
  font-size: .875rem; font-weight: var(--fw-medium); text-decoration: none;
}
.portal-chip:hover { border-color: var(--gold); color: var(--navy); }
.portal-chip.is-active { background: var(--navy); border-color: var(--navy); color: #fff; }

/* The shared vehicle field set (includes/forms/vehicle-fields.php) is written
   once for staff and dealers, so the public stylesheet has to understand the
   handful of classes it emits. These mirror admin.css rather than restyling
   the markup, which would be forking the form by other means. */
.vehicle-form fieldset.wizard-step {
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: var(--space-3); margin: 0 0 var(--space-3);
}
.vehicle-form legend {
  font-family: var(--font-display); font-weight: var(--fw-semi); color: var(--navy);
  padding-inline: 8px; font-size: 1.05rem;
}
.vehicle-form .field + .field, .vehicle-form .form-row + .field,
.vehicle-form .field + .form-row, .vehicle-form .form-row + .form-row { margin-top: var(--space-2); }
.input--mono { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; letter-spacing: .04em; }
.feedback:empty { display: none; }
.feedback { font-size: .85rem; margin-top: 5px; color: var(--ink-muted); }
@media (min-width: 768px) { .form-row--3 { grid-template-columns: repeat(3, 1fr); } }

/* --- Reduced motion ------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  /* Static map: solid routes, no travelling vessel, no pulsing pins. */
  .trade-map .lane { animation: none; stroke-dasharray: none; }
  .trade-map .lane-ship { display: none; }
  .trade-map .pin-pulse { animation: none; opacity: 0; }
}

/* --- Language switcher -----------------------------------------------------
   Markup is includes/lang-switcher.php and is owned elsewhere — a <nav> holding
   a <ul> of <a class="lang-switch__item">, the current one also carrying
   .is-active and aria-current="true". Two options, English and Українська, no
   flags. Nothing below assumes an order, a count, or a particular string, so
   adding a third locale to availableLocales() needs no CSS change.

   It is rendered TWICE — once in .header-actions and once in the footer — so
   every rule here is scoped to one of the two, and the shared skeleton (the
   flex row and the separator) is all either instance inherits.

   FONTS AND UKRAINIAN CYRILLIC. Checked before writing any of this:
   assets/fonts/ is empty apart from desktop.ini, and there is not one
   @font-face rule in this stylesheet, tokens.css, help.css, admin.css or any
   PHP template. "Manrope" and "Inter" are named in --font-display and
   --font-body but no file ever loads them, so every page is already rendering
   in the next entry on each stack — system-ui, i.e. Segoe UI on Windows, which
   covers а-я, і, ї, є and ґ in full. Nothing is missing and no override is
   needed here. It also means that IF the two families are self-hosted later,
   they must be subset with Cyrillic: browsers fall back per glyph, so a
   Latin-only Manrope would leave "Українська" in Segoe UI beside a Manrope
   "English" in the same control — visibly two typefaces in one switcher.
   That is a job for the @font-face rules, not for this block. */

.lang-switch ul { display: flex; align-items: center; }
.lang-switch li { display: flex; align-items: center; }
.lang-switch__item {
  font-family: var(--font-display);
  text-decoration: none;
  /* The names are proper nouns in two scripts; breaking "Українська" across
     lines inside a header row would be worse than a slightly wider control. */
  white-space: nowrap;
  /* Reserved on every item, transparent until active, so switching language
     does not move the other option by two pixels. */
  border-bottom: 2px solid transparent;
  transition: color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}

/* Separator. content is an EMPTY string with a background, not a "|" or a
   "/" character: generated text content is exposed to the accessibility tree
   by several screen readers, and a pipe announced between two language names
   is noise. An empty box painted with a background colour has nothing to
   announce, while still drawing the same hairline. Decorative by construction,
   not by an aria attribute we cannot put on a pseudo-element anyway. */
.lang-switch li:not(:last-child)::after {
  content: "";
  width: 1px; height: .85em;
  margin-inline: var(--space-1);
  background: currentColor; opacity: .35;
}

/* --- Header instance: a utility control, deliberately quiet ---------------- */
/*
   It sits in .header-actions, which is a flex row. `order: -1` puts it BEFORE
   the gold "Get a Quote" button in the visual row while leaving the DOM order
   alone: the CTA stays the last thing in the header for tab order and for
   anyone reading the source, and the button keeps the outer edge where a
   primary action belongs. Muted ink and display type one step down from the
   nav keep it from reading as a third navigation item.

   BELOW 1024px it stays in the header, visible, and this is the deliberate
   part. The bottom tab bar owns mobile navigation but has exactly five fixed
   slots and no room for a sixth; the footer copy is a full page-scroll away;
   and language is the one control a visitor may need before they can read
   anything else on the page. It does not crowd anything, because at that
   width .header-actions is empty apart from this: .header-phone and the
   .hide-on-mobile quote button are both switched off until 1024px, and the
   header row is brand + switcher with the whole middle of the bar spare
   (measured 88px of clear space between the two at 390px, 128px at 430px, and
   still 18px at 320px). The tap targets are padded to a
   comfortable height on mobile and tightened at 1024px, where the control is
   sharing the row with the phone number and the CTA and should recede.
*/
/* The `color` here is not for the links — each one sets its own — it is what
   currentColor resolves to in the li::after separator. Left unset, the li
   inherits --ink from <body> and paints a near-black hairline, which is
   invisible on the overlay header's dark hero. */
.site-header .lang-switch { order: -1; flex-shrink: 0; color: var(--ink-muted); }
.site-header .lang-switch__item {
  font-size: .8125rem;
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
  /* Generous block padding on mobile only — this is a thumb target there and a
     pointer target on desktop. */
  padding: 10px 2px;
}
.site-header .lang-switch__item:hover { color: var(--navy); }

/* Active state carries THREE cues, never colour alone: a darker ink, a heavier
   weight, and a gold rule beneath. Any one of them survives greyscale, a
   low-contrast display or a colour vision deficiency. */
.site-header .lang-switch__item.is-active {
  color: var(--navy);
  font-weight: var(--fw-bold);
  border-bottom-color: var(--gold);
}

/* --- The 1024-1279 squeeze -------------------------------------------------
   Measured, in Ukrainian, at 1024px: brand 149 + six-item nav 556 + switcher
   136 + CTA 184 + three 16px gaps = 1073px inside a 976px container. The nav
   is the flex item that gives, so it shrank to 481px and the longer links
   ("Як це працює", "Про нас") broke onto a second line inside their own <li>,
   leaving a ragged two-height nav row. English fits at its natural size and
   never showed it.

   Fixed by compacting the whole header row for this one band and restoring it
   at 1280, where Ukrainian fits unaided (measured 1073px inside 1200px). The
   nav links are also pinned to one line: a link that cannot fit should make
   the row visibly tight, not silently hyphenate a navigation label.

   Note this does slightly tighten the ENGLISH header between 1024 and 1279 as
   well — nav type from 15.2px to 14px and the gaps in by 8px. Applying it
   unconditionally was the deliberate choice: one code path, and the header
   cannot be laid out differently per locale from CSS anyway. */
@media (min-width: 1024px) {
  .site-header__inner { gap: var(--space-1); }
  .nav ul { gap: var(--space-2); }
  .nav a { font-size: .875rem; white-space: nowrap; }
  .header-actions .btn--sm { padding-inline: 14px; }
  .site-header .lang-switch__item { font-size: .75rem; padding: 4px 2px; }
}

@media (min-width: 1280px) {
  .site-header__inner { gap: var(--space-2); }
  .nav ul { gap: var(--space-3); }
  .nav a { font-size: .95rem; }
  .header-actions .btn--sm { padding-inline: 18px; }
  .site-header .lang-switch__item { font-size: .8125rem; }
}

/* Over a dark hero the header is transparent, so the switcher has to invert
   with it. Same three cues, re-pitched: white ink instead of navy, and the
   same gold rule, which reads on both grounds. */
.site-header--overlay .lang-switch { color: rgba(255,255,255,.72); }
.site-header--overlay .lang-switch__item { color: rgba(255,255,255,.72); }
.site-header--overlay .lang-switch__item:hover { color: #fff; }
.site-header--overlay .lang-switch__item.is-active {
  color: #fff;
  border-bottom-color: var(--gold);
}

/* --- Footer instance ------------------------------------------------------
   It is rendered into .footer-bottom, the flex row that already carries the
   copyright line and the legal links, so it takes no margin of its own — that
   row's own gap and space-between placement position it, and a margin-top here
   would knock it out of line with the two items beside it.

   Roomier than the header copy at .875rem: a step up from the .85rem legal
   links it sits among, because changing language matters more than a link to
   the cookie policy, but still short of the .925rem link columns above so it
   does not read as a fifth column that has slipped down the page. */
.site-footer .lang-switch { color: rgba(255,255,255,.55); }
.site-footer .lang-switch__item {
  font-size: .875rem;
  font-weight: var(--fw-normal);
  color: rgba(255,255,255,.72);
  padding: 6px 2px;
}
.site-footer .lang-switch__item:hover { color: var(--gold-400); }
.site-footer .lang-switch__item.is-active {
  color: #fff;
  font-weight: var(--fw-semi);
  border-bottom-color: var(--gold);
}

/* --- Print ---------------------------------------------------------------- */
@media print {
  .site-header, .site-footer, .cookie-banner, .hero__actions, .nav-toggle,
  .tabbar, .lang-switch { display: none !important; }
  body { padding-bottom: 0; }
  body { color: #000; }
}
