: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;
  text-decoration: none;
  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/shadow
     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. The white box and shadow are deliberately visible
     from initial page load, not introduced only after scrolling. */
  padding: 1.3rem 0.5rem 0.5rem;
  margin-top: -0.8rem;
  background: #fff;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.14);
  /* square, flat corners -- matches this site's own no-rounded-corners
     rule elsewhere, unlike the hamburger's pill treatment below. */
  transition: background 0.3s ease, box-shadow 0.3s ease;
}

.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;
}

/* the `>` matters: below 860px the mega panels are moved INTO this list as
   accordion sections (see layoutNavMenus() in hero.js), and a descendant
   selector styled every link inside them as a nowrap nav pill too, which
   blew the drawer well past the viewport width. Only direct children are
   nav pills. */
.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. It is visible by default, mirroring the logo's
   square background above; fully rounded, unlike the logo's flat corners. */
.site-nav__toggle::before {
  content: "";
  position: absolute;
  inset: -10px -14px;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.14);
  z-index: -1;
  transition: background 0.3s ease, box-shadow 0.3s ease;
}

/* The homepage header rests directly on the opening visual. Keep its
   logo and hamburger unboxed until the visitor scrolls; inner pages retain
   the shared white backgrounds and shadows from their first frame. */
.home-page .site-nav:not(.site-nav--scrolled) .site-nav__logo {
  background: transparent;
  box-shadow: none;
}

.home-page .site-nav:not(.site-nav--scrolled) .site-nav__logo img {
  filter: none;
}

.home-page .site-nav:not(.site-nav--scrolled) .site-nav__toggle::before {
  background: transparent;
  box-shadow: none;
}

