:root {
  --teal: #c4e7ee;
  --mint: #e0fdee;
  --pale: #eef9ee;
  --yellow-green: #8bd136;
  --pale-yellow-green: #cde494;
  --ink: #103322;
  /* titles: Poppins. body copy: the Typekit kit's "arial" family (loaded via
     the use.typekit.net link in <head>). */
  --font-display: "Poppins", "Helvetica Neue", Arial, sans-serif;
  --font-body: "arial", "Helvetica Neue", Arial, sans-serif;
  --brand-blue: #25408f;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  background: #fff;
  color: var(--ink);
  /* a teammate flagged the page appearing to "move left to right" -- a
     screen recording showed content shift left then snap back with the
     cursor never moving, which is a horizontal trackpad-swipe rubber-band
     bounce (Safari/Chrome's own overscroll gesture feedback), not
     anything actually animating in this codebase. overscroll-behavior-x
     alone suppresses that bounce.
     `overflow-x: hidden` was tried alongside it and reverted -- setting
     overflow-x explicitly on <html> forces the UA to compute overflow-y
     as `auto` instead of `visible` (CSS overflow spec: one axis can't
     stay `visible` once the other is a non-visible value), which changes
     which element is the real scrolling container and threw off the
     timing of every scroll-position-dependent effect on this page (Lenis,
     the hero scroll-frame, stack-shrink, ...). overscroll-behavior-x does
     NOT have that side effect, so it's kept on its own. */
  overscroll-behavior-x: none;
}

/* once JS confirms #site-waves exists to paint something underneath it,
   drop body's own opaque white fill -- without this, body's normal-flow
   background box paints ABOVE a negative-z-index fixed descendant (see
   #site-waves below), hiding the canvas completely regardless of its own
   z-index. Gated behind html.js (same pattern as [data-reveal] above it
   in this file) so a JS failure/slow load just means no waves, not a
   see-through page with nothing under it. */
html.js body {
  background: transparent;
}

/* --- site-wide wave background -----------------------------------------
   A persistent canvas painted BEHIND every section (setupSiteWaves() in
   hero.js draws continuously-animated translucent sine lines onto it) --
   not a hero-specific effect. position:fixed + z-index:-1 keeps it
   pinned to the viewport and below all normal page content; it's
   invisible wherever a section paints a fully opaque background over it
   (the hero video, .about__image, ...) and shows through wherever a
   section uses one of this site's existing translucent rgba-alpha
   gradient washes instead -- same behavior the sponsorships.aramco.com
   reference this was modeled on has with its own photography. */
#site-waves {
  position: fixed;
  inset: 0;
  z-index: -1;
  display: block;
  pointer-events: none;
}

/* --- one-scroll/one-keypress-per-section snapping: REMOVED (used to be
   scroll-snap-type on html + scroll-snap-align/-stop on .snap-section).
   It worked fine on its own, but now that Lenis owns scrolling (see
   hero.js) the two fight: native scroll-snap forcibly jumps the real
   scrollTop to the nearest snap point on its own timing, behind Lenis's
   back, which desyncs Lenis's internally tracked scroll position from the
   actual one and produces a visible jump/glitch on the next scroll input
   -- confirmed by forcing a native scroll into #performance-highlights's
   old snap zone, which jumped straight to the snap point regardless of
   Lenis's animation state. Lenis has its own opt-in snap addon if this
   behavior is wanted back; native scroll-snap-type alongside Lenis is not
   a safe middle ground. The .snap-section class has been removed from
   index.html (it only ever marked #hero and #performance-highlights). */

/* --- stacked-panel parallax (Hero -> About -> Highlights -> Trust ->
   Joint Letter -> CEO Message): each panel is a normal-flow 100vh block
   that goes `position: sticky; top: 0`, so it pins in place the instant it
   reaches the viewport top. The next panel, still approaching from below
   in ordinary scroll flow, is stacked at a higher z-index -- so as it
   scrolls upward it visibly slides up and over the still-pinned panel
   beneath it, right up until it reaches the top and pins itself. Hero
   joined this stack (at the lowest z-index, so About covers it first) so
   the very first scroll -- hero into About -- gets the same cover motion
   as every later transition, instead of being a plain scroll-away. This
   region deliberately has no scroll-snap-align: it needs continuous
   scroll to animate the cover, not discrete per-section jumps. --- */
.stack {
  position: relative;
}

.stack section {
  position: sticky;
  top: 0;
}

.stack section#hero { z-index: 0; }
.stack section#about { z-index: 1; }
.stack section#highlights { z-index: 2; }
/* #global-footprint (Trust), #joint-letter, and #ceo-message are no
   longer inside .stack -- each flows normally below Highlights instead
   of sticky-pinning/covering the one before it, per explicit request to
   drop the parallax stacking from Trust onward. */

/* --- scroll-entry reveal utility --------------------------------------
   Any element with [data-reveal="up|left|right|scale"] starts hidden/offset
   and gets .is-revealed added by the IntersectionObserver in hero.js the
   first time it scrolls into view (fires once -- re-triggering on scroll-
   back-up would read as jittery, not delightful, on a report site).
   Repeated groups (chip grids, card rows) stagger via a per-item
   transition-delay set alongside each group's own rules further down,
   rather than a generic nth-child scheme here, to match this file's
   existing one-rule-per-class style.

   Everything here is gated behind html.js (set by the inline script at the
   top of <head>) -- without it, a JS failure or slow/blocked script would
   leave every [data-reveal] element at opacity:0 forever, permanently
   hiding content instead of just losing the entrance animation. */
html.js [data-reveal] {
  opacity: 0;
  transition: opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1), transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}

html.js [data-reveal="up"] { transform: translateY(56px); }
html.js [data-reveal="left"] { transform: translateX(-64px); }
html.js [data-reveal="right"] { transform: translateX(64px); }
html.js [data-reveal="scale"] { transform: scale(0.9); }
/* opacity-only, no transform -- for the Read More buttons, which already
   animate `transform` themselves on :hover (a lift). Combining that with
   this utility's transform-based entrance would fight over the same
   property; fading them in avoids the clash entirely. */
html.js [data-reveal="fade"] { transform: none; }

html.js [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Joint Letter and CEO Message: entrance animation removed per client
   request -- content renders immediately at full opacity/position instead
   of fading/sliding in on scroll. */
#joint-letter [data-reveal],
#ceo-message [data-reveal] {
  opacity: 1;
  transform: none;
  transition: none;
}

/* --- Read More button: flat solid pill -------------------------------
   Replaces the earlier liquid-glass treatment (frosted blur + canvas-
   generated dynamic rim/specular lighting, ported from a reference
   React/WebGL component -- see HANDOFF for that round's history if it
   ever needs resurrecting). Liquid-glass turned out to be the only
   blurred/glass-styled control anywhere on the site -- every other CTA
   (.site-nav__cta, nav pills, carousel arrows) is a flat solid-fill
   pill with a plain lift-on-hover, no blur -- so it read as an outlier
   against the rest of the page rather than a highlight. This matches
   that existing flat-pill language instead: same recipe as
   .site-nav__cta (solid brand-blue, white text, pill radius, lift +
   shadow on hover), reused here as a shared class since it's now the
   same visual treatment in every one of the 7 places it appears.

   Structure: <button class="btn-cta ...">Read More</button> -- no
   inner spans needed (the old __specular/__rim/__label children existed
   only for the glass layering, which is gone). The component-specific
   class (.hero-readmore etc.) still owns position/layout/
   transition-delay. */
.btn-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 999px;
  background: var(--brand-blue);
  color: #fff;
  font-family: var(--font-display);
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.btn-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

.btn-cta:active {
  transform: translateY(0) scale(0.97);
}

@media (prefers-reduced-motion: reduce) {
  .btn-cta,
  .btn-cta:hover,
  .btn-cta:active { transition: none; }
}

/* white-fill variant for buttons sitting on dark/photo backgrounds
   (Trust's dark navy "Global Footprint" section, Joint Letter, CEO
   Message, Verticals) where solid brand-blue would blend in too close
   to the backdrop's own hue -- same reasoning the liquid-glass
   --light variant had, just for a solid fill instead of a tinted one. */
.btn-cta--light {
  background: #fff;
  color: var(--brand-blue);
}

/* --- top navigation --- */

.site-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  /* no background here -- the logo sits directly on the video */
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

/* auto-hides on scroll down, reappears on scroll up -- see setupNavAutoHide()
   in hero.js. Skipped entirely while the mobile drawer or a mega-menu panel
   is open (that guard lives in the JS, not here) so it can't slide away out
   from under an open dropdown. */
.site-nav.site-nav--hidden {
  transform: translateY(-130%);
}

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

