/* ============================================================
   CRONIMET TERMINALS — Main Stylesheet
   Lava New Media | 2026
   ============================================================ */

/* ------------------------------------------------------------
   CUSTOM PROPERTIES
------------------------------------------------------------ */
:root {
  --blue:       #041a3d;
  --green:      #52ae32;
  --metal:      #91a2ac;
  --dark-metal: #041a3d;
  --red:        #ff2800;
  --white:      #ffffff;
  --off-white:  #f4f5f6;

  --grey-10: #e0e8ee;
  --grey-30: #bbc7cf;
  --grey-50: #91a2ac;
  --grey-70: #677a84;
  --grey-90: #40545e;

  --gradient: linear-gradient(135deg, #52ae32 0%, #41b8d5 100%);

  --nav-h: 72px;
  --page-pad: 60px;       /* horizontal breathing room inside boxes / footer */
  --content-max: 1400px;  /* the content grid; with --page-pad it defines THE left content edge */
  --box-overlap: 80px;    /* how much box bleeds past section edge */
  --split-img: 57vw;      /* image column width */

  /* THE COPY EDGE (standing rule): every piece of page copy on desktop
     starts on this one shared left edge. The reference is the approved
     copy position of the "Backed by the CRONIMET Group" box: a 120px
     floor from the viewport edge until the --content-max grid centers
     past it (viewports over 1640px), then the centered grid's left
     line. The header (logo + nav) is the OTHER permitted edge and
     keeps its --page-pad gutter; do not derive it from this token.
     Use the token as horizontal padding on full-width bands, as
     margin-left on full-bleed copy blocks (hero, page header), or as a
     grid spacer column (split sections). The 100% inside it must
     resolve against a full-viewport-width containing block; never use
     it inside an already-padded container. Backgrounds and images are
     exempt (they may bleed or inset freely), but text sitting on them
     is not. The floor steps down to --page-pad at <=1100px so tablet
     and mobile layouts stay exactly as they are. */
  --copy-floor: 120px;
  --copy-edge: max(var(--copy-floor), calc((100% - var(--content-max)) / 2));

  /* THE ARROW SIZE (standing rule, 2026-08-07). The green arrow renders
     at ONE size everywhere on the site: every component, every
     breakpoint. It does NOT inherit the size of the label beside it,
     so a 12px commodities label and a 16px service row show the same
     glyph. Before this token the arrow rendered at six different sizes
     (16 / 14.08 / 13.12 / 12.48 / 12 / 10.88px); 16px is the largest
     and the approved one, the service-row size. Set it here, never in
     a component. Arial's arrow is exactly 1em wide, so the glyph
     occupies 16px of advance in every context.
     --arrow-gap is the matching space to the adjacent text: the old
     em-derived gaps (0.5em of a 12px label = 6px) read pinched once
     the glyph grew, so the gap is fixed too. */
  --arrow-size: 16px;
  --arrow-gap: 8px;
}

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

html {
  font-size: 16px;
  scroll-behavior: smooth;
  -webkit-font-smoothing: antialiased;
}

/* LOCKED (2026-08-06): horizontal scroll is impossible, everywhere,
   forever. clip, not hidden — clip creates no scroll container and
   does not break position: sticky. This is a safety net on top of
   source fixes: no element may ever render wider than the viewport,
   and any overflow found in QA gets fixed at the source, never by
   leaning on this rule. */
html, body {
  overflow-x: clip;
}

/* THE CANVAS BEYOND THE PAGE IS NAVY (2026-08-08, Ed). Everything
   outside the page box takes its colour from html/body: the strip
   behind the iOS status bar, the overscroll rubber band at both ends,
   and the browser chrome tint alongside the theme-color meta in
   header.php. That was off-white, which is why the very top of the
   screen on a real iPhone read white above the navy header.
   The off-white that shows BETWEEN sections is a different job, and it
   moves to #content below: it is the page canvas, not the void around
   it. Every section lives inside <main id="content">, and the header
   and footer outside it are navy already, so the two surfaces meet
   exactly where they always did. */
html {
  background: var(--blue);
}

body {
  font-family: 'Source Sans 3', sans-serif;
  background: var(--blue);
  color: var(--white);
}

#content {
  background: var(--off-white);   /* ← KEY: off-white shows between sections */
}

img, video { display: block; max-width: 100%; }
address { font-style: normal; }
ul { list-style: none; }
a { color: inherit; text-decoration: none; }

/* ------------------------------------------------------------
   TYPOGRAPHY HELPERS
------------------------------------------------------------ */
.accent { color: var(--green); }

.label-sm {
  display: flex;
  align-items: baseline; /* arrow sits on the FIRST line when the label wraps; standing rule for all arrow+text rows */
  gap: var(--arrow-gap);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--metal);
  margin-bottom: 0.75rem;
}

.label-sm--light { color: var(--metal); }

/* The arrow carries its own size (see --arrow-size): never inherited
   from the label beside it, so it is identical in every component. */
.arrow {
  color: var(--green);
  font-weight: 700;
  font-size: var(--arrow-size);
  /* Labels track out to 0.12 to 0.14em and the arrow inherits that as a
     trailing letter-space, which quietly added 1.5 to 2.2px on top of
     the row gap and made the gap different in every component. Zero it
     so --arrow-gap IS the spacing. With the gap at 8px this reproduces
     the pre-16px spacing of the label rows almost exactly. */
  letter-spacing: 0;
}

.headline-rule {
  width: 40px;
  height: 1px;
  background: var(--green);
  margin: 1.25rem 0 1.75rem;
}

/* ------------------------------------------------------------
   TEXT LINKS IN COPY (the locked link standard, 2026-08-07)
   Rest: bold, no underline — the weight against regular-weight
   surrounding copy is the non-color distinction. Hover AND keyboard
   focus bring the green underline, plus the surface's focus ring.
   Green is never the resting color of a body-size link.
   Two surface variants; the component carries the behavior, the
   variant carries the color. Used by the CRONIMET Group parent-site
   links (cronimet_group_link) and available to any future body link.
------------------------------------------------------------ */
.text-link {
  font-weight: 700;
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.text-link:hover,
.text-link:focus-visible { text-decoration-line: underline; }

.text-link--on-dark { color: var(--white); }
.text-link--on-dark:focus-visible {
  outline: 2px solid var(--green); /* 6.1:1 on navy */
  outline-offset: 3px;
}

.text-link--on-light { color: var(--blue); }
.text-link--on-light:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}

/* Widow/orphan rule (standing): wrapped display text breaks in balanced
   lines; supporting copy never strands a single word on its last line.
   (Headlines broken by explicit field rows are unaffected: balance
   works per <br> segment.) Progressive enhancement, no layout shift. */
.split__headline,
.service-band__headline,
.cta-headline,
.contacts-headline,
.form-heading,
.form-success__headline,
.network-map__headline,
.page-interior-title {
  text-wrap: balance;
}

.hero-body,
.page-header__lede,
.intro-band__text,
.split__body,
.service-desc,
.service-band__intro,
.service-band__note,
.cta-body,
.form-intro,
.form-success__line,
.network-map__body,
.nm-card__address {
  text-wrap: pretty;
}

/* ------------------------------------------------------------
   PILL BUTTONS
   Rule: pill outline only. Filled ONLY for primary CTA.
------------------------------------------------------------ */
.btn-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--arrow-gap);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 12px 26px;
  border-radius: 100px;
  border: 1.5px solid transparent;
  cursor: pointer;
  transition: background 0.22s ease, color 0.22s ease, border-color 0.22s ease;
  white-space: nowrap;
}

.btn-pill--filled {
  background: var(--green);
  border-color: var(--green);
  color: var(--blue); /* site-wide standard: navy on green fill; white is 2.82:1 and fails AA */
}
.btn-pill--filled:hover {
  background: transparent;
  color: var(--white);
}
/* Arrows follow the button text on green fill, return to green when the fill drops */
.btn-pill--filled .arrow,
.btn-pill--outline-green:hover .arrow { color: var(--blue); }
.btn-pill--filled:hover .arrow { color: var(--green); }

.btn-pill--outline-white {
  background: transparent;
  border-color: rgba(255,255,255,0.5);
  color: var(--white);
}
.btn-pill--outline-white:hover {
  border-color: var(--white);
  background: rgba(255,255,255,0.08);
}

.btn-pill--outline-green {
  background: transparent;
  border-color: var(--green);
  color: var(--white);
}
.btn-pill--outline-green:hover {
  background: var(--green);
  color: var(--blue); /* green-filled state, same navy-text standard */
}

.btn-pill--lg {
  font-size: 0.88rem;
  padding: 16px 36px;
}

/* Visible keyboard focus (2026-08-10, WCAG 2.4.7). The component had
   NO focus style and the reset drops the browser default, so the
   primary CTA on every page was invisible to a keyboard visitor.

   Two-tone ring, the same device as .nm-marker and .nm-card, because
   the pill is a shared component that has to read on every surface it
   can land on: solid navy, navy-over-photography (hero, CTA band),
   and light, with three fills of its own underneath (green, outline
   white, outline green). No single colour clears 3:1 on all of them.

   The marker's order is REVERSED here (navy inside, grey-10 outside)
   for two reasons the marker does not have. The outward-facing band
   is the one measured against the page, and grey-10 is the token the
   contrast audit already proved on photography (5.80:1 on the hero's
   worst frame, where navy would disappear); and the inward-facing
   band sits against the button, which can be a green fill, where navy
   is 6.11:1 and grey-10 would be only 2.27:1.

   Measured: grey-10 on navy 13.89:1, navy on off-white 15.77:1,
   grey-10 on the hero's worst frame 5.80:1, navy on the green fill
   6.11:1. Every boundary clears the 3:1 non-text minimum.

   box-shadow, not outline, so the ring follows the 100px pill radius.
   The transparent outline is the site's existing forced-colors
   fallback (same device as the Forminator field rule): box-shadow is
   dropped in forced-colors mode and the outline becomes a real
   system-coloured ring. :focus-visible only, so a mouse click never
   draws it, and resting, hover and active states are untouched. */
.btn-pill:focus-visible {
  outline: 2px solid transparent;
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--blue), 0 0 0 6px var(--grey-10);
}

/* ------------------------------------------------------------
   NAVIGATION
------------------------------------------------------------ */
/* Skip link: parked above the viewport until keyboard focus lands on it */
.skip-link {
  position: fixed;
  top: -100px;
  left: 24px;
  z-index: 300;
  background: var(--blue);
  color: var(--white);
  border: 1px solid var(--green);
  border-radius: 100px;
  padding: 12px 24px;
  font-weight: 600;
  transition: top 0.2s ease;
}
.skip-link:focus { top: 12px; }

/* Visible keyboard focus on the navy header (green ring passes the 3:1 non-text rule at 6.1:1) */
.site-nav a:focus-visible,
.nav-toggle:focus-visible,
.skip-link:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

/* The header is SOLID navy at the top edge, in every state (2026-08-08,
   Ed): on a notched phone this strip sits directly under the status
   bar, and anything translucent there lets the page show through
   behind the clock.
   The safe-area padding is defensive. The site does not set
   viewport-fit=cover, so iOS keeps the content inside the safe area and
   paints the inset from the html/body navy above; env() therefore
   resolves to 0 today and this adds nothing. If viewport-fit=cover is
   ever introduced, this already carries the header's navy up behind the
   status bar, and at that point every offset derived from --nav-h
   (hero-content, page-header, form-band scroll-margin, page-interior)
   needs the same inset added or content will sit under the notch. */
.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 100;
  background: var(--blue);
  padding-top: env(safe-area-inset-top, 0px);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  transition: background 0.3s ease, box-shadow 0.3s ease, transform 260ms ease;
}

/* Hide on scroll down, show on scroll up (ported from the pghopera build).
   Under reduced motion the hide/show still functions, just without the slide. */
.site-nav.is-hidden { transform: translateY(-100%); }

@media (prefers-reduced-motion: reduce) {
  .site-nav, .skip-link { transition: none; }
}

/* Scrolled state keeps its lift but drops the 4% transparency and the
   blur behind it (2026-08-08, Ed): under a phone status bar that 4%
   let page content ghost through behind the clock. The blur went with
   it because there is nothing left to blur, and it was costing a
   compositor layer on every scroll for no visible gain. */
.site-nav.scrolled {
  background: var(--blue);
  box-shadow: 0 2px 40px rgba(0,0,0,0.35);
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--nav-h);
  /* content-max plus its gutters (border-box), so the logo sits exactly
     on the shared left content edge at every width */
  max-width: calc(var(--content-max) + 2 * var(--page-pad));
  margin: 0 auto;
  padding: 0 var(--page-pad);
}