.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: 60px;
  }
 
  .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;
  }
  /* Open, the bar becomes a full-screen sheet: logo across the top, then
     the menu using the FULL width under it. It used to stay a flex ROW
     (logo | menu), which left every pill starting at the logo's right edge
     -- a wide empty column down the left of the drawer. */
  .site-nav.is-open .site-nav__inner {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    min-height: 100dvh;
  }

  /* the logo keeps its content-width white box -- stretched, it would be a
     full-width white band across the top of the sheet */
  .site-nav.is-open .site-nav__logo { align-self: flex-start; }

  .site-nav.is-open .site-nav__menu {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    padding-top: 1.2rem;
  }

  /* the close X moves to the top-right corner, level with the logo -- in
     flow it would be stranded halfway down a now full-height sheet.
     .site-nav__inner is already position:relative. */
  .site-nav.is-open .site-nav__toggle {
    position: absolute;
    top: 1.9rem;
    right: 6vw;
    margin: 0;
  }

  /* --- the open drawer is a solid brand-blue sheet (mobile only) --------
     At rest the nav is still transparent chrome sitting on whatever is
     under it; only .is-open paints. It covers the whole bar, logo
     included, so the drawer reads as one panel rather than a blue column
     next to a floating logo. Everything inside inverts below: the pills
     were white-on-page, which would disappear against this. */
  .site-nav.is-open .site-nav__inner {
    background: var(--brand-blue);
    /* square: the sheet runs the full height of the screen, so there is no
       bottom edge left to round off */
    box-shadow: 0 18px 40px rgba(16, 51, 34, 0.22);
  }

  /* the homepage drops the logo's white box until the visitor scrolls,
     which would leave the full-colour mark sitting on the blue sheet --
     the box comes back for as long as the drawer is open. The .home-page
     selector matches that rule's specificity and wins on order. */
  .home-page .site-nav.is-open .site-nav__logo,
  .site-nav.is-open .site-nav__logo {
    background: #fff;
  }

  /* outlined-on-blue instead of white-on-page. The filled/outlined
     distinction that marks the expanded section is kept, just inverted --
     see the [aria-expanded="true"] rule below. */
  .site-nav.is-open .site-nav__links > a,
  .site-nav.is-open .site-nav__link {
    background: transparent;
    border-color: rgba(255, 255, 255, 0.42);
    color: #fff;
    box-shadow: none;
  }

  .site-nav.is-open .site-nav__link[aria-expanded="true"] {
    background: #fff;
    border-color: #fff;
    color: var(--brand-blue);
  }

  /* solid brand-blue on brand-blue would vanish -- the CTA swaps to the
     light variant it already uses on dark sections elsewhere */
  .site-nav.is-open .site-nav__cta {
    background: #fff;
    border-color: #fff;
    color: var(--brand-blue);
  }

  /* VIA keeps its light fill (it reads as a distinct product entry point,
     not another nav pill) -- only its border needs to stop being a faint
     blue-on-blue line */
  .site-nav.is-open .site-nav__via {
    background: #fff;
    border-color: #fff;
  }

  /* the X sits on the blue now, so it loses its own white pill */
  .site-nav.is-open .site-nav__toggle::before {
    background: transparent;
    box-shadow: none;
  }

  .site-nav.is-open .site-nav__toggle span,
  .site-nav.is-open .site-nav__toggle span::before,
  .site-nav.is-open .site-nav__toggle span::after {
    background: #fff;
  }

  /* accordion contents, same inversion (the panel's own layout rules are
     further down, in the accordion block -- these are only its colours) */
  .site-nav__mega--inline .site-nav__mega-links a {
    color: rgba(255, 255, 255, 0.92);
  }

  .site-nav__mega--inline .site-nav__mega-links a:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
  }

  .site-nav__mega--inline .site-nav__mega-links a::before {
    background: #fff;
  }

  .site-nav__mega--inline .site-nav__mega-links h4,
  .site-nav__mega--inline .site-nav__mega-subhead {
    color: #fff;
    opacity: 0.7;
  }

  /* unbuilt chapters stay visibly muted against the blue, same as they are
     against white on desktop */
  .site-nav__mega--inline .site-nav__mega-links span {
    color: rgba(255, 255, 255, 0.5);
  }

  .site-nav__mega--inline .site-nav__mega-links span::after {
    color: rgba(255, 255, 255, 0.72);
    background: rgba(255, 255, 255, 0.16);
  }

  .site-nav__mega--inline .site-nav__tabs {
    border-bottom-color: rgba(255, 255, 255, 0.28);
  }

  .site-nav__mega--inline .site-nav__tab {
    background: transparent;
    border-color: rgba(255, 255, 255, 0.42);
    color: #fff;
  }

  .site-nav__mega--inline .site-nav__tab.is-active {
    background: #fff;
    border-color: #fff;
    color: var(--brand-blue);
  }
}

/* --- 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(70vh, 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: 200px;
  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;
  overscroll-behavior-y: contain;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  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-subhead {
  margin: 0.75rem 0.7rem 0.05rem;
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--brand-blue);
}

.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;
}

/* --- Reports panel: three tabbed lists ---------------------------------
   The tab strip stays put while only the list below it scrolls, so the
   container gives up the scrolling it does for every other panel and
   hands it to .site-nav__tabpanels instead. */
.site-nav__mega-links--tabbed {
  overflow: hidden;
  gap: 0;
}

.site-nav__tabs {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding-bottom: 0.7rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid rgba(37, 64, 143, 0.14);
}

.site-nav__tab {
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1;
  color: #2b3540;
  background: #fff;
  border: 1.5px solid rgba(37, 64, 143, 0.22);
  border-radius: 999px;
  padding: 0.5rem 1.05rem;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}

.site-nav__tab:hover {
  color: var(--brand-blue);
  border-color: var(--brand-blue);
}

/* filled while selected -- same treatment .site-nav__link gets while its
   own panel is open, so "current" reads the same way at both levels */
.site-nav__tab.is-active {
  background: var(--brand-blue);
  border-color: var(--brand-blue);
  color: #fff;
}