.site-nav__inner {
  /* position+z-index here (not just on the <header>) is required --
     .site-nav__mega is a sibling *inside* <header>, positioned with its
     own z-index. Within header's shared stacking context, a positioned
     z-index:19 sibling paints OVER non-positioned in-flow content
     regardless of the header's own z-index being higher; without this,
     opening any mega-menu made the whole logo+links bar invisible
     (painted-under, not actually removed -- rects/opacity checked fine in
     devtools, just hidden behind the panel). */
  position: relative;
  z-index: 2;
  max-width: 1520px;
  margin: 0 auto;
  padding: 0.8rem 4vw 0.8rem 6vw;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* logo/menu drop in from just above the top edge once .site-nav.is-loaded
   is set (see hideLoader() in hero.js -- fires the moment the loader fades
   out, so the nav visibly arrives right as the loader disappears rather
   than sitting there statically the whole time). Gated behind html.js,
   same reasoning as the [data-reveal] utility above: if JS never runs,
   is-loaded never gets added, so this can't be allowed to hide the nav
   permanently. */
html.js .site-nav__logo,
html.js .site-nav__menu {
  opacity: 0;
  transform: translateY(-140px) scale(0.85);
  /* overshoot easing (the cubic-bezier dips past 1 before settling) reads
     as a bouncy drop rather than a plain slide -- opacity still eases in
     smoothly on its own curve so it doesn't flicker on the overshoot */
  transition: opacity 0.6s ease-out, transform 1.1s cubic-bezier(0.34, 1.56, 0.64, 1);
}

html.js .site-nav__menu { transition-delay: 0.22s; }

html.js .site-nav.is-loaded .site-nav__logo,
html.js .site-nav.is-loaded .site-nav__menu {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html.js .site-nav__logo,
  html.js .site-nav__menu {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

.site-nav__logo {
  display: block;
  flex: 0 0 auto;
  line-height: 0;
  /* top padding carries an extra 0.8rem (matching .site-nav__inner's own
     padding-top exactly) offset by an equal negative top margin just
     below -- the two cancel out for the logo IMAGE's own rendered
     position (unchanged from before), but the box's background/border
     edge itself extends up by that same 0.8rem, reaching all the way to
     the header's own top edge (y:0) instead of stopping at the inner
     padding's floor. Only visible once scrolled (background is
     transparent otherwise), but applied unconditionally since it's a
     layout offset, not a visual one, and toggling it on/off would need
     its own transition anyway. */
  padding: 1.3rem 0.5rem 0.5rem;
  margin-top: -0.8rem;
  /* square, flat corners -- matches this site's own no-rounded-corners
     rule elsewhere, unlike the hamburger's pill treatment below. */
  background: transparent;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}

.site-nav--scrolled .site-nav__logo {
  background: #fff;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.14);
}

.site-nav__logo img {
  /* +15% over the original 46px */
  height: 53px;
  width: auto;
  display: block;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.2));
}

/* individual bordered pill buttons floating directly on the page --
   replaced a single frosted-glass bar holding all the links, closer to
   getminds.ai's nav than the earlier glass-box treatment. .site-nav__menu
   is now a plain flex layout wrapper, not a visual container itself. */
.site-nav__menu {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.site-nav__links {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: 0 0 auto;
}

.site-nav__links a,
.site-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.86rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  background: #fff;
  border: 1.5px solid rgba(37, 64, 143, 0.22);
  border-radius: 999px;
  padding: 0.55rem 1.1rem;
  cursor: pointer;
  font-family: inherit;
  box-shadow: 0 2px 10px rgba(16, 51, 34, 0.06);
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.site-nav__links a svg,
.site-nav__link svg {
  flex: 0 0 auto;
}

.site-nav__links a:hover,
.site-nav__links a:focus-visible,
.site-nav__link:hover,
.site-nav__link:focus-visible {
  border-color: var(--brand-blue);
  color: var(--brand-blue);
}

/* filled solid while its mega-menu panel is open -- was previously an
   underline indicator; a filled pill reads more clearly as "active" and
   matches the reference's filled-pill treatment for the current item */
.site-nav__link[aria-expanded="true"] {
  background: var(--brand-blue);
  border-color: var(--brand-blue);
  color: #fff;
}

.site-nav__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  flex: 0 0 auto;
  background: var(--brand-blue);
  color: #fff;
  text-decoration: none;
  font-size: 0.86rem;
  font-weight: 600;
  padding: 0.6rem 1.3rem;
  border: 1.5px solid var(--brand-blue);
  border-radius: 999px;
  white-space: nowrap;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.site-nav__cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
}

/* Nav collapses behind this hamburger at EVERY width now, not just mobile
   -- .site-nav__links/.site-nav__cta are hidden by default (below) and
   only reveal on .site-nav.is-open, same toggle regardless of viewport.
   Desktop reveal is a plain `display` flip back to the pills' own normal
   flex-row layout (nothing else needed, see the unconditional .is-open
   rules below); the max-width:860px query further down layers on the
   narrow-viewport-only stacked/column treatment on top of that. */
.site-nav__toggle {
  display: block;
  flex: 0 0 auto;
  width: 22px;
  height: 18px;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  position: relative;
}

/* pill background, painted via a pseudo-element sized larger than the
   button's own 30x24 hit box (rather than resizing the button itself,
   which would require re-centering the three bar spans) -- z-index:-1
   keeps it behind the bars. Only visible once scrolled, mirroring the
   logo's own square background above; fully rounded, unlike the logo's
   flat corners, per the client's explicit ask for a pill here. */
.site-nav__toggle::before {
  content: "";
  position: absolute;
  inset: -10px -14px;
  border-radius: 999px;
  background: transparent;
  z-index: -1;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}

.site-nav--scrolled .site-nav__toggle::before {
  background: #fff;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.14);
}

.site-nav__toggle span,
.site-nav__toggle span::before,
.site-nav__toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--ink);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.site-nav__toggle span { top: 8px; }
.site-nav__toggle span::before { top: -6px; }
.site-nav__toggle span::after { top: 6px; }

.site-nav__links { display: none; }
.site-nav__cta { display: none; }

/* --- Ask VIA: entry point to via.html ---------------------------------
   Deliberately NOT hidden behind the hamburger the way .site-nav__links
   and .site-nav__cta are. Those collapse at every width (Round 10), which
   leaves the logo and the hamburger as the only things on screen at rest;
   putting VIA alongside them is what makes the feature discoverable at
   all. If this is ever restyled, the constraint to preserve is legibility
   over BOTH the hero video (green/teal, at scroll position 0) and the
   plain white sections below it -- which is why it carries a solid light
   fill rather than the transparent-with-border treatment the in-drawer
   nav pills use. Those only ever appear over the drawer's own backdrop. */
.site-nav__via {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: 0 0 auto;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--brand-blue);
  background: rgba(255, 255, 255, 0.92);
  border: 1.5px solid rgba(37, 64, 143, 0.22);
  border-radius: 999px;
  padding: 0.5rem 1.05rem;
  white-space: nowrap;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
}
/* the wordmark carries the same letterspacing as VIA's own header, so the
   button and the destination read as the same product */
.site-nav__via strong { font-weight: 700; letter-spacing: 0.13em; }
.site-nav__via svg { flex-shrink: 0; opacity: 0.85; }
.site-nav__via:hover {
  background: var(--brand-blue);
  border-color: var(--brand-blue);
  color: #fff;
  transform: translateY(-1px);
}
.site-nav__via:hover svg { opacity: 1; }

@media (max-width: 860px) {
  /* space is tight next to the hamburger -- drop the verb, keep the mark
     and the wordmark, which is enough to identify it */
  .site-nav__via { padding: 0.45rem 0.8rem; gap: 0.4rem; }
  .site-nav__via span { font-size: 0; }
  .site-nav__via strong { font-size: 0.78rem; }

  /* Inside the open drawer it becomes a full-width row like every other
     nav pill, and there's room to say "Ask VIA" in full again. */
  .site-nav.is-open .site-nav__via {
    width: 100%;
    justify-content: flex-start;
    padding: 0.6rem 1.1rem;
    margin-top: 0.6rem;
  }
  .site-nav.is-open .site-nav__via span { font-size: 0.85rem; }
}

.site-nav.is-open .site-nav__links { display: flex; }
.site-nav.is-open .site-nav__cta { display: inline-flex; }

/* hamburger -> X, same at every width */
.site-nav.is-open .site-nav__toggle span { transform: rotate(45deg); }
.site-nav.is-open .site-nav__toggle span::before { transform: translateY(6px) rotate(-90deg); opacity: 0; }
.site-nav.is-open .site-nav__toggle span::after { transform: translateY(-6px) rotate(-90deg); }

@media (max-width: 860px) {
  .site-nav__toggle { margin: 0 0.4rem; }

  /* each pill already carries its own border/background/padding now, so
     the stacked mobile list just needs vertical gap between them -- no
     more border-bottom separators or width/text-align overrides */
  .site-nav.is-open .site-nav__links {
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
    width: 100%;
    padding-top: 0.8rem;
  }
  .site-nav.is-open .site-nav__links a,
  .site-nav.is-open .site-nav__link {
    width: 100%;
    justify-content: flex-start;
  }

  .site-nav.is-open .site-nav__cta {
    margin: 0.8rem 0 0.6rem;
  }
  .site-nav.is-open .site-nav__inner { align-items: flex-start; }
  .site-nav.is-open .site-nav__menu { flex-direction: column; align-items: stretch; }

  .site-nav.is-open .site-nav__toggle { align-self: flex-end; }
}

/* --- desktop nav mega-menu (Round 8 redesign, then refined once more same
   round): a compact card-style dropdown, now the same size and same screen
   position every time -- whichever of the 4 buttons is clicked, the same
   box appears in the same place, only its content swaps. (Earlier in this
   round each panel was individually sized/positioned under its own
   trigger; client feedback was that the inconsistency itself read as
   unpolished, more than any single panel's size -- so position/size are
   now computed once from fixed reference points, not per-button, see
   positionMega() in hero.js.) Replaces the original (pre-Round-7) design:
   a panel that always opened flush against the far-right edge regardless
   of which button was clicked, and stretched all the way to the bottom of
   the viewport, leaving a large dead white gap under whichever chapter had
   the fewest links. Each panel: a representative image (brought back after
   being cut earlier in this round for eating height with no payoff -- it
   earns its space now that the panel's overall size is fixed and content-
   independent, so a short link list no longer means a mostly-empty box),
   a small header repeating the trigger's own icon+label, then the
   chapter's links. Menu items mirror the site footer's chapter lists
   exactly (same copy, same href-vs-plain-span split for sections that
   don't exist yet -- those get a "Soon" tag + muted color instead of
   rendering identically to a real link, see .site-nav__mega-links span) --
   see hero.js for the open/close logic (one panel open at a time,
   Escape/outside-click/link-click all close it) and .site-footer for the
   shared copy source. */

