/* ==========================================================================
   NORVA LOGISTICS LLC — Design System (light)
   Brand kit:  Navy #11274a · Lime #c3d82e · Off-white #f7f9fa
               Wordmark: Norvas Expanded · Lockup subtitle: Montserrat
   --------------------------------------------------------------------------
   Surfaces use rounded corners with real 1px borders and real box-shadows.
   The logo's 45-degree geometry lives in the ACCENTS instead — chevron list
   marks, the eyebrow tick, the nav indicator, the diamond map nodes.
   --------------------------------------------------------------------------
   0.  Fonts & tokens        8.  Cards, service tiles, lists
   1.  Reset & base          9.  Process, rail, figures
   2.  Layout primitives     10. Forms
   3.  Typography            11. Footer
   4.  Buttons & links       12. Motion
   5.  Header / navigation   13. Utilities
   6.  Cinematic hero        14. Responsive
   7.  Shared components     15. WhatsApp button / 16. Print
   ========================================================================== */

/* ------------------------------------------------------ 0. FONTS & TOKENS */
@font-face {
  font-family: "Norvas";
  src: url("../fonts/Norvas-Expanded.ttf") format("truetype");
  font-weight: 400 700;
  font-display: swap;
}

:root {
  /* Brand primitives */
  --brand-navy:  #11274a;
  --brand-lime:  #c3d82e;
  --brand-white: #f7f9fa;

  /* Paper */
  --bg:   #ffffff;
  --bg-2: #f5f8fb;
  --bg-3: #eaeff6;

  /* Ink */
  --text:   #11274a;
  --text-2: #40567a;
  --muted:  #64789a;
  --faint:  #8d9db8;
  --on-navy:      #f2f6fb;
  --on-navy-soft: #b9c8de;

  /* Lines */
  --line:      rgba(17, 39, 74, .11);
  --line-2:    rgba(17, 39, 74, .20);
  --line-soft: rgba(17, 39, 74, .06);

  /* Accent — lime is a FILL colour, never a text colour on white */
  --accent:      #c3d82e;
  --accent-2:    #d3e64d;
  --accent-deep: #7f9410;
  --accent-ink:  #11274a;
  --accent-wash: rgba(195, 216, 46, .16);

  --grad-line: linear-gradient(90deg, transparent, var(--line-2) 18%, var(--line-2) 82%, transparent);

  /* Fluid type scale */
  --fs-h1:    clamp(2.05rem, 1.25rem + 3.2vw, 3.6rem);
  --fs-h2:    clamp(1.7rem, 1.2rem + 2.2vw, 2.8rem);
  --fs-h3:    clamp(1.22rem, 1.05rem + .8vw, 1.62rem);
  --fs-h4:    clamp(1.04rem, .98rem + .3vw, 1.2rem);
  --fs-lead:  clamp(1.02rem, .96rem + .34vw, 1.22rem);
  --fs-body:  1rem;
  --fs-sm:    .9375rem;
  --fs-xs:    .8125rem;
  --fs-micro: .6875rem;

  /* Space */
  --gutter:    clamp(1.25rem, .6rem + 2.4vw, 2.75rem);
  --sec-y:     clamp(3.75rem, 2.4rem + 5.5vw, 7.5rem);
  --maxw:      1240px;
  --maxw-text: 68ch;
  /* The header bar runs wider than the reading shell — at 1240px the logo
     and nav sit marooned in the middle of a large display. */
  --maxw-bar:  1660px;

  /* Radii */
  --r-xs:   6px;
  --r-sm:   10px;
  --r:      14px;
  --r-lg:   20px;
  --r-xl:   28px;
  --r-pill: 999px;

  /* Elevation */
  --sh-1: 0 1px 2px rgba(17, 39, 74, .05), 0 8px 20px -10px rgba(17, 39, 74, .14);
  --sh-2: 0 2px 6px rgba(17, 39, 74, .07), 0 22px 44px -18px rgba(17, 39, 74, .22);
  --sh-accent: 0 6px 16px -4px rgba(147, 168, 20, .45);

  /* Motion */
  --ease:     cubic-bezier(.22, .61, .36, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  --font-brand:   "Norvas", "Montserrat", "Segoe UI", system-ui, sans-serif;
  --font-display: "Montserrat", "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-body:    "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
}

/* Chevron lifted from the mark — the brand's angular DNA lives here now */
.chev {
  width: 22px;
  height: 8px;
  flex: none;
  background: var(--accent);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 38% 100%);
}

/* -------------------------------------------------------- 1. RESET & BASE */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(760px 520px at 88% -6%, rgba(195, 216, 46, .18), transparent 62%),
    radial-gradient(820px 620px at -6% 4%, rgba(17, 39, 74, .06), transparent 60%);
}
body > * { position: relative; z-index: 1; }

img, svg { display: block; max-width: 100%; }
svg { height: auto; }
a { color: inherit; text-decoration: none; }

h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -.022em;
  margin: 0;
  color: var(--text);
  text-wrap: balance;
}

p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--accent); color: var(--accent-ink); }
:focus-visible { outline: 2px solid var(--brand-navy); outline-offset: 3px; border-radius: 4px; }

.skip-link {
  position: absolute;
  left: 1rem;
  top: -5rem;
  z-index: 999;
  padding: .7rem 1.15rem;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  transition: top .25s var(--ease);
}
.skip-link:focus { top: 1rem; }

/* ----------------------------------------------------- 2. LAYOUT PRIMITIVES */
.shell {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--sec-y); position: relative; }
.section--tight { padding-block: clamp(2.75rem, 1.8rem + 3.4vw, 5rem); }
.section--pull  { padding-top: 0; }
.section--tint  { background: var(--bg-2); }
/* runs to the header's width rather than the reading measure */
.section--wide .shell { max-width: var(--maxw-bar); }

/* --- fixed photographic backdrop -------------------------------------
   The image stays pinned to the viewport while the section scrolls over it.
   clip-path on the section makes it the containing block for the fixed
   child, so the plate is clipped to the section but not scrolled with it —
   which is what `background-attachment: fixed` is meant to do, without its
   long-standing breakage on iOS.

   The veil is doing real work: this is a light section with navy headings
   and muted body copy, and the photograph underneath is high-contrast. */
.section--plate {
  position: relative;
  isolation: isolate;
  clip-path: inset(0);
}
.section--plate::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background-size: cover;
  background-position: center;
}
/* Negative z-index paints above the section's own background but below its
   content, so the navy field shows through wherever the veil is thin. */
.section--plate::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: rgba(9, 20, 40, .82);
}

/* --- which photograph, per section --- */
.plate--port::before {
  background-image: url("../img/backgrounds/background.jpg");
  background-image: image-set(
    url("../img/backgrounds/background.webp") type("image/webp"),
    url("../img/backgrounds/background.jpg") type("image/jpeg"));
}
.plate--yard::before {
  background-image: url("../img/hero/container-desktop.jpg");
  background-image: image-set(
    url("../img/hero/container-desktop.webp") type("image/webp"),
    url("../img/hero/container-desktop.jpg") type("image/jpeg"));
}
.plate--ship::before {
  background-image: url("../img/hero/ship-desktop.jpg");
  background-image: image-set(
    url("../img/hero/ship-desktop.webp") type("image/webp"),
    url("../img/hero/ship-desktop.jpg") type("image/jpeg"));
}
/* these scenes have portrait crops, so tall viewports get the right framing
   instead of a hard centre-crop of the landscape one */
@media (max-aspect-ratio: 1/1) {
  .plate--yard::before {
    background-image: url("../img/hero/container-mobile.jpg");
    background-image: image-set(
      url("../img/hero/container-mobile.webp") type("image/webp"),
      url("../img/hero/container-mobile.jpg") type("image/jpeg"));
  }
  .plate--ship::before {
    background-image: url("../img/hero/ship-mobile.jpg");
    background-image: image-set(
      url("../img/hero/ship-mobile.webp") type("image/webp"),
      url("../img/hero/ship-mobile.jpg") type("image/jpeg"));
  }
}
/* a pinned backdrop is a motion effect — let it scroll with the section */
@media (prefers-reduced-motion: reduce) {
  .section--plate::before { position: absolute; }
}

/* Inverted section. Set as a block of navy so it reads as a hard band between
   two white sections — the page's only full-bleed colour field. */
.section--navy { background: var(--brand-navy); color: var(--on-navy); }
/* headings default to navy ink, which is invisible here — reset by element
   as well as by class, since not every heading carries a .h2/.h3 */
.section--navy h2, .section--navy h3, .section--navy h4,
.section--navy .h2, .section--navy .h3, .section--navy .h4 { color: #fff; }
.section--navy .eyebrow { color: rgba(255, 255, 255, .72); }
.section--navy .lead { color: var(--on-navy-soft); }
.section--navy .section-head--split { border-bottom-color: rgba(255, 255, 255, .22); }

.rule { height: 1px; border: 0; margin: 0; background: var(--grad-line); }

.grid { display: grid; gap: clamp(1rem, .5rem + 1.4vw, 1.75rem); }
.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: start;
}

/* --------------------------------------------------------- 3. TYPOGRAPHY */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .65rem;
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1rem;
}
.eyebrow::before {
  content: "";
  width: 22px; height: 8px;
  flex: none;
  background: var(--accent);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 38% 100%);
}
.eyebrow--plain::before { display: none; }

.h1 { font-size: var(--fs-h1); }
.h2 { font-size: var(--fs-h2); }
.h3 { font-size: var(--fs-h3); }
.h4 { font-size: var(--fs-h4); letter-spacing: -.01em; }