.site-nav__tabpanels {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
}

.site-nav__tabpanel {
  display: none;
}

/* two columns via `columns`, not grid: multi-column flows the list DOWN
   the first column then into the second, which is how a list is read.
   A 2-track grid would run it across the page in row order instead. */
.site-nav__tabpanel.is-active {
  display: block;
  columns: 2;
  column-gap: 1.4rem;
}

.site-nav__tabpanel.is-active a {
  break-inside: avoid;
}

/* --- mobile: the same panels, as an in-drawer accordion ----------------
   The floating card has nowhere to sit at this width. It used to just be
   display:none'd, which left every sub-link unreachable on a phone -- the
   four buttons jumped straight to a landing page and the rest of each
   chapter was only ever visible on desktop. Now layoutNavMenus() in
   hero.js moves each panel into the drawer directly after its own button
   and tags it --inline; the rules below undo the fixed-card treatment and
   make it an accordion section instead. Desktop is untouched: above 860px
   the panel moves back out to <header> and loses the class. */
@media (max-width: 860px) {
  /* a panel still parked in <header> -- JS hasn't run, or we're mid-resize
     -- stays hidden, since as a fixed card it would cover the page */
  .site-nav__mega { display: none; }

  /* no card of its own: it opens inside the drawer, under a pill that
     already carries the white fill and border. Collapsed height rather
     than display:none so it can animate; max-height (not a measured
     px height) keeps it independent of content length -- the lists are
     short enough that the overshoot isn't visible. */
  .site-nav__mega--inline {
    display: block;
    position: static;
    width: auto;
    max-height: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    overflow: hidden;
    transition: max-height 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .site-nav__mega--inline.is-open {
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior-y: contain;
  }

  /* the drawer is a flex item of .site-nav__inner, so it sizes to its
     content unless every link in the chain is allowed to shrink -- without
     min-width:0 the longest link title (the Joint Letter one) sets the
     width and pushes the whole drawer off the right edge */
  .site-nav.is-open .site-nav__menu {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 100%;
  }

  .site-nav.is-open .site-nav__links,
  .site-nav__mega--inline {
    min-width: 0;
    max-width: 100%;
  }

  /* wrap rather than widen -- these titles are full sentences */
  .site-nav__mega--inline .site-nav__mega-links a,
  .site-nav__mega--inline .site-nav__mega-links span {
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* the header image is a 200px strip, and the head just repeats the label
     of the pill that opened it -- both are dead weight in a drawer */
  .site-nav__mega--inline .site-nav__mega-image,
  .site-nav__mega--inline .site-nav__mega-head {
    display: none;
  }

  /* the panel scrolls as one now, so the inner lists give up their own
     overflow. The left rule is what marks the links as belonging to the
     pill above them, in place of the card border they had on desktop. */
  .site-nav__mega--inline .site-nav__mega-links,
  .site-nav__mega--inline .site-nav__tabpanels {
    overflow: visible;
  }

  /* the rule stands in for the card border the panel has on desktop --
     white, since the drawer is a blue sheet at this width */
  .site-nav__mega--inline .site-nav__mega-links {
    padding: 0.35rem 0.2rem 0.5rem 0.85rem;
  }

  .site-nav__mega--inline .site-nav__mega-links--split {
    grid-template-columns: 1fr;
  }

  /* two columns of file names in a phone-width drawer is unreadable */
  .site-nav__mega--inline .site-nav__tabpanel.is-active {
    columns: 1;
  }

  .site-nav__mega--inline .site-nav__mega-links--tabbed {
    overflow: visible;
  }

  /* the collapsed panels are now flex items between the pills, so the
     list's own `gap` would double the space between every pair of them
     (pill -> gap -> zero-height panel -> gap -> pill). Spacing moves onto
     the pills themselves, which the panels don't get. */
  .site-nav.is-open .site-nav__links {
    gap: 0;
  }

  .site-nav.is-open .site-nav__links > * + .site-nav__link,
  .site-nav.is-open .site-nav__links > * + a {
    margin-top: 0.6rem;
  }

  /* +/- marking each pill as expandable -- the drawer's pills are the
     accordion headers now, not shortcuts to a page. Two bars: ::after is
     the horizontal one (always there, so collapsed reads "+" and expanded
     reads "-"), ::before is the vertical one, which rotates flat and fades
     as the section opens. currentColor tracks the pill's own state, so
     both flip to blue when it fills white. */
  .site-nav.is-open .site-nav__link {
    position: relative;
    padding-right: 2.6rem;
  }

  .site-nav.is-open .site-nav__link::before,
  .site-nav.is-open .site-nav__link::after {
    content: "";
    position: absolute;
    right: 1.05rem;
    top: 50%;
    width: 12px;
    height: 2px;
    margin-top: -1px;
    background: currentColor;
    transition: transform 0.25s ease, opacity 0.25s ease;
  }

  .site-nav.is-open .site-nav__link::before {
    transform: rotate(90deg);
  }

  .site-nav.is-open .site-nav__link[aria-expanded="true"]::before {
    transform: rotate(0);
    opacity: 0;
  }

  /* an open section can push the drawer past the bottom of the screen --
     four pills, the CTA, VIA and a 60vh panel. The drawer scrolls itself
     rather than growing off-screen (it is inside a position:fixed nav, so
     the page scroll can't reach it). Only while open: at rest the nav must
     stay a plain non-scrolling box, or it would swallow wheel events over
     the top of the page. */
  .site-nav.is-open .site-nav__inner {
    max-height: 100dvh;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    padding-bottom: 1.4rem;
    /* a classic (non-overlay) scrollbar takes its width out of the sheet
       and leaves a pale strip down the right edge of the blue -- the
       drawer is a full-bleed panel, so the bar is hidden and the drawer is
       scrolled by dragging it, the way it is on a real phone anyway */
    scrollbar-width: none;
  }

  .site-nav.is-open .site-nav__inner::-webkit-scrollbar {
    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: 57px;
  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. */
}

@media (max-width: 760px) {
  .hero-title { font-size: clamp(2rem, 9vw, 2.8rem); }
}

.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;
  pointer-events: none;
}

.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) {
  /* Plain centre here, unlike the desktop `70% 30%`. That anchor (and the
     `47% 30%` correction that used to live in this rule) existed because
     the 1920x1080 landscape edit had to be cropped hard left-right to
     fill a phone screen, which shoved the subject out of frame. This
     breakpoint now loads about-mbl-home.mp4 instead -- a 766x1364
     portrait cut already framed for phones (see the <source media> pair
     in index.html) -- so re-anchoring would only push a correctly-framed
     shot back off-centre. */
  .about__image { object-position: 50% 50%; }
  .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; }

/* in normal flow inside .highlights__left, NOT absolutely positioned --
   it used to be pinned at `top: calc(50% + 36px)`, a fixed offset that
   assumed a single-line title. The title is vertically centered, so as
   soon as "Key Highlights FY26" wrapped to two lines (which it does at
   most desktop widths) the second line grew downward into the button and
   the two overlapped. Letting the flex column's gap space them means the
   button always sits below the title whatever it wraps to. */
.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); }
  /* anchored to .highlights__left's own 14% origin rather than a bare 28%,
     so the chips sit a fixed distance below the title regardless of
     viewport height (at a bare 28%, 14% and 28% collapse toward each
     other in px on short screens). */
  .highlights__chips { top: calc(14% + 105px); right: auto; left: 7vw; bottom: auto; transform: none; grid-template-columns: minmax(180px, 60vw); grid-auto-rows: 76px; gap: 0.75rem; }

  /* mobile order is title -> numbers -> CTA, so the button drops out of
     .highlights__left's flex column (where desktop keeps it directly under
     the title) and is pinned below the chips instead. The offset is in px
     off .highlights__left's own top, which shares the chips' 14% origin --
     chips start 105px down and are 3 x 76px rows + 2 x 12px gaps = 252px
     tall, so 357px is their bottom edge and 381px clears it. */
  .highlights__readmore {
    position: absolute;
    left: 0;
    top: 381px;
  }
}