.site-nav__mega {
  /* top uses a JS-measured CSS var (see hero.js, setNavHeightVar), not a
     hardcoded px guess -- the nav's real rendered height can differ by
     viewport/font-load timing, and a fixed px value here either leaves a
     gap above the panel or overlaps the nav, depending which way it's
     wrong. Falls back to 141px only if the var somehow isn't set yet.
     +14px on top of that is a deliberate visual gap so the card reads as
     a separate floating element under the pill, not fused flush to it.
     `left`/`width` are set inline by positionMega() in hero.js right
     before opening -- JS-driven (not a fixed CSS value) only because the
     right edge needs to track the Download Report CTA's real position
     (viewport-width-dependent), not because it varies per trigger anymore --
     every panel gets the exact same computed left/width. */
  position: fixed;
  top: calc(var(--nav-height, 141px) + 14px);
  left: 0;
  z-index: 1;
  width: min(480px, 90vw);
  max-height: min(64vh, 560px);
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1.5px solid rgba(37, 64, 143, 0.16);
  border-radius: 20px;
  /* clips .site-nav__mega-image/-head/-links to the rounded corners --
     simpler than giving each stacked child its own matching top/bottom
     radius, and .site-nav__mega-links keeps its own independent
     overflow-y: auto for internal scrolling regardless. Rounded corners
     are a deliberate exception to the site's usual flat-corner rule (see
     Design System in HANDOFF.md) -- requested specifically for this
     dropdown card, not a blanket change to boxes/chips elsewhere. */
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(16, 51, 34, 0.16);
  pointer-events: none;
  /* fade + slight upward-settle + scale reads as a dropdown "popping" open
     near the button that opened it -- the old clip-path wipe made sense
     when the panel spanned from the nav to the bottom of the screen, but
     on a small content-sized card it just looks like an odd sliding sliver. */
  opacity: 0;
  transform: translateY(-10px) scale(0.98);
  transform-origin: top left;
  transition: opacity 0.2s cubic-bezier(0.22, 1, 0.36, 1), transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.site-nav__mega.is-open {
  pointer-events: auto;
  opacity: 1;
  transform: translateY(0) scale(1);
}

.site-nav__mega-image {
  /* fixed height, not the old clamp(120px, 26vh, 260px) that ate up to
     ~40% of a panel which itself used to stretch to the viewport bottom --
     this panel's total height is now bounded by its own content instead,
     so a shorter, fixed strip reads as a header image, not a competing
     section. */
  flex: 0 0 128px;
  height: 128px;
  width: 100%;
  object-fit: cover;
  display: block;
  background: #fff;
}

.site-nav__mega-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.1rem 1.5rem;
  color: var(--brand-blue);
  border-bottom: 1px solid rgba(37, 64, 143, 0.12);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  flex: 0 0 auto;
}

.site-nav__mega-links {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.7rem 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.site-nav__mega-links a,
.site-nav__mega-links span {
  display: block;
  font-size: 0.96rem;
  line-height: 1.4;
  color: #2b3540;
  padding: 0.6rem 0.7rem;
  position: relative;
}

.site-nav__mega-links a {
  text-decoration: none;
  padding-left: 1.15rem;
  transition: color 0.2s ease, background 0.2s ease, padding-left 0.2s ease;
}

/* left accent bar that grows in on hover, echoing the brand-blue used
   everywhere else on the site rather than the generic underline the old
   plain-link style used. */
.site-nav__mega-links a::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 0.55rem;
  bottom: 0.55rem;
  width: 3px;
  background: var(--brand-blue);
  transform: scaleY(0);
  transition: transform 0.2s ease;
}

.site-nav__mega-links a:hover {
  color: var(--brand-blue);
  background: rgba(37, 64, 143, 0.06);
  padding-left: 1.4rem;
}

.site-nav__mega-links a:hover::before {
  transform: scaleY(1);
}

/* chapters that don't exist as a section yet (plain <span>, not <a>) --
   previously rendered in the exact same color/weight as a real link, which
   read as a broken/dead link rather than an intentionally-unbuilt page.
   Muted color + a small "Coming soon" tag + no hover state makes the
   distinction obvious at a glance instead of something you discover by
   clicking and getting nothing. */
.site-nav__mega-links span {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  color: #9aa3ab;
  cursor: default;
}

.site-nav__mega-links span::after {
  content: "Soon";
  flex: 0 0 auto;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #aeb4ba;
  background: #f1f3f5;
  padding: 0.2rem 0.45rem;
}

.site-nav__mega-links h4 {
  margin: 0.6rem 0.7rem 0.1rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--brand-blue);
  opacity: 0.65;
}

.site-nav__mega-links--split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1.4rem;
}

.site-nav__mega-links--split > div {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

@media (max-width: 860px) {
  .site-nav__mega { display: none; }
}

/* .hero is now 2 viewport-heights tall (was 1) -- the extra height is pure
   scroll "runway", not extra visible content, giving the scroll-frame
   effect (below) a full viewport-height of dedicated scroll distance to
   finish closing in BEFORE the next section (About) starts its normal
   stack-cover slide over Hero. Without this runway, both effects shared
   the same single viewport-height of scroll, so About was already halfway
   covering Hero by the time the frame had only half-closed -- client
   feedback was that About shouldn't arrive until the square has fully
   formed. Everything actually visible lives in the nested .hero-viewport
   (100vh, fixed), not directly in .hero -- see that rule below for why. */
.hero {
  position: relative;
  width: 100%;
  height: 200vh;
  min-height: 1120px;
  overflow: hidden;
  /* deliberately no opaque background here -- .hero-video-wrap is now
     inset from .hero's edges (see below), leaving a visible margin where
     #site-waves (a fixed canvas painted behind the whole page, see
     main.css's #site-waves rule) needs to show through. An opaque
     fallback here would block that margin permanently, not just during
     video load. #site-waves itself paints a solid white base under its
     wave lines the instant JS runs, so it already serves as the "video
     still loading" fallback this used to handle with a flat color. */
}

/* the actual visible "window" -- exactly one viewport tall/positioned at
   .hero's own top, regardless of .hero's own (now doubled) height. Every
   child below (video-wrap, frame, content, scroll cue) keeps its existing
   percentage-based sizing unchanged, because percentages resolve against
   the nearest positioned ancestor -- which is THIS element, not .hero
   directly -- so none of those rules needed touching just because .hero
   itself got taller. `position: sticky` isn't needed here: .hero itself is
   already the sticky, z-indexed element (from ".stack section", see
   above), and while it's stuck its own top edge sits at the viewport's
   top for the entire (now 200vh) dwell -- this being `position: absolute;
   top: 0` inside it is enough to always render at the visible viewport
   top for that whole duration, no independent stickiness required. */
.hero-viewport {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
}

/* Sized/offset larger than the hero (-6%/112%) -- the cursor-parallax
   drift this overscan originally existed for (a translate on this
   wrapper, set by hero.js) was removed per client feedback ("not that
   great"), but the overscan itself stays: setupHeroFrame()'s clip-path
   math below explicitly measures insets from THIS element's own
   (deliberately larger) edges and adds the 6% overscan back in to land
   on-screen at the intended position -- shrinking this back to 100%
   would need that formula reworked too, for a value that's otherwise
   harmless to leave as-is (the video still fully covers via
   object-fit:cover, just with a hair of unused crop margin at rest). */
.hero-video-wrap {
  position: absolute;
  inset: -6%;
  z-index: 1;
  width: 112%;
  height: 112%;
  overflow: hidden;
}

.hero-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transform-origin: center center;
  animation: hero-ken-burns 24s ease-in-out infinite alternate;
}

@keyframes hero-ken-burns {
  from { transform: scale(1); }
  to { transform: scale(1.1); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-video { animation: none; }
}

.hero-content {
  position: relative;
  z-index: 3;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* centered, not left-anchored -- was `padding: 0 6vw` on a full-width
     flex column with left-aligned children; center-aligning the eyebrow/
     title/subtitle/button as a group (not just the text within a still-
     left-anchored block) needs both align-items:center (so each child
     shrink-wraps and centers instead of stretching to a left edge) and a
     constrained/auto-margined width so the whole block sits in the middle
     of the viewport, not just the middle of a 92vw-wide left-hand column. */
  align-items: center;
  text-align: center;
  margin: 0 auto;
  max-width: min(720px, 92vw);
  padding: 0 6vw;
  pointer-events: none;
}

.hero-eyebrow,
.hero-title,
.hero-subtitle {
  /* always white, both over the video AND once the scroll-frame's square
     window has closed past the text's own width (at typical desktop
     widths the square, capped independently of the title's rendered
     width -- see squareSize in hero.js -- ends up narrower than it, so
     the outer parts of the text end up sitting over the exposed wave
     background, #site-waves, not the video). This used to swap to
     brand-blue there instead (#hero-content.is-framed, toggled by
     setupHeroFrame()'s scroll tick), which read as illegible/low-contrast
     against the mixed video+white backdrop during the transition itself,
     not just at either endpoint. A layered shadow -- a tight dark
     near-outline plus a soft blurred halo -- holds up as white-on-white
     over the wave background well enough that the color no longer needs
     to change at all. */
  color: #fff;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.7),
    0 0 6px rgba(0, 0, 0, 0.55),
    0 4px 18px rgba(0, 0, 0, 0.45);
}

.hero-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0 0 0.8rem;
}

.hero-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 5vw, 4rem);
  line-height: 1.06;
  margin: 0 0 0.9rem;
  font-weight: 600;
  /* deliberately two lines now (a <br> in the markup splits
     "Accelerating"/"Growth."), not natural wrap -- white-space: nowrap
     would fight that forced break. */
}

.hero-subtitle {
  font-family: var(--font-display);
  font-size: clamp(1rem, 1.8vw, 1.4rem);
  margin: 0;
  max-width: 30ch;
}

.hero-content.is-dimmed {
  opacity: 0.25;
  transition: opacity 0.5s ease;
}

.hero-readmore {
  pointer-events: auto;
  /* was `align-self: flex-start` to hug the old left-anchored block's left
     edge -- with .hero-content now centered, flex-start would have pinned
     the button to the block's own left edge instead of centering it under
     the (centered) title/subtitle above it. */
  align-self: center;
  margin-top: 1.6rem;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}

/* --- hero scroll-frame effect ------------------------------------------
   .hero-video-wrap itself is clipped (via clip-path, set inline by
   hero.js's setupHeroFrame() on every scroll tick) into a centered square
   window as the user scrolls through Hero's own one-viewport-height
   "dwell" -- the same scroll distance during which About's box slides up
   to cover Hero (see .stack above). Can't be pure CSS: the final inset
   depends on both scroll progress AND the viewport's own aspect ratio,
   recalculated on resize.

   Used to be 4 opaque white bars layered ON TOP of a still-full-size
   video (faking a shrink by progressively covering its edges) -- replaced
   with a real clip-path on the video itself so the newly-exposed area is
   genuine empty space, not video-hidden-under-a-div. Since
   .hero/.hero-viewport have no background of their own (see .hero's own
   comment), that empty space falls through to #site-waves -- a fixed
   canvas painted behind the whole page (setupSiteWaves() in hero.js) --
   so the client's animated wave background shows through specifically as
   this effect plays, not as a permanent margin around the video. The
   hero text's own shadow (see .hero-eyebrow/.hero-title/.hero-subtitle
   below) is tuned to stay legible against this exposed area too, not
   just the video -- it stays white throughout rather than swapping color
   once the frame closes. */