.lead  { font-size: var(--fs-lead); line-height: 1.6; color: var(--text-2); max-width: var(--maxw-text); }

/* Body copy set in two columns. On a wide bar a single run of prose reaches
   140 characters a line, which is roughly twice a comfortable measure. */
.prose-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.25rem, .8rem + 2vw, 3rem);
  color: var(--text-2);
  line-height: 1.7;
}
.body  { color: var(--text-2); max-width: var(--maxw-text); }
.small { font-size: var(--fs-sm); color: var(--muted); }
.xs    { font-size: var(--fs-xs); color: var(--faint); }

/* Lime is too light for text on white — highlight behind the word instead */
.hl {
  background: linear-gradient(180deg, transparent 58%, var(--accent) 58%, var(--accent) 94%, transparent 94%);
  padding-inline: .04em;
}

.section-head { max-width: 760px; margin-bottom: clamp(2rem, 1.4rem + 2.2vw, 3.25rem); }

/* Editorial variant: title left, supporting line right, hairline rule beneath.
   Suits the wide container — a stacked head leaves the right two-thirds empty. */
.section-head--split {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(1.25rem, .8rem + 2vw, 3.5rem);
  align-items: end;
  max-width: none;
  padding-bottom: clamp(1.25rem, .9rem + 1.4vw, 2rem);
  border-bottom: 1px solid var(--line-2);
}
/* Bottom alignment assumes the right column is a single supporting line. When
   it carries a stack -- a lead with a tag row under it -- matching the two
   baselines drops the title far below the copy it belongs with, so these heads
   align from the top instead. */
.section-head--split.section-head--top { align-items: start; }
.section-head--split.section-head--top .lead { padding-bottom: 0; }
.section-head--split .h2 { line-height: 1.05; }
.section-head--split .lead { margin: 0; padding-bottom: .2rem; }
.section-head .lead { margin-top: 1.05rem; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .lead { margin-inline: auto; }
.section-head--center .eyebrow::before { display: none; }

/* ------------------------------------------------------ 4. BUTTONS & LINKS */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-bd: var(--line-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  padding: .88rem 1.7rem;
  border: 1.5px solid var(--btn-bd);
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: transform .25s var(--ease), background .25s var(--ease),
              border-color .25s var(--ease), box-shadow .25s var(--ease), color .25s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-ink);
  --btn-bd: var(--accent);
  font-weight: 700;
  box-shadow: var(--sh-accent);
}
.btn--primary:hover { --btn-bg: var(--accent-2); --btn-bd: var(--accent-2); box-shadow: 0 10px 24px -6px rgba(147, 168, 20, .5); }

.btn--navy { --btn-bg: var(--brand-navy); --btn-fg: #fff; --btn-bd: var(--brand-navy); box-shadow: var(--sh-1); }
.btn--navy:hover { --btn-bg: #17325c; --btn-bd: #17325c; }

.btn--ghost { --btn-bg: var(--bg); --btn-bd: var(--line-2); }
.btn--ghost:hover { --btn-bg: var(--bg-2); --btn-bd: var(--brand-navy); }

.btn--onnavy { --btn-bg: transparent; --btn-fg: #fff; --btn-bd: rgba(255, 255, 255, .4); }
.btn--onnavy:hover { --btn-bg: rgba(255, 255, 255, .12); --btn-bd: rgba(255, 255, 255, .7); }

/* square-cornered variant, to sit with the hard-edged sections */
.btn--sharp { border-radius: 0; }

.btn--lg { padding: 1.02rem 2.05rem; font-size: var(--fs-body); }
.btn--sm { padding: .58rem 1.15rem; font-size: var(--fs-xs); }
.btn--block { width: 100%; }

.btn .arw { transition: transform .3s var(--ease); }
.btn:hover .arw { transform: translateX(4px); }

.btn-row { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; }

.tlink {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
  padding-bottom: 2px;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 2px;
  transition: color .25s var(--ease);
}
/* The underline holds its weight on hover — it used to grow to 8px, which
   reads as a highlighter swipe on a standalone link. The colour shift and the
   arrow carry the hover instead; links without an arrow still respond. */
.tlink:hover { color: var(--accent-deep); }
.tlink .arw { transition: transform .3s var(--ease); }
.tlink:hover .arw { transform: translateX(3px); }

/* -------------------------------------------------- 5. HEADER / NAVIGATION */
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  padding-block: .85rem;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: padding .35s var(--ease), background .35s var(--ease),
              border-color .35s var(--ease), box-shadow .35s var(--ease);
}
.header.is-stuck {
  padding-block: .5rem;
  background: rgba(255, 255, 255, .85);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  backdrop-filter: blur(16px) saturate(160%);
  border-bottom-color: var(--line);
  box-shadow: 0 10px 30px -22px rgba(17, 39, 74, .5);
}

/* The bar spans wider than the content shell below it */
.header .shell { max-width: var(--maxw-bar); }

.header__inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; }

.brand { display: inline-flex; align-items: center; }
.brand__lockup {
  height: clamp(32px, 28px + .8vw, 42px);
  width: auto;
  transition: height .35s var(--ease);
}
.header.is-stuck .brand__lockup { height: clamp(29px, 26px + .6vw, 36px); }

.nav { display: flex; align-items: center; gap: .15rem; }
.nav a {
  position: relative;
  padding: .5rem .95rem;
  border-radius: var(--r-pill);
  /* Norvas Expanded, same face as the wordmark. It is a wide display cut,
     so it runs a step smaller and tighter than the body sans would. */
  font-family: var(--font-brand);
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: .01em;
  color: var(--text-2);
  transition: color .25s var(--ease);
}
/* Hover rule: an underline that wipes in from the left, inset to the text
   rather than the padding box. ::after is already taken by the current-page
   marker, so this sits on ::before. */
.nav a::before {
  content: "";
  position: absolute;
  left: .95rem;
  right: .95rem;
  bottom: .3rem;
  height: 2px;
  /* same lime as the Contact Us button, so the bar has one accent colour */
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .32s var(--ease-out);
}
.nav a:hover { color: var(--text); }
.nav a:hover::before, .nav a:focus-visible::before { transform: scaleX(1); }
.nav a[aria-current="page"] { color: var(--text); font-weight: 600; }
.nav a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: .02rem;
  transform: translateX(-50%);
  width: 14px; height: 6px;
  background: var(--accent);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 38% 100%);
}
.nav .btn { display: none; }
.nav a.btn { color: var(--btn-fg); }

.header__actions { display: flex; align-items: center; gap: .75rem; }

/* Header CTA — square corners against a bar of otherwise pill shapes, so it
   reads as the one hard edge in the row. Lime holds up both over the hero
   photograph and against white once the bar sticks. */
.header__cta {
  border-radius: 0;
  /* wide and low — a letterbox against the pills, not another lozenge */
  padding: .58rem 1rem;
  min-width: clamp(150px, 11vw, 190px);
  font-family: var(--font-brand);
  font-size: var(--fs-xs);
  letter-spacing: .03em;
  text-transform: uppercase;
}

.nav-toggle {
  display: none;
  width: 44px; height: 44px;
  border: 1.5px solid var(--line-2);
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .7);
  cursor: pointer;
  padding: 0;
  place-items: center;
  /* Without this the three auto rows stretch to fill the 44px button, putting
     the bars 15.7px apart instead of 7px — so the translateY(±7px) in the open
     state stopped short and the X rendered as a chevron. */
  align-content: center;
}
.nav-toggle span {
  display: block;
  width: 18px; height: 2px;
  border-radius: 2px;
  background: var(--text);
  transition: transform .32s var(--ease), opacity .2s var(--ease);
}
.nav-toggle span + span { margin-top: 5px; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ------------------------------------------------------ 6. CINEMATIC HERO
   One full screen of photography and nothing else. The plate is an <img>
   rather than a background so the browser can pick the right crop, load it
   at high priority and treat it as the LCP element. */
.hero {
  position: relative;
  isolation: isolate;
  /* svh tracks the collapsing mobile URL bar; dvh would resize mid-scroll */
  height: 100svh;
  min-height: 480px;
  overflow: hidden;
  background: #060d18;
  color: #fff;
}

/* --- the plate ---------------------------------------------------- */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
}

/* Cross-fade with no travel. Only the incoming plate animates: it fades up
   on a higher layer while the outgoing one sits fully opaque underneath,
   and is only dropped once the new plate is solid. Fading both at once is
   what causes the classic mid-dissolve flash — at the halfway point neither
   is opaque and the dark section background shows through the pair. */
.hero__slide {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transition: opacity 1s linear;
  will-change: opacity;
}
.hero__slide.is-active { z-index: 2; opacity: 1; }
/* held at full opacity underneath until the incoming plate has arrived */
.hero__slide.is-out { z-index: 1; opacity: 1; transition: none; }

.hero__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
}
/* The drift runs on every plate at once, continuously, rather than starting
   over on whichever slide is showing. That is what keeps the change smooth:
   both plates are always at the identical scale, so a cross-fade blends two
   frames of the same size. Restarting it per slide meant the incoming plate
   was smaller than the outgoing one mid-fade, and removing it from the
   outgoing plate snapped that image back to 1.0 — read as a zoom-out at the
   exact moment the change began. */
.hero__slide img { animation: plateDrift 30s ease-in-out infinite alternate; }
/* Three full-screen images being scaled forever is real compositor work on
   every frame. Once the hero has scrolled away it buys nothing, so the script
   parks it — all plates together, which keeps them in phase. */