/* --- 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: 1.2rem 7vw;
    gap: 0.75rem;
  }
  .trust__map {
    display: block;
    width: 100%;
    padding: 0;
  }
  /* same inline SVG as desktop, just capped shorter -- mobile used to swap
     in a separate flattened map-mbl-img.svg here, which meant two assets
     to keep in sync and no live count-up (a raster/flat SVG has no text
     nodes to animate). The .trust__stats-list below still carries the
     readable figures at phone widths. */
  .trust__map-image {
    display: none;
  }
  .trust__map > .trust__map-title {
    display: none;
  }
  .trust__stats-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 360px;
    gap: 0.45rem;
  }
  .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.5rem 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);
  }
  .trust__readmore {
    padding: 0.65rem 2rem;
  }
}

@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);
}

/* height is 127%, not 100%, to cut the photo off above the clasped hands.
   The hands start ~79% down the source image, but the panel (50vw x
   100vh) only overflows the 8:9 photo by ~12% on a typical window, so
   even a top-anchored `object-position` bottomed out around 88% -- still
   low enough to show a sliver of hand at the panel's bottom edge. Making
   the img box taller than its overflow:hidden parent instead crops to a
   FIXED fraction that doesn't drift with viewport: box and panel both
   scale with the section height, so the visible slice is always
   1 / 1.27 = ~79% of the photo, landing just above the hands at every
   window size.

   object-fit: cover on the over-tall box scales the photo by height, so
   it also spills ~5% off each side -- that grazes the outer edge of the
   two men's shoulders, which is the trade for losing the hands cleanly. */