.hero-scroll {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 32px;
  transform: translateX(-50%);
  width: 30px;
  height: 46px;
  border-radius: 20px;
  border: 2px solid rgba(16, 51, 34, 0.5);
  background: rgba(255, 255, 255, 0.15);
  cursor: pointer;
  padding: 0;
}

.hero-scroll span {
  position: absolute;
  top: 8px;
  left: 50%;
  width: 4px;
  height: 8px;
  border-radius: 2px;
  background: rgba(16, 51, 34, 0.7);
  transform: translateX(-50%);
  animation: scrollDot 1.6s ease-in-out infinite;
}

@keyframes scrollDot {
  0%   { top: 8px; opacity: 1; }
  70%  { top: 26px; opacity: 0; }
  100% { top: 26px; opacity: 0; }
}

@media (max-width: 720px) {
  .hero-content { max-width: 90%; padding: 0 7vw; }
  .hero { min-height: 960px; }
  .hero-viewport { min-height: 480px; }
}

/* --- about section: one full-bleed photo, a headline directly on it, and a
   few small floating stat chips -- no eyebrow/paragraph/split-image layout --- */

.about {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
}

.about__image {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 70% 30%;
  display: block;
}

.about__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(90deg, rgba(8, 15, 12, 0.6) 0%, rgba(8, 15, 12, 0.15) 42%, rgba(8, 15, 12, 0) 65%),
    linear-gradient(0deg, rgba(8, 15, 12, 0.45) 0%, rgba(8, 15, 12, 0) 35%);
}

.about__title {
  position: absolute;
  z-index: 3;
  left: 6vw;
  top: 26%;
  margin: 0;
  max-width: 26ch;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.9rem, 4.2vw, 3.4rem);
  line-height: 1.14;
  color: #fff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.35);
}

/* the 4 numeric stat chips (130+ Products, 50+ Plants, 45+ Years, 2,300+
   Employees) -- .about's only supporting content now (a Purpose/Vision/
   Promise/Values slider briefly occupied this section too, in an earlier
   pass this same round; it was removed again, see HANDOFF.md). Right-
   docked as its own 2x2 grid, independent of .about__title's left-side
   column -- `right: 6vw` mirrors the title's own `left: 6vw` so the two
   feel symmetric. `top: 26%` matches the title's own top so both columns
   start at the same height. */
.about__stats {
  position: absolute;
  z-index: 3;
  top: 26%;
  right: 6vw;
  display: grid;
  grid-template-columns: repeat(2, 190px);
  grid-auto-rows: 108px;
  gap: 1rem;
}

.about__stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.2rem;
  padding: 0.9rem 1.1rem;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 10px 24px rgba(0, 0, 0, 0.2);
}

.about__stat-value {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  color: #fff;
}

.about__stat-label {
  font-size: 0.78rem;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.85);
}

.about__stat--1 { transition-delay: 0s; }
.about__stat--2 { transition-delay: 0.15s; }
.about__stat--3 { transition-delay: 0.3s; }
.about__stat--4 { transition-delay: 0.45s; }

.about__readmore {
  position: absolute;
  z-index: 3;
  left: 6vw;
  /* chained off the title's own top (26%) + its rendered height (4 lines
     at the clamp()'s max font-size, ~246px) + a gap -- sits close under
     the heading instead of down at the section's bottom edge. The title's
     copy is fixed/short, so this is safe to chain unlike the old numeric-
     chip/slider content that used to sit here (see HANDOFF.md) -- that
     had variable-length copy and icon rows, which is why it was bottom-
     anchored instead; a short, unchanging 4-line heading doesn't have
     that problem. */
  top: calc(26% + 270px);
  transition-delay: 0.6s;
  pointer-events: auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}


@media (max-width: 760px) {
  /* `object-position: 70% 30%` (the desktop default) was tuned for a wide
     crop showing both the scientist AND the equipment to her right, with
     text overlaid on the empty space to her left. A narrow/tall mobile
     viewport crops `object-fit: cover` almost entirely left-right (the
     image is 3840x2160 -- far wider than tall relative to a phone screen,
     so `cover` scales it to fill the viewport's height and trims width,
     not height), so that same 70% horizontal anchor pushed her mostly out
     of frame instead of just re-cropping the sides. Recentered on her
     actual position in the source photo (~47% across), not 50% flat --
     50% would still clip part of her given how much width gets trimmed at
     phone aspect ratios. */
  .about__image { object-position: 47% 30%; }
  .about__title { max-width: 14ch; top: 20%; left: 7vw; font-size: clamp(1.6rem, 6vw, 2.4rem); }
  /* no room for a side-by-side right column on a phone-width screen --
     .about__stats drops the desktop right-docked position for a centered
     2x2 grid in the gap below the title, same left/right (7vw) as
     everything else at this width. */
  .about__stats { top: 44%; right: 7vw; left: 7vw; grid-template-columns: 1fr 1fr; grid-auto-rows: 92px; gap: 0.6rem; }
  /* chained close under the stats grid (44% + its own 2-row height + gap)
     instead of pinned to the section's bottom edge, matching the desktop
     "close to the heading" treatment -- title/stats/button all cluster
     together near the top instead of the button sitting on its own far
     below everything else. */
  .about__readmore { top: calc(44% + 210px); bottom: auto; left: 7vw; }
}

/* short desktop windows: the 2x2 stat grid + its gap to the title/button
   above/below adds up to a fixed block that doesn't shrink with viewport
   height, which on a short window can push Read More close to the fold --
   .about__readmore itself doesn't need adjusting (it's bottom-anchored,
   see above), just trim the stats grid's own footprint a little. */
@media (max-height: 700px) {
  .about__stats { grid-auto-rows: 88px; gap: 0.7rem; }
}

/* --- Key Highlights FY26 --- */

.highlights {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
}

.highlights__image {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 60%;
  display: block;
}

.highlights__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(0deg, rgba(5, 8, 4, 0.55) 0%, rgba(5, 8, 4, 0.25) 45%, rgba(5, 8, 4, 0.35) 100%),
    linear-gradient(90deg, rgba(5, 8, 4, 0.55) 0%, rgba(5, 8, 4, 0.1) 55%);
}

.highlights__left {
  position: absolute;
  z-index: 3;
  left: 6vw;
  top: 50%;
  max-width: min(32vw, 420px);
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2rem;
}

.highlights__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.9rem, 4vw, 3rem);
  color: #fff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.4);
}

.highlights__chips {
  position: absolute;
  z-index: 3;
  right: 6vw;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  grid-template-columns: 240px;
  grid-auto-rows: 96px;
  gap: 1rem;
}

.highlights__chip {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.2rem;
  padding: 0.9rem 1.1rem;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 10px 24px rgba(0, 0, 0, 0.2);
}

.highlights__chip-value {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 2px 16px rgba(0, 0, 0, 0.4);
}

.highlights__chip-value small {
  font-size: 0.85rem;
  font-weight: 600;
}

.highlights__chip-label {
  font-size: 0.78rem;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.85);
}

/* scroll-triggered stagger (see [data-reveal] in the scroll-entry reveal
   utility near the top of this file) -- was previously a page-load
   animation-delay, which only ever animated for whatever was on screen at
   load; staggering off each element's own scroll-into-view instead means
   every section's chip grid gets the same effect, not just the first one. */
.highlights__chip--1 { transition-delay: 0.1s; }
.highlights__chip--2 { transition-delay: 0.25s; }
.highlights__chip--3 { transition-delay: 0.4s; }
.highlights__chip--4 { transition-delay: 0.55s; }

.highlights__readmore {
  transition-delay: 0.7s;
  pointer-events: auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}


@media (max-width: 760px) {
  .highlights__left { left: 7vw; top: 14%; max-width: 80vw; transform: none; gap: 1.25rem; }
  .highlights__title { font-size: clamp(1.6rem, 6vw, 2.2rem); }
  .highlights__chips { top: 38%; right: auto; left: 7vw; bottom: auto; transform: none; grid-template-columns: minmax(180px, 60vw); grid-auto-rows: 76px; gap: 0.75rem; }
}

/* --- Trusted to Serve Globally --- */

/* background transparent, not a solid gradient fill -- lets #site-waves
   (the fixed, animated wave canvas, see that rule near the top of this
   file) peek through faintly wherever the video/scrim above it aren't
   fully opaque, matching Joint Letter and CEO Message. */
.trust {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
  background: transparent;
  display: flex;
  align-items: flex-start;
}

/* opacity < 1 so #site-waves shows through the video itself, not just
   around its edges -- the video covers the whole section (inset:0,
   object-fit:cover), so there's no gap for waves to show through unless
   the video layer itself is partly see-through. */
.trust__video {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.5;
  display: block;
}

.trust__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(135deg, rgba(11, 25, 71, 0.72) 0%, rgba(11, 25, 71, 0.5) 100%);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.trust__inner {
  position: relative;
  z-index: 3;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  /* centered, not left-anchored -- the map and the Read More button
     (the heading was removed) center as a group. */
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  padding: 0 6vw;
}

.trust__readmore {
  pointer-events: auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
  /* second in the stagger order now (was third, behind .trust__text) --
     .trust__text is gone and this button moved from inside .trust__content
     to after .trust__map, becoming the last element revealed instead of
     the middle one. */
  transition-delay: 0.3s;
}


.trust__map {
  width: 100%;
  padding: 0.4rem 0 0;
  transition-delay: 0.15s;
}

.trust__map-title {
  margin: 0.9rem 0 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #5bc0e8;
  text-align: center;
}