/* -------------------------------------------------------
   SITE LOGO — two-file approach
   gf-monogram-white.svg  : GF circle only (viewBox 0 0 56.9 56.9)
   cronimet-wordmark-only-white.svg : CRONIMET text only (viewBox 68 0 215.46 56.9)
   TERMINALS lives in the same flex column as the wordmark
   img, so it left-aligns to the wordmark automatically.
   No percentage math needed.
------------------------------------------------------- */
.site-logo {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1;
  text-decoration: none;
  flex-shrink: 0;
}

.site-logo-row {
  display: inline-flex;
  align-items: center;
  gap: 14px;
}

.site-logo-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1;
}

.site-logo-monogram {
  display: block;
  width: auto;
  flex-shrink: 0;
}

.site-logo-wordmark {
  display: block;
  width: auto;
}

.site-logo-sublabel {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 400;
  letter-spacing: 0.25em;
  color: #91a2ac;
  margin-top: 2px;
  margin-left: 4px; /* optical adjustment — aligns T under C stroke, not SVG edge */
}

/* Nav header */
.site-nav .site-logo-monogram { height: 36px; }
.site-nav .site-logo-wordmark { height: 28px; }
.site-nav .site-logo-sublabel { font-size: 11px; }

/* Footer */
.site-footer .site-logo-monogram { height: 48px; }
.site-footer .site-logo-wordmark { height: 38px; }
.site-footer .site-logo-sublabel { font-size: 13px; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 2.5rem;
}

.nav-link {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 1rem; /* 16px, measured from the cronimet.de main menu; pairs with the 0.02em tracking */
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--metal);
  position: relative;
  transition: color 0.2s ease;
}

.nav-link::after {
  content: '';
  position: absolute;
  bottom: -3px;
  left: 0;
  width: 0;
  height: 1px;
  background: var(--green);
  transition: width 0.25s ease;
}

.nav-link:hover,
.nav-link.active { color: var(--white); }

.nav-link:hover::after,
.nav-link.active::after { width: 100%; }

.nav-cta {
  color: var(--metal) !important; /* rests like a nav link; white is the hover state */
  border: 1px solid var(--green);
  padding: 7px 44px;
  border-radius: 100px;
  transition: background 0.2s ease, color 0.2s ease;
}
.nav-cta::after { display: none; }
.nav-cta:hover {
  background: var(--green);
  color: var(--blue) !important; /* white on green is 2.82:1 and fails AA; navy is 6.11:1 */
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}
.nav-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--white);
  transition: transform 0.25s ease, opacity 0.2s ease;
}

/* ------------------------------------------------------------
   HERO
------------------------------------------------------------ */
.hero {
  position: relative;
  height: 100vh;
  min-height: 680px;
  overflow: hidden;
  background: var(--blue);
  display: flex;
  flex-direction: column;
}

/* Video background — sits behind all content */
.hero-video-wrapper {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 0;
}

.hero-video-wrapper video {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  max-width: none;  /* override global reset max-width: 100% */
  object-fit: cover;
}

.hero-overlay {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background: rgba(4, 26, 61, 0.6);
  z-index: 1;
}

/* Content wrapper — fills full hero height, centers content vertically */
.hero-content {
  position: relative;
  z-index: 2;
  width: 100%;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: var(--nav-h);
}

/* Single left-aligned content column, starting on the shared left
   content edge (the old 8vw margin was its own third edge) */
.hero-block {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin-left: var(--copy-edge);
  max-width: 700px;
}

.hero-headline {
  display: flex;
  flex-direction: column;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  line-height: 0.88;
  letter-spacing: -0.03em;
}

.hl {
  display: block;
  font-size: clamp(3rem, 7vw, 6rem);
  color: var(--white);
}

.hl--green { color: var(--green); }

/* TEXT OVER PHOTOGRAPHY steps to --grey-10 (2026-08-08 contrast audit,
   LOCKED). Metal Grey is the body color on SOLID navy, where it measures
   6.53:1. It is not the floor on navy-over-photography: the hero's 60%
   overlay sits on a moving image, and sampling the actual composited
   pixels across the loop put the metal eyebrow at 2.73:1 on its worst
   frame (2.31:1 at the brightest pixel) — a real AA failure, not a
   marginal one. grey-30 (the old step-up) only reached 4.17:1 there.
   grey-10 measures 5.80:1 on the worst frame and 4.92:1 at the
   brightest single pixel, so it clears 4.5:1 even against a specular
   highlight. The same step applies to the closing CTA band's copy over
   its photo. This does NOT relax the brand rule: body copy on solid
   navy is still Metal Grey, and grey-10 is still not white. Re-measure
   (sample the composited pixels, do not assume) if the hero video or
   the CTA photograph is ever replaced. */
.hero-eyebrow {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--grey-10);
  display: flex;
  align-items: baseline; /* arrow on the first line when wrapped */
  gap: var(--arrow-gap);
}

.hero-body {
  font-size: clamp(0.95rem, 1.1vw, 1.05rem);
  line-height: 1.65;
  color: var(--grey-10);
  max-width: 480px;
}

.hero-ctas {
  display: flex;
  gap: 12px;
}

.hero-scroll {
  position: absolute;
  bottom: 40px;
  right: 60px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.hero-scroll-label {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--grey-10); /* over-photography step, see .hero-eyebrow */
  text-transform: uppercase;
  writing-mode: vertical-rl;
}

.hero-scroll-line {
  width: 1px;
  height: 44px;
  background: linear-gradient(to bottom, var(--green), transparent);
  animation: scrollPulse 2.2s ease-in-out infinite;
}

@keyframes scrollPulse {
  0%, 100% { opacity: 0.35; transform: scaleY(1); }
  50%       { opacity: 1;    transform: scaleY(1.1); }
}

/* Image background hero (interior pages — no video) */
.hero-img-wrapper {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 0;
}

.hero-img-bg {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background-size: cover;
  background-position: center;
}

/* ------------------------------------------------------------
   PAGE HEADER — compact interior page opener. The standard for
   every page except Home: arrow eyebrow, page H1 at a reduced
   display scale, green rule, one supporting line, and an
   optional per-page photo on the right that bleeds to the right
   viewport edge and hangs --box-overlap past the band bottom
   (the split-section overlap grammar). The photo runs clean, no
   overlay: text never sits on it. No buttons, no scroll
   indicator. A page header, not a landing moment.
------------------------------------------------------------ */
.page-header {
  --ph-pad-b: 48px;
  position: relative;
  z-index: 2; /* the figure hang paints over the next section */
  background: var(--blue);
  padding: calc(var(--nav-h) + 48px) 0 var(--ph-pad-b);
}

.page-header__block {
  /* the shared left content edge, same token as .hero-block */
  margin-left: var(--copy-edge);
  padding-right: var(--page-pad);
  max-width: 900px;
}

/* Figure variant: the text column reserves the figure's width plus a
   gap; both use the same clamp so they can never disagree */
.page-header--figure .page-header__block {
  padding-right: 0;
  margin-right: calc(clamp(300px, 34vw, 560px) + 48px);
}

.page-header__eyebrow {
  display: flex;
  align-items: baseline; /* arrow on the first line when wrapped */
  gap: var(--arrow-gap);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--metal);
  margin-bottom: 1.5rem;
}

.page-header__title {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  line-height: 0.9;
  letter-spacing: -0.03em;
  /* Clearly below the hero's clamp(3rem, 7vw, 6rem), still above the
     section headlines' 4.5rem cap, and sized so a one-row headline
     stays one line beside the figure column at desktop widths. Line
     breaks come only from the field's rows, never from wrapping. */
  font-size: clamp(2.4rem, 4.6vw, 4.75rem);
  color: var(--white);
}

.page-header .headline-rule { margin: 1.5rem 0 1.75rem; }

.page-header__lede {
  font-size: clamp(0.95rem, 1.1vw, 1.05rem);
  line-height: 1.65;
  color: var(--metal);
  /* Widow rule: the supporting line fits on ONE line at desktop.
     34rem clears the Services line with margin; keep new ledes short
     enough to fit this measure. */
  max-width: 34rem;
}

/* Figure: absolutely positioned against the band so it bleeds to the
   right viewport edge, tops out level with the text content, and hangs
   --box-overlap past the navy bottom edge into the next section,
   echoing the split overlap. */
.page-header__figure {
  position: absolute;
  top: calc(var(--nav-h) + 48px); /* level with the band's content top */
  right: 0;
  bottom: calc(-1 * var(--box-overlap));
  width: clamp(300px, 34vw, 560px);
}

.page-header__figure-img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-color: var(--dark-metal);
}

/* Same breathing room after the header as after the hero; also
   clears the figure hang (80px) with room to spare */
.page-header + .split { margin-top: 120px; }

/* ------------------------------------------------------------
   INTRO BAND — editorial lead paragraph directly below the page
   header (Facilities standard; any interior page may use it).
   One calm paragraph on off-white, on the copy edge, one step
   above body scale. Top padding clears the header figure's
   --box-overlap hang; spacing to the next section comes from the
   sibling rules below.
------------------------------------------------------------ */
.intro-band {
  background: var(--off-white);
  padding: 140px var(--copy-edge) 0;
}

.intro-band__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.intro-band__text {
  font-size: clamp(1.1rem, 1.5vw, 1.45rem);
  line-height: 1.6;
  color: var(--blue);
  max-width: 34em;
}

.intro-band + .split { margin-top: 160px; }

/* ------------------------------------------------------------
   SPLIT SECTIONS — core asymmetric offset layout
   Images bleed to viewport edge. Boxes overlap images by
   --box-overlap (80px) horizontally and vertically.
------------------------------------------------------------ */
.split {
  display: grid;
  position: relative;
  /* No horizontal padding — images must touch viewport edges */
  /* Vertical spacing provided by margin from sibling rules below */
}

/* Image left / box right */
.split--img-left {
  grid-template-columns: var(--split-img) 1fr;
}

/* Image right / box left. A spacer column pins the box copy to the
   shared copy edge (the old fixed 1fr + padding grammar left this text
   at 120px from the viewport edge — a third left edge on wide screens).
   The navy panel is a background, so it may run past the edge; the
   text inside it may not. */
.split--img-right {
  grid-template-columns: var(--copy-edge) 1fr var(--split-img);
}

.split--img-right .split__img-col { grid-column: 3; }

/* Space above first split section (off-white shows after dark hero) */
.hero + .split {
  margin-top: 120px;
}

/* Space between consecutive split sections */
.split + .split {
  margin-top: 200px;
}

/* Space between last split and stats band */
.split + .stats-band {
  margin-top: 160px;
}

/* Space between stats band and next split */
.stats-band + .split {
  margin-top: 160px;
}

/* ------- Image column ------- */
.split__img-col {
  position: relative;
  /* min-height ensures image has height even if box content is short */
  min-height: 600px;
}

.split__img-bg {
  position: absolute;
  /* Extend image 80px above/below to visually match box height */
  inset: calc(-1 * var(--box-overlap)) 0;
  background-size: cover;
  background-position: center;
  /* Fallback gradient if Unsplash image fails to load */
  background-color: var(--dark-metal);
}

/* ------- Content column ------- */
.split__content-col {
  display: flex;
  align-items: center;
}

/* img-left: box is on the right, overlaps image on its LEFT */
.split--img-left .split__content-col {
  padding: var(--box-overlap) var(--page-pad) var(--box-overlap) 0;
}

.split--img-left .split__box {
  margin-left: calc(-1 * var(--box-overlap));
  padding: 56px var(--page-pad) 56px calc(var(--box-overlap) + 40px);
}

/* img-right: box is on the left, overlaps image on its RIGHT. The
   content column starts on the copy edge; the panel pulls back by its
   own interior padding so the text (not the panel) lands on the edge. */
.split--img-right .split__content-col {
  grid-column: 2;
  padding: var(--box-overlap) 0;
}

.split--img-right .split__box {
  margin-left: calc(-1 * var(--page-pad));
  margin-right: calc(-1 * var(--box-overlap));
  padding: 56px calc(var(--box-overlap) + 40px) 56px var(--page-pad);
}

/* ------- Box ------- */
.split__box {
  width: 100%;
  position: relative;
  z-index: 2;
  margin-top: calc(-1 * var(--box-overlap));
  margin-bottom: calc(-1 * var(--box-overlap));
  box-shadow: 0 24px 80px rgba(4,26,61,0.22), 0 4px 16px rgba(0,0,0,0.12);
}

.split__box--navy    { background: var(--blue); }
.split__box--darkgrey { background: var(--dark-metal); }

.sect-num {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  font-size: clamp(5rem, 7vw, 8rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: rgba(255,255,255,0.06);
  margin-bottom: -1rem;
  user-select: none;
}

.split__headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
  color: var(--white);
  margin-bottom: 0;
}