.joint-letter__image {
  width: 100%;
  height: 128%;
  object-fit: cover;
  object-position: 50% 0;
  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 {
  display: none;
  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) {
  .site-footer__col.site-footer__col--wide{
    padding-left: 0;
  }
  .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; }
  /* back to a plain 100% box -- the 30vh strip here already crops well
     above the hands, so it doesn't need the over-tall box desktop uses
     (and would only lose the heads to it). */
  .joint-letter__image { height: 100%; 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;
}

/* holds the title and its Read More button as one column, so the pair
   occupies a single slot in .performance__inner's row opposite the
   carousel. The 34vw cap moved here off .performance__title, which now
   fills this box rather than sizing itself. */
.performance__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1.2rem, 2.5vw, 2rem);
  max-width: 34vw;
}

.performance__title {
  margin: 0;
  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);
}

/* white fill (btn-cta--light) rather than solid brand blue -- same call
   as Trust and CEO Message, since this sits on a photo whose scrim is
   already in the blue family */
.performance__readmore {
  flex: 0 0 auto;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.75rem 2.3rem;
}

/* 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);
  display: block;
}

.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 {
  display: none;
  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%);
  }
  /* Read More moves BELOW the chart on mobile (it sits under the heading
     on desktop). `display: contents` dissolves the .performance__copy box
     so the title and the button become direct children of the stacked
     .performance__inner column -- `order` only sorts siblings, and while
     the button is nested one level down there is no way to send it past
     .performance__side. The wrapper carries no styling of its own here,
     so there is nothing to lose by dropping its box. */
  .performance__copy { display: contents; }
  .performance__title { order: 1; max-width: none; }
  .performance__side { order: 2; width: 100%; }
  .performance__readmore { order: 3; align-self: center; }
  .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__copy { gap: 1rem; }
  .performance__title { font-size: clamp(1.4rem, 2.6vw, 2rem); }
  .performance__readmore { padding: 0.6rem 1.9rem; }
  .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);
}