/* enlarged further after the heading above it was removed -- with no title
   competing for vertical room, the map can take up most of the section.

   This is now an inlined <svg> (was a plain <img src="...svg">, switched
   so the percentage figures inside it -- see .js-countup below -- are
   real DOM text nodes JS can animate, not opaque raster/image content).
   Unlike <img>, an inline <svg> has no automatic intrinsic aspect ratio
   for `height: auto` to resolve against, so `aspect-ratio` does that job
   explicitly instead -- matching the viewBox as tightened below (the
   original viewBox, 0 0 4827.3 1886.1, had ~525 units of empty space on
   the left of the actual map content and almost none on the right,
   which read as visibly off-center; the viewBox was tightened to the
   real content bounding box, see the inline svg's viewBox attribute in
   index.html, so this ratio matches that tightened box, not the
   original artboard). */
.trust__map-image {
  display: block;
  width: 100%;
  max-width: 1320px;
  height: auto;
  aspect-ratio: 4473.58 / 1590.07;
  max-height: 66vh;
  object-fit: contain;
  margin: 0 auto;
}

/* mobile-only text substitute for the map -- at phone widths the map
   image shrinks so much its region labels/percentages become unreadable
   (barely a visible smudge), so mobile swaps it for a plain list of the
   same figures instead of forcing users to squint at a tiny graphic. */
.trust__stats-list {
  display: none;
}

@media (max-width: 860px) {
  .trust__inner { padding: 0 7vw; gap: 1.1rem; }
  .trust__map { display: none; }
  .trust__stats-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 360px;
    gap: 0.7rem;
  }
  .trust__stats-list .trust__map-title { margin: 0 0 0.3rem; }
  .trust__stat-row {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.7rem 1rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.18);
  }
  .trust__stat-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.3rem;
    color: #fff;
  }
  .trust__stat-region {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.85);
  }
}

@media (max-height: 700px) {
  .trust__map-image { max-height: 58vh; }
  .trust__map-title { margin-top: 0.4rem; }
}

/* --- Joint Letter from the Chairman & Co-Chairman --- */

/* background is transparent, not this site's own solid/gradient fill --
   #site-waves (the fixed, animated wave canvas painted behind the whole
   page, see that rule near the top of this file) shows through as a
   white background with faint moving wave lines, same mechanism already
   used wherever else on the site exposes it. */
.joint-letter {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
  background: transparent;
}

/* the photo is a full-bleed panel flush to the section's left edge, top
   to bottom, sized to exactly half the section -- a clean 50/50 split
   with the card, not the earlier "photo edge-to-edge, card floating in
   overlap over its inner edge" layout (see the card comment below for
   why that changed). */
.joint-letter__photo {
  position: absolute;
  z-index: 1;
  left: 0;
  top: 0;
  bottom: 0;
  width: 50%;
  overflow: hidden;
}

/* Straight 50/50 split, not a floating overlapping card -- client asked
   for the photo to cover exactly half the screen and the text to cover
   the other half. Fills the right half edge-to-edge (top/bottom/right
   all at the section's own edges), square corners (there's no "floating
   over the photo" anymore, so a rounded/shadowed card would look like a
   mistake sitting flush against three edges). Vertically centers its
   content via flex, same as before. */
.joint-letter__card {
  position: absolute;
  z-index: 3;
  left: 50%;
  right: 0;
  top: 0;
  bottom: 0;
  background: var(--brand-blue);
  padding: 2.5rem clamp(2rem, 6vw, 5rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.joint-letter__quote {
  display: block;
  width: 2.6rem;
  height: auto;
  margin-bottom: 1rem;
}

.joint-letter__salutation {
  margin: 0 0 0.5rem;
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #8ecbee;
  transition-delay: 0.25s;
}

.joint-letter__title {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.4rem, 2.3vw, 2rem);
  line-height: 1.2;
  color: #fff;
  transition-delay: 0.1s;
}

.joint-letter__lead {
  margin: 0 0 1.5rem;
  font-size: 0.92rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.88);
  transition-delay: 0.35s;
}

.joint-letter__readmore {
  pointer-events: auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
  transition-delay: 0.5s;
}

.joint-letter__signatures {
  transition-delay: 0.65s;
  display: flex;
  gap: 2.4rem;
  margin-top: 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
}

.joint-letter__signature {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.joint-letter__signature-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: #fff;
}

.joint-letter__signature-title {
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.75);
}

.joint-letter__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 14%;
  display: block;
}

/* the colorful accent (this site's own blue/green/yellow trio, see the
   Read More divider and Performance Highlights) sits near the seam
   between photo and card, echoing the reference layout's diagonal
   color pop there. */
/* z-index 2, BELOW the card (z-index 3, see .joint-letter__card above)
   -- was z-index 4 (above the card), which let this decorative shape sit
   on top of the card's own text whenever the card's content ran long
   enough to reach this accent's fixed bottom:10% position (the card's
   height is content-driven, this accent's position isn't, so the two
   were never guaranteed to stay clear of each other). Above the photo
   (z-index 1) so it still pops at the photo/card seam, just tucked
   behind the card everywhere the two overlap. */
.joint-letter__accent-pop {
  position: absolute;
  z-index: 2;
  bottom: 10%;
  left: 50%;
  margin-left: -42px;
  width: 84px;
  height: 84px;
  border-radius: 18px;
  background: linear-gradient(135deg, #5bc0e8, #8bc751 55%, #ffcb05 100%);
  transform: rotate(45deg);
}

/* Pulsating radar rings, centered on the single point where the two
   sections meet -- Joint Letter's own bottom edge / CEO Message's own top
   edge, at the photo/card seam's horizontal position (50%). Half the
   circle renders in each section (top half in Joint Letter, bottom half in
   CEO Message); each section's own overflow:hidden crops it there, so the
   two halves read as one continuous circle split across the boundary,
   matching the client's own sketch. clip-path additionally masks off
   whichever side is the photo, so the arcs only ever cross the blue card
   background. Joint Letter has photo-left/card-right (clip the left half);
   CEO Message is mirrored (clip the right half, see .ceo-message's own
   layout note above). z-index 4, above the card (3), but pointer-
   events:none and low-opacity strokes so it reads as ambient decoration,
   not something blocking the card's text. */
.joint-letter__pulse,
.ceo-message__pulse {
  position: absolute;
  z-index: 4;
  inset: 0;
  pointer-events: none;
}
.joint-letter__pulse { clip-path: inset(0 0 0 50%); }
.ceo-message__pulse { clip-path: inset(0 50% 0 0); }

.joint-letter__pulse .pulse-ring {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 640px;
  height: 640px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.55);
  transform: translate(-50%, 50%) scale(0.3);
  opacity: 0;
  animation: pulse-ring-anim 3s cubic-bezier(0.16, 0.6, 0.3, 1) infinite;
}
.ceo-message__pulse .pulse-ring {
  position: absolute;
  left: 50%;
  top: 0;
  width: 640px;
  height: 640px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.55);
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  animation: pulse-ring-anim-ceo 3s cubic-bezier(0.16, 0.6, 0.3, 1) infinite;
}
.pulse-ring:nth-child(2) { animation-delay: 1s; }
.pulse-ring:nth-child(3) { animation-delay: 2s; }

@keyframes pulse-ring-anim {
  0% { transform: translate(-50%, 50%) scale(0.3); opacity: 0.65; }
  70% { opacity: 0.15; }
  100% { transform: translate(-50%, 50%) scale(1.5); opacity: 0; }
}

@keyframes pulse-ring-anim-ceo {
  0% { transform: translate(-50%, -50%) scale(0.3); opacity: 0.65; }
  70% { opacity: 0.15; }
  100% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .joint-letter__pulse .pulse-ring {
    animation: none;
    opacity: 0.35;
    transform: translate(-50%, 50%) scale(0.9);
  }
  .ceo-message__pulse .pulse-ring {
    animation: none;
    opacity: 0.35;
    transform: translate(-50%, -50%) scale(0.9);
  }
}

@media (max-width: 860px) {
  .joint-letter { display: flex; flex-direction: column; height: auto; min-height: 0; overflow: visible; gap: 1rem; padding: 100px 7vw 2rem; }
  .joint-letter__accent-pop { display: none; }
  .joint-letter__pulse { display: none; }
  .joint-letter__photo { order: 1; position: relative; inset: auto; width: 100%; height: 30vh; flex-shrink: 0; border-radius: 20px; box-shadow: none; }
  .joint-letter__image { object-position: 50% 8%; }
  .joint-letter__card { order: 2; position: relative; inset: auto; left: auto; top: auto; transform: none; width: 100%; border-radius: 20px; padding: 1.3rem 1.4rem 1.1rem; }
  .joint-letter__quote { width: 1.8rem; margin-bottom: 0.5rem; }
  .joint-letter__salutation { font-size: 0.72rem; margin-bottom: 0.35rem; }
  .joint-letter__title { font-size: clamp(1.25rem, 5.6vw, 1.6rem); margin-bottom: 0.5rem; }
  .joint-letter__lead { font-size: 0.8rem; line-height: 1.45; margin-bottom: 0.8rem; }
  .joint-letter__signatures { flex-direction: column; gap: 0.5rem; margin-top: 0.7rem; padding-top: 0.6rem; }
  .joint-letter__signature-name { font-size: 0.85rem; }
  .joint-letter__signature-title { font-size: 0.72rem; }
}

@media (max-height: 700px) and (min-width: 861px) {
  .joint-letter__card { padding: 1.4rem 2.2rem; }
  .joint-letter__quote { width: 2rem; margin-bottom: 0.6rem; }
  .joint-letter__salutation { margin-bottom: 0.35rem; }
  .joint-letter__title { font-size: clamp(1.2rem, 2.2vw, 1.6rem); margin-bottom: 0.6rem; }
  .joint-letter__lead { margin-bottom: 0.9rem; }
  .joint-letter__signatures { margin-top: 0.9rem; padding-top: 0.8rem; }
}

/* --- Performance Highlights carousel --- */

.performance {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
}

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

.performance__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(180deg, rgba(8, 15, 12, 0.35) 0%, rgba(8, 15, 12, 0.55) 100%);
}

/* row, not a bottom-anchored column -- title on the left, the carousel
   confined to its own column on the right (see .performance__carousel
   below) instead of spanning the section's full width. Mobile reverts to
   the original stacked column, see the media query further down. */
.performance__inner {
  position: relative;
  z-index: 3;
  height: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 3vw;
  padding: 0 6vw;
}

.performance__title {
  margin: 0;
  max-width: 34vw;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.8rem, 3.2vw, 2.6rem);
  color: #fff;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.35);
}