.split__body {
  font-size: clamp(0.88rem, 1vw, 1rem);
  line-height: 1.7;
  color: var(--metal);
  margin-bottom: 1rem;
  max-width: 44ch;
}

/* ------- Service list (Section 01) ------- */
.service-list {
  margin: 0 0 1.75rem;
}

.service-row {
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 0 16px;
  padding: 18px 0;
  border-top: 1px solid rgba(255,255,255,0.08);
  /* Arrow rows align to the first line, and they align by BASELINE, not
     by box top: the arrow is a fixed 16px against a 0.92rem name, so
     top alignment left the glyph riding 2.5px low. */
  align-items: baseline;
}

.service-row:last-child { border-bottom: 1px solid rgba(255,255,255,0.08); }

.service-n {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  /* grey-70 is 3.85:1 on navy and fails AA; grey-50 clears it at 6.53:1,
     matching .service-band--navy .service-band__num. No page renders a
     numbered service row today (split-section always emits the arrow),
     so this is a latent case closed, not a visible change. */
  color: var(--grey-50);
  padding-top: 2px;
}

.service-name {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--white);
  margin-bottom: 4px;
}

/* Item body inside a split's service rows (Home service cards,
   Facilities storage and modes lists). 0.83rem computed to 13.28px,
   which passed contrast but was below the site's own comfortable
   reading floor for running copy on navy; 0.94rem is 15.04px
   (2026-08-10, legibility pass). Colour and weight are unchanged, and
   this is the only size that moves: the lede, .split__body, labels
   and headings all keep their scale. line-height stays the unitless
   1.55, so leading scales with the size (13.28/20.58 becomes
   15.04/23.31) and needs no separate adjustment. */
.service-desc {
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--metal);
}

/* ------- Tags (Section 02) ------- */
.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 1.25rem 0 1.75rem;
}

.tag {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--metal);
  border: 1px solid rgba(145,162,172,0.3);
  padding: 5px 12px;
}

.coord-block {
  display: flex;
  gap: 20px;
  margin-top: 1.5rem;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--metal);
}

/* ------- Parent stats (Section 04) ------- */
.parent-stats {
  display: flex;
  align-items: center;
  gap: 0;
  margin: 1.75rem 0;
}

.parent-stat { flex: 1; }

.parent-stat-num {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  font-size: clamp(2.5rem, 4vw, 4.5rem);
  letter-spacing: -0.04em;
  line-height: 1;
  color: rgba(255,255,255,0.2);
}

.parent-stat-label {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--metal);
  margin-top: 6px;
}

.parent-stat-div {
  width: 1px;
  height: 60px;
  background: rgba(255,255,255,0.1);
  margin: 0 28px;
  flex-shrink: 0;
}

/* ------------------------------------------------------------
   STATS BAND — Section 03, full-width dark navy
------------------------------------------------------------ */
.stats-band {
  background: var(--blue);
  padding: 100px var(--copy-edge);
  position: relative;
  z-index: 1;
}

.stats-inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  max-width: var(--content-max);
  margin: 0 auto;
  /* default align-items: stretch — all stat columns same height */
}

/* Each stat is a flex column: number fills space, rule/label/sub at bottom */
.stat {
  display: flex;
  flex-direction: column;
  padding: 0 clamp(20px, 3vw, 44px);
  border-right: 1px solid rgba(145, 162, 172, 0.3);
}
.stat:first-child { padding-left: 0; }
.stat:last-child  { border-right: none; padding-right: 0; }

/* Number fills available height; text bottom-aligns so rule lines up */
.stat-num {
  flex: 1;
  display: flex;
  align-items: flex-end;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  font-size: clamp(3.5rem, 8vw, 7rem);
  line-height: 0.88;
  letter-spacing: -0.04em;
  color: var(--white);
  padding-bottom: 0.5rem;
}

.stat-rule {
  width: 100%;
  height: 1px;
  background: var(--green);
  margin: 0 0 0.6rem;
  flex-shrink: 0;
}

.stat-label {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--white);
  margin-bottom: 4px;
}

.stat-sub {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.78rem;
  color: var(--metal);
  letter-spacing: 0.05em;
}

.stats-divider {
  display: none;
}

/* ------------------------------------------------------------
   COMMODITIES BAND — static off-white section
------------------------------------------------------------ */
.commodities-band {
  background: var(--off-white);
  padding: 80px var(--copy-edge);
  margin-top: 160px;
}

.commodities-inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.commodities-label {
  display: flex;
  align-items: baseline; /* arrow on the first line when wrapped */
  gap: var(--arrow-gap);
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--grey-90); /* metal is 2.41:1 on off-white; grey-90 clears AA at 7.2:1 */
  margin-bottom: 1.5rem;
}

.commodities-list {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--blue);
  line-height: 1.6;
}

/* A commodity never splits across lines (2026-08-08, Ed): "INDUSTRIAL
   MINERALS" broke in two mid-item on phones, which read as two
   commodities. Every item is its own unbreakable span at every width.
   Safe on desktop by measurement: the longest item is 17.06em wide, so
   at the 2.4rem cap it needs 655px of the 1185px content column, and
   the ratio only improves as the clamp scales down with the viewport.
   The mobile size below is derived from the same 17.06em; if a longer
   commodity is ever added, re-measure and re-derive it. */
.comm-item { white-space: nowrap; }

.comm-dot {
  color: var(--grey-70); /* green is 2.58:1 on off-white; grey-70 clears the 3:1 non-text minimum at 4.1:1 */
  padding: 0 0.2rem;
}

/* ------------------------------------------------------------
   SERVICE BAND — full-width editorial list section (Services).
   Light or navy; numbered index items or arrow rows; optional
   card-scale offset figure beside the header.
------------------------------------------------------------ */
.service-band {
  padding: 120px var(--copy-edge);
}

.service-band--light { background: var(--off-white); color: var(--blue); }
.service-band--navy  { background: var(--blue);      color: var(--white); }

.service-band__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

/* Watermark numeral follows the split-section spec; light surface
   needs the navy-tinted ghost instead of the white one.
   Alpha 0.11 (2026-08-08, Ed): at 0.05 the numeral measured 1.10:1
   against the off-white and effectively disappeared, while the navy
   band's white ghost sits at 1.17:1 — the light watermark was the
   fainter of the two. 0.11 reads as 1.25:1: unmistakably a design
   element, still nowhere near text contrast, so it stays behind the
   content. The navy bands' rgba(255,255,255,0.06) is unchanged. */
.service-band--light .sect-num { color: rgba(4, 26, 61, 0.11); }

/* Eyebrow on off-white: metal is 2.41:1 there; grey-90 clears AA
   (same correction as .commodities-label) */
.service-band--light .label-sm { color: var(--grey-90); }

.service-band__head-text { margin-bottom: 56px; }

/* Figure variant: the inner container becomes a 2-column grid. The
   right rail track carries the image cap (minmax max = 440px) so the
   cap lives on the TRACK, not the item — an item in an auto track can
   overflow during lazy-load reflow (the bug that let the packaging
   photo paint at its 783px natural width). Composition: figure top
   edge on the band content's first grid line, right edge flush to the
   1400px container's last grid line, natural height, no overhang. */
.service-band__inner--figure {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 440px);
  grid-template-areas:
    "head fig"
    "list fig";
  column-gap: 80px;
  align-items: start;
}

.service-band__inner--figure .service-band__head-text { grid-area: head; }
.service-band__inner--figure .service-band__list      { grid-area: list; }

.service-band__headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
}

.service-band--light .service-band__headline { color: var(--blue); }
.service-band--navy  .service-band__headline { color: var(--white); }

.service-band__intro {
  font-size: clamp(0.88rem, 1vw, 1rem);
  line-height: 1.7;
  max-width: 52ch;
}

.service-band--light .service-band__intro { color: var(--grey-90); }
.service-band--navy  .service-band__intro { color: var(--metal); }

.service-band__figure {
  grid-area: fig;
  width: 100%;
  margin: 0;
}

.service-band__figure img {
  width: 100%;
  height: auto;
  display: block;
}

/* Items flow in CSS columns (not grid) so each row keeps its own
   height and the rhythm stays even when a name wraps */
.service-band__list {
  column-count: 2;
  column-gap: 80px;
}

.service-band__item {
  break-inside: avoid;
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: 0 14px;
  padding: 18px 0;
  /* Index figure sits on the name's baseline, same as .service-row.
     Baseline, not box top: the figure is either a fixed 16px arrow or
     a 0.68rem numeral, and neither top-aligns onto the name's cap
     line on its own. This replaces the old padding-top optical nudge
     on .service-band__num, which only ever suited the numeral. */
  align-items: baseline;
}

.service-band--navy  .service-band__item { border-top: 1px solid rgba(255,255,255,0.08); }
.service-band--light .service-band__item { border-top: 1px solid rgba(4,26,61,0.12); }

.service-band__num {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
}

.service-band--navy  .service-band__num { color: var(--grey-50); } /* grey-70 fails AA on navy */
.service-band--light .service-band__num { color: var(--grey-90); }

.service-band__name {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.service-band--navy  .service-band__name { color: var(--white); }
.service-band--light .service-band__name { color: var(--blue); }

.service-band__note {
  font-size: 0.78rem;
  line-height: 1.5;
  margin-top: 2px;
}

.service-band--navy  .service-band__note { color: var(--metal); }
.service-band--light .service-band__note { color: var(--grey-90); }

/* Section spacing: bands carry their own padding; splits need the
   same breathing room against them as against stats bands */
.split + .service-band,
.service-band + .split { margin-top: 160px; }

/* ------------------------------------------------------------
   CONTACTS SECTION (About page)
------------------------------------------------------------ */
.contacts-section {
  background: var(--blue);
  padding: 100px var(--copy-edge);
  margin-top: 160px;
  border-top: 1px solid rgba(255,255,255,0.08);
}

.contacts-inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.contacts-headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.8rem, 5vw, 5.5rem);
  color: var(--white);
  margin: 0 0 3rem;
}

.contacts-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid rgba(255,255,255,0.08);
}

.contact-card {
  padding: 2.5rem 2.5rem 2.5rem 0;
  border-right: 1px solid rgba(255,255,255,0.08);
}

.contact-card:last-child {
  border-right: none;
  padding-right: 0;
}

.contact-card:not(:first-child) {
  padding-left: 2.5rem;
}

.contact-card-label {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--metal);
  margin-bottom: 1rem;
}

.contact-detail {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.95rem;
  color: var(--white);
  line-height: 1.8;
  font-style: normal;
  transition: color 0.2s ease;
}

a.contact-detail:hover { color: var(--green); }

.contact-detail--meta {
  color: var(--metal);
  font-size: 0.85rem;
}

/* External links in a contact card follow the locked link standard
   (2026-08-08): bold white on the navy band, no underline at rest, the
   green underline on hover and keyboard focus. The old treatment
   (metal at rest, green TEXT on hover) broke the standing rule that
   green is never readable text on dark — only the footer link hover
   carries that exception. */
a.contact-detail--link {
  color: var(--white);
  font-weight: 700;
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
/* a.contact-detail:hover above is (0,2,1); these must match it to win */
a.contact-detail--link:hover,
a.contact-detail--link:focus-visible {
  color: var(--white);
  text-decoration-line: underline;
}
a.contact-detail--link:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

/* ------------------------------------------------------------
   CTA SECTION — the site-wide closing band above the footer.
   Photo background + navy overlay when an image is set (the
   standard); flat navy as the no-image fallback.
------------------------------------------------------------ */
/* The LEFT padding is the copy edge, as everywhere else on the site.
   The RIGHT padding is only --page-pad, and that is deliberate: the
   copy-edge rule governs where copy STARTS, and the CTA right cell is
   named in that rule as a grid-positioned column the edge does not
   apply to. Symmetric copy-edge padding was the root of the band's
   column collapse (2026-08-10, Ed): at 1101px the --copy-floor engages
   and takes 80px off EACH side, so the band lost 160px of working
   width at the exact point the display type stepped up, and the
   headline column went BACKWARDS as the viewport grew (643px at
   1100px, 390px at 1101px). Releasing the right side returns ~100px
   of that in the starved 1101 to 1640 range. Above 1640 nothing moves:
   --content-max caps .cta-inner at 1400 and the band's right edge
   lands exactly where every other section's does. */
.cta-section {
  position: relative;
  overflow: hidden;
  background: var(--blue);
  padding: 120px var(--page-pad) 120px var(--copy-edge);
  border-top: 1px solid var(--green);
  margin-top: 0;
}

.cta-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  z-index: 0;
}

/* Same 60% navy as the hero overlay: the approved photo-under-text treatment */
.cta-overlay {
  position: absolute;
  inset: 0;
  background: rgba(4, 26, 61, 0.6);
  z-index: 1;
}