/* --- story overlay: full page copy ------------------------------------
   The FY26 Highlights overlay is filled from <template>s lifted out of
   each card's own page (see .story-overlay-content in index.html), so it
   arrives carrying that page's classes. Those live in earnings-quality.css,
   which index.html doesn't load -- these rules cover the handful that
   actually appear, scoped to the overlay so they can't reach anything
   else on the page. */
.story-overlay-content {
  display: none;
}

/* .report-overlay__body strong is blue for the Joint Letter and CEO
   overlays, where <strong> marks the handful of lead-in phrases in an
   otherwise plain letter. The copy cloned in here uses <strong> quite
   differently -- it is the body text of every metric -- so that rule
   turned whole paragraphs blue and left nothing to distinguish the
   headings. Reset to the body colour; the figure and its heading keep
   brand blue through their own rules below. */
#story-overlay .report-overlay__body strong {
  color: inherit;
}

.report-overlay__body .earnings-overview__copy p {
  margin: 0 0 1.3rem;
}

/* New Platforms is the one card built before this pattern existed, so its
   intro and figure list carry .np-split__* names for what the others call
   .earnings-overview__copy and .earnings-metrics -- same job, so they get
   the same treatment rather than falling through to unstyled blocks. */
.report-overlay__body .earnings-metrics,
.report-overlay__body .np-split__items {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.8rem 2.5rem;
  margin-top: 2.4rem;
  padding-top: 2rem;
  border-top: 1px solid rgba(37, 64, 143, 0.16);
}

.report-overlay__body .np-split__lead {
  margin: 0 0 1.3rem;
}

.report-overlay__body .earnings-metric {
  margin: 0;
}

.report-overlay__body .earnings-metric__value {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  font-weight: 700;
  color: var(--brand-blue);
}

.report-overlay__body .earnings-metric__value small {
  font-size: 0.65em;
  font-weight: 700;
}

.report-overlay__body .earnings-metric__heading {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 1.2;
  font-weight: 700;
  color: var(--brand-blue);
}

.report-overlay__body .earnings-metric__copy {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.45;
}

/* Operational's award list, the one card that carries a block after its
   figures. The rule above each entry is the same cyan tick the page uses. */
.report-overlay__body .operational-recognitions {
  margin-top: 2.4rem;
}

.report-overlay__body .operational-recognitions__title {
  margin: 0 0 1.4rem;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  color: #1b2733;
}

.report-overlay__body .operational-recognitions__grid p {
  position: relative;
  margin: 0 0 1.2rem;
  padding-top: 1rem;
  font-size: 0.95rem;
  line-height: 1.45;
}

.report-overlay__body .operational-recognitions__grid p::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 30px;
  height: 5px;
  border-radius: 999px;
  background: #4fc3e7;
}

@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; }
  .trust{
      height: auto;
    padding-top: 50px;
}
}

/* --- FY26 Highlights: 7 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 7 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: 40vh;
  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 overview: 3 business-unit cards, then the 6 verticals they serve
   (see the #bu-intro markup in index.html). Opaque white for the same
   reason .bu-split below is: the fixed #site-waves canvas reading through
   dense text is clutter. */
.bu-intro {
  position: relative;
  z-index: 1;
  padding: clamp(3rem, 7vw, 6rem) 0;
  background: #fff;
  padding-top: 0;
}

/* Bootstrap-shaped grid, scoped to this section. about.css defines the
   same class names globally for the inner pages; index.html doesn't load
   that file, and scoping here means neither definition can leak into the
   other on pages that end up with both. */
.bu-intro .container {
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding: 0 6vw;
}

.bu-intro .row {
  display: flex;
  flex-wrap: wrap;
  margin: 0 -14px;
}

.bu-intro [class*="col-"] {
  width: 100%;
  padding: 0 14px;
}