/* wraps the carousel AND the dots, stacked in a column, so the dots
   render below the graph card -- they used to be a third sibling in
   .performance__inner's own row alongside the title and the carousel,
   which (once that row layout was introduced for the title/carousel
   split) pushed them out to the right of the carousel instead of
   underneath it. */
.performance__side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  width: min(560px, 46vw);
  flex-shrink: 0;
}

.performance__carousel {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  width: 100%;
}

.performance__viewport {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.performance__track {
  display: flex;
  gap: 24px;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: transform;
}

.performance__card {
  flex: 0 0 auto;
  background: rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(16px) saturate(180%);
  -webkit-backdrop-filter: blur(16px) saturate(180%);
  border-radius: 0;
  padding: 1.6rem;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 14px 34px rgba(0, 0, 0, 0.22);
}

/* staggered scroll-in; also fires again each time a card pages into view
   via the carousel's own translateX (performance__viewport clips it via
   overflow:hidden, so IntersectionObserver correctly treats an off-screen
   card as not-yet-intersecting) -- a nice bonus, not just an entrance */
.performance__card:nth-child(1) { transition-delay: 0s; }
.performance__card:nth-child(2) { transition-delay: 0.15s; }
.performance__card:nth-child(3) { transition-delay: 0.3s; }

/* stat-panel dashboard modules (bar chart + related-metric tiles, one glass
   panel per group) built in plain CSS/HTML (no image assets) */

.statpanel {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  grid-template-rows: 1fr 1fr;
  height: 100%;
  gap: 0;
}

.statpanel__chart {
  grid-column: 1;
  grid-row: 1 / span 2;
  display: flex;
  flex-direction: column;
  padding-right: 1.4rem;
  border-right: 1px solid rgba(255, 255, 255, 0.25);
}

.statpanel__chart-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.8rem;
  margin-bottom: 1rem;
}

.statpanel__chart-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: #fff;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
}

.statpanel__unit {
  font-size: 0.68rem;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.7);
}

.statpanel__current {
  flex: none;
  font-size: 0.72rem;
  color: rgba(255, 255, 255, 0.75);
  white-space: nowrap;
}

.statpanel__current strong {
  color: #ffcb05;
  font-weight: 700;
}

.statpanel__bars {
  flex: 1;
  display: flex;
  align-items: flex-end;
  gap: 14px;
  min-height: 220px;
}

.statpanel__col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
}

.statpanel__val {
  font-size: 0.8rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.9);
  white-space: nowrap;
}

.statpanel__track {
  width: 36%;
  height: 190px;
  display: flex;
  align-items: flex-end;
}

.statpanel__bar {
  width: 100%;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.25);
}

.statpanel__bar--accent {
  background: #ffcb05;
}

.statpanel__bar-label {
  font-size: 0.7rem;
  color: rgba(255, 255, 255, 0.7);
}

.statpanel__tile {
  padding-left: 1.4rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.statpanel__tile + .statpanel__tile {
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}

.statpanel__tile-title {
  margin: 0 0 0.4rem;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.75);
}

.statpanel__tile-value {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.6rem;
  line-height: 1.1;
  color: #fff;
}

.statpanel__tile-delta {
  margin: 0.35rem 0 0;
  font-size: 0.7rem;
  font-weight: 600;
  color: #8bc751;
}

@media (max-width: 860px) {
  .statpanel { grid-template-columns: 1fr; grid-template-rows: auto auto auto; }
  .statpanel__chart { grid-column: 1; grid-row: 1; padding-right: 0; border-right: none; padding-bottom: 1rem; border-bottom: 1px solid rgba(255, 255, 255, 0.25); }
  .statpanel__chart-title { font-size: 0.95rem; }
  .statpanel__bars { min-height: 160px; gap: 8px; }
  .statpanel__track { width: 40%; height: 140px; }
  .statpanel__val { font-size: 0.72rem; }
  .statpanel__bar-label { font-size: 0.62rem; }
  .statpanel__tile { padding-left: 0; padding-top: 0.9rem; }
  .statpanel__tile + .statpanel__tile { border-top: 1px solid rgba(255, 255, 255, 0.2); margin-top: 0.9rem; }
  .statpanel__tile-value { font-size: 1.3rem; }
}

.performance__arrow {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.9);
  color: var(--brand-blue);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  pointer-events: auto;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
}

.performance__arrow:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
}

.performance__arrow:disabled {
  opacity: 0.4;
  cursor: default;
  transform: none;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
}

.performance__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.6rem;
}

.performance__dot {
  width: 9px;
  height: 9px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.45);
  cursor: pointer;
  pointer-events: auto;
  transition: width 0.25s ease, background 0.25s ease;
}

.performance__dot.is-active {
  width: 24px;
  background: #fff;
}

/* mobile: image and content no longer overlap at all -- was a full-bleed
   background image (position:absolute inset:0) with the title/carousel
   overlaid on top via a scrim for contrast, which on a narrow viewport
   left the graph card sitting directly over the men in the photo,
   fighting each other for attention. Now a flat stack: a fixed-height
   image banner up top (cropped to the two men specifically, see
   object-position below), normal document flow underneath it. */
@media (max-width: 860px) {
  .performance {
    display: flex;
    flex-direction: column;
    height: auto;
    min-height: 0;
    overflow: visible;
  }
  .performance__bg {
    position: relative;
    inset: auto;
    height: 34vh;
    object-position: 32% 30%;
  }
  .performance__scrim { display: none; }
  .performance__inner {
    position: relative;
    height: auto;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    padding: 1.6rem 7vw 4vh;
    gap: 1.4rem;
    background: linear-gradient(180deg, #0f2359 0%, #142a63 100%);
  }
  .performance__title { max-width: none; }
  .performance__side { width: 100%; }
  .performance__carousel { width: 100%; gap: 0.6rem; }
  .performance__arrow { width: 38px; height: 38px; }
  .performance__card { padding: 1rem; }
}

/* short desktop windows (laptop browser chrome eating into viewport height,
   not a narrow-width/mobile case) -- .statpanel__bars/.statpanel__track are
   fixed px heights that don't scale with viewport height on their own, so
   on a short window this section's bottom-anchored title+card block was
   tall enough to push the title up underneath the fixed nav. Shrinks the
   chart and tightens the surrounding gaps so the whole block fits below
   the nav instead. */
@media (max-height: 700px) {
  .performance__inner { gap: 1.2rem; padding-bottom: 3vh; }
  .performance__title { font-size: clamp(1.4rem, 2.6vw, 2rem); }
  .statpanel__bars { min-height: 140px; }
  .statpanel__track { height: 120px; }
}

/* --- CEO & Managing Director's Message --- */

/* mirrors .joint-letter's layout (see that component's own rules above)
   -- full-height photo flush to one edge, quote card floating over its
   inner edge, transparent section background exposing #site-waves.
   Mirrored left/right: photo sits on the RIGHT here (was left for Joint
   Letter), card on the LEFT overlapping the photo's left edge. */
.ceo-message {
  position: relative;
  width: 100%;
  height: 100vh;
  min-height: 560px;
  overflow: hidden;
  background: transparent;
}

.ceo-message__photo {
  position: absolute;
  z-index: 1;
  right: 0;
  top: 0;
  bottom: 0;
  width: 50%;
  overflow: hidden;
}

.ceo-message__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 14%;
  display: block;
}

/* Straight 50/50 split -- see the identical note on .joint-letter__card
   above (same ask, mirrored). Fills the left half edge-to-edge, square
   corners, no floating shadow. */
.ceo-message__card {
  position: absolute;
  z-index: 3;
  right: 50%;
  left: 0;
  top: 0;
  bottom: 0;
  background: var(--brand-blue);
  padding: 2.5rem clamp(2rem, 6vw, 5rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.ceo-message__quote {
  display: block;
  width: 2.6rem;
  height: auto;
  margin-bottom: 1rem;
}

.ceo-message__title {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.4rem, 2.3vw, 2rem);
  line-height: 1.2;
  color: #fff;
  transition-delay: 0.1s;
}

.ceo-message__lead {
  margin: 0 0 1.5rem;
  font-size: 0.92rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.88);
  transition-delay: 0.25s;
}

.ceo-message__readmore {
  pointer-events: auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
  transition-delay: 0.4s;
}

.ceo-message__signature {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  margin-top: 2rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  transition-delay: 0.55s;
}

.ceo-message__signature-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: #fff;
}

.ceo-message__signature-title {
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.75);
}

/* the colorful accent (this site's own blue/green/yellow trio) sits near
   the seam between photo and card, mirroring .joint-letter__accent-pop. */
/* z-index 2, below the card -- see the identical note on
   .joint-letter__accent-pop above. */
.ceo-message__accent-pop {
  position: absolute;
  z-index: 2;
  bottom: 10%;
  right: 50%;
  margin-right: -42px;
  width: 84px;
  height: 84px;
  border-radius: 18px;
  background: linear-gradient(135deg, #5bc0e8, #8bc751 55%, #ffcb05 100%);
  transform: rotate(45deg);
}

@media (max-width: 860px) {
  .ceo-message { display: flex; flex-direction: column; height: auto; min-height: 0; overflow: visible; gap: 1rem; padding: 100px 7vw 2rem; }
  .ceo-message__accent-pop { display: none; }
  .ceo-message__pulse { display: none; }
  .ceo-message__photo { order: 1; position: relative; inset: auto; width: 100%; height: 30vh; flex-shrink: 0; border-radius: 20px; box-shadow: none; }
  .ceo-message__image { object-position: 50% 8%; }
  .ceo-message__card { order: 2; position: relative; inset: auto; right: auto; top: auto; transform: none; width: 100%; border-radius: 20px; padding: 1.3rem 1.4rem 1.1rem; }
  .ceo-message__quote { width: 1.8rem; margin-bottom: 0.5rem; }
  .ceo-message__title { font-size: clamp(1.25rem, 5.6vw, 1.6rem); margin-bottom: 0.5rem; }
  .ceo-message__lead { font-size: 0.8rem; line-height: 1.45; margin-bottom: 0.8rem; }
  .ceo-message__signature { margin-top: 0.7rem; padding-top: 0.6rem; }
}

@media (max-height: 700px) and (min-width: 861px) {
  .ceo-message__card { padding: 1.4rem 2.2rem; }
  .ceo-message__quote { width: 2rem; margin-bottom: 0.6rem; }
  .ceo-message__title { font-size: clamp(1.2rem, 2.2vw, 1.6rem); margin-bottom: 0.6rem; }
  .ceo-message__lead { margin-bottom: 0.9rem; }
  .ceo-message__signature { margin-top: 1.2rem; padding-top: 1rem; }
}

/* --- full-screen "Read More" report overlay --- */

/* Cinematic entry: was a flat translateY slide at a single speed (panel and
   content arriving in the same instant, no sense of depth). Now three
   layers settle in sequence, slowest-to-fastest easing so it reads as one
   deliberate motion rather than a snap -- the panel itself slides up AND
   scales down from a slight zoom (1.06 -> 1, like the camera pulling back),
   the blurred backdrop photo settles its own zoom a beat later (see
   .report-overlay__video below), and the text content rises in last (see
   .report-overlay__inner below), after the panel has mostly arrived. */
.report-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  overflow: hidden;
  transform: translateY(100%) scale(1.06);
  opacity: 0;
  transition: transform 0.9s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.7s ease;
  pointer-events: none;
}