/* Asymmetric by design. An even 1fr 1fr split gave the headline half
   the band while the headline is the only thing in it that cannot
   reflow: the display type scales at 5vw and the longest line on the
   site, the Facilities "GROUND TO STORE IT.", measures 9.474em, so it
   needs roughly 60% of the working width at every desktop size while
   the body column only ever needs enough for the pill button. The
   button is the hard floor here at 284px intrinsic (a pill never
   wraps), which is why the right track is minmax(284px, 1fr) rather
   than a bare fr: it can never be squeezed under its own content, and
   the left track carries any shortfall instead of overflowing.

   The ratio steps at 1100 because the band's geometry genuinely
   changes there: below it the copy floor follows --page-pad and the
   band is wide, so the body column can stay generous; above it the
   floor engages and the headline needs the larger share. Derived
   against 9.474em headroom at 901, 1101, 1440 and 1920; re-derive both
   ratios if a longer CTA headline is ever authored.

   margin-right auto, not margin auto: with an asymmetric padding pair
   the old centering would have pushed the inner off the copy rail on
   wide screens. Left-aligning pins the first column to the copy edge
   and lets --content-max cap the width from the right. */
.cta-inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1.85fr) minmax(284px, 1fr);
  gap: 60px;
  align-items: center;
  max-width: var(--content-max);
  margin: 0 auto 0 0;
}

/* Over a photo every piece of small text steps to grey-10 (the
   over-photography rule at .hero-eyebrow; measured against the actual
   composited pixels of the CTA photograph under the 60% overlay).
   The eyebrow was the miss: it kept metal and measured 4.09:1 on the
   band average (2.00:1 at the brightest pixel), an AA failure. The
   body and phone line passed at grey-30 (6.57 and 6.94) but thinned to
   ~3.2:1 in the bright water, so they take the same step. Measured at
   grey-10: eyebrow 8.70, body 9.14, phone line 9.66. */
.cta-section--photo .cta-body,
.cta-section--photo .cta-phone-line,
.cta-section--photo .label-sm { color: var(--grey-10); }

.cta-headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.8rem, 5vw, 5.5rem);
  color: var(--white);
}

.cta-body {
  font-size: clamp(0.9rem, 1.05vw, 1.05rem);
  line-height: 1.7;
  color: var(--metal);
  max-width: 44ch;
  margin-bottom: 2rem;
}

.cta-phone-line {
  margin-top: 1rem;
  font-size: 0.85rem;
  color: var(--metal);
}

.cta-phone {
  color: var(--white);
  font-weight: 600;
}
.cta-phone:hover { text-decoration: underline; }

/* ------------------------------------------------------------
   FOOTER
------------------------------------------------------------ */
/* Footer keeps --page-pad padding (NOT --copy-edge) so the decorative
   full-width green rule keeps its 60px inset. The copy inners make up
   the difference with side margins: inside this padded box,
   (copy-floor - page-pad) + centering reproduces the copy edge
   exactly, and collapses to 0 below 1100px where the floor follows
   the gutter (tablet/mobile unchanged). */
.site-footer {
  --footer-inset: max(calc(var(--copy-floor) - var(--page-pad)), calc((100% - var(--content-max)) / 2));
  background: var(--blue);
  padding: 0 var(--page-pad) 48px;
}

/* The green rule runs the FULL viewport width at every breakpoint
   (2026-08-08, Ed): it is the site's top-of-footer divider, so it
   reads as a band edge, not as a content-width underline. The footer
   keeps its --page-pad gutter for everything else; the rule cancels
   that gutter with matching negative side margins and width: auto, so
   it fills the footer's border box exactly — flush to both viewport
   edges, never a pixel past them (no overflow to clip). */
.footer-rule {
  width: auto;
  margin-left: calc(-1 * var(--page-pad));
  margin-right: calc(-1 * var(--page-pad));
  height: 1px;
  background: var(--green);
  opacity: 0.5;
  margin-bottom: 72px;
}

.footer-inner {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1.5fr 1.5fr;
  gap: 60px;
  max-width: var(--content-max);
  margin: 0 var(--footer-inset) 56px;
}

/* Logo only since 2026-08-08 (Ed): the FAIR / RELIABLE / GLOBAL
   tagline and its .footer-tagline rule were removed site-wide. */
.footer-brand { display: flex; flex-direction: column; gap: 12px; }

.footer-nav ul { display: flex; flex-direction: column; gap: 10px; }

.footer-nav a {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.85rem;
  color: var(--metal);
  transition: color 0.2s ease;
}
.footer-nav a:hover { color: var(--white); }

/* The footer menu items carry the green arrow glyph in the markup at
   every width (filter in inc/theme-helpers.php), but it only shows in
   the mobile footer, where the nav is restyled as a block of tap
   targets. The desktop footer is unchanged. */
.footer-nav .arrow { display: none; }

.footer-address {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.85rem;
  line-height: 1.8;
  color: var(--metal);
  margin-bottom: 12px;
}

.footer-link {
  display: block;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.85rem;
  color: var(--metal);
  margin-bottom: 6px;
  transition: color 0.2s ease;
}
.footer-link:hover { color: var(--green); }

/* Labeled contact lines (Phone: / Fax:). The fax number is plain text
   by rule; the phone anchor sits inline after its label. */
.footer-contact-line {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.85rem;
  color: var(--metal);
  margin-bottom: 6px;
}
.footer-contact-line .footer-link {
  display: inline;
  margin-bottom: 0;
}

.footer-parent-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--grey-50); /* grey-70 is 3.85:1 on navy; grey-50 clears AA at 6.53:1 */
  font-family: 'Source Sans 3', sans-serif;
  margin-bottom: 6px;
}

.footer-parent-name {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--white);
  margin-bottom: 4px;
}

/* The parent-company attribution links to the CRONIMET Group site
   (2026-08-08, Ed). Locked link standard on navy: bold white, no
   underline at rest, green underline on hover and keyboard focus.
   inline-flex so the mobile footer can give it a real tap target. */
.footer-parent-link {
  display: inline-flex;
  align-items: center;
  color: var(--white);
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.footer-parent-link:hover,
.footer-parent-link:focus-visible { text-decoration-line: underline; }
.footer-parent-link:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

.footer-parent-city {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.8rem;
  color: var(--metal);
}

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: var(--content-max);
  margin: 0 var(--footer-inset);
  padding-top: 28px;
  border-top: 1px solid rgba(255,255,255,0.06);
}

.footer-legal {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.75rem;
  color: var(--grey-50); /* grey-70 fails AA on navy, see footer-parent-label */
}
.footer-legal a { color: var(--grey-50); transition: color 0.2s ease; }
.footer-legal a:hover { color: var(--white); }

/* ------------------------------------------------------------
   SCROLL ANIMATION INITIAL STATES (GSAP controls reveal)
   All pre-reveal offsets live under html.js (end of file) so they
   only ever apply when the reveal will actually run. The old
   ungated .split--img-* translateX(±40px) rules here parked
   visible boxes 40px off the copy edge for reduced-motion and
   no-JS visitors (html.js never lands for them); never reintroduce
   an offset outside the html.js gate.
------------------------------------------------------------ */

/* ------------------------------------------------------------
   RESPONSIVE
------------------------------------------------------------ */
@media (max-width: 1100px) {
  :root {
    --split-img: 52vw;
    --page-pad: 40px;
    --box-overlap: 60px;
    --copy-floor: var(--page-pad); /* copy edge follows the gutter below true desktop */
  }

  /* Below the copy floor the band is wide again, so the body column
     gets its share back (see the ratio note at .cta-inner). */
  .cta-inner { grid-template-columns: minmax(0, 1.6fr) minmax(284px, 1fr); }
}