.hero.is-offscreen .hero__slide img { animation-play-state: paused; }
@keyframes plateDrift {
  from { transform: scale(1.02); }
  to   { transform: scale(1.09); }
}

/* --- slide rail ----------------------------------------------------
   Vertical bars rather than dots, pinned to the right edge and centred on
   the plate. The active bar fills top to bottom over the dwell, so the rail
   doubles as a timer for the slide you are on. */
.hero__dots {
  position: absolute;
  right: clamp(.9rem, .4rem + 1.4vw, 1.85rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  /* A tight shadow rather than a soft one: the rail has to separate from a
     pale sky as well as a dark hull, and a wide blur washes out on the sky. */
  filter: drop-shadow(0 0 3px rgba(6, 13, 24, .8));
}
.hero__dot {
  position: relative;
  /* the button is the tap target; the bar inside it is what you see */
  width: 26px;
  height: 42px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.hero__dot::before,
.hero__dot::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4px;
  height: 34px;
  margin: -17px 0 0 -2px;
  border-radius: 999px;
}
/* The track runs near-solid white so the navy fill reads against the track
   itself rather than against the photograph — at lower track opacity the fill
   sinks into the darker plates and only shows up against sky. */
.hero__dot::before {
  background: rgba(255, 255, 255, .85);
  transition: background .35s var(--ease);
}
.hero__dot:hover::before { background: #fff; }
/* the fill — brand navy */
.hero__dot::after {
  background: var(--brand-navy);
  transform: scaleY(0);
  transform-origin: top center;
}
/* duration tracks DWELL in app.js */
.hero__dot.is-active::after { animation: dotFill 3s linear forwards; }
@keyframes dotFill {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* Two jobs: keep the transparent header's white logo and nav legible against
   the sky, and lay a graded falloff up from the foot for the headline to sit
   on. Weighted to the bottom-left, the way an ND grad would be. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(6, 13, 24, .55) 0%, rgba(6, 13, 24, .22) 12%, transparent 26%),
    linear-gradient(0deg, rgba(6, 13, 24, .93) 0%, rgba(6, 13, 24, .78) 14%, rgba(6, 13, 24, .45) 30%, rgba(6, 13, 24, .14) 48%, transparent 64%),
    linear-gradient(90deg, rgba(6, 13, 24, .6) 0%, rgba(6, 13, 24, .22) 34%, transparent 58%);
}

/* --- headline ------------------------------------------------------ */
.hero__copy {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 1;
  width: 100%;
  max-width: var(--maxw-bar);
  margin-inline: auto;
  padding: 0 var(--gutter) clamp(1.9rem, 1.2rem + 2.4vw, 3.4rem);
}

/* Norvas Expanded — the wordmark's own face. A wide cut that sets as caps,
   so it wants more leading and a shorter measure than a text face. */
.hero__title {
  font-family: var(--font-brand);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: -.005em;
  color: #fff;
  /* the scrim does most of the work; this holds the strokes over the
     brighter patches of tarmac */
  text-shadow: 0 2px 28px rgba(6, 13, 24, .7);
  animation: heroRise 1.1s var(--ease-out) both;
  animation-delay: .15s;
}
@keyframes heroRise {
  from { opacity: 0; transform: translateY(1.1rem); }
  to   { opacity: 1; transform: none; }
}

.hero__lede {
  display: block;
  /* "A logistics partner for" measures 18.1x its font-size in this face, so
     4.8vw is the largest size that still fits gutter-to-gutter on one line.
     The cap only bites below ~490px; above that the clamp wins. */
  font-size: min(clamp(1.4rem, .95rem + 1.7vw, 2.7rem), 4.8vw);
  color: rgba(255, 255, 255, .88);
  letter-spacing: .02em;
}

/* --- rotating service line ------------------------------------------
   One line tall with the words stacked inside it, so the block never
   reflows as the phrases change length. */
.hero__rotator {
  display: block;
  position: relative;
  height: 1.14em;
  margin-top: .28em;
  /* "Customs clearance" — the longest phrase — measures 15x its font-size in
     this face, so it fits the gutter-to-gutter width at 100vw/15 minus the
     two gutters. That cap only bites below ~700px; above it the clamp wins. */
  font-size: min(clamp(2.05rem, 1.1rem + 3.3vw, 4.15rem), calc(6.4vw - 3px));
  overflow: hidden;
}
.hero__word {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  color: var(--accent);
  opacity: 0;
  transform: translateY(100%);
  transition: opacity .5s var(--ease), transform .55s var(--ease-out);
}
.hero__word.is-active { opacity: 1; transform: none; }
/* the outgoing word clears upward while the next one rises into its place */
.hero__word.is-out { opacity: 0; transform: translateY(-100%); transition-duration: .45s; }

/* Lime as a text colour, which the rest of the site forbids — but that rule
   is about lime on white, where it fails contrast. Over the hero's darkened
   plate it is the same accent the rotating service word already uses. */
.hero__sub-accent { color: var(--accent); }

.hero__sub {
  margin-top: clamp(1.35rem, .95rem + 1.5vw, 2.6rem);
  max-width: 64ch;
  font-size: clamp(.95rem, .9rem + .25vw, 1.1rem);
  line-height: 1.55;
  color: rgba(255, 255, 255, .8);
  text-shadow: 0 1px 18px rgba(6, 13, 24, .6);
  animation: heroRise 1.1s var(--ease-out) both;
  animation-delay: .3s;
}

/* --- header over the plate ------------------------------------------ */
.header--overlay { position: fixed; top: 0; left: 0; right: 0; }
.header--overlay:not(.is-stuck) .nav a { color: rgba(255, 255, 255, .86); }
.header--overlay:not(.is-stuck) .nav a:hover { color: #fff; }
.header--overlay:not(.is-stuck) .nav a[aria-current="page"] { color: #fff; }
.header--overlay:not(.is-stuck) .nav-toggle {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .35);
}
.header--overlay:not(.is-stuck) .nav-toggle span { background: #fff; }

/* Lockup inks are class-driven so the logo can invert over the plate */
.brand__lockup .lk-ink { fill: var(--lk-ink, #11274a); transition: fill .35s var(--ease); }
.brand__lockup .lk-accent { fill: var(--accent); }
.header--overlay:not(.is-stuck) .brand__lockup { --lk-ink: #ffffff; }

@media (prefers-reduced-motion: reduce) {
  /* plates still cross-fade; they just lose the push-in */
  .hero__slide img { animation: none; }
  /* the bar shows which slide is current without counting down to it */
  .hero__dot.is-active::after { animation: none; transform: scaleY(1); }
  .hero__title, .hero__sub { animation: none; }
  /* the words still change — they just cross-fade in place rather than slide */
  .hero__word { transform: none; }
  .hero__word.is-out { transform: none; }
}

/* Short landscape viewports — a phone turned sideways gets the desktop crop
   at 100svh, which is only ~400 px tall. Let it breathe instead. */
@media (max-height: 460px) and (orientation: landscape) {
  .hero { height: auto; min-height: 0; aspect-ratio: 16 / 9; }
}

/* On a handset the sentence needs five or six lines whatever the size, so it
   gets more leading — at the desktop's tight 1.16 that many caps lines pack
   into a solid block. */
@media (max-width: 640px) {
  /* Lift the block above the WhatsApp button, which is fixed bottom-right */
  .hero__copy { padding-bottom: 5.5rem; }
}

/* ------------------------------------------------------ 7. SHARED PIECES */
.panel {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--sh-1);
}
.panel--pad { padding: clamp(1.4rem, 1rem + 1.7vw, 2.4rem); }
.panel--flat { box-shadow: none; background: var(--bg-2); }

/* Lime rule on the panel's top edge */
.glow-edge { position: relative; overflow: hidden; }
.glow-edge::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 72px; height: 3px;
  border-radius: 0 0 3px 0;
  background: var(--accent);
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .34rem .8rem;
  border: 1px solid var(--line-2);
  border-radius: var(--r-pill);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-2);
}
.tag--accent { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.tag--soft   { border-color: transparent; background: var(--accent-wash); color: var(--text); }
.tag-row { display: flex; flex-wrap: wrap; gap: .5rem; }

.note {
  padding: .95rem 1.15rem;
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--accent-wash);
  font-size: var(--fs-xs);
  color: var(--text-2);
  line-height: 1.6;
}

/* --------------------------------------- 8. CARDS, SERVICE TILES & LISTS */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .78rem;
  padding: clamp(1.3rem, 1rem + 1vw, 1.85rem);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--sh-1);
  transition: transform .35s var(--ease-out), box-shadow .35s var(--ease), border-color .3s var(--ease);
}
.card:hover {
  transform: translateY(-4px);
  box-shadow: var(--sh-2);
  border-color: var(--line-2);
}
.card__icon {
  width: 46px; height: 46px;
  display: grid;
  place-items: center;
  border-radius: var(--r);
  background: var(--accent);
  color: var(--accent-ink);
  flex: none;
}
.card__title { font-size: var(--fs-h4); }
.card__text  { font-size: var(--fs-sm); color: var(--muted); line-height: 1.62; }
.card__foot  { margin-top: auto; padding-top: .55rem; }
.card__index {
  position: absolute;
  top: 1.1rem; right: 1.25rem;
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--faint);
}