@media (min-width: 1000px) {
  .bu-intro .col-xl-4 { flex: 0 0 33.333333%; max-width: 33.333333%; }
  .bu-intro .col-xl-6 { flex: 0 0 50%; max-width: 50%; }
}

.bu-intro__title {
  margin: 0 0 clamp(1.4rem, 3vw, 2.2rem);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.4rem, 2.6vw, 2rem);
  color: var(--brand-blue);
}

/* the second heading completes the sentence the first one opens ("Our 3
   BUs offer solutions..." / "...across 6 focussed verticals"), so it gets
   the gap above that marks it as a continuation rather than a new topic */
.bu-intro__title--second {
  margin-top: clamp(2.5rem, 5vw, 4rem);
}

.bu-intro__row {
  row-gap: 28px;
}

/* --- the three BU cards ------------------------------------------------
   Each card is a colored header pill over a bullet list, with a rule down
   the left edge tying the two together -- the BU's own color carries all
   three (pill fill, rule, bullet dots), which is what distinguishes the
   cards from each other since their structure is identical. */
.bu-card {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.bu-card__head {
  margin: 0;
  padding: 0.85rem 1.4rem;
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 1.7vw, 1.3rem);
  color: #fff;
}

.bu-card__list {
  flex: 1;
  margin: 0;
  padding: 1.2rem 0 1.2rem 1.6rem;
  list-style: none;
  border-left: 2px solid;
  /* square off the top-left so the rule reads as dropping out of the
     pill above it rather than as a detached bracket */
  border-bottom-left-radius: 18px;
  border-bottom: 2px solid transparent;
}

.bu-card__list li {
  position: relative;
  margin-bottom: 1rem;
  padding-left: 1.1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.4;
  color: #1c2b4a;
}

.bu-card__list li:last-child { margin-bottom: 0; }

.bu-card__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

/* Chemical Intermediates' six short entries, two per line */
.bu-card__list--two {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1rem;
  align-content: start;
}