@media (max-width: 900px) {
  :root {
    --split-img: 100%;
    --page-pad: 28px;
    --box-overlap: 0px;
  }

  /* Stack split sections vertically on tablet/mobile */
  .split--img-left,
  .split--img-right {
    grid-template-columns: 1fr;
    grid-template-rows: 360px auto;
  }

  .split--img-left .split__img-col,
  .split--img-right .split__img-col {
    grid-row: 1;
    grid-column: 1;
    min-height: 360px;
  }

  /* ONE mobile edge (standing rule): the navy panel runs full-bleed
     and its interior padding IS --page-pad, so the box copy lands on
     the same edge as the logo. The old 28px column padding + 32px box
     padding stacked to a 60px inset — a second, deeper edge. */
  .split--img-left .split__content-col,
  .split--img-right .split__content-col {
    grid-row: 2;
    grid-column: 1;
    /* MOBILE BAND RHYTHM (2026-08-08, Ed): the tail of off-white below
       a navy box. See the band-transition block at the end of this
       breakpoint for the full set. */
    padding: 0 0 32px;
  }

  .split__img-bg { inset: 0; }

  .split--img-left .split__box,
  .split--img-right .split__box {
    margin: -40px 0 0;
    padding: 48px var(--page-pad);
  }

  /* ---------- MOBILE BAND RHYTHM (2026-08-08, Ed, LOCKED) ----------
     One set of numbers for every band transition at <=900px, so a
     navy band ending and a light section starting reads as a seam,
     not as a hole. Bands carry 56px of their own vertical padding
     (the closing CTA photo band keeps 64px so the headline has room
     over the image), the gap BETWEEN bands is 32px, and the tail of
     off-white under a split's navy box is 32px. Content-to-content
     across a split-to-band seam is therefore 120px instead of the
     old 192px, of which 64px is visible background instead of 120px.
     Several of these bands (stats, commodities, CTA) never had a
     mobile padding override at all and were still carrying their
     100 to 120px desktop values on a 390px screen — that was most of
     the dead space. Desktop (>900px) is untouched. */
  .hero + .split           { margin-top: 32px; }
  .page-header + .split    { margin-top: 32px; }
  .split + .split          { margin-top: 0; }
  .split + .stats-band     { margin-top: 32px; }
  .stats-band + .split     { margin-top: 32px; }
  .stats-band              { padding-top: 56px; padding-bottom: 56px; }
  .commodities-band        { margin-top: 32px; padding-top: 56px; padding-bottom: 56px; }

  /* COMMODITIES ON MOBILE: a stacked list, one item per line, no dots
     (2026-08-08, Ed). The inline dot-separated run broke badly here:
     separators stranded at the start of wrapped lines and multi-word
     commodities split across lines. Stacking is the only layout where
     neither can happen, because there is never more than one item on a
     line for a separator to sit between. The dots are hidden rather
     than removed from the markup so desktop keeps its run untouched;
     whitespace-only text between the spans is not rendered as a flex
     item, so nothing is left behind.

     The size is derived, not guessed: the longest item is 17.06em
     wide, so it fits the shared copy edge whenever the font is at most
     (available width / 17.06). Available width here is the measure
     minus the bullet gutter below, and / 18 against a 17.06em item
     keeps about 5% headroom at 360px (17.0px against an 17.9px
     ceiling); the size rises with the screen until the 1.5rem cap
     takes over at ~486px. Re-derive it if a longer commodity is ever
     added OR if the gutter changes; the nowrap on .comm-item means a
     too-long item would overflow rather than wrap, and the mobile QA
     sweep is what would catch it. */
  .commodities-list {
    --comm-bullet: 6px;
    --comm-gutter: calc(var(--comm-bullet) + var(--arrow-gap));
    display: flex;
    flex-direction: column;
    font-size: min(1.5rem, calc((100vw - 40px - var(--comm-gutter)) / 18));
    line-height: 1.3;
  }
  .commodities-list .comm-dot { display: none; }

  /* MOBILE BULLETS (2026-08-10, Ed). Stacking above drops the
     separators, which left the items with no marker at all. Each item
     gets a leading dot at <=900px only; the desktop run is untouched.
     It is a ::before, so no markup changes and the marker is not
     announced by a screen reader, which is what we want: it is
     decoration, and the items are already read as separate items.

     Colour: --grey-70, the SAME token as the .comm-dot separators
     above (2026-08-10, Ed). One list in two layouts reads as one
     component, so its marker is one colour; it launched green the same
     day and that made the two halves look like two different lists.
     Grey-70 is 4.1:1 on off-white, past the 3:1 non-text minimum, and
     is the reason the separators are not green either (green is 2.58:1
     there).

     Size: 6px, a quarter of the 24px item text at the top of this
     range. The dot has to read as a marker and not as a character in
     the line, which is exactly what the desktop separator does here,
     since it inherits the full text size.

     Spacing: --arrow-gap, the same 8px that sets every arrow-plus-label
     pair on the site, so the marker keeps the site's visual rhythm.
     The gap is the whole spacing, so the dot zeroes the list's 0.04em
     tracking the way .arrow does (see the --arrow-gap note in :root).

     Hanging indent: padding-left holds the text column at bullet plus
     gap, and the matching negative text-indent pulls the first line
     (which starts with the dot) back out to the edge. So the DOT lands
     on --page-pad, the text starts after it, and a wrapped second line
     aligns under the text rather than running back under the dot. Same
     grammar as the arrow rows, where the glyph owns the mobile edge and
     the label sits in the gutter beside it. text-indent applies because
     the items are flex children of the stacked list and so are
     blockified; on desktop they are inline spans and neither property
     is in play.

     Vertical: the dot centers on the CAP line, not the baseline, or it
     sits visibly low against all-caps type. An inline-block with no
     in-flow content takes its baseline from its bottom margin edge, so
     raising it by (cap middle - half the dot) puts the dot's middle on
     the middle of the caps. Source Sans 3's cap height is 0.66em, so
     the cap middle is 0.33em above the baseline. */
  .commodities-list .comm-item {
    padding-left: var(--comm-gutter);
    text-indent: calc(-1 * var(--comm-gutter));
  }

  .commodities-list .comm-item::before {
    content: "";
    display: inline-block;
    width: var(--comm-bullet);
    height: var(--comm-bullet);
    margin-right: var(--arrow-gap);
    border-radius: 50%;
    background: var(--grey-70);
    letter-spacing: 0;
    vertical-align: calc(0.33em - var(--comm-bullet) / 2);
  }
  .contacts-section        { margin-top: 32px; padding: 56px var(--page-pad); }
  .cta-section             { margin-top: 0; padding-top: 64px; padding-bottom: 64px; }

  /* Service band: stack head -> figure -> list in one column */
  .service-band            { padding: 56px var(--page-pad); }
  .split + .service-band,
  .service-band + .split   { margin-top: 32px; }
  .service-band__head-text { margin-bottom: 40px; }
  .service-band__inner--figure {
    grid-template-columns: 1fr;
    grid-template-areas: "head" "fig" "list";
    column-gap: 0;
  }
  .service-band__figure    { max-width: 440px; margin: 0 0 40px; }
  .service-band__list      { column-count: 1; }

  /* Index figures and arrows stay INLINE with their text at every
     breakpoint (standing rule, 2026-08-07). Both list components drop
     their hanging column here and put the figure and the first line of
     the name in ONE line box: the wrapper collapses with
     display: contents and the name goes inline, so the two sit side by
     side on the item's first line, the figure itself lands on the
     shared --page-pad edge, and the copy wraps back to that same edge
     below instead of hanging off a second, deeper one (the reason the
     index used to stack). A shared line box also baseline-aligns the
     figure for free, no float and no optical nudge. The note/desc
     follows as its own block on the edge; the name's block margin does
     not survive going inline, so the gap moves onto that block. */
  .service-band__item { display: block; padding: 18px 0; }
  .service-band__body { display: contents; }
  .service-band__num  { margin-right: 10px; }
  .service-band__note { margin-top: 4px; }

  .service-row { display: block; }
  .service-row > div { display: contents; }
  .service-row > .arrow { margin-right: 10px; }
  .service-name { display: inline; }
  .service-desc { margin-top: 4px; }

  /* Contacts: stack on tablet */
  .contacts-grid { grid-template-columns: 1fr; }
  .contact-card,
  .contact-card:not(:first-child) { padding: 1.5rem 0; border-right: none; border-top: 1px solid rgba(255,255,255,0.08); }
  .contact-card:first-child { border-top: none; padding-top: 1.5rem; }

  /* Stats: 2×2 grid on tablet */
  .stats-inner {
    grid-template-columns: 1fr 1fr;
    gap: 0;
  }

  .stat { padding: 0 clamp(16px, 2.5vw, 28px); }
  /* 2x2 grid: both left-column stats (1 and 3) sit flush on the mobile
     edge; only first-child was zeroed before, leaving stat 3 inset */
  .stat:nth-child(odd) { padding-left: 0; }
  .stat:nth-child(2) { border-right: none; padding-right: 0; }
  .stat:nth-child(n+3) {
    border-top: 1px solid rgba(145, 162, 172, 0.2);
    margin-top: 36px;
    padding-top: 36px;
  }
  .stat:nth-child(4) { border-right: none; padding-right: 0; }

  /* CTA stacks */
  .cta-inner { grid-template-columns: 1fr; gap: 40px; }

  /* ---------- MOBILE FOOTER (2026-08-08, Ed) ----------
     Four clearly separated blocks instead of one undifferentiated
     grey column: the brand lock-up centered under a green rule, the
     nav as a stack of real tap targets, the contact block with the
     phone and email reading as actions, and the parent-company
     attribution centered at the bottom. Hairlines and spacing carry
     the separation. Desktop (>900px) is untouched.

     Deliberate exception to the one-mobile-edge rule, at Ed's
     instruction: the brand block and the attribution are CENTERED.
     Everything else in the footer (nav, contact, legal) still lands
     on the shared --page-pad edge, and the arrow glyph is what sits
     on it in the nav rows, per the site-wide arrow grammar. */
  .footer-rule { margin-bottom: 48px; }

  .footer-inner {
    display: block;
    margin-bottom: 32px;
  }

  /* 1. Brand: the logo lock-up, centered. No divider under it
     (2026-08-08, Ed): the block launched with the site's 40px green
     rule below the lock-up, but with the tagline gone the same day it
     was a rule under a logo with nothing to divide, and the hairline
     that opens the nav block below already separates the two. */
  .footer-brand {
    align-items: center;
    text-align: center;
  }

  /* 2. Nav: white, a step up in size and weight, 44px tap targets,
     green arrow on the copy edge so the rows read as actions */
  .footer-nav {
    margin-top: 28px;
    border-top: 1px solid rgba(255, 255, 255, 0.10);
  }
  .footer-nav ul { gap: 0; }
  .footer-nav li + li { border-top: 1px solid rgba(255, 255, 255, 0.06); }
  .footer-nav .arrow { display: inline; }
  .footer-nav a {
    display: flex;
    align-items: center;
    gap: var(--arrow-gap);
    min-height: 48px; /* past the 44px minimum with the row borders */
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--white);
  }
  /* Green on tap/hover is the footer's sanctioned exception to
     "green is never readable text on dark" */
  .footer-nav a:hover,
  .footer-nav a:active { color: var(--green); }
  .footer-nav a:focus-visible,
  .footer-contact a:focus-visible,
  .footer-parent-link:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 2px;
  }

  /* 3. Contact: address stays quiet grey, phone and email read as
     actions in white bold and carry their own 44px targets */
  .footer-contact {
    margin-top: 28px;
    padding-top: 24px;
    border-top: 1px solid rgba(255, 255, 255, 0.10);
  }
  .footer-address { margin-bottom: 14px; }
  .footer-contact-line {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 0;
    font-size: 0.9rem;
  }
  .footer-contact .footer-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    margin-bottom: 0;
    color: var(--white);
    font-weight: 700;
    font-size: 0.95rem;
  }
  .footer-contact .footer-link:hover,
  .footer-contact .footer-link:active { color: var(--green); }

  /* 4. Attribution: centered at the bottom, the name a tappable link */
  .footer-parent {
    margin-top: 28px;
    padding-top: 28px;
    border-top: 1px solid rgba(255, 255, 255, 0.10);
    text-align: center;
  }
  .footer-parent-name { font-size: 1rem; margin-bottom: 2px; }
  .footer-parent-link { min-height: 44px; padding: 0 4px; }

  /* Nav */
  .nav-toggle { display: flex; }

  .nav-links {
    display: none;
    position: absolute;
    top: var(--nav-h);
    left: 0;
    width: 100%;
    background: rgba(4,26,61,0.98);
    flex-direction: column;
    padding: 20px var(--page-pad) 28px; /* menu text on the shared mobile edge */
    gap: 0;
    border-bottom: 1px solid rgba(255,255,255,0.08);
  }
  .nav-links.open { display: flex; }
  .nav-links li { border-bottom: 1px solid rgba(255,255,255,0.06); }
  .nav-links li:last-child { border-bottom: none; } /* no divider under the Contact Us pill */
  /* 44px minimum touch targets: text size + vertical padding keep every row past the minimum */
  .nav-link { display: block; padding: 14px 0; font-size: 1.125rem; }
  .nav-cta { margin-top: 12px; padding: 13px 44px; text-align: center; }

  /* Hero block — reduce left margin at tablet */
  .hero-block {
    margin-left: var(--page-pad);
    max-width: 90vw;
  }

  /* Scroll indicator hugs the shared mobile gutter (desktop keeps 60) */
  .hero-scroll { right: var(--page-pad); }

  /* Page header — same margin reduction, tighter vertical rhythm.
     Figure variant stacks: text first, then the photo full-bleed at a
     modest fixed height, flush to the band bottom, no hang. */
  .page-header { padding: calc(var(--nav-h) + 44px) 0 44px; }
  .page-header__block { margin-left: var(--page-pad); padding-right: var(--page-pad); }
  .page-header--figure { padding-bottom: 0; }
  /* padding-right must be restated: the desktop figure rule zeroes it
     (higher specificity than the plain mobile block rule), which let
     the lede run to the right screen edge on phones */
  .page-header--figure .page-header__block { margin-right: 0; margin-bottom: 40px; padding-right: var(--page-pad); }
  .page-header__figure { position: relative; inset: auto; width: 100%; height: 220px; }

  /* Intro band: no figure hang to clear on mobile (the header photo is
     a flush relative strip), so the lead drops to the section rhythm */
  .intro-band { padding-top: 44px; }
  .intro-band + .split { margin-top: 32px; }
}

@media (max-width: 768px) {
  /* Hero body — flow freely, no width constraint */
  .hero-body {
    max-width: 100%;
    font-size: 0.95rem;
  }

  /* The hero CTA treatments are identical on desktop and mobile
     (2026-08-08, Ed): GET IN TOUCH is the solid green pill, OUR
     SERVICES is the outline. The old rule here stripped the fill off
     whichever button was filled, so both read as outlines on a phone
     and the hero had no primary action at the width where it matters
     most. Both stay full width in the stacked layout below. */
}