.card--navy { background: var(--brand-navy); border-color: var(--brand-navy); }
.card--navy .card__title { color: #fff; }
.card--navy .card__text  { color: var(--on-navy-soft); }
.card--navy .tlink { color: #fff; }
.card--navy .list-check li { color: var(--on-navy-soft); }

/* --- service tiles --------------------------------------------------
   Photographic tiles on a hard-edged modular grid: no radius anywhere, a
   hairline gutter so the images read as separate panels rather than one
   mosaic. Seven tiles fall 2 / 3 / 2 across six columns. */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 6px;
}
.svc-grid > .svc:nth-child(1),
.svc-grid > .svc:nth-child(2) { grid-column: span 3; }
.svc-grid > .svc:nth-child(3),
.svc-grid > .svc:nth-child(4),
.svc-grid > .svc:nth-child(5) { grid-column: span 2; }
.svc-grid > .svc:nth-child(6),
.svc-grid > .svc:nth-child(7) { grid-column: span 3; }

.svc {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  isolation: isolate;
  overflow: hidden;
  min-height: clamp(230px, 12rem + 9vw, 340px);
  padding: clamp(1.15rem, .85rem + 1.1vw, 1.85rem);
  background: var(--brand-navy);
  color: #fff;
}

.svc__media { position: absolute; inset: 0; z-index: -2; display: block; }
.svc__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Graded from the foot, where the copy sits. Heavier than the hero's grade
   because these frames are small and the type sits close to the image. */
.svc::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(0deg,
    rgba(6, 13, 24, .92) 0%, rgba(6, 13, 24, .72) 26%,
    rgba(6, 13, 24, .34) 52%, rgba(6, 13, 24, .1) 78%);
  transition: opacity .45s var(--ease);
}

.svc__index {
  position: absolute;
  top: clamp(1.15rem, .85rem + 1.1vw, 1.85rem);
  left: clamp(1.15rem, .85rem + 1.1vw, 1.85rem);
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .18em;
  color: rgba(255, 255, 255, .72);
}
/* the accent tick that marks the tile as a link */
.svc__index::after {
  content: "";
  display: block;
  width: 22px;
  height: 3px;
  margin-top: .55rem;
  background: var(--accent);
  transition: width .45s var(--ease-out);
}

.svc__body { display: block; position: relative; }
.svc__title {
  display: block;
  font-family: var(--font-brand);
  font-size: clamp(1.1rem, .92rem + .6vw, 1.5rem);
  line-height: 1.15;
  color: #fff;
}
.svc__text {
  display: block;
  margin-top: .5rem;
  max-width: 32ch;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: rgba(255, 255, 255, .78);
}

/* Hover is deliberately cheap — opacity and a 20px width change. Scaling the
   image instead meant seven large bitmaps re-rastering on hover, which is
   what made this grid stutter. */
.svc:hover::after { opacity: .78; }
.svc:hover .svc__index::after { width: 44px; }
.svc:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* --- what to expect -------------------------------------------------
   Four cells divided by hairlines rather than four boxes: on a navy field
   a bordered card would just be a lighter rectangle floating on colour. */
.expect {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.expect__item {
  padding: clamp(1.4rem, 1rem + 1.6vw, 2.4rem) clamp(1.15rem, .8rem + 1.4vw, 2rem);
  border-left: 1px solid rgba(255, 255, 255, .18);
}
/* no rule against the outer edge — the first cell aligns with the heading */
.expect__item:first-child { border-left: 0; padding-left: 0; }

.expect__num {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.4rem, 1.6rem + 2.6vw, 4rem);
  line-height: .9;
  letter-spacing: -.04em;
  /* ghosted, so it anchors the cell without competing with the title */
  color: rgba(255, 255, 255, .16);
}
.expect__title {
  margin-top: clamp(1rem, .7rem + 1vw, 1.6rem);
  font-family: var(--font-brand);
  font-size: clamp(1.02rem, .92rem + .35vw, 1.25rem);
  line-height: 1.2;
  /* two titles run to two lines and two do not; reserving the second line
     keeps the body copy on one baseline across the row */
  min-height: 2.4em;
}
.expect__title::before {
  content: "";
  display: block;
  width: 26px;
  height: 3px;
  margin-bottom: .85rem;
  background: var(--accent);
}
.expect__text {
  margin-top: .7rem;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--on-navy-soft);
}

/* --- fact cells ------------------------------------------------------
   The light-field counterpart to .expect: same hairline-divided cells,
   carrying a label and a value instead of a numbered point. */
.facts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0;
  /* no top rule: the split head already closes with one, and a second line
     a few rem below it reads as a mistake */
}
.facts__item {
  padding: clamp(1.35rem, 1rem + 1.5vw, 2.25rem) clamp(1.15rem, .8rem + 1.4vw, 2rem);
  border-left: 1px solid var(--line);
}
.facts__item:first-child { border-left: 0; padding-left: 0; }

.facts__k {
  display: flex;
  align-items: center;
  gap: .6rem;
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
}
.facts__k::before {
  content: "";
  width: 16px; height: 3px;
  flex: none;
  background: var(--accent);
}
.facts__v {
  margin: .85rem 0 0;
  font-family: var(--font-brand);
  font-size: clamp(1.05rem, .95rem + .45vw, 1.4rem);
  line-height: 1.2;
  color: var(--text);
  /* one value runs to two lines; reserving the space keeps the cells level */
  min-height: 2.4em;
}

/* --- inner-page hero -------------------------------------------------
   A short photographic band, not the homepage's full screen — an inner page
   should start, not open. The header on these pages is opaque and sticky, so
   the band simply begins beneath it. */
.pagehero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  /* The header is fixed and transparent over this, as it is over the home
     page hero, so the photograph runs to the top edge and the copy needs the
     bar's height back as padding. Taller than it was: the opening frame is
     the first thing a visitor meets on these pages, and a 460px band read as
     a strip above the content rather than an opening. */
  min-height: min(68svh, 620px);
  padding-block: clamp(7rem, 5rem + 5vw, 9.5rem) clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
  overflow: hidden;
  background: var(--brand-navy);
  color: #fff;
}
.pagehero .shell { max-width: var(--maxw-bar); }
.pagehero__media { position: absolute; inset: 0; z-index: -2; display: block; }
.pagehero__media img { width: 100%; height: 100%; object-fit: cover; }
.pagehero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    /* Top scrim. The bar is transparent over this frame and the lockup is
       white, so the strip behind it cannot be left to the photograph -- a
       bright sky washed the wordmark out completely. The home page gets the
       same guarantee from .hero__scrim. */
    linear-gradient(180deg, rgba(6, 13, 24, .66) 0%, rgba(6, 13, 24, .28) 14%, transparent 26%),
    linear-gradient(0deg, rgba(6, 13, 24, .92) 0%, rgba(6, 13, 24, .6) 45%, rgba(6, 13, 24, .3) 100%),
    linear-gradient(90deg, rgba(6, 13, 24, .55) 0%, transparent 60%);
}
.pagehero h1 { color: #fff; max-width: 18ch; }
.pagehero .eyebrow { color: rgba(255, 255, 255, .75); }
.pagehero .lead { color: rgba(255, 255, 255, .82); margin-top: 1.1rem; max-width: 52ch; }

/* --- service index ---------------------------------------------------
   Seven anchors in one hairline grid. A page of seven long blocks needs a
   way in that is not scrolling past six of them. */
.svc-index {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-2);
  border: 1px solid var(--line-2);
}
/* the detail pages list the other six, so the track has one column fewer */
.svc-index--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

.svc-index a {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  height: 100%;
  padding: clamp(.95rem, .75rem + .9vw, 1.4rem);
  background: var(--bg);
  transition: background .3s var(--ease), color .3s var(--ease);
}
.svc-index a:hover { background: var(--accent-wash); }
.svc-index__n {
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--muted);
}
.svc-index__t {
  font-family: var(--font-brand);
  font-size: clamp(.85rem, .8rem + .22vw, 1rem);
  line-height: 1.2;
  color: var(--text);
}

/* --- service block ---------------------------------------------------
   Photograph one side, detail the other, alternating down the page. Seven
   identical slabs is what the old layout did; the flip is what stops this
   reading as one long uniform column. */
/* tighter than a normal section: seven of these stacked at full section
   padding leaves a screen-height gap between each pair */
.svc-block { padding-block: clamp(1.85rem, 1.3rem + 2vw, 3.25rem); }

.svc-block__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 1rem + 3.5vw, 4.5rem);
  align-items: center;
}
.svc-block--flip .svc-block__media { order: 2; }

.svc-block__media { display: block; align-self: stretch; min-height: 300px; }
.svc-block__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.svc-block__n {
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: .75rem;
}
.svc-block__n::before {
  content: "";
  display: block;
  width: 26px;
  height: 3px;
  margin-bottom: .8rem;
  background: var(--accent);
}
.svc-block__body .lead { margin-top: 1rem; max-width: 46ch; }

.svc-block__k {
  margin-top: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-2);
  font-family: var(--font-body);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
}
.svc-block__body .list-check { margin-top: 1rem; }

.svc-block__note {
  margin-top: 1.35rem;
  padding-left: 1.1rem;
  border-left: 3px solid var(--accent);
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--text-2);
}
.svc-block__note b { color: var(--text); font-weight: 600; }
.svc-block__more { margin-top: clamp(1.25rem, 1rem + .8vw, 1.75rem); }

/* --- square chips ----------------------------------------------------
   The old .tag is a pill; these sit with the hard-edged sections. */
.chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.chip {
  display: inline-flex;
  align-items: center;
  padding: .42rem .8rem;
  border: 1px solid var(--line-2);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-2);
}

/* back link sitting above the title in a page hero. flex + fit-content rather
   than inline-flex, or it shares a line with the eyebrow that follows it. */
.crumb {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: .55rem;
  margin-bottom: 1.1rem;
  font-size: var(--fs-xs);
  color: rgba(255, 255, 255, .72);
  transition: color .25s var(--ease);
}
.crumb:hover { color: #fff; }
.crumb .arw { transition: transform .3s var(--ease); }
.crumb:hover .arw { transform: translateX(-3px); }

/* --- cargo matrix ----------------------------------------------------
   The grid lines are the design. A 1px gap over a line-coloured background
   draws every rule at once, so the cells butt together with no doubling and
   no radius — cheaper and more exact than bordering each cell. */
.matrix {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-2);
  border: 1px solid var(--line-2);
}
.matrix--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* five destinations on the 404 */
.matrix--nav { grid-template-columns: repeat(5, minmax(0, 1fr)); }
/* the whole cell is the link, so it fills rather than sitting inside padding */
.matrix__link404 {
  display: flex;
  flex-direction: column;
  gap: .45rem;
  height: 100%;
  padding: clamp(1.15rem, .85rem + 1.3vw, 1.9rem);
}
.matrix--nav .matrix__cell { padding: 0; }

.matrix__cell {
  display: flex;
  flex-direction: column;
  gap: .45rem;
  padding: clamp(1.15rem, .85rem + 1.3vw, 1.9rem);
  background: var(--bg);
  transition: background .3s var(--ease);
}
.matrix__cell:hover { background: var(--accent-wash); }

.matrix__title {
  font-family: var(--font-brand);
  font-size: clamp(.95rem, .9rem + .28vw, 1.15rem);
  line-height: 1.2;
  color: var(--text);
  /* three of the eight names wrap; reserving the line keeps the qualifiers
     on one baseline across each row */
  min-height: 2.4em;
}
.matrix__text {
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--muted);
}
/* pushed to the foot of the cell so it reads as the cell's action, not as a
   word inside the sentence above it */
.matrix__link { margin-top: auto; padding-top: .9rem; align-self: flex-start; }

/* --- cargo cards -----------------------------------------------------
   Commodity cards: the photograph carries the goods, the plate below
   carries the words. Deliberately the inverse of .svc, where the copy sits
   over a darkened frame — two photographic grids on one page need
   different anatomy or the second reads as a repeat of the first.
   Bordered rather than gap-lined like .matrix, because a 1px background
   grid would show through the images at the card corners. */
.cargo {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}

/* Photographic tiles, built like the service tiles further up the page: the
   photograph is the field rather than the subject, graded from the foot so a
   name and one qualifying line can sit on it. The commodity is read from the
   words, and the picture sets the tone -- which is the way round the client
   asked for, and the reason these no longer carry a white plate. */
.cargo__cell {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  isolation: isolate;
  overflow: hidden;
  min-height: clamp(180px, 9rem + 7vw, 260px);
  padding: clamp(1rem, .8rem + .9vw, 1.5rem);
  background: var(--brand-navy);
  color: #fff;
}

.cargo__media { position: absolute; inset: 0; z-index: -2; display: block; }
.cargo__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease-out);
}

/* Same grade as the service tile. These frames are smaller, so it carries a
   touch more weight at the foot to keep two lines legible over a busy shot. */
.cargo__cell::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(0deg,
    rgba(6, 13, 24, .94) 0%, rgba(6, 13, 24, .76) 30%,
    rgba(6, 13, 24, .36) 58%, rgba(6, 13, 24, .12) 82%);
  transition: opacity .45s var(--ease);
}

.cargo__body { position: relative; display: block; }
.cargo__n {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .18em;
  color: rgba(255, 255, 255, .68);
}
.cargo__title {
  display: block;
  margin-top: .5rem;
  font-family: var(--font-brand);
  font-size: clamp(.95rem, .9rem + .28vw, 1.15rem);
  line-height: 1.2;
  color: #fff;
}
.cargo__text {
  display: block;
  margin-top: .35rem;
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: rgba(255, 255, 255, .78);
}

/* Cheap hover, as on the service tiles: the grade lifts and the photograph
   creeps. No layout property is touched. */
.cargo__cell:hover::after { opacity: .82; }
.cargo__cell:hover .cargo__media img { transform: scale(1.05); }

/* --- process rail ----------------------------------------------------
   Read as a track rather than a list: one hairline crossing the section with
   a diamond node per stage. The diamond is the mark's 45-degree geometry,
   the same angle as the chevron used elsewhere. The line runs past the last
   node and fades, so the sequence reads as continuing to the delivery
   rather than stopping at stage four. */
.rail {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: clamp(1.25rem, .8rem + 2vw, 3rem);
}
.rail::before {
  content: "";
  position: absolute;
  top: 7px;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line-2);
  -webkit-mask-image: linear-gradient(90deg, #000 82%, transparent);
  mask-image: linear-gradient(90deg, #000 82%, transparent);
}

.rail__step { position: relative; padding-top: clamp(2rem, 1.6rem + 1.4vw, 2.75rem); }

/* 14px square on its corner — its bounding-box centre lands on the track */
.rail__node {
  position: absolute;
  top: 0;
  left: 0;
  width: 14px;
  height: 14px;
  background: var(--accent);
  transform: rotate(45deg);
}

.rail__n {
  display: block;
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
}
.rail__title {
  margin-top: .7rem;
  font-family: var(--font-brand);
  font-size: clamp(1.02rem, .92rem + .4vw, 1.3rem);
  line-height: 1.2;
  min-height: 2.4em;
}
.rail__text {
  margin-top: .55rem;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--muted);
  max-width: 30ch;
}

/* --- get in touch ----------------------------------------------------
   The channels are rows, not a button cluster: each one spans the column so
   the whole width is the hit area, and the value sits in the same place
   every time. The office row is the same shape but inert. */
.reach {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 1.2rem + 3vw, 5rem);
  align-items: center;
}
.reach__lead .lead { margin-top: 1.1rem; max-width: 40ch; }
.reach__lead .btn { margin-top: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem); }

.reach__list { border-top: 1px solid rgba(255, 255, 255, .2); }
.reach__row {
  display: grid;
  grid-template-columns: clamp(5.5rem, 8vw, 8rem) minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 1rem;
  padding: clamp(1.05rem, .85rem + .8vw, 1.5rem) 0;
  border-bottom: 1px solid rgba(255, 255, 255, .2);
  transition: padding-inline .35s var(--ease), background .35s var(--ease);
}
.reach__k {
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .58);
}
.reach__v {
  font-family: var(--font-brand);
  font-size: clamp(1rem, .92rem + .45vw, 1.35rem);
  line-height: 1.2;
  color: #fff;
  overflow-wrap: anywhere;
}
/* The office address runs to three lines. At the full value size it would
   outweigh the single-line email and WhatsApp rows above it, so it steps down
   a notch and opens its leading — an address block, not a headline. */
.reach__v--addr {
  font-size: clamp(.9rem, .85rem + .28vw, 1.1rem);
  line-height: 1.45;
}
.reach__arw {
  font-size: 1.1rem;
  color: var(--accent);
  transition: transform .35s var(--ease-out);
}

a.reach__row:hover {
  background: rgba(255, 255, 255, .06);
  /* pad inward on hover so the row reads as pressable without moving text
     out of the column it shares with the rows above and below */
  padding-inline: .9rem;
}
a.reach__row:hover .reach__arw { transform: translateX(5px); }
.reach__row--static .reach__v { color: var(--on-navy-soft); }

/* Light variant, for the same rows on a white field. Used on the contact page,
   where the channels have to be readable straight away rather than waiting for
   the closing band. */
.reach--light .reach__list { border-top-color: var(--line-2); }
.reach--light .reach__row { border-bottom-color: var(--line); }
.reach--light .reach__k { color: var(--muted); }
.reach--light .reach__v { color: var(--text); }
.reach--light a.reach__row:hover { background: var(--accent-wash); }
.reach--light .reach__row--static .reach__v { color: var(--text-2); }

/* --- inline strip ----------------------------------------------------
   One row: what we need, the list of it, and the way through. */
.strip {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(1.25rem, .8rem + 2vw, 3rem);
  margin-top: clamp(2rem, 1.4rem + 2.2vw, 3.25rem);
  padding: clamp(1.35rem, 1rem + 1.5vw, 2rem) clamp(1.35rem, 1rem + 1.5vw, 2.25rem);
  background: var(--bg-2);
  border: 1px solid var(--line);
}
/* stacked variant, for when the strip sits in a column rather than a full row */
.strip--stack {
  grid-template-columns: minmax(0, 1fr);
  justify-items: start;
  align-items: start;
  gap: 1rem;
  margin-top: 0;
}
.strip--stack .strip__list { flex-direction: column; gap: .55rem; }

.strip__k {
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
}
.strip__list {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.65rem;
  font-size: var(--fs-sm);
  color: var(--text-2);
}
.strip__list li {
  display: flex;
  align-items: center;
  gap: .55rem;
}
.strip__list li::before {
  content: "";
  width: 11px; height: 5px;
  flex: none;
  background: var(--accent);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 38% 100%);
}

.card--feature {
  justify-content: space-between;
  min-height: 250px;
  background: linear-gradient(150deg, var(--accent-wash), transparent 58%), var(--bg);
}