.bu-card--specialty .bu-card__head { background: #29b6e8; }
.bu-card--specialty .bu-card__list { border-left-color: #29b6e8; border-bottom-color: #29b6e8; }
.bu-card--specialty .bu-card__list li::before { background: #29b6e8; }

.bu-card--nutrition .bu-card__head { background: #ffcb05; }
.bu-card--nutrition .bu-card__list { border-left-color: #ffcb05; border-bottom-color: #ffcb05; }
.bu-card--nutrition .bu-card__list li::before { background: #ffcb05; }

.bu-card--chemical .bu-card__head { background: #8dc63f; }
.bu-card--chemical .bu-card__list { border-left-color: #8dc63f; border-bottom-color: #8dc63f; }
.bu-card--chemical .bu-card__list li::before { background: #8dc63f; }

/* --- the six vertical photo tiles ------------------------------------- */
/* each tile is a link into its own vertical on industry-and-business-
   verticals.html. The wrapper carries no styling of its own -- the tile
   inside still owns the whole visual -- it only has to stop being an inline
   box, or the figure inside would sit on a text baseline and leave a gap
   under every tile. */
.bu-tile-link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.bu-tile-link:hover .bu-tile img {
  transform: scale(1.04);
}

.bu-tile {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: 18px;
}

.bu-tile img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

/* the scrim is on the caption itself, not a separate overlay element --
   these photos are busy at the bottom edge and plain white type on them
   was unreadable on several */
.bu-tile__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 2.5rem 1.4rem 1.1rem;
  background: linear-gradient(0deg, rgba(8, 16, 34, 0.82) 0%, rgba(8, 16, 34, 0) 100%);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  color: #fff;
}

@media (max-width: 999px) {
  /* below the grid's xl breakpoint every col is full width, so the cards
     stack and the pill/rule pairing still holds */
  .bu-intro__row { row-gap: 22px; }
}

@media (max-width: 560px) {
  .bu-card__list--two { grid-template-columns: minmax(0, 1fr); }
}

.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; }
  .verticals{
    padding-bottom: 5px;
  }
  section#global-footprint {
    height: 100vh;
}
}

/* --- 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. */

/* --- shared previous / next navigation for report detail pages -------- */
.report-page-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  width: 100%;
  padding: 24px 1.4vw;
  background: #fff;
}

/* Both links sit open, showing the page they lead to, instead of the
   earlier 60px arrow box that widened to reveal its title only on hover
   -- at rest that told a reader nothing about where either arrow went.
   Arrow and title are ordinary flex children now rather than absolutely
   positioned, so a long title wraps and grows the pill instead of
   overflowing a fixed 60px-tall box; the two links share the row via
   `flex: 1 1 0`. Hover keeps the lift and shadow as the affordance the
   width change used to provide. */
.report-page-nav__link {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 14px;
  flex: 1 1 0;
  max-width: 560px;
  min-height: 60px;
  padding: 14px 28px;
  border: 1px solid #b7d7c8;
  border-radius: 50px;
  background: #fff;
  color: var(--brand-blue);
  text-decoration: none;
  transition: color 0.25s ease, border-color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease;
}

.report-page-nav__link::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    38deg,
    #DAEFE4 0%,
    #DDF0DC 22%,
    #DFF0D7 42%,
    #EAF4DC 62%,
    #F1F7E6 82%,
    #F4F9EB 100%
  );
  /* the gradient wash is the resting fill now, not a hover reveal */
  opacity: 1;
}

.report-page-nav__link:hover,
.report-page-nav__link:focus-visible {
  color: var(--brand-blue);
  border-color: #8fc0aa;
  box-shadow: 0 12px 28px rgba(26, 69, 50, 0.12);
  transform: translateY(-2px);
}

.report-page-nav__link:focus-visible {
  outline: 3px solid rgba(37, 64, 143, 0.28);
  outline-offset: 3px;
}

.report-page-nav__link svg {
  flex: 0 0 auto;
  z-index: 1;
  width: 18px;
  height: 32px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.25s ease;
}

.report-page-nav__link span {
  z-index: 1;
  flex: 1 1 auto;
  color: var(--brand-blue);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
}

/* the arrow leads on prev and trails on next -- the markup already puts
   them in that order, so text-align is all that's needed to push each
   title toward its own edge */
.report-page-nav__link--prev span { text-align: left; }
.report-page-nav__link--next span { text-align: right; }

/* nudge the arrow outward on hover, since the pill no longer changes
   width to signal it is interactive */
.report-page-nav__link--prev:hover svg,
.report-page-nav__link--prev:focus-visible svg {
  transform: translateX(-4px);
}

.report-page-nav__link--next:hover svg,
.report-page-nav__link--next:focus-visible svg {
  transform: translateX(4px);
}

@media (max-width: 767px) {
  /* both titles are on screen at once now, so the pills stack rather than
     splitting a phone-width row into two ~45% columns where the longer
     titles would run to five or six lines each */
  .report-page-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    padding: 18px 5vw;
  }
  .report-page-nav__link {
    max-width: none;
    min-height: 56px;
    padding: 12px 20px;
    gap: 10px;
    border-radius: 50px;
  }
  .report-page-nav__link svg { width: 14px; height: 24px; }
  .report-page-nav__link span { font-size: 12px; }
}

.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: none;
  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. It uses
   real hero-video buffering plus a minimum visible 0-100 sequence, then
   fades only after 100% has been shown. 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: 80px 7vw 4vh; }
  /* Keep the same moving background used on desktop. The video remains
     capped to one viewport and fades into the footer's dark base below. */
  .site-footer__video { display: block; }
  .site-footer__glass {
    background: rgba(0, 0, 0, 0.62);
  }
  .site-footer__columns { grid-template-columns: 1fr; gap: 2rem; }
  .site-footer__split { grid-template-columns: 1fr; gap: 1.2rem; }
}