@media (max-width: 600px) {
  :root { --page-pad: 20px; }

  /* CTA headline: DERIVED from the longest line, the same way the
     stacked commodities size is (2026-08-10, Ed). The band is one
     column here, so the measure is exactly 100vw - 2 * 20px, and the
     longest CTA line on the site is 9.474em. Fitting it needs
     9.474f <= 100vw - 40, i.e. f <= 0.1056vw - 4.22, which at 360px
     allows 0.0939vw; 9.1vw keeps ~3% headroom there and more above.
     The 2.8rem cap is the unchanged desktop-floor value and still
     governs from about 492px up, so nothing above that width moves.
     Re-derive if a longer CTA headline is ever authored. */
  .cta-headline { font-size: clamp(2rem, 9.1vw, 2.8rem); }

  /* Hero block — full width with the shared --page-pad margins */
  .hero-block {
    margin-left: var(--page-pad);
    margin-right: var(--page-pad);
    max-width: calc(100vw - 2 * var(--page-pad));
    gap: 1.1rem;
  }

  /* Shift the centered text block up so the stacked CTAs clear the scroll
     indicator on short viewports. Margin on the block moves it within the
     centering; padding on .hero would drag the indicator up with it because
     .hero-scroll anchors to .hero-content's bottom edge. */
  .hero-block { margin-bottom: 130px; }

  /* Stats: single column on mobile */
  .stats-inner {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .stat,
  .stat:nth-child(2),
  .stat:nth-child(n+3),
  .stat:nth-child(4) {
    border-right: none;
    border-top: 1px solid rgba(145, 162, 172, 0.2);
    padding: 28px 0;
    margin-top: 0;
  }

  .stat:first-child {
    border-top: none;
    padding-top: 0;
  }

  /* Left-aligned, not centered: mobile copy sits on ONE edge, and a
     centered legal line floats off it. (The footer's brand block and
     parent attribution are the two sanctioned centered exceptions,
     see the mobile footer block above; the legal strip is not one.) */
  .footer-bottom { flex-direction: column; gap: 10px; align-items: flex-start; text-align: left; }

  .hero-ctas { flex-direction: column; }

  .hero-ctas .btn-pill { justify-content: center; }
}

/* ============================================================
   WP THEME ADDITIONS (not in prototype styles.css)
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-scroll-line { animation: none; }
}

/* Pre-reveal initial states, applied only when animations will run. */
html.js .hl { opacity: 0; transform: translateY(48px); }
html.js .hero-eyebrow,
html.js .hero-body,
html.js .hero-ctas { opacity: 0; transform: translateY(12px); }
html.js .page-header [data-ph-fade] { opacity: 0; transform: translateY(16px); }
html.js .hero-scroll { opacity: 0; }
/* Box and stat reveals: initial state here, class flip from the
   IntersectionObserver in main.js, motion via CSS transition
   (cubic-bezier below = power2.out). The resting .is-revealed state is
   transform: none so revealed copy sits exactly on the copy rail; a
   reveal that plays late or not at all can only ever leave the element
   hidden mid-fade, never parked off the rail once revealed. */
html.js [data-animate="box"] {
  opacity: 0;
  transform: translateX(40px);
  transition: opacity 0.75s cubic-bezier(0.33, 1, 0.68, 1), transform 0.75s cubic-bezier(0.33, 1, 0.68, 1);
}
html.js [data-animate="stat"] {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.65s cubic-bezier(0.33, 1, 0.68, 1), transform 0.65s cubic-bezier(0.33, 1, 0.68, 1);
}
/* Mobile pre-reveal offset is VERTICAL: at <=900px the boxes are
   full-bleed, and a translateX offset extended the page 40px past the
   right viewport edge while un-revealed — scrollable overflow that
   iOS let the user drag sideways (the drag-the-page-off-screen bug).
   The reveal becomes a rise-up on mobile, matching the stats. */
@media (max-width: 900px) {
  html.js [data-animate="box"] { transform: translateY(24px); }
}

html.js [data-animate="box"].is-revealed,
html.js [data-animate="stat"].is-revealed,
html.js [data-animate="box-inner"].is-revealed {
  opacity: 1;
  transform: none;
}

/* SPLIT BOX REVEAL ON MOBILE reveals the COPY, not the panel
   (2026-08-08, Ed, LOCKED). At <=900px the split stacks image over
   box and the navy box sits 40px up over the photo by design (the
   overlap grammar). Fading the PANEL through that overlap painted a
   translucent navy rectangle across the bottom of the photograph for
   the length of the reveal, with the box shadow smearing another
   ~56px of navy above it: it read as a rendering fault, not as
   motion. So on mobile the panel is simply there — solid, static,
   correct from the first paint — and the reveal moves to the copy
   inside it, which never overlaps any image. The desktop reveal is
   untouched: there the panel slides in from the right over the image
   column, which is the whole point of the composition.
   .split__box-inner is a plain wrapper around the box content; it
   carries no layout of its own so the panel padding still sets the
   copy rail. */
html.js [data-animate="box-inner"] { opacity: 1; } /* inert on desktop */

@media (max-width: 900px) {
  html.js .split__box[data-animate="box"] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  html.js .split__box [data-animate="box-inner"] {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.75s cubic-bezier(0.33, 1, 0.68, 1), transform 0.75s cubic-bezier(0.33, 1, 0.68, 1);
  }
  /* Restated here, after the pre-reveal rule above: the generic
     .is-revealed rule ties this selector's specificity and loses on
     source order, which would strand the copy hidden. */
  html.js .split__box [data-animate="box-inner"].is-revealed {
    opacity: 1;
    transform: none;
  }
  html.js .split__box [data-animate="box-inner"].is-settled {
    transition: none;
  }
}

/* The hidden-document safety valve settles instantly: transition
   timelines are frozen while a document never renders, so a reveal
   with a transition would report the un-revealed computed values
   forever. is-settled drops the transition so the resting values
   apply the moment the class lands. */
html.js [data-animate="box"].is-settled,
html.js [data-animate="stat"].is-settled,
html.js [data-animate="box-inner"].is-settled {
  transition: none;
}

/* Map reveal (Contact network map, 2026-08-07): the landmass fades
   in, then the twelve markers stagger in quickly (per-marker delays
   set inline by the observer, mirroring the stats-band stagger).
   Opacity ONLY — the map and its markers never translate, so marker
   geometry is transform-free at every moment and the resting state
   needs no transform reset. The marker transition is scoped to
   opacity so the inline reveal delay never lags the focus ring or
   hover states. */
html.js [data-animate="map"] .network-map__svg {
  opacity: 0;
  transition: opacity 0.75s cubic-bezier(0.33, 1, 0.68, 1);
}
html.js [data-animate="map"] .nm-marker {
  opacity: 0;
  transition: opacity 0.3s cubic-bezier(0.33, 1, 0.68, 1);
}
html.js [data-animate="map"].is-revealed .network-map__svg,
html.js [data-animate="map"].is-revealed .nm-marker {
  opacity: 1;
}
html.js [data-animate="map"].is-settled .network-map__svg,
html.js [data-animate="map"].is-settled .nm-marker {
  transition: none;
}

/* ------------------------------------------------------------
   INTERIOR PAGES (legal shells, 404, fallback content)
------------------------------------------------------------ */
.page-interior {
  background: var(--off-white);
  color: var(--blue);
  padding: calc(var(--nav-h) + 80px) var(--copy-edge) 120px;
  min-height: 60vh;
}

.page-interior-inner {
  max-width: 840px;
  /* reading measure stays 840; the section's --copy-edge padding puts
     the column on the shared left edge (ragged right, never centered) */
  margin: 0 auto 0 0;
}

.page-interior-title {
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
  margin-bottom: 32px;
}

.page-interior-inner p {
  line-height: 1.65;
  margin-bottom: 1em;
}

.page-interior-inner h2,
.page-interior-inner h3 {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 1.6em 0 0.6em;
}

.page-interior-inner ul {
  list-style: disc;
  padding-left: 1.4em;
  margin-bottom: 1em;
}

.page-interior--404 {
  background: var(--blue);
  color: var(--metal);
}

.page-interior--404 .page-interior-title {
  color: var(--white);
}

.page-interior--404 p {
  margin-bottom: 1.5em;
}

/* Mobile band rhythm (2026-08-08): the legal shells and the 404 close
   with the CTA band, so their bottom padding joins the same 56px
   set as every other band at <=900px. */
@media (max-width: 900px) {
  .page-interior { padding-top: calc(var(--nav-h) + 56px); padding-bottom: 56px; }
}

@media (max-width: 768px) {
  .page-interior { padding-left: var(--page-pad); padding-right: var(--page-pad); }
}

/* ------------------------------------------------------------
   CONTACT FORM SECTION — one full-width NAVY band (2026-08-08
   redesign, Ed). The previous composition is retired in full: the
   white surface, the navy panel bleeding off the left viewport
   edge, the right-bleed terminal photograph and the Terminal
   Operations text block that sat on the white are all gone. The
   navy is the SECTION now, not a panel inside it, so there is no
   bleed math left here at all: the band is simply full width and
   the copy sits on the standard --copy-edge padding like every
   other band on the site.

   Page rhythm is now navy header, navy form band, grey-blue map
   section, navy footer. The header and the form band read as one
   continuous navy field, with the page header's right-bleed figure
   hanging across the seam.

   Desktop: two columns on the locked copy edge, the form left and
   a white info card right, separated by the site's standard 80px
   gutter so the card never crowds the fields. Mobile (≤900):
   stacked, form first, card below, both full width on the shared
   copy edge, with the card keeping its navy header bar and green
   rule.

   The card is the SAME component as the network map's location
   cards further down this page: square corners, navy header bar,
   green rule under it, white body, same type sizes and spacing.
   Its declarations ARE the .nm-card declarations, shared by
   selector rather than copied (see the NETWORK MAP section), so
   the two can never drift apart. It carries no pill button: info
   only.

   The fields keep the SCREENED treatment unchanged (they were
   always navy-backed, so the redesign asks nothing of them):
   filled with white at 7% over the navy, a soft lift (#162A4B
   effective), not a white box, inside a visible grey-70 border on
   all four sides (3.85:1 on navy, 3.2:1 on the fill, both past
   the 3:1 non-text minimum) at 2px so the red error flip stays
   reflow-free. White input text on the screened fill (14.3:1),
   white labels at readable size, green required glyphs (green on
   navy is 6.1:1, past even the 4.5:1 text bar; aria-required
   carries the semantics), a green focus ring on every field and
   the button, red error borders (4.6:1 on navy, 3.8:1 on the
   screened fill) with white message text carrying the words
   (17.2:1), and the standard green pill submit with navy text
   whose hover drops the fill to transparent with white text and a
   green border (the on-dark pill grammar). The audience skews
   older; the fields must read unmistakably as form fields. The
   10px input radius is the ONLY sanctioned exception to the
   sharp-or-pill corner rule (Ed, 2026-08-07).
------------------------------------------------------------ */
/* Band 1: the white intro. Horizontal padding lives on __inner so the
   navy band below can be a full-width child of this same section with
   no bleed math (--copy-edge inside it still resolves its 100% against
   the full-width section, which is what the token needs). */
.form-section {
  background: var(--white);
  padding: 0;
}

/* Mobile anchor landing (≤1100, 2026-08-07, Ed): on these widths the
   page-header figure sits directly above/into the section (flush
   220px strip at ≤900, a 60px right-side hang into the section at
   901-1100), so the desktop offset landed with the bottom half of
   that image filling the gap above the section. Subtracting the
   section's top padding lands the HEADLINE 24px below the header
   instead: the section top sits at or behind the fixed nav, so none
   of the figure is visible on arrival. Pairs with the anchor-landing
   hold in main.js, which keeps the nav shown through the anchor
   scroll on these widths (hide-on-scroll would read the programmatic
   scroll as scrolling down and slide the header away from over the
   calibrated landing). Applies to direct loads and same-page clicks
   alike, and to the reduced-motion instant jump (scroll-margin is
   honored by both). Desktop (>1100) keeps the offset above,
   untouched. */
.form-section__inner {
  max-width: calc(var(--content-max) + 2 * var(--copy-edge));
  margin: 0 auto;
  padding: 96px var(--copy-edge) 64px;
}

.form-section .form-heading {
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
  color: var(--blue);
  margin-bottom: 16px;
}

/* grey-90 on white is 7.9:1 (AA); metal would be ~2.8:1 */
.form-section .form-intro {
  color: var(--grey-90);
  line-height: 1.65;
  /* No measure cap (2026-08-08, Ed): the supporting line must sit on
     ONE line at desktop, and the old 50ch cap broke this 88-character
     sentence in two. It runs about 650px, so it fits the content
     column at every desktop width and wraps naturally once stacked. */
  max-width: none;
  margin-bottom: 0;
}

/* Band 2: the navy form band, full viewport width. The anchor lives
   here, not on the white intro above it, so every contact CTA lands on
   the form itself: the offset clears the fixed nav (worst case, header
   visible) plus breathing room. Smooth scroll comes from the html
   rule; reduced motion already flips it to auto (instant jump). One
   offset at every width now, because the thing being landed on is the
   band's own top edge rather than a headline sitting inside it. */
.form-band {
  background: var(--blue);
  padding: 90px var(--copy-edge) 120px;
  scroll-margin-top: calc(var(--nav-h) + 24px);
}

.form-band__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

/* Two columns on the copy edge: form left, contact block right, with
   the site's standard 80px gutter between them (the same one the CTA
   band and the service-band figure use). The right track is fixed
   within a narrow range so the block keeps a stable measure; the form
   takes the rest. align-items: start pins the contact block to the top
   of the column, level with the form, and leaves the navy below it
   deliberately clean rather than centering it in a tall column. */
.form-section__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
  column-gap: 80px;
  align-items: start;
}

/* The fields keep their established measure; the track can be wider
   than the form on generous screens, and that extra width is
   breathing room to the right of the fields, not stretched inputs. */
.form-section .forminator-ui.forminator-custom-form {
  max-width: 680px;
}

/* ------- The contact block, reversed out on the navy -------
   Plain type, not a card (2026-08-08 revision, Ed): the site's eyebrow
   style for the label, the same 40px green rule the other dark bands
   use under it, then the locked contact block. Hierarchy carries the
   scan: the company name and the two links are white with weight, the
   address and the fax number sit quieter in Metal Grey, which is
   6.53:1 on navy and the brand body color on dark.

   Reveal: opacity only. The generic [data-animate="box"] pre-reveal is
   a translateX(40px), and this column's right edge sits ON the copy
   rail, so a rightward offset would push it into the viewport edge
   while un-revealed. That is the horizontal-overflow trap the
   no-overflow rule names, and it is also what would flash the block
   across the band. It fades in place instead. */
.form-contact { align-self: start; }

html.js .form-contact[data-animate="box"] { transform: none; }

/* Measured, not assumed: with align-items: start on the grid, the
   eyebrow's top and Forminator's first field label top land on exactly
   the same line (both at the grid's top edge, 0px apart at 1440), so
   no nudge is needed. The margin reset only removes .label-sm's own
   0.75rem, which would otherwise double up with the rule's margin
   below. Re-measure if either component's box changes. */
.form-contact__label { margin-bottom: 0; }

.form-contact .headline-rule { margin: 0.9rem 0 1.6rem; }