/* Lists */
.list-check { display: grid; gap: .62rem; }
.list-check li {
  display: flex;
  gap: .75rem;
  font-size: var(--fs-sm);
  color: var(--text-2);
  line-height: 1.55;
}
.list-check li::before {
  content: "";
  width: 15px; height: 8px;
  margin-top: .42rem;
  flex: none;
  background: var(--accent);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 38% 100%);
}

.list-arrow { display: grid; gap: .55rem; }
.list-arrow li { display: flex; gap: .65rem; font-size: var(--fs-sm); color: var(--muted); }
.list-arrow li::before { content: "→"; color: var(--accent-deep); flex: none; }

/* Two columns of ruled key/value rows. Ten rows in a single column on a
   1660px bar would set the values across most of the screen.

   Each column is its own list rather than one grid flowing across both: with
   a shared grid, a value that wraps to two lines pushes its own row down and
   the rules stop meeting across the gap, which reads as a broken table. */
.specs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(2rem, 1.2rem + 3vw, 4.5rem);
}
.specs__col { border-bottom: 1px solid var(--line); }

.spec {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.65fr);
  gap: .7rem 1.5rem;
  padding-block: .9rem;
  border-top: 1px solid var(--line);
  align-items: baseline;
}
.spec__k { font-size: var(--fs-xs); letter-spacing: .13em; text-transform: uppercase; color: var(--faint); }
.spec__v { font-size: var(--fs-sm); color: var(--text-2); }

.statband {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--sh-1);
  overflow: hidden;
}
.statband__cell { padding: clamp(1.35rem, 1rem + 1.2vw, 2.1rem); border-right: 1px solid var(--line); }
.statband__cell:last-child { border-right: 0; }
.statband__val {
  font-family: var(--font-display);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.85rem, 1.4rem + 1.8vw, 2.7rem);
  line-height: 1;
  letter-spacing: -.035em;
}
.statband__val sup { font-size: .42em; color: var(--accent-deep); top: -.95em; }
.statband__label { margin-top: .6rem; font-size: var(--fs-xs); color: var(--muted); }

.stat-card {
  padding: 1.25rem 1.45rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  box-shadow: var(--sh-1);
}
.stat-card__val {
  font-family: var(--font-display);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.55rem, 1.3rem + 1vw, 2rem);
  line-height: 1.1;
  letter-spacing: -.03em;
}
.stat-card__label { margin-top: .5rem; font-size: var(--fs-xs); color: var(--muted); }

/* Accordion */
/* The head above runs the full bar width, but answers set at 1300px are
   unreadable — hold the accordion to a sane measure and centre it. */
.faq { max-width: 62rem; margin-inline: auto; }

.acc { border-top: 1px solid var(--line); }
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.3rem 0;
  background: none;
  border: 0;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-h4);
  text-align: left;
  cursor: pointer;
  transition: color .25s var(--ease);
}
.acc__btn:hover { color: var(--accent-deep); }
.acc__sign { position: relative; width: 20px; height: 20px; flex: none; }
.acc__sign::before, .acc__sign::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  border-radius: 2px;
  background: var(--accent-deep);
  transition: transform .35s var(--ease);
}
.acc__sign::after { transform: rotate(90deg); }
.acc__btn[aria-expanded="true"] .acc__sign::after { transform: rotate(0deg); }
.acc__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .42s var(--ease-out); }
.acc__btn[aria-expanded="true"] + .acc__panel { grid-template-rows: 1fr; }
.acc__panel > div { overflow: hidden; }
.acc__body { padding-bottom: 1.45rem; }

/* ------------------------------------------- 9. PROCESS, RAIL & FIGURES */
.process { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: clamp(2rem, 1rem + 4vw, 4.5rem); }
.process__sticky { position: sticky; top: 6.5rem; align-self: start; }
.process__steps { display: grid; gap: clamp(.9rem, .6rem + 1.1vw, 1.4rem); }

.step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 1.3rem;
  padding: clamp(1.25rem, 1rem + 1vw, 1.8rem);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--sh-1);
  transition: transform .35s var(--ease-out), box-shadow .35s var(--ease);
}
.step:hover { transform: translateX(5px); box-shadow: var(--sh-2); }
.step__n {
  width: 44px; height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--r);
  background: var(--accent);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--accent-ink);
  flex: none;
}
.step__title { font-size: var(--fs-h4); margin-bottom: .35rem; }
.step__text { font-size: var(--fs-sm); color: var(--muted); line-height: 1.62; }

.mapwrap {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--sh-1);
  padding: clamp(1rem, .5rem + 2vw, 2.25rem);
  overflow: hidden;
}
.mapwrap__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-xs);
  color: var(--muted);
}
.legend-dot { display: inline-flex; align-items: center; gap: .5rem; }
.legend-dot::before {
  content: "";
  width: 9px; height: 9px;
  background: var(--accent);
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}
.legend-dot--navy::before { background: var(--brand-navy); }

/* CTA — the one navy block on a light page */
.cta {
  position: relative;
  overflow: hidden;
  padding: clamp(2.1rem, 1.4rem + 3vw, 3.9rem);
  border-radius: var(--r-xl);
  background:
    radial-gradient(120% 150% at 88% 0%, rgba(195, 216, 46, .22), transparent 55%),
    var(--brand-navy);
  color: var(--on-navy);
  text-align: center;
  box-shadow: var(--sh-2);
}
.cta h2, .cta .h2 { color: #fff; }
.cta .lead, .cta .eyebrow { color: var(--on-navy-soft); }
.cta .btn-row { justify-content: center; margin-top: 1.8rem; }
.cta .xs { color: rgba(255, 255, 255, .55); }

/* -------------------------------------------------- 10. ENQUIRY FORM
   One centred card, and eleven fields grouped into three short asks. The
   grouping is the whole design: presented as one list, eleven fields read
   as a form to be endured; as "about you", "the shipment" and "anything
   else", it reads as three questions, only the first of which is required.

   The card posts to /api/enquiry.php, which writes to the database and
   emails the office. Without JavaScript it still posts — the endpoint
   answers a plain form with a redirect back to this page — and if the
   backend is not configured the page falls back to its old mailto action. */
.enq {
  max-width: 860px;
  margin-inline: auto;
  padding: clamp(1.5rem, 1rem + 2.4vw, 3rem);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-2);
}
.enq__head { text-align: center; margin-bottom: clamp(1.75rem, 1.2rem + 1.6vw, 2.75rem); }
.enq__head .h2 { margin-top: .3rem; }
.enq__head .lead {
  max-width: 54ch;
  margin-inline: auto;
  margin-top: .9rem;
}

.enq__form { display: grid; gap: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem); }

/* A fieldset with the browser's border and padding removed. It stays a
   fieldset because the legend is what tells a screen reader which group
   each field belongs to. */
.enq__set { min-width: 0; margin: 0; padding: 0; border: 0; }
.enq__legend {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  padding: 0 0 1rem;
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
}
/* The rule runs from the label to the right edge, which is what makes the
   three groups read as bands rather than as three stacked forms. */
.enq__legend::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.enq__n {
  display: grid;
  place-items: center;
  width: 1.55rem;
  height: 1.55rem;
  font-size: var(--fs-micro);
  letter-spacing: .04em;
  color: var(--accent-ink);
  background: var(--accent);
  border-radius: 50%;
}

.enq__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(.9rem, .7rem + .6vw, 1.35rem);
}

.field { display: flex; flex-direction: column; gap: .45rem; }
.field--wide { grid-column: 1 / -1; }

.field > label {
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Required is marked on the label rather than by a bare asterisk on the
   input, so it is announced with the field name instead of after it. */
.field > label .req { color: var(--accent-deep); }

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: .82rem 1rem;
  font-family: inherit;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.field textarea { min-height: 8.5rem; resize: vertical; }
.field select { appearance: none; cursor: pointer; padding-right: 2.6rem; }

/* The chevron is drawn rather than imported: one more request for a 12px
   mark is not worth it, and this way it inherits the ink colour. */
.field--select { position: relative; }
.field--select::after {
  content: "";
  position: absolute;
  right: 1.05rem;
  bottom: 1.25rem;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  pointer-events: none;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--faint); }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--line-2); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

/* Only after the browser has judged the value — :invalid alone would paint
   every empty required field red before anyone has typed a character. */
.field input:not(:placeholder-shown):invalid,
.field textarea:not(:placeholder-shown):invalid {
  border-color: #c0392b;
}

/* The honeypot. Not display:none and not hidden — a bot that reads the
   stylesheet skips both — but off-screen, unlabelled in the tab order, and
   ignored by assistive tech. A person never meets it; a form-filler fills
   everything it can see in the DOM and gives itself away. */
.enq__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.enq__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .85rem;
}
.enq__actions .btn { min-width: 13rem; justify-content: center; }

.enq__note {
  margin: 0;
  text-align: center;
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--muted);
}
.enq__note a { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }

/* One box for both outcomes. The accent bar turns red on failure rather
   than the whole panel, so a failed send does not read as an error page. */
.enq__flash {
  margin: 0 0 1.5rem;
  padding: 1rem 1.15rem;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--text);
  background: var(--accent-wash);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-xs);
}
.enq__flash--bad {
  background: rgba(192, 57, 43, .07);
  border-left-color: #c0392b;
}
.enq__flash strong { display: block; margin-bottom: .15rem; }

/* ---- Sending state ---------------------------------------------------
   The progress belongs in the button the visitor just pressed, not in a
   message elsewhere on the page. The button keeps its width — .enq__actions
   sets a min-width — so swapping "Send enquiry" for "Sending…" moves
   nothing around it. */