.report-overlay.is-open {
  transform: translateY(0) scale(1);
  opacity: 1;
  pointer-events: auto;
}

.report-overlay__video {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: blur(50px);
  transform: scale(1.32);
  transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
}

.report-overlay.is-open .report-overlay__video {
  transform: scale(1.2);
}

.report-overlay__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: rgba(255, 255, 255, 0.6);
}

.report-overlay__close-float {
  position: absolute;
  z-index: 5;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: none;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 600;
  padding: 0.7rem 1.4rem;
  border-radius: 999px;
  cursor: pointer;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.report-overlay__close-float:hover {
  transform: translateX(-50%) translateY(-2px);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.22);
}

.report-overlay__close-x {
  font-size: 1.2rem;
  line-height: 1;
}

.report-overlay__scroll {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.report-overlay__inner {
  /* rises in last, after the panel/backdrop have mostly settled (see the
     cinematic-entry note on .report-overlay above) -- opacity+translateY
     only, no scale, so body text never looks blurry mid-transition. */
  opacity: 0;
  transform: translateY(36px);
  transition: opacity 0.6s ease 0.3s, transform 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.3s;
  max-width: 1100px;
  margin: 0 auto;
  padding: 9vh 6vw 12vh;
}

.report-overlay.is-open .report-overlay__inner {
  opacity: 1;
  transform: translateY(0);
}

.report-overlay__title {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  line-height: 1.05;
  font-weight: 600;
  color: var(--brand-blue);
  margin: 0;
}

.report-overlay__subtitle {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.6vw, 1.9rem);
  color: var(--brand-blue);
  margin: 0.4rem 0 0;
}

.report-overlay__divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2.2rem 0;
}

.report-overlay__divider span {
  display: block;
  height: 14px;
  border-radius: 999px;
}
.report-overlay__divider span:nth-child(1) { background: #5bc0e8; width: 160px; }
.report-overlay__divider span:nth-child(2) { background: #8bc751; width: 60px; }
.report-overlay__divider span:nth-child(3) { background: #ffcb05; width: 52px; }

.report-overlay__lead {
  font-size: clamp(1.1rem, 1.8vw, 1.35rem);
  line-height: 1.55;
  color: var(--brand-blue);
  margin: 0 0 1.6rem;
}

.report-overlay__body {
  font-size: 1rem;
  line-height: 1.65;
  color: #2b2b2b;
  margin: 0 0 1.3rem;
}

.report-overlay__body strong {
  color: var(--brand-blue);
}

@media (prefers-reduced-motion: reduce) {
  .report-overlay,
  .report-overlay__video,
  .report-overlay__inner {
    transition: none !important;
  }
  .report-overlay__video { transform: scale(1.2); }
  .report-overlay__inner { transform: none; }
}

.report-overlay__quote {
  display: block;
  width: 2.6rem;
  height: auto;
  margin: 0 0 1.6rem;
}

@media (max-width: 760px) {
  .report-overlay__inner { padding: 12vh 7vw 14vh; }
}

/* --- FY26 Highlights: 8 cards arranged in a WebGL "fanned arc" carousel
   (Three.js), ported from a reference build's Social/Natural Capital
   sections -- an arc of portrait cards with the active one centered and
   forward, the rest fanning out to either side with perspective. Card
   image src/title/body are read off the (hidden) .story__card buttons by
   setupStoryFan() in hero.js, which creates the actual <canvas> and plane
   meshes; the title/body/Read More below update to match the active card
   (unlike the reference, which used one static caption -- these 8 cards
   each have genuinely distinct copy, so a per-card info panel fits the
   content better than a fixed caption would). See hero.js for the full
   mechanism writeup. */

/* ----- story-wrap: TWO full-viewport screens, not one shared strip --
   heading gets its own screen, the slider gets a full screen entirely to
   itself right after.

   No background image/gradient wash of its own -- transparent, so
   #site-waves (the fixed, animated wave canvas, see that rule near the
   top of this file) shows through as a plain white backdrop with the
   site's own wave-line texture, same mechanism as Trust/Joint Letter/CEO
   Message, replacing the old plant-photo-plus-tint-wash background. */
.story-wrap {
  position: relative;
  background: transparent;
}

/* screen 1: heading alone, nothing else on screen with it. */
.story-intro {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 7vw;
}

/* screen 2: the slider, full viewport to itself -- the WebGL fan is an
   absolute full-bleed backdrop layer (see .story__fan below) and the
   stage (arrows/info/dots) floats above it, pinned to the bottom by
   this flex column. */
.story {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 100vh;
  padding: 0 0 2vh;
}

.story__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 4vw, 3rem);
  color: var(--ink);
}

.story__subtitle {
  margin: 0.5rem 0 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  color: var(--brand-blue);
}

/* ----- fan (WebGL canvas area) -------------------------------------- */
.story__fan {
  /* absolute full-bleed backdrop covering the WHOLE section, not a
     flow strip between the heading and the info text -- the reference's
     cards are tall enough to crop at the viewport top, which is only
     possible if the canvas itself spans the full section (a canvas
     always clips its drawing to its own box). setupStoryFan() sizes
     CARD_H/R directly off this box's rendered dimensions (see
     frameCamera()/CARD_H in hero.js), so this is also what drives the
     cards to reference height. z-index 0 keeps it behind the head/stage
     text (both position:relative, z-index above). */
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}

.story__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* real markup kept only so setupStoryFan() can read image src + the
   data-title/data-body attributes off each button -- WebGL renders the
   actual visible cards, these stay invisible. */
.story__card { display: none; }

/* ----- stage: arrows flank the per-card info panel ------------------- */
.story__stage {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 3vw, 32px);
  max-width: 720px;
  margin: 0 auto;
  padding: 0 7vw;
}

.story__arrow {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid rgba(16, 51, 34, 0.14);
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(16, 51, 34, 0.08);
  transition: background 0.2s ease, color 0.2s ease, transform 0.2s ease;
}

.story__arrow:hover {
  background: var(--ink);
  color: #fff;
  transform: translateY(-2px);
}

.story__arrow svg { width: 16px; height: 16px; }

.story__info {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: 0.6rem;
  justify-items: center;
  text-align: center;
}

.story__info-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1.3;
  color: var(--ink);
}

/* replaces the old .story__info-readmore button -- the centered photo
   itself is the click target now (see setupStoryFan()'s raycast click
   handler in hero.js), this is just a small affordance hint so that's
   discoverable. */
.story__info-hint {
  margin: 0.1rem 0 0;
  font-size: 0.78rem;
  font-style: italic;
  color: rgba(16, 51, 34, 0.55);
}

.story__dots {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.2rem;
}

/* segmented progress bar, not plain dots -- matches the reference's one
   longer/filled segment for the active index among several shorter ticks.
   Same markup/class names as the old dots (only .story__dots/.story__dot's
   own CSS changed), since the JS click/is-active wiring didn't need to
   change, only how each segment looks. */
.story__dot {
  width: 14px;
  height: 4px;
  border-radius: 2px;
  border: none;
  padding: 0;
  background: rgba(16, 51, 34, 0.28);
  cursor: pointer;
  transition: background 0.2s ease, width 0.2s ease;
}

.story__dot.is-active {
  width: 30px;
  background: var(--brand-blue);
}

@media (max-width: 760px) {
  /* no .story__fan height override anymore -- it's an absolute inset:0
     backdrop of the whole slider screen at every width now. */
  /* section itself shortened from a full 100vh -- shrinking the cards'
     own BAND_H (see setupStoryFan() in hero.js) alone just left empty
     space above them, since .story stayed a full viewport tall
     regardless of how much of it the cards actually filled; the ask was
     to reduce the section's own height, not just leave a gap. */
  .story { min-height: 76vh; }
  .story__stage { gap: 10px; padding: 0 4vw; }
  .story__arrow { width: 40px; height: 40px; }
  .story__info-title { font-size: 1.05rem; }
}

/* --- Industry & Business Verticals: static content section (no scroll-
   jacking, no full-bleed photo -- plain white/pale background since it's
   dense with its own 6 photos + text already). Each of the 3 BUs is a row:
   BU name + bullet list on the left, its 2 paired vertical-market photos
   on the right. Row color (blue/yellow/green) reuses the exact 3 accent
   colors already used for the Read More divider / Performance Highlights
   -- no new palette introduced. */

.verticals {
  position: relative;
  background: #fff;
  /* min() with a fixed px floor, not just 10vh -- this section starts in
     plain scroll flow right after .story, so scrolling to exactly its top
     boundary puts this padding directly behind the fixed .site-nav on
     short viewports; same class of bug fixed on .story__head/.story__pin. */
  padding: max(10vh, 155px) 7vw 8vh;
}

.verticals__head {
  max-width: 800px;
  margin: 0 auto 6vh;
  text-align: center;
}

/* three overlapping spheres, floating above the title -- purely
   decorative (aria-hidden), same soft-shadowed-orb language as the
   reference layout this section is modelled on. Radial gradient per orb
   for a subtle 3D highlight rather than a flat tinted circle. */