.form-contact__company {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.8;
  color: var(--white);
}

.form-contact__address {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 1rem;
  line-height: 1.8;
  color: var(--metal);
  margin-bottom: 1.25rem;
}

.form-contact__line {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 1rem;
  line-height: 1.9;
  color: var(--metal);
}

/* The locked link standard on a dark surface: bold white, no underline
   at rest, green underline on hover and keyboard focus. */
.form-contact__link {
  color: var(--white);
  font-weight: 700;
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.form-contact__link:hover,
.form-contact__link:focus-visible { text-decoration-line: underline; }
.form-contact__link:focus-visible {
  outline: 2px solid var(--green); /* 6.1:1 on navy */
  outline-offset: 3px;
}

/* ------- Forminator overrides: the boxed-field spec -------
   Vertical rhythm (2026-08-07 fix): ONE spacing value, 22px,
   between every field group at every width. The grid row-gap
   covers columns stacking inside a row, the row margin covers
   row-to-row, and both use the same value. The !importants are
   load-bearing: Forminator's grid stylesheet ties our
   specificity and loads after main.css — its 20px row margin
   was silently beating our value, and its responsive rule adds
   margin-bottom: 20px to stacked columns at narrow widths,
   which doubled with the row-gap into a 40px hole below the
   Name field while every other gap sat at 20px. Zeroing the
   column margins keeps the rhythm uniform, and it holds when a
   validation message appears because the message flows inside
   its own field group. */
.form-section .forminator-ui.forminator-custom-form .forminator-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px 24px;
  margin-bottom: 22px !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-row:last-child {
  margin-bottom: 0 !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-col {
  float: none;
  width: auto;
  padding: 0;
  margin: 0 !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-col-12 {
  grid-column: 1 / -1;
}

/* Labels above the fields at readable size, not micro-labels;
   white on the navy panel (17.2:1) */
.form-section .forminator-ui.forminator-custom-form .forminator-field .forminator-label {
  font-family: 'Source Sans 3', sans-serif !important;
  font-weight: 600 !important;
  font-size: 0.95rem !important;
  letter-spacing: 0.01em;
  color: var(--white) !important;
  margin-bottom: 8px;
}

/* Green required glyph, back from the original navy build: on navy
   green is 6.1:1 — past the 4.5:1 text bar, so the marker passes AA
   even read as text; aria-required carries the semantics */
.form-section .forminator-ui.forminator-custom-form .forminator-required {
  color: var(--green);
  margin-left: 2px;
}

.form-section .forminator-ui.forminator-custom-form .forminator-input,
.form-section .forminator-ui.forminator-custom-form .forminator-textarea {
  width: 100%;
  /* The screened-back fill: white at 7% over the panel navy reads as
     a soft lift (#162A4B effective; white text on it is 14.3:1), and
     the grey-70 border is visible on all four sides (3.85:1 on navy,
     3.2:1 on the fill, both past the 3:1 non-text minimum). The
     border keeps the white-box build's 2px width, so field geometry
     is unchanged and the red error flip causes no reflow. */
  background: rgba(255, 255, 255, 0.07) !important;
  border: 2px solid var(--grey-70) !important;
  border-radius: 10px !important; /* sanctioned corner exception, inputs only */
  color: var(--white) !important;
  caret-color: var(--white);
  font-family: 'Source Sans 3', sans-serif !important;
  font-size: 1.05rem !important;
  line-height: 1.4 !important;
  padding: 14px 18px !important;
  height: auto !important;
  box-shadow: none !important;
  outline: 2px solid transparent; /* becomes a real outline in forced-colors mode */
  outline-offset: 2px;
  transition: border-color 0.2s ease, background-color 0.2s ease,
              box-shadow 0.2s ease, outline-color 0.2s ease;
}

/* No field carries placeholder text today (03-forminator.php seeds
   every placeholder empty), but if one ever does: grey-50 on the
   screened fill is 5.4:1, past the 4.5:1 text bar. Opacity pinned
   because Firefox dims placeholders by default, which would undercut
   the measured ratio. */
.form-section .forminator-ui.forminator-custom-form .forminator-input::placeholder,
.form-section .forminator-ui.forminator-custom-form .forminator-textarea::placeholder {
  color: var(--grey-50) !important;
  opacity: 1 !important;
}

/* Hover: the original navy build's soft green glow as a pre-focus
   hint (decorative enhancement; the focus ring below is the state
   that carries the accessibility requirement) */
.form-section .forminator-ui.forminator-custom-form .forminator-input:hover,
.form-section .forminator-ui.forminator-custom-form .forminator-textarea:hover {
  box-shadow: 0 0 0 3px rgba(82, 174, 50, 0.35) !important;
}

/* The focus state keyboard users can never lose: a solid green ring
   offset from the white box, over the navy panel (green on navy is
   6.1:1, well past the 3:1 non-text minimum — the ring that failed
   on the light surfaces works here) */
.form-section .forminator-ui.forminator-custom-form .forminator-input:focus,
.form-section .forminator-ui.forminator-custom-form .forminator-textarea:focus {
  border-color: var(--grey-70) !important; /* re-asserted: Forminator styles focus borders */
  outline: 2px solid var(--green) !important; /* !important: Forminator resets focus outlines */
  outline-offset: 2px;
  box-shadow: none !important;
  /* the original build's slight extra lift under the ring; white
     text is still 13.1:1 on the 10% fill */
  background: rgba(255, 255, 255, 0.1) !important;
  color: var(--white) !important;
}

/* Browser autofill repaints to the screened fill with white text.
   Autofill cannot show an alpha fill (the inset-shadow trick needs a
   solid), so it paints #162A4B, the blended equivalent of the 7%
   screen over the panel navy. */
.form-section .forminator-ui.forminator-custom-form .forminator-input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px #162a4b inset !important;
  -webkit-text-fill-color: var(--white) !important;
  caret-color: var(--white);
}

.form-section .forminator-ui.forminator-custom-form .forminator-textarea {
  min-height: 170px !important; /* Forminator's own min-height rule carries !important */
  resize: vertical;
}

/* Inline validation on the navy panel: the grey-70 border flips red,
   legible against both surfaces it borders (3.8:1 on the screened
   fill, 4.6:1 on navy, both past the 3:1 non-text minimum), and the
   plain-language message under the field is white semibold (17.2:1):
   the text carries the words in the surface's safe color, the border
   carries the alert color, same logic as the light build's navy
   messages. */
.form-section .forminator-ui.forminator-custom-form .forminator-field.forminator-has_error .forminator-input,
.form-section .forminator-ui.forminator-custom-form .forminator-field.forminator-has_error .forminator-textarea {
  border-color: var(--red) !important;
  box-shadow: none !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-error-message {
  background: transparent !important;
  border: 0 !important;
  padding: 6px 0 0 !important;
  color: var(--white) !important;
  font-weight: 600;
  font-size: 0.9rem;
}

/* Submit: the standard green pill, navy text on green fill (locked
   button standard; white on green fails AA) */
.form-section .forminator-ui.forminator-custom-form .forminator-button-submit {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700 !important;
  font-size: 0.88rem !important;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  background: var(--green) !important;
  color: var(--blue) !important;
  border: 1.5px solid var(--green) !important;
  border-radius: 100px !important;
  padding: 16px 36px !important;
  margin-top: 10px;
  cursor: pointer;
  transition: background 0.22s ease, color 0.22s ease, border-color 0.22s ease;
}

/* Hover drops the fill (the on-dark pill grammar, back from the
   original navy build): green border, white text on the navy panel
   (17.2:1); the navy-fill hover was the on-light state and would
   dissolve into the panel */
.form-section .forminator-ui.forminator-custom-form .forminator-button-submit:hover {
  background: transparent !important;
  border-color: var(--green) !important;
  color: var(--white) !important;
}

/* Keyboard focus: the SAME ring as .btn-pill:focus-visible, so the
   site has one focus treatment rather than two (2026-08-10). This
   button is not the .btn-pill component (Forminator renders it), so
   the pill rule cannot reach it; the values are duplicated here on
   purpose and the two rules must be changed together.

   Navy inner band, grey-10 outer band, matching the pill exactly.
   The outward band is grey-10 on the .form-band navy behind it:
   13.89:1, far past the 3:1 non-text minimum. The inward band is
   navy against the button's own green fill: 6.11:1.

   box-shadow rather than outline, per the pill: it follows the
   button's 100px radius, and it is not subject to any native control
   quirk. !important is required, not decorative: Forminator's sheet
   loads later and its [data-design="default"] .forminator-button
   rules set box-shadow, including one on :active that would otherwise
   out-specify this rule when a keyboard visitor presses Enter (that
   fires :active and :focus-visible together). The transparent outline
   is the forced-colors fallback used on the pill and on the fields
   above: box-shadow is dropped in forced-colors mode and the outline
   becomes a real system-coloured ring.

   NOTE for future QA, learned the hard way here: this button computes
   transition: 0.2s (an ALL-property transition, because Forminator's
   later sheet overrides the transition list set above, which carries
   no !important). In a non-rendered document the transition clock is
   frozen at t=0, so getComputedStyle reports the PRE-state of any
   transitioned property forever, including for a forced inline
   !important probe. That made the previous outline rule read as
   outline-width: 0px and look broken when it was not. Suppress
   transitions before trusting any computed focus style here. */
.form-section .forminator-ui.forminator-custom-form .forminator-button-submit:focus-visible {
  outline: 2px solid transparent !important;
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--blue), 0 0 0 6px var(--grey-10) !important;
}

/* Response messages: white text on the navy panel (17.2:1), a green
   rule accent for success and a red one for errors (decorative
   accents; the text carries the outcome, and Forminator's default
   pink boxes are off-brand) */
.form-section .forminator-ui.forminator-custom-form .forminator-response-message {
  background: transparent !important;
  border: 0 !important;
  border-left: 2px solid var(--green) !important;
  border-radius: 0 !important;
  color: var(--white) !important;
  font-size: 1rem;
  line-height: 1.6;
  padding: 4px 0 4px 14px !important;
  margin: 0 0 28px !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-response-message.forminator-error {
  border-left-color: var(--red) !important;
}

.form-section .forminator-ui.forminator-custom-form .forminator-response-message p {
  color: var(--white) !important;
}

/* ------- The success state (2026-08-09, Ed) -------
   A successful submission REPLACES the form with this panel, in the
   space the form occupied, and it stays replaced for the page view:
   the form does not come back, and there is deliberately no "send
   another message" control (the reset is a reload or a fresh visit).
   Forminator carries the same decision in its own settings
   (submission-behaviour: behaviour-hide, autoclose off, seeded by
   03-forminator.php), so the fields cannot return and the
   confirmation can never fade out on a timer, which is what made the
   old one so easy to miss. The wrap/panel swap and the scroll live in
   main.js (CONTACT FORM SUCCESS section).

   Nothing new is invented here. The headline is the .form-heading
   display treatment reversed out for navy, the divider is the site's
   standard 40px .headline-rule, the mark is a literal text character
   in Source Sans 3 (which carries U+2713) exactly as the green arrow
   is a literal character, and the corners stay square: no card, no
   box, no shadow.

   Deliberately NOT animated in. An entrance transition rides the
   rendering pipeline, and a pipeline that never runs strands the
   element at opacity 0; this is the one piece of copy on the site
   that must never fail to appear. The swap plus the scroll carries
   the state change on its own. */
.form-section__form-wrap[hidden],
.form-success[hidden] { display: none; }

.form-success {
  padding: 0 0 8px;
  /* main.js scrolls the panel here on submit, so the visitor sees the
     state change wherever they were in the form. Same offset as the
     band's own anchor: clears the fixed header at every width, worst
     case (header shown). Reduced motion already flips the html
     scroll-behavior to auto and lands at the same place. */
  scroll-margin-top: calc(var(--nav-h) + 24px);
}

/* Focus is moved here programmatically so the confirmation is
   announced; the panel itself is the visible feedback, so it takes no
   ring for that. A keyboard visitor who tabs back to it still gets
   one. */
.form-success:focus { outline: none; }
.form-success:focus-visible {
  outline: 2px solid var(--green); /* 6.1:1 on navy */
  outline-offset: 8px;
}

/* Decorative and aria-hidden, so the text-contrast bar does not apply
   to it; green on navy is 6.1:1 in any case. Green as a glyph and a
   1px rule only, never as a fill behind type. */
.form-success__check {
  font-family: 'Source Sans 3', sans-serif;
  /* 900, the display weight, so the mark reads as deliberate against
     the headline under it rather than as a stray tick. Source Sans 3
     carries U+2713 in every weight the theme self-hosts, Black
     included, so the heavier face is a real glyph and not a synthesized
     one. U+2714 (HEAVY CHECK MARK) would be the obvious way to add
     weight and is deliberately NOT used: it is an emoji-set character,
     and some platforms render it in emoji presentation. */
  font-weight: 900;
  font-size: 3.25rem;
  line-height: 1;
  color: var(--green);
  margin-bottom: 16px;
}

/* White on navy is 17.2:1. Wraps to a second line on a phone rather
   than overflowing, per the no-overflow rule. */
.form-success__headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
  color: var(--white);
}

/* Body copy on solid navy is Metal Grey, 6.53:1, the brand rule. */
.form-success__line {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 1.05rem;
  line-height: 1.7;
  color: var(--metal);
  max-width: 34rem;
}

@media (max-width: 900px) {
  /* Mobile band rhythm (2026-08-08): the white intro band and the navy
     band each carry the shared 56px, so the seam between them reads
     like every other band transition on the site. */
  .form-section__inner { padding: 64px var(--page-pad) 56px; }
  .form-band { padding: 56px var(--page-pad); }
  /* Stacked: white intro band, then the navy band with the form full
     width and the contact block below it, everything on the shared
     mobile copy edge. Nothing bleeds here. */
  .form-section__grid { grid-template-columns: 1fr; row-gap: 48px; }
  .form-section .forminator-ui.forminator-custom-form { max-width: none; }
  .form-section .forminator-ui.forminator-custom-form .forminator-row {
    grid-template-columns: 1fr;
  }
  /* 44px tap targets on the phone and email links */
  .form-contact__link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  /* The mark steps down with the headline so the panel keeps its
     proportions on a phone; the 48px row-gap above the contact block
     is the space below it, so the panel drops its own tail. */
  .form-success { padding: 0; }
  .form-success__check { font-size: 2.75rem; }
}

/* ------------------------------------------------------------
   NETWORK MAP — full-width light closing section (Contact page,
   interactive rework 2026-08-07). Solid navy contiguous-US
   landmass (inline SVG, no external map services) on the grey-10
   surface: the landmass is the union of the us-atlas state
   polygons with the Great Lakes as evenodd holes (2026-08-07
   lakes fix — Michigan and the lake states show their real
   shorelines), grey-70 interior state hairlines from the same
   source, twelve Sustainable Green HTML-button markers placed by
   real geography, one white info card open at a time (navy
   header bar, green accent rule, square corners with a white
   border and deep shadow since the 2026-08-07 revision), and the
   multi-column location index below. Behavior lives in main.js
   (NETWORK MAP section). This section replaces the closing CTA
   band on Contact only: the one page without the band
   (standing-rule exception).
------------------------------------------------------------ */
.network-map {
  background: var(--grey-10);
  padding: 110px var(--copy-edge) 120px;
}

.network-map__inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

/* Eyebrow on grey-10: metal is ~2.4:1 there; grey-90 clears AA */
.network-map .label-sm { color: var(--grey-90); }

.network-map__headline {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.025em;
  line-height: 0.92;
  font-size: clamp(2.2rem, 3.8vw, 4.5rem);
  color: var(--blue);
}

.network-map .headline-rule { margin: 1.5rem 0 1.75rem; }

.network-map__body {
  font-size: clamp(0.9rem, 1.05vw, 1rem);
  line-height: 1.7;
  color: var(--grey-90);
  max-width: 52ch;
}

/* Site link grammar (2026-08-07): rest = bold navy, no underline
   (bold weight is the non-color distinction from surrounding text);
   hover and keyboard focus bring the green underline. */
.network-map__body a {
  color: var(--blue);
  font-weight: 700;
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.network-map__body a:hover,
.network-map__body a:focus-visible { text-decoration-line: underline; }
.network-map__body a:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}

/* ------- Stage: map dominant, card beside/overlapping it ------- */
.network-map__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 360px);
  align-items: center;
  margin-top: 64px;
}