.enq__form.is-sending .enq__actions [data-enquiry-wa] { opacity: .45; pointer-events: none; }

.btn .spinner {
  display: none;
  width: 1rem;
  height: 1rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
.btn.is-sending .spinner { display: block; }
.btn.is-sending .arw { display: none; }
.btn.is-sending { cursor: progress; }
/* Sent, and deliberately staying disabled: the enquiry is in, and a second
   press would only send it twice. */
.btn.is-sent .arw,
.btn.is-sent .spinner { display: none; }

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  /* The ring still reads as "busy" standing still, next to the word. */
  .btn .spinner { animation: none; }
}

/* ---- Thank-you dialog -------------------------------------------------
   A native <dialog>, so the browser supplies the focus trap, the inert
   background and Escape-to-close. On a phone it comes up from the bottom as
   a sheet, which is where a thumb is; on a desktop it sits centred. */
.modal {
  width: min(30rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  overflow: visible;
}
.modal::backdrop {
  background: rgba(8, 16, 30, .55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.modal__card {
  position: relative;
  padding: clamp(1.6rem, 1.2rem + 1.8vw, 2.4rem);
  text-align: center;
  background: var(--bg);
  border-radius: var(--r-lg);
  box-shadow: 0 30px 70px -20px rgba(8, 16, 30, .55);
}
.modal__mark {
  display: grid;
  place-items: center;
  width: 3.1rem;
  height: 3.1rem;
  margin: 0 auto 1.1rem;
  background: var(--accent);
  border-radius: 50%;
}
.modal__mark path {
  fill: none;
  stroke: var(--accent-ink);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.modal__title {
  /* Inherits --font-display like every other heading. Norvas is the
     wordmark face and is drawn in capitals only — right for a nav link or a
     card title, shouty for a thank-you. */
  margin: 0 0 .6rem;
  font-size: clamp(1.15rem, 1.05rem + .5vw, 1.42rem);
  line-height: 1.25;
  color: var(--text);
  text-wrap: balance;
}
.modal__text {
  margin: 0 auto;
  max-width: 32ch;
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--muted);
}
.modal__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .6rem;
  margin-top: 1.6rem;
}
.modal__actions .btn { flex: 1 1 auto; justify-content: center; }

.modal__x {
  position: absolute;
  top: .7rem;
  right: .7rem;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.modal__x:hover { color: var(--text); background: var(--bg-2); }
.modal__x path { stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

.modal[open] { animation: modalIn .38s var(--ease-out); }
.modal[open]::backdrop { animation: modalFade .38s var(--ease); }

@keyframes modalIn {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
@keyframes modalFade { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 560px) {
  /* Bottom sheet: full width, square at the foot, square-shouldered at the
     top — and it arrives from below rather than growing from the middle. */
  .modal {
    width: 100%;
    max-width: none;
    margin: auto auto 0;
  }
  .modal__card {
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    padding-bottom: max(1.6rem, env(safe-area-inset-bottom));
  }
  .modal__actions { flex-direction: column-reverse; }
  .modal__actions .btn { width: 100%; }
  .modal[open] { animation: sheetIn .38s var(--ease-out); }
}
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(100%); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .modal[open] { animation: modalFade .2s linear; }
}

[hidden] { display: none !important; }

@media (max-width: 640px) {
  .enq {
    padding: 1.4rem 1.15rem;
    border-radius: var(--r);
  }
  .enq__grid { grid-template-columns: minmax(0, 1fr); }
  .enq__actions .btn { width: 100%; }
}

/* -------------------------------------------------- 10b. CONTACT CHANNELS
   Email and WhatsApp, alongside the form above rather than instead of it. */
.channel {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.05rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg);
  box-shadow: var(--sh-1);
  transition: transform .35s var(--ease-out), box-shadow .35s var(--ease), border-color .3s var(--ease);
}
a.channel:hover { transform: translateY(-2px); box-shadow: var(--sh-2); border-color: var(--line-2); }
.channel__icon {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--accent-wash);
  color: var(--accent-ink);
  flex: none;
}
.channel__k { display: block; font-size: var(--fs-micro); letter-spacing: .15em; text-transform: uppercase; color: var(--faint); }
.channel__v { display: block; font-size: var(--fs-sm); font-weight: 500; color: var(--text); overflow-wrap: break-word; }

/* ------------------------------------------------------------ 11. FOOTER */
.footer {
  margin-top: clamp(2.75rem, 2rem + 2.5vw, 4.5rem);
  border-top: 1px solid var(--line);
  background: var(--bg-2);
  padding-block: clamp(2.5rem, 2rem + 2.2vw, 4rem) 1.6rem;
}
.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(2rem, 1rem + 3vw, 4rem);
}

/* --- homepage footer -------------------------------------------------
   Links first, then the lockup drawn at the full width of the bar as the
   closing statement. Hairline columns rather than gutters, so the block
   matches the sections above it. */
.footer--mark .shell { max-width: var(--maxw-bar); }
/* This footer butts straight onto the navy Get in touch band, so the gap the
   other pages need between a white section and the footer reads as a stray
   white stripe here. */
/* Both pages using this footer close on a navy band, so the gap the stacked
   footer needs above it would show as a white stripe between the two. */
.footer--mark { margin-top: 0; border-top: 0; }

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 1fr));
}
.footer__top > .footer__col {
  padding-inline: clamp(1rem, .7rem + 1.3vw, 2.25rem);
  border-left: 1px solid var(--line);
}
.footer__top > .footer__col:first-child { border-left: 0; padding-left: 0; }

.footer__mark {
  display: block;
  margin-top: clamp(2.5rem, 1.8rem + 2.6vw, 4.25rem);
  padding-top: clamp(1.75rem, 1.2rem + 2vw, 3rem);
  border-top: 1px solid var(--line-2);
}
/* At 4.7:1 a full-bar lockup is around 350px tall — the largest thing on the
   page, which is the point. height:auto matters: with a fixed height the SVG
   just centres its artwork inside the box and stays small. */
.footer__lockup {
  width: 100%;
  height: auto;
}
.footer__col h4 {
  font-family: var(--font-body);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 1rem;
}
.footer__col li + li { margin-top: .55rem; }
.footer__col a { font-size: var(--fs-sm); color: var(--text-2); transition: color .25s var(--ease); }
.footer__col a:hover { color: var(--text); }
/* scoped to the stacked footer the inner pages use — the homepage footer
   draws its lockup at full width instead, and this would cap it at 38px */
.footer__grid .brand__lockup { height: 38px; }

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  align-items: center;
  margin-top: clamp(2rem, 1.5rem + 2.2vw, 3.25rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-xs);
  color: var(--faint);
}