.verticals__orbs {
  position: relative;
  height: 64px;
  margin: 0 auto 1.5rem;
  width: 84px;
}

.verticals__orbs span {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, #fff, #cfe0f2 70%, #a9c3e0 100%);
  box-shadow: 0 10px 22px rgba(11, 25, 71, 0.14);
}

.verticals__orbs span:nth-child(1) { width: 40px; height: 40px; top: 0; left: 20px; }
.verticals__orbs span:nth-child(2) { width: 40px; height: 40px; top: 24px; left: 0; }
.verticals__orbs span:nth-child(3) { width: 40px; height: 40px; top: 24px; left: 40px; }

/* Bigger, lighter weight, two-tone title -- modelled on the reference's
   large centered headline that switches color between its two lines
   instead of one flat brand-blue block. .verticals__title-accent (the
   first line) uses this site's own existing green accent (already used on
   the Read More divider/Performance Highlights, not a new color
   introduced just for this); the second line keeps the site's standard
   brand-blue heading color, unchanged from before. */
.verticals__title {
  margin: 0 0 1.3rem;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.1rem, 4.4vw, 3.4rem);
  color: var(--brand-blue);
  line-height: 1.18;
}

.verticals__title-accent {
  color: #4a9b6e;
}

.verticals__intro {
  max-width: 640px;
  margin: 0 auto;
  font-size: 1.02rem;
  line-height: 1.7;
  color: #667284;
}

.verticals__cta {
  text-align: center;
  margin: 3rem 0 0;
}

.verticals__readmore {
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}


/* --- Business Unit showcase: full-viewport, split in half per vertical --
   New section, separate from #verticals/#molecule above (not a replacement
   for it -- see the HTML comment there). Each column is image-on-top,
   copy-below: the image takes a fixed share of the viewport height
   (58vh), the eyebrow/heading/body/CTA fill the rest, vertically centred.
   First of a planned 3 (one per business unit); Nutrition and Chemical
   Intermediates will follow the same .bu-split shape once their images
   exist -- reuse these classes, don't fork new ones per section. */
.bu-split {
  position: relative;
  z-index: 1;
  display: flex;
  min-height: 100vh;
  /* opaque, matching #verticals's own explicit white background above --
     without it the sitewide #site-waves molecule-network canvas shows
     through behind the copy (it's a fixed full-page background), which
     reads as clutter under body text even though it works fine as an
     ambient backdrop for sparser sections. */
  background: #fff;
}

.bu-split__col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.bu-split__media {
  flex: 0 0 58vh;
  overflow: hidden;
}

.bu-split__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.bu-split__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 2.5rem clamp(1.6rem, 5vw, 4rem);
}

.bu-split__eyebrow {
  display: block;
  margin: 0 0 0.7rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brand-blue);
}

.bu-split__title {
  margin: 0 0 1.1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.7rem, 2.8vw, 2.3rem);
  color: var(--brand-blue);
}

.bu-split__body {
  max-width: 420px;
  margin: 0 0 1.7rem;
  font-size: 1rem;
  line-height: 1.65;
  color: #445064;
}

.bu-split__cta {
  align-self: flex-start;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}

@media (max-width: 760px) {
  .bu-split { flex-direction: column; min-height: auto; }
  .bu-split__media { flex-basis: 42vh; }
  .bu-split__content { padding: 1.8rem; }
  .bu-split__body { max-width: none; }
}

/* --- site footer: full-bleed video, one edge-to-edge frosted glass panel
   whose blur/opacity fades left (fully frosted, where the text sits) to
   right (clear video showing through) via a single mask-image gradient on
   .site-footer__glass -- not per-column glass boxes. Content sits in a
   normal-flow column grid mirroring the source report's table of contents
   (5 chapter groups), not full-bleed-photo-per-section like the rest of
   the site, since it's dense reference text rather than a hero moment. */

.site-footer {
  position: relative;
  overflow: hidden;
  /* what .site-footer__video/__glass fade INTO (see their mask-image) once
     they end at 100dvh -- .site-footer itself runs many screens taller
     than that for the dense TOC columns, and this used to fall through to
     body's plain white, creating a hard rectangular seam where the video
     abruptly cut off mid-content instead of a deliberate section break.
     Dark now to match the black-tinted glass + white text below. */
  background: #12181f;
  /* top padding chained off the JS-measured --nav-height var (see
     setNavHeightVar in hero.js, same technique the mega-menu panel uses)
     instead of a hardcoded max(8vh, 140px) guess -- the guess assumed the
     real nav would never exceed ~140px, and whenever it did (font
     rendering/browser differences, or just being wrong), the fixed nav
     bar overlapped the first row of footer column headers. Wrapped in a
     max() with a fixed 190px floor as a safety net -- --nav-height is
     measured off a live DOM element mid-entrance-animation and has
     already been caught computing a stale/negative value once; if that
     ever happens again this stops it from collapsing the padding instead
     of just shrinking the gap. */
  padding: max(calc(var(--nav-height, 141px) + 2vh), 100px) 6vw 4vh;
}

.site-footer__video {
  /* height: 100dvh, NOT 100% -- .site-footer itself is much taller than
     one screen (5 dense TOC columns), and height:100% previously matched
     that full multi-screen height, forcing object-fit:cover to zoom in
     absurdly on a box far taller than it is wide, which is why the video
     read as barely-visible/frozen rather than a visible moving backdrop.
     Capping it to one viewport height keeps the crop sane; the footer's
     lower content (below the first screen) sits on the plain page
     background once the video's fixed-height box ends. */
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100dvh;
  object-fit: cover;
  display: block;
  /* dissolves into .site-footer's own background over the last 20% of its
     height instead of cutting off in a hard rectangle -- the mask is
     purely vertical (top-to-bottom), so it doesn't touch the glass
     layer's separate left-to-right contrast gradient below. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 80%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 80%, transparent 100%);
}

.site-footer__glass {
  /* the LEFT-RIGHT fade is carried entirely by the white-wash opacity
     gradient below, NOT a mask-image -- an earlier version masked the
     whole glass layer down to fully transparent on the right, which put
     the rightmost columns directly against the raw video with nothing to
     lift contrast for the text sitting on top of it. backdrop-filter blur
     stays constant edge-to-edge (it softens the video everywhere, which
     is harmless to text legibility); only the wash's opacity thins out
     left-to-right, and never drops low enough to lose contrast for the
     text sitting on top of it.

     Text is white now (was dark navy/charcoal), so the wash flipped from
     white to black to still darken the video enough behind it -- same
     contrast-lifting job, opposite direction.

     The TOP-BOTTOM mask-image added below is a different axis and doesn't
     have that problem: it fades this layer out in lockstep with
     .site-footer__video underneath (same gradient stops), so by the time
     either is transparent the page's own dark background has taken over
     -- contrast only improves going down, never drops with nothing to
     back it up the way the horizontal case did. */
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  height: 100dvh;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 80%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 80%, transparent 100%);
  background: linear-gradient(to right, rgba(0, 0, 0, 0.68) 0%, rgba(0, 0, 0, 0.5) 45%, rgba(0, 0, 0, 0.32) 100%);
  backdrop-filter: blur(0.5px) saturate(140%);
  -webkit-backdrop-filter: blur(0.5px) saturate(140%);
}

.site-footer__inner {
  position: relative;
  z-index: 3;
  max-width: 1560px;
  margin: 0 auto;
}

.site-footer__logo {
  display: block;
  height: 52px;
  margin: 0 0 5vh;
}

.site-footer__columns {
  display: grid;
  grid-template-columns: repeat(4, 1fr) 1.7fr;
  gap: 2.4rem;
}

.site-footer__heading {
  margin: 0 0 0.6rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #fff;
}

.site-footer__heading--sub {
  margin-top: 1.6rem;
}

.site-footer__accent {
  display: block;
  width: 42px;
  height: 4px;
  margin-bottom: 1rem;
  background: linear-gradient(90deg, #5bc0e8, #8bc751 50%, #ffcb05 100%);
}

.site-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.site-footer__list li {
  font-size: 0.8rem;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.85);
}

.site-footer__list a {
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
}

.site-footer__list a:hover {
  color: #5bc0e8;
  text-decoration: underline;
}

.site-footer__subheading {
  margin: 0 0 0.5rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.76rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.65);
}

.site-footer__split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.6rem;
}

.site-footer__bottom {
  margin-top: 5vh;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  text-align: center;
  font-size: 0.76rem;
  color: rgba(255, 255, 255, 0.65);
}

@media (max-width: 960px) {
  .site-footer__columns { grid-template-columns: 1fr 1fr; }
  .site-footer__col--wide { grid-column: 1 / -1; }
}

/* --- initial-load loader: the hero section carries a video (plus 3 more
   further down the page), so without this the first paint is either a
   blank frame or a jarring pop-in once the video is ready to play. Tracks
   the hero video's own buffering via the `progress` event (not a fake
   timer) so the fill honestly reflects load state, then fades out on
   `canplaythrough`. See setupLoader() in hero.js. --- */
html.is-loading,
html.is-loading body {
  overflow: hidden;
}

.site-loader {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  background: linear-gradient(160deg, var(--mint), var(--teal));
  opacity: 1;
  transition: opacity 0.5s ease;
}

.site-loader.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.site-loader__logo {
  height: 44px;
  width: auto;
}

.site-loader__box {
  position: relative;
  width: 64px;
  height: 100px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.4);
  border: 3px solid var(--brand-blue);
  border-radius: 16px;
}

.site-loader__fill {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 0%;
  background: var(--brand-blue);
  transition: height 0.2s ease-out;
}

.site-loader__percent {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--brand-blue);
}

@media (max-width: 760px) {
  .site-footer { padding: 200px 7vw 4vh; }
  /* video dropped entirely on mobile -- .site-footer already has its own
     dark background (#12181f, see above) for the video to fade into at
     its own bottom edge, so hiding it just leaves that same flat dark
     background showing through the glass instead, no gap. */
  .site-footer__video { display: none; }
  .site-footer__glass {
    background: rgba(0, 0, 0, 0.85);
  }
  .site-footer__columns { grid-template-columns: 1fr; gap: 2rem; }
  .site-footer__split { grid-template-columns: 1fr; gap: 1.2rem; }
}