.network-map__figure {
  position: relative;
  z-index: 1;
}

.network-map__svg {
  display: block;
  width: 100%;
  height: auto;
}

.nm-land { fill: var(--blue); }

/* Interior state boundaries (2026-08-07): hairlines so the landmass
   is not one solid dark shape. Grey-70, the tokens' mid blue-grey:
   clearly lighter than the navy (3.85:1) but nowhere near white.
   vector-effect pins the stroke to 1px at ANY rendered size, so the
   hairline never thickens on small screens. Decorative geography
   inside an aria-hidden SVG; no contrast requirement applies. */
.nm-states {
  fill: none;
  stroke: var(--grey-70);
  stroke-width: 1px;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* ------- Markers: real buttons positioned over the SVG ------- */
.nm-marker {
  position: absolute;
  transform: translate(-50%, -50%); /* layout centering, not a reveal state */
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  padding: 0;
  border-radius: 50%;
  cursor: pointer;
}

.nm-marker__dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--green); /* 6.1:1 against the navy landmass */
  border: 2px solid var(--grey-10);
  transition: box-shadow 0.2s ease;
}

.nm-marker--featured { z-index: 2; }
.nm-marker--featured .nm-marker__dot {
  width: 19px;
  height: 19px;
}

.nm-marker:hover .nm-marker__dot {
  box-shadow: 0 0 0 5px rgba(82, 174, 50, 0.25);
}

.nm-marker.is-active .nm-marker__dot {
  box-shadow: 0 0 0 5px rgba(82, 174, 50, 0.4);
}

/* Focus ring readable on the navy landmass AND the light shore:
   light inner ring, navy outer ring */
.nm-marker:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--grey-10), 0 0 0 5px var(--blue);
}

/* ------- Info card ------- */
.network-map__card-col {
  position: relative;
  z-index: 3;
  /* overlaps the map's empty Atlantic edge (the split-overlap
     grammar); no marker sits past x=880 of the 960 viewBox */
  margin-left: -70px;
}

/* THE INFO CARD COMPONENT. Two placements share these exact
   declarations, joined by selector rather than duplicated so they can
   never drift apart: the map's location cards (.nm-card, one open at a
   time, with a close control and a Get Directions pill) and the same
   location details as an inline accordion panel under their list row
   at ≤900px (.nm-acc). Both are square-cornered white boxes with a
   navy header bar, a green accent rule under it, and the same body
   type. Anything added below must be added for both, or scoped to one
   on purpose with a note saying why. (The Contact form section briefly
   carried a third, .form-card; it was replaced 2026-08-08 by reversed
   out type directly on the navy band.) */
.nm-card,
.nm-acc {
  background: var(--white);
  /* Square corners (2026-08-07): the sanctioned radius now covers
     form inputs only. Separation from the navy landmass where the
     desktop overlap crosses it: the white border gives the navy
     header bar a crisp light edge, the deepened shadow carries the
     lift, and the state hairlines texture the landmass beneath. */
  border: 1px solid var(--white);
  overflow: hidden;
  box-shadow: 0 28px 80px rgba(4, 26, 61, 0.38), 0 8px 24px rgba(0, 0, 0, 0.20);
}

.nm-card__head,
.nm-acc__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--blue);
  border-bottom: 2px solid var(--green); /* the accent rule under the header bar */
  padding: 14px 12px 14px 20px;
}

.nm-card__name,
.nm-acc__name {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--white);
}

.nm-card__close {
  background: none;
  border: 0;
  padding: 2px 8px;
  font-family: 'Source Sans 3', sans-serif;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--metal);
  cursor: pointer;
  transition: color 0.2s ease;
}
.nm-card__close:hover { color: var(--white); }
.nm-card__close:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}

.nm-card__body,
.nm-acc__body {
  padding: 20px 20px 24px;
}

.nm-card__address {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--blue);
  margin-bottom: 14px;
}

.nm-card__line {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.95rem;
  line-height: 1.9;
  color: var(--grey-90);
}

.nm-card__value { color: var(--blue); }

.nm-card__link {
  color: var(--blue);
  font-weight: 700; /* the site link grammar: bold navy at rest, no underline */
  text-decoration: none;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.nm-card__link:hover,
.nm-card__link:focus-visible { text-decoration-line: underline; }
.nm-card__link:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* The card takes programmatic focus after a list row scrolls it into
   view on desktop, so screen reader users land on the result of their
   click. tabindex="-1" focus from a mouse must not draw a ring, hence
   the :focus reset; keyboard focus gets the marker's two-tone ring,
   which is the only one readable both over the navy landmass and over
   the light section around it. */
.nm-card:focus { outline: none; }
.nm-card:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--grey-10), 0 0 0 6px var(--blue),
              0 28px 80px rgba(4, 26, 61, 0.38);
}

/* ------- Inline accordion (≤900px) -------
   On phones and tablets the list rows sit far below the map, so
   opening the map card put the answer off screen and the tap read as
   doing nothing (2026-08-08, Ed). At these widths the row discloses
   the SAME details inline directly beneath it instead, in the same
   card component. Desktop keeps the map card and never renders these.
   The panel has no shadow of its own: it sits in the flow of a list on
   a flat surface, not floating over the landmass. */
@media (min-width: 901px) {
  .nm-acc { display: none; }
}

@media (max-width: 900px) {
  .nm-acc {
    box-shadow: none;
    border-color: rgba(4, 26, 61, 0.15);
    margin: 0 0 20px;
  }
  /* The flagship card is the server default for desktop; at these
     widths the default-open state is the accordion instead, so this
     one card starts hidden. JS removes the class as soon as it owns
     the state, so a marker tap can open this location normally. */
  .nm-card--default { display: none; }
}

/* Get Directions: the standard filled pill (navy text on green fill,
   the locked button standard). Hover flips to navy fill with white
   text so the state stays AA on the white card. */
.nm-card__directions {
  display: inline-flex;
  align-items: center;
  gap: var(--arrow-gap);
  margin-top: 16px;
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  background: var(--green);
  color: var(--blue);
  border: 1.5px solid var(--green);
  border-radius: 100px;
  padding: 11px 24px;
  transition: background 0.22s ease, color 0.22s ease, border-color 0.22s ease;
}
.nm-card__directions .arrow { color: var(--blue); }
.nm-card__directions:hover {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--white);
}
.nm-card__directions:hover .arrow { color: var(--green); }
.nm-card__directions:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}

/* ------- Location index: multi-column button rows ------- */
.network-map__list {
  margin-top: 72px;
  column-count: 3;
  column-gap: 60px;
}

.network-map__loc {
  break-inside: avoid;
}

.nm-loc {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 16px;
  /* zero horizontal padding: the row text sits exactly on the copy
     rail (desktop) / the shared mobile edge */
  padding: 12px 0;
  background: none;
  border: 0;
  border-top: 1px solid rgba(4, 26, 61, 0.15);
  text-align: left;
  cursor: pointer;
  transition: background-color 0.2s ease, box-shadow 0.2s ease;
}

.nm-loc:hover { background: rgba(4, 26, 61, 0.06); }

.nm-loc.is-active .nm-loc__name {
  text-decoration: underline;
  text-decoration-color: var(--green);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

.nm-loc:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.nm-loc__name {
  font-family: 'Source Sans 3', sans-serif;
  font-weight: 700;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--blue);
}

.nm-loc__city {
  font-family: 'Source Sans 3', sans-serif;
  font-size: 0.82rem;
  color: var(--grey-90);
}

@media (max-width: 1100px) {
  .network-map__list { column-count: 2; }
}

@media (max-width: 900px) {
  /* Mobile band rhythm (2026-08-08): 56px like every other band */
  .network-map { padding: 56px var(--page-pad); }
  .network-map__stage {
    grid-template-columns: 1fr;
    margin-top: 44px;
  }
  /* Card below the map, in normal flow, never a viewport overlay. This
     is where a MARKER tap lands, directly under the map the visitor is
     already looking at. List rows disclose inline instead. */
  .network-map__card-col {
    margin: 28px 0 0;
    max-width: 420px;
  }
  /* ONE column here (2026-08-08): the rows now expand inline, and an
     expanding panel inside a CSS multi-column layout re-balances the
     whole list on every tap. Single column also gives the panel the
     full copy width. */
  .network-map__list { column-count: 1; margin-top: 44px; }
  /* 44px minimum tap target on every row; align-content centers the
     rows' wrapped lines in the taller box (the flex container aligns
     its LINES, not its items, once there is spare cross-axis space) */
  .nm-loc { min-height: 44px; align-content: center; }
}


/* Arrow glyph: the prototype loaded Source Sans 3 from Google Fonts,
   whose latin subset excludes U+2192, so every arrow on the reference
   site actually renders in the platform UI font. Source Sans 3's own
   arrow is visibly narrower. Pin arrows to the system stack so the
   approved look is reproduced deliberately rather than by accident. */
.arrow,
.hero-scroll-label + .hero-scroll-line {
  font-family: Helvetica, Arial, sans-serif;
}