/* ---------------------------------------------------------- 12. MOTION */
[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
[data-reveal].is-in { opacity: 1; transform: none; }

.route-draw {
  stroke-dasharray: var(--len, 600);
  stroke-dashoffset: var(--len, 600);
  animation: draw 3.4s var(--ease-out) forwards;
  animation-delay: var(--dly, .3s);
}
@keyframes draw { to { stroke-dashoffset: 0; } }

.pulse { animation: pulse 3s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
@keyframes pulse {
  0%, 100% { opacity: .35; transform: scale(.8); }
  50%      { opacity: 1;   transform: scale(1.3); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
  .route-draw { stroke-dashoffset: 0; }
}

/* --------------------------------------------------------- 13. UTILITIES */
.stack    { display: grid; gap: 1rem; }
.stack-lg { display: grid; gap: 1.5rem; }
.mt-1 { margin-top: .75rem; }
.mt-2 { margin-top: 1.5rem; }
.mt-3 { margin-top: 2.25rem; }
.center { text-align: center; }
.sticky-top { position: sticky; top: 6.5rem; align-self: start; }
.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;
}
.channel__v, .footer__col a, .spec__v, .card__text, .lead, .body { overflow-wrap: break-word; }

/* -------------------------------------------------------- 14. RESPONSIVE */
/* Above the drawer breakpoint the header CTA is the route to Contact, so the
   plain nav link would be a second copy of it. It stays in the drawer only. */
@media (min-width: 901px) {
  .nav__contact { display: none; }
}

@media (max-width: 1080px) {
  /* four columns: tiles fall 2 / 2 / 2 / 1, the last one running full width */
  .svc-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .svc-grid > .svc:nth-child(n) { grid-column: span 2; }
  .svc-grid > .svc:nth-child(7) { grid-column: span 4; }

  /* two up: the rule moves to the top of the second row, and the left-hand
     cell of each row loses its vertical divider */
  .expect { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .expect__item:nth-child(odd) { border-left: 0; padding-left: 0; }
  .expect__item:nth-child(n+3) {
    border-top: 1px solid rgba(255, 255, 255, .18);
    margin-top: clamp(.5rem, .3rem + .8vw, 1rem);
  }

  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .facts__item:nth-child(odd) { border-left: 0; padding-left: 0; }
  .facts__item:nth-child(n+3) { border-top: 1px solid var(--line); }

  /* two up rather than three — at a third of a tablet the product spreads
     in the photographs stop being legible */
  .cargo { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .matrix { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* three cells into two columns leaves a hole, and the grid lines are a
     background, so the hole renders as a grey block. Widen the last one. */
  .matrix--3 > .matrix__cell:last-child { grid-column: span 2; }
  /* five into two leaves a hole in the last row; let the fifth run wide */
  .matrix--nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .matrix--nav > .matrix__cell:last-child { grid-column: span 2; }

  /* seven across gets too narrow to read — wrap to two rows of four/three */
  .svc-index { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* seven into four (or two) leaves a hole in the last row, which shows as a
     grey cell because the grid lines are a background. Widen the last one. */
  .svc-index li:last-child { grid-column: span 2; }

  /* the flip is a desktop rhythm; stacked, the image always leads */
  .svc-block__inner { grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
  .svc-block--flip .svc-block__media { order: 0; }
  .svc-block__media { min-height: 0; aspect-ratio: 16 / 10; }

  /* two-up: the divider moves to the top of the second row */
  .footer__top { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer__top > .footer__col:nth-child(odd) { border-left: 0; padding-left: 0; }
  .footer__top > .footer__col:nth-child(n+3) {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--line);
  }

  /* The track turns vertical rather than breaking across two rows — a
     horizontal rail that wraps stops reading as one continuous run. */
  .rail { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
  .rail::before {
    top: 7px;
    bottom: 0;
    left: 6px;
    right: auto;
    width: 1px;
    height: auto;
    -webkit-mask-image: linear-gradient(180deg, #000 84%, transparent);
    mask-image: linear-gradient(180deg, #000 84%, transparent);
  }
  .rail__step { padding: 0 0 clamp(1.6rem, 1.2rem + 1.4vw, 2.25rem) 2.5rem; }
  .rail__step:last-child { padding-bottom: 0; }
  .rail__node { top: .3rem; }
  .rail__title { min-height: 0; }
  .rail__text { max-width: none; }

  .reach { grid-template-columns: minmax(0, 1fr); gap: 2.25rem; }
  .reach__lead .lead { max-width: none; }

  /* the strip stacks: label, list, then the link on its own line. justify-items
     keeps the button at its own width instead of stretching across the row. */
  .strip {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    justify-items: start;
    gap: 1rem;
  }

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

@media (max-width: 900px) {
  .nav-toggle { display: grid; }
  .nav {
    position: fixed;
    inset: 0 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 5.25rem var(--gutter) 2rem;
    /* No white slab. A dark blur instead: the drawer opens over a different
       backdrop on every page — a photographic hero at the top, plain white
       once you have scrolled — and only its own tone can guarantee the links
       stay readable in all of them. */
    background: rgba(6, 13, 24, .8);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    backdrop-filter: blur(18px) saturate(140%);
    border-bottom: 1px solid rgba(255, 255, 255, .16);
    box-shadow: 0 24px 48px -30px rgba(0, 0, 0, .65);
    transform: translateY(-100%);
    transition: transform .45s var(--ease-out);
    max-height: 100dvh;
    overflow-y: auto;
  }
  .nav.is-open { transform: none; }
  .nav a {
    padding: 1rem .25rem;
    font-size: 1.05rem;
    border-radius: 0;
    /* Fixes the homepage bug: .header--overlay:not(.is-stuck) painted these
       white, and the drawer used to be white too — invisible links. Set here
       so the drawer's own colours win regardless of the header state. */
    color: rgba(255, 255, 255, .88);
    border-bottom: 1px solid rgba(255, 255, 255, .14);
  }
  .header--overlay:not(.is-stuck) .nav a { color: rgba(255, 255, 255, .88); }
  .nav a:hover,
  .header--overlay:not(.is-stuck) .nav a:hover,
  .nav a[aria-current="page"] { color: #fff; background: none; }

  /* the bar inverts with the drawer so the lockup and burger stay legible */
  .header.is-menu-open .brand__lockup { --lk-ink: #ffffff; }
  .header.is-menu-open .nav-toggle {
    background: rgba(255, 255, 255, .12);
    border-color: rgba(255, 255, 255, .38);
  }
  .header.is-menu-open .nav-toggle span { background: #fff; }
  /* keep the lockup above the drawer panel rather than behind it */
  .brand { position: relative; z-index: 1; }
  /* Each drawer row already has a rule beneath it — a hover underline on top
     of that would read as a double line, and there is no hover on touch. */
  .nav a::before { display: none; }
  .nav a[aria-current="page"]::after { left: auto; right: .25rem; bottom: 50%; transform: translateY(50%); }
  .nav .btn { display: inline-flex; margin: 1.3rem 0 0; }
  .header__actions .btn { display: none; }

  /* the title and its supporting line stack rather than sharing a row */
  .section-head--split { grid-template-columns: minmax(0, 1fr); gap: 1.1rem; align-items: start; }

  .split, .process { grid-template-columns: minmax(0, 1fr); }
  .process__sticky, .sticky-top { position: static; }
  .cols-3, .cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .statband { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .statband__cell:nth-child(2n) { border-right: 0; }
  .statband__cell:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
}

@media (max-width: 640px) {
  .svc-grid { grid-template-columns: minmax(0, 1fr); }
  .svc-grid > .svc:nth-child(n) { grid-column: span 1; }
  .svc { min-height: 210px; }

  .expect { grid-template-columns: minmax(0, 1fr); }
  .expect__item { border-left: 0; padding-inline: 0; }
  .expect__item + .expect__item { border-top: 1px solid rgba(255, 255, 255, .18); }

  .facts { grid-template-columns: minmax(0, 1fr); }
  .facts__item { border-left: 0; padding-inline: 0; }
  .facts__item + .facts__item { border-top: 1px solid var(--line); }
  .strip__list { flex-direction: column; gap: .55rem; }

  /* one column would be eight tall rows; two keeps the block scannable */
  .matrix__cell { padding: 1rem .9rem; }

  /* still two up here — at ~290px a card the goods are readable. The
     reserved second title line goes: rows are short enough to scan
     without the baselines being aligned. */
  .cargo__body { padding: .85rem .8rem 1rem; }
  .cargo__title { min-height: 0; }

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

  /* the label stacks above the value rather than sharing a cramped row */
  .reach__row { grid-template-columns: minmax(0, 1fr) auto; gap: .3rem 1rem; }
  .reach__k { grid-column: 1 / -1; }

  .footer__top { grid-template-columns: minmax(0, 1fr); }
  .footer__top > .footer__col { border-left: 0; padding-inline: 0; }
  .footer__top > .footer__col + .footer__col {
    margin-top: 1.75rem;
    padding-top: 1.75rem;
    border-top: 1px solid var(--line);
  }
  .cols-2, .cols-3, .cols-4 { grid-template-columns: minmax(0, 1fr); }
  .footer__grid { grid-template-columns: minmax(0, 1fr); }
  .spec { grid-template-columns: minmax(0, 1fr); gap: .3rem; }
  .specs { grid-template-columns: minmax(0, 1fr); }
  .prose-2 { grid-template-columns: minmax(0, 1fr); }
  .statband { grid-template-columns: minmax(0, 1fr); }
  .statband__cell { border-right: 0; border-bottom: 1px solid var(--line); }
  .statband__cell:last-child { border-bottom: 0; }
  .btn { width: 100%; }
  .btn-row { flex-direction: column; align-items: stretch; }
  .step { grid-template-columns: minmax(0, 1fr); gap: .85rem; }
}

@media (max-width: 480px) {
  :root { --gutter: 1.15rem; }
  /* half a phone is ~165px of photograph — the spreads stop reading, so
     the cards go full width for the last step down */
  .cargo { grid-template-columns: minmax(0, 1fr); }
  .acc__btn { font-size: 1rem; gap: 1rem; padding: 1.1rem 0; }
  .footer__bottom { flex-direction: column; align-items: flex-start; gap: .5rem; }
}

@media (max-height: 640px) {
  .process__sticky, .sticky-top { position: static; }
}

/* ------------------------------------------- 15. FLOATING WHATSAPP BUTTON */
.wa-fab {
  position: fixed;
  right: clamp(1rem, .5rem + 1.4vw, 1.75rem);
  bottom: max(clamp(1rem, .5rem + 1.4vw, 1.75rem), env(safe-area-inset-bottom, 0px));
  z-index: 95;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: 50%;
  background: #25d366;
  color: #fff;
  box-shadow: 0 6px 16px rgba(37, 211, 102, .38), 0 2px 6px rgba(17, 39, 74, .18);
  transition: transform .3s var(--ease-out), box-shadow .3s var(--ease);
}
.wa-fab svg { width: 29px; height: 29px; }
.wa-fab:hover, .wa-fab:focus-visible {
  transform: translateY(-3px) scale(1.05);
  box-shadow: 0 12px 26px rgba(37, 211, 102, .45), 0 3px 8px rgba(17, 39, 74, .2);
}
.wa-fab::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid #25d366;
  animation: waRing 2.6s var(--ease-out) 3s 3;
  pointer-events: none;
  opacity: 0;
}
@keyframes waRing {
  0%   { opacity: .65; transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.7); }
}
@media (prefers-reduced-motion: reduce) { .wa-fab::after { animation: none; } }
@media (max-width: 480px) {
  .wa-fab { width: 52px; height: 52px; }
  .wa-fab svg { width: 27px; height: 27px; }
}

/* ------------------------------------------------------------- 16. PRINT */
@media print {
  body::before, .header, .footer, .nav-toggle, .wa-fab, .hero { display: none !important; }
  body { background: #fff; }
  .card, .panel, .step, .cta, .statband, .mapwrap, .channel, .stat-card {
    background: #fff;
    box-shadow: none;
    border: 1px solid #ccc;
    color: #000;
  }
  .cta h2, .cta .lead { color: #000; }
}
