/* ============================================================
   PRIME ACADEMY GROUP · primeacg.com — v4
   base.css — foundations — tokens, reset, type, layout
   Lifted verbatim out of the single-file pages in www/website/.
   ============================================================ */
/* ============================================================
   PRIME ACADEMY GROUP - HOMEPAGE SPINE (v1)
   Shared tokens, primitives, components, motion scaffold.
   Section authors ADD tiny scoped rules on top - never edit
   this block's component classes.
   ============================================================ */

/* ---------- TOKENS (design-system.html verbatim + motion) ---------- */
:root{
  --deep:#0D47A1;          /* Deep Blue      - primary            */
  --navy:#14213D;          /* Midnight Navy  - ink / dark surface */
  --red:#E53935;           /* Signal Red     - scarce accent      */
  --azure:#2196F3;         /* Bright Blue    - secondary          */
  --azure-soft:color-mix(in srgb, var(--azure) 85%, #fff);  /* lifted azure for text on dark */
  --deep-dark:color-mix(in srgb, var(--deep) 84%, #000);    /* pressed deep blue, primary hover */
  --slate:#757575;         /* Slate Grey     - muted              */
  --mist:#F2F4F7;          /* Mist           - light surface      */
  --white:#FFFFFF;

  --prime-gradient:linear-gradient(135deg,#0D47A1 0%,#2196F3 100%);
  --dark-surface:#0B1220;  /* deepest navy-black (rare) */

  --ink:var(--navy);
  --line:#DCE1EA;
  --line-soft:#E8ECF3;
  --line-on-dark:rgba(255,255,255,.14);
  --ring-on-dark:rgba(255,255,255,.30);
  --glass-navy:color-mix(in srgb, var(--navy) 64%, transparent);

  --font-display:"Montserrat","Segoe UI",system-ui,sans-serif;
  --font-body:"Poppins","Segoe UI",system-ui,sans-serif;

  /* Display tracking. Banded by RENDERED SIZE, not by heading level: how tight type
     wants to be is a function of how big it is drawn, so a 31px h3 in a card belongs
     in the same band as a 41px .h3-lg, and a 21px h3 does not. Pick the band from the
     TOP of the element's clamp - that is the desktop size, and the one you judge by.
     Tune the whole site from here; do not write a raw letter-spacing on a heading. */
  --track-display:-.025em;  /* 60px and up  hero display lines            */
  --track-xl:-.02em;        /* 40 - 62px    h1, h2                        */
  --track-lg:-.015em;       /* 26 - 41px    h3-lg, large card titles      */
  --track-md:-.01em;        /* 20 - 25px    h3, mid card titles           */
  --track-sm:-.005em;       /* under 20px   small card titles             */

  --maxw:85vw;
  --gutter:clamp(20px,4vw,44px);   /* reference nav side inset ~41px */
  /* Two shapes, and only two. Every rounded box on the site is one of them.
     --radius-sm  controls and labels: inputs, tags, chips on images, the nav bar
     --radius     everything else, whatever its size: cards, panels, screenshots
     Pills use 999px and circles use 50% — those are shapes, not scale steps.
     Do not introduce a third value. If something looks wrong at 8px, the fix is
     the element, not a new radius. */
  --radius:8px;
  --radius-sm:4px;

  /* motion
     ONE curve for the whole site, CSS and GSAP alike: easeOutCubic. It starts fast
     and settles gently, which is what an entrance or a hover response should do. The
     curve this replaced (.64,.05,0,1) was the opposite shape - slow off the mark, then
     most of the travel dumped in a burst at the end - so a 735ms reveal read as a 300ms
     snap. Scrubbed animations stay linear; they are driven by scroll, not by time.
     --dur is the slow movement step (image zooms).
     The reveal used to say "0.9s, set in main.js - keep the two in step if either
     moves", which is exactly the arrangement that let them drift apart. Both live
     here now and main.js reads them off :root, so there is one place to change a
     timing and nothing to keep in step by hand. */
  --ease:cubic-bezier(.33,1,.68,1);
  --ease-alt:var(--ease);   /* alias - existing consumers, one curve underneath */

  /* FOUR RUNGS. Every transition on the site is one of them.
     There were sixteen hardcoded durations before this - .18 .2 .22 .24 .25 .3
     .34 .35 .4 .45 .5 .55 .6 .7 - and no human can tell .18s from .22s, so the
     spread was noise, not intent. It came from every rule picking its own number
     because --dur was the only token and it was the wrong size for a hover.
     Pick the rung by what the thing IS, not by how it looks in isolation:
       fast   a control answering you           hover, focus, colour, icon nudge
       state  something opening or closing      menus, panels, accordions
       dur    something large physically moving image zoom, long travel
       reveal arriving on scroll                the one in main.js
     If a new rule wants a fifth value, it almost certainly wants one of these. */
  --dur-fast:.18s;
  --dur-state:.3s;
  --dur:.735s;
  --reveal-dur:.9s;
  --reveal-stagger:.1s;

  /* The frost on the secondary-on-dark button. Only does visible work over
     photography, which is where every ghost button on this site sits. */
  --frost:blur(12px) saturate(140%);
}

/* ---------- RESET / BASE ---------- */
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
html.lenis,html.lenis body{ height:auto; }
.lenis.lenis-smooth{ scroll-behavior:auto !important; }
.lenis.lenis-smooth [data-lenis-prevent]{ overscroll-behavior:contain; }
.lenis.lenis-stopped{ overflow:hidden; }

body{
  margin:0;
  font-family:var(--font-body);
  font-weight:400;
  font-size:16px;
  line-height:1.65;
  color:var(--ink);
  background:var(--white);
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  /* Inherited, so every paragraph, list item and span on the site gets it: the last line of
     a block is never left holding one short word. Headings set `balance` in their own rule,
     which is more specific and still wins. */
  text-wrap:pretty;
}
body.nav-open{ overflow:hidden; }
img,video,canvas,svg{ max-width:100%; display:block; }
/* `all` here, on every anchor on the site, meant any property a link ever
   changed got animated - including layout ones. A nav item that shifts on a
   breakpoint, a card link that changes padding on hover: those crawled over
   .3s instead of just being where they belong. Colour is what this rule is
   for, so colour is what it animates. */
a{ color:var(--deep); text-decoration:none; text-underline-offset:3px; transition:color var(--dur-fast) var(--ease); }
button{ font:inherit; color:inherit; }
ul{ list-style:none; margin:0; padding:0; }

/* ---------- TYPOGRAPHY SCALE ---------- */
/* The reference notes on each step below record the ORIGINAL reference design.
   Prime deviates from it on two axes, deliberately and across every step: all
   display headings run weight 500, not 600, and tracking comes from the --track-*
   tokens above, roughly half the reference's. Keep both consistent if a step is
   ever added - a single 600, or a raw letter-spacing that skips the tokens, reads
   as a mistake next to the rest. */
h1,h2,h3{ margin:0; font-family:var(--font-display); }
.h1,h1{  /* reference d1 4.5rem/61px, lh1.0, ls-.04em, weight 600 */
  font-family:var(--font-display); font-weight:500;
  font-size:clamp(40px,6vw,62px); line-height:1.02; letter-spacing:var(--track-xl);
}
.h2,h2{  /* reference d2 3.5rem/48px, lh1.0, ls-.04em, weight 600 */
  font-family:var(--font-display); font-weight:500;
  font-size:clamp(30px,4.5vw,48px); line-height:1.04; letter-spacing:var(--track-xl);
  /* Set here rather than on .section-head: several section titles sit in their own column
     and never pass through that block, and those were exactly the ones left stranding a
     single word on the last line. Does nothing to a heading that already fits on one. */
  text-wrap:balance;
}
.h3-lg{  /* reference d3 3rem/41px, lh1.0, ls-.04em, weight 600 */
  font-family:var(--font-display); font-weight:500;
  font-size:clamp(26px,3.4vw,32px); line-height:1.08; letter-spacing:var(--track-lg);
}
.h3,h3{  /* reference d5 1.5rem/20.6px, lh1.24, ls-.02em, weight 600 */
  font-family:var(--font-display); font-weight:500;
  font-size:21px; line-height:1.24; letter-spacing:var(--track-md);
}
/* ---------- TWO-COLOUR HEADINGS ----------
   The second half of a heading sits in a lighter weight and a blue chosen by the
   surface underneath it. ONE rule for the whole site.

   This used to be five near-identical blocks, one per page stylesheet, each
   naming its own sections by hand (.s-story h2 b, .t-range h2 b, .p-who h2 b …).
   That is exactly how the home page ended up with none of them and every heading
   there in a single colour: its sections live in this file, and nobody wrote a
   sixth block for them.

   The three values are not a guess. Every highlighted heading on the site was
   measured first - 59 of them across six pages - and they fall into exactly
   these three cases with no exceptions. */
h1 b,h2 b,h3 b{ font-weight:500; color:var(--deep); }
.section--dark h1 b,.section--dark h2 b,.section--dark h3 b{ color:var(--azure); }
.section--deep h1 b,.section--deep h2 b,.section--deep h3 b{ color:var(--azure-soft); }
.lead{  /* reference subtitle 1.125rem/15.4px, lh1.64 */
  font-family:var(--font-body); font-weight:400;
  font-size:clamp(16px,1.6vw,18px); line-height:1.6;
}
.body-lg{ font-size:18px; line-height:1.6; }
.caption{ font-size:13px; line-height:1.5; color:var(--slate); }
p{ margin:0 0 1em; }
p:last-child{ margin-bottom:0; }

.overline{
  font-family:var(--font-display); font-weight:600; font-size:12px;
  letter-spacing:.10em; text-transform:uppercase; color:var(--slate);
}
.stat-number{
  font-family:var(--font-display); font-weight:800;
  font-size:clamp(28px,3.4vw,44px); line-height:1; letter-spacing:-.02em;
  color:var(--deep); display:block;
}
.text-muted{ color:var(--slate); }

/* ---------- EYEBROW ---------- */
.eyebrow{
  font-family:var(--font-display); font-weight:600; font-size:12px;
  letter-spacing:.16em; text-transform:uppercase; color:var(--deep);
  display:inline-block;
  margin:0 0 18px;
}

/* ---------- LAYOUT ---------- */
.container{
  max-width:var(--maxw); margin-inline:auto;
  padding-inline:var(--gutter);
  width:100%;
}
.container--wide{ max-width:1440px; }
.section{ padding-block:clamp(64px,7vw,88px); position:relative; }  /* reference section vertical ~86px */
.bleed{ width:100vw; margin-left:calc(50% - 50vw); }

/* 64ch is a reading measure. It belongs to the prose in this block, NOT to the block —
   capping the block capped the heading too, at 643px on every screen from 1280 to 2560,
   which broke short titles onto two lines with room to spare ("Six things it handles, end
   to end" needed 731px and could never get it). The heading now takes the column; the
   paragraphs keep the measure. `balance` on .h2 evens the lines wherever a column really
   is too narrow, so no title strands a single word. */
.section-head{ margin-bottom:clamp(30px,4vw,46px); }
.section-head p{ max-width:64ch; }

.grid{ display:grid; gap:clamp(18px,2.2vw,26px); }
.grid--2{ grid-template-columns:repeat(2,1fr); }
.grid--3{ grid-template-columns:repeat(3,1fr); }

/* ---------- SURFACE VARIANTS + DARK FLIP ---------- */
.section--mist{ background:var(--mist); }
.section--dark{ background:var(--navy); color:#fff; }
.section--deep{ background:var(--deep); color:#fff; }
.section--dark .eyebrow,
.section--deep .eyebrow{ color:var(--azure); }
.section--dark .text-muted,
.section--deep .text-muted,
.section--dark .lead,
.section--deep .lead{ color:var(--mist); }
.section--dark .stat-number,
.section--deep .stat-number{ color:#fff; }
.section--dark hr,
.section--deep hr{ border:0; border-top:1px solid var(--line-on-dark); }

/* ---------- BUTTONS ---------- */
/* Three levels of emphasis: primary, secondary, text.
   Flat fills. No gradient anywhere, and nothing carries a drop shadow at rest.
   Primary simply darkens on hover and keeps its weight. Both secondaries
   invert instead: they fill solid and their label drops out, so a secondary
   sitting beside a primary reads as the quieter of the two until you reach
   for it. Nothing travels, so nothing needs clipping
   and no pseudo-element is involved - which is what keeps the whole-card click
   targets in .s-footcta__block and .p-plan (plans.css) working, since those
   rely on .btn::after being free to stretch over the card.

   Timing is the arrow's: .34s, the same figure the arrow has always travelled
   on, so the colour swap and the arrow are one gesture rather than two events
   at different speeds. Keep these in step if either moves. The 1px press stays
   short at .1s, since a press should feel immediate. */
.btn{
  position:relative; isolation:isolate;
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  height:46px; padding:0 24px; 
  border-radius:10px;
  font-family:var(--font-display); font-weight:600; font-size:13.5px;
  letter-spacing:.04em; text-transform:uppercase;
  border:0; cursor:pointer; text-decoration:none; white-space:nowrap;
  transition:background-color var(--dur-state) var(--ease-alt), box-shadow var(--dur-state) var(--ease-alt), color var(--dur-state) var(--ease-alt), transform var(--dur-fast) var(--ease-alt);
}
.btn-label{ position:relative; z-index:1; }

/* the arrow lives in a fixed box, so sliding it never nudges the label */
.btn-arrow{
  position:relative; z-index:1; width:16px; height:16px; flex:none;
  display:grid; place-items:center; overflow:hidden;
}
.btn-arrow svg{ display:block; width:16px; height:16px;
  transition:transform var(--dur-state) var(--ease); }
.btn:hover .btn-arrow svg,
.btn:focus-visible .btn-arrow svg{ transform:translateX(3px); }

.btn:active{ transform:translateY(1px); }
.btn:focus-visible{ outline:2px solid var(--azure); outline-offset:3px; }
.btn[disabled],.btn.is-disabled{ pointer-events:none; opacity:.42; box-shadow:none; }

/* ---- PRIMARY ----
   Does not invert. Flat brand blue that simply darkens under the cursor, so
   the strongest call to action never gives up its weight on hover. The label
   stays white throughout. Derived from --deep rather than hardcoded, so a
   change to the brand blue carries through. */
.btn-primary{
  color:#fff;
  background-color:var(--deep);
}
.btn-primary:hover,.btn-primary:focus-visible{
  background-color:var(--deep-dark);
}

/* ---- SECONDARY on light ----
   The exact mirror: white with a hairline, filling solid brand blue with the
   label dropping out. Put next to a primary, the two trade places under the
   cursor. No frost here, a flat mist or white section gives a backdrop blur
   nothing to work on. */
.btn-line{
  background-color:var(--white); color:var(--deep);
  box-shadow:inset 0 0 0 1px var(--line);
}
.btn-line:hover,.btn-line:focus-visible{
  background-color:var(--deep); color:#fff;
  box-shadow:inset 0 0 0 1px var(--deep);
}

/* ---- SECONDARY on dark ----
   Frosted, because this one always sits over photography or video: the hero,
   the footer CTA blocks, the who/why bands. Fills solid white with the label
   inverting to navy. */
.btn-ghost{
  background-color:rgba(255,255,255,.10); color:#fff;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.28);
  -webkit-backdrop-filter:var(--frost);
          backdrop-filter:var(--frost);
}
.btn-ghost:hover,.btn-ghost:focus-visible{
  background-color:var(--white); color:var(--navy);
  box-shadow:inset 0 0 0 1px var(--white);
}
/* Frost OFF in the two whole-card click targets, and this is not cosmetic.
   A backdrop-filter makes the element a containing block for absolutely
   positioned descendants, which stops .btn::after stretching over the card and
   silently kills the whole-card link in .s-footcta__block and .p-plan.
   No loss visually: the footer CTA button sits at the foot of its block where
   the scrim is already 88% navy, and the plan cards are a flat panel, so there
   is nothing behind either one for a blur to work on. If the frost is ever
   wanted back in those two places, the click target has to move first. */
.s-footcta__block .btn-ghost,.p-plan .btn-ghost{
  -webkit-backdrop-filter:none; backdrop-filter:none;
}
/* PRESS DIP OFF in the same two blocks, and for exactly the same reason as the
   frost above: a transform ALSO makes an element a containing block for
   absolutely positioned descendants. .btn:active{transform:translateY(1px)}
   therefore collapsed .btn::after from covering the whole card down to covering
   the button, at the instant of the press. mousedown landed on the link,
   mouseup landed on whatever was underneath, and the browser dispatched click
   to their common ancestor - the <article>, which has no href. So the card did
   nothing and only the button itself worked.

   It never showed up in hit-testing, because nothing is :active when you ask
   elementFromPoint, and it never showed up under prefers-reduced-motion either,
   since that already sets transform:none further down. Keep this rule beside
   the frost one: anything that makes .btn a containing block breaks these two. */
.s-footcta__block .btn:active,.p-plan .btn:active{ transform:none; }

@media (prefers-reduced-motion:reduce){
  .btn,.btn-arrow svg{ transition:none; }
  .btn:active{ transform:none; }
}

/* ---------- LINK-ARROW (the text + arrow level) ---------- */
/* No rule under the words. This level stays text and arrow only: colour steps
   to azure and the arrow travels. The inversion belongs to the button levels. */
.link-arrow{
  display:inline-flex; align-items:center; gap:8px;
  font-family:var(--font-display); font-weight:600; font-size:13px;
  letter-spacing:.05em; text-transform:uppercase;
  color:var(--deep); text-decoration:none;
  transition:color var(--dur-fast) var(--ease-alt);
}
.link-arrow svg{ width:16px; height:16px; flex:none;
  transition:transform var(--dur-state) var(--ease); }
.link-arrow:hover{ color:var(--azure); }
.link-arrow:hover svg{ transform:translateX(3px); }
.section--dark .link-arrow,
.section--deep .link-arrow{ color:#fff; }
.section--dark .link-arrow:hover,
.section--deep .link-arrow:hover{ color:var(--azure); }

/* ---------- CARD ---------- */
.card{
  background:var(--white); border:1px solid var(--line);
  border-radius:var(--radius); overflow:hidden; position:relative;
}
.card--mist{ background:var(--mist); }
.card--hover{ transition:transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease); }
.card--hover:hover{
  transform:translateY(-3px);
  box-shadow:0 16px 34px -20px rgba(20,33,61,.45);
}
/* translucent tile on dark surfaces */
.card--glass{
  background:rgba(255,255,255,.06); border-color:var(--line-on-dark); color:#fff;
}

/* ---------- CALLOUT ----------
   One aside used across the site: a tinted panel with a hairline, no coloured
   left edge. Title is optional; use it when the aside needs naming, skip it
   when the sentence explains itself. */
.callout{
  /* the box carries its own measure, so text and box always agree and
     no dead space opens up on the right at wide viewports */
  max-width:62ch;
  padding:clamp(20px,2.2vw,26px);
  background:var(--mist);
  border:1px solid var(--line);
  border-radius:var(--radius);
  transition:background var(--dur-fast) var(--ease-alt), border-color var(--dur-fast) var(--ease-alt);
}
/* slight hover. these are asides, not cards you click, so the border warms
   and the fill shifts a touch. no lift, no shadow. */
.callout:hover{ background:#EDF1F7; border-color:#C7D2E0; }
.callout__title{
  margin:0 0 10px;
  font-family:var(--font-display); font-weight:600;
  font-size:clamp(17px,1.6vw,20px); line-height:1.2; letter-spacing:-.02em;
  color:var(--navy);
}
/* doubled class so the component beats section-level paragraph caps like
   .t-manual p and .t-map p, which load later and would otherwise win */
.callout.callout > p{ margin:0 0 12px; max-width:none; font-size:14.5px; line-height:1.58; color:var(--slate); }
.callout > p:last-child{ margin-bottom:0; }
.callout > .link-arrow{ font-size:12.5px; }

/* the callout always contrasts with what it sits on:
   mist on white, white on mist, translucent on dark */
.section--mist .callout{ background:var(--white); }
.section--mist .callout:hover{ background:#FBFCFE; border-color:#C7D2E0; }

/* on dark sections the tint inverts, the hairline follows */
.section--dark .callout,.section--deep .callout{
  background:rgba(255,255,255,.05);
  border-color:var(--line-on-dark);
}
.section--dark .callout:hover,.section--deep .callout:hover{
  background:rgba(255,255,255,.085);
  border-color:var(--ring-on-dark);
}
.section--dark .callout__title,.section--deep .callout__title{ color:#fff; }
.section--dark .callout > p,.section--deep .callout > p{ color:rgba(255,255,255,.72); }

/* ---------- CHIP ---------- */
.chip{
  display:inline-flex; align-items:center; gap:8px;
  padding:8px 14px; border-radius:999px;
  font-family:var(--font-display); font-weight:600; font-size:12px; letter-spacing:.04em;
  background:var(--mist); color:var(--navy); border:1px solid var(--line);
}
.chip .dot{ width:7px; height:7px; border-radius:50%; background:var(--red); flex:none; }
.chip--status{
  background:rgba(229,57,53,.08); border-color:rgba(229,57,53,.30); color:#B3241F;
}
.section--dark .chip,
.section--deep .chip{
  background:rgba(255,255,255,.06); border-color:var(--line-on-dark); color:#fff;
}

/* ---------- HERO GLASS BAR ---------- */
.hero-glass{
  position:absolute; left:0; right:0; bottom:0; width:100%; z-index:2;
  background:color-mix(in srgb, var(--navy) 86%, transparent);
  -webkit-backdrop-filter:blur(35px); backdrop-filter:blur(35px);
  border:1px solid var(--line-on-dark);
  border-radius:0;
}

/* ---------- PARALLAX FRAME ---------- */
.parallax{ position:absolute; inset:0; overflow:hidden; z-index:0; }
.parallax-inner{
  position:absolute; top:-5%; left:0; width:100%; height:110%;
  will-change:transform;
}
.parallax-inner > img,
.parallax-inner > video{ width:100%; height:100%; object-fit:cover; }

/* ---------- NAV (one white bar: logo | centred links | CTA) ---------- */
.nav{
  position:fixed; top:0; left:0; right:0; z-index:60;
  display:flex; align-items:center; justify-content:space-between; gap:24px;
  padding:24px var(--gutter);
  transition:padding var(--dur-state) var(--ease);
}
/* The scrolled state is the navy ramp on .nav::before, which lives in
   components.css because that is where it was already written for narrow
   screens. It now runs at every width. */
/* THE TRACKS: logo, links, CTA. The outer tracks are equal 1fr, so the link row
   is centred on the nav, not on the leftover space after the logo. Logo and CTA
   can change width independently and the links stay put.

   There is no surface here any more. This used to be a white pill that faded in
   on scroll; the scrolled state is now the gradient on .nav::before above, so
   this element only does layout. The padding and min-height stay because they
   set where the logo and CTA sit, and dropping them would move both. */
.nav-island{
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:20px;
  flex:1;
  min-height:56px; padding:7px 26px;
}
.nav-island > .nav-brand{ justify-self:start; }
.nav-island > .nav-menu{ justify-self:center; }
/* The CTA is a pill inside a pill. For the two curves to nest, the white ring
   around the button has to be even all the way round - and it is 9px above and
   below, set by the bar's 7px padding plus its 1px border. So the button is
   pulled out of the 26px inline padding to leave the same 9px on the right.
   Done with a negative margin rather than asymmetric padding on purpose: the
   grid tracks stay untouched, so the link row stays exactly centred. */
.nav-island > .nav-actions{ justify-self:end; margin-right:-18px; }
.nav.is-shrunk{ padding-top:14px; padding-bottom:14px; }
.nav-brand{
  font-family:var(--font-display); font-weight:800; font-size:clamp(17px,1.8vw,19px);
  letter-spacing:-.01em; line-height:1;
  background:var(--prime-gradient); -webkit-background-clip:text; background-clip:text; color:transparent;
}
.nav-menu{ display:flex; align-items:center; gap:2.3em; }  /* reference link gap ~27px */
.nav-menu > .nav-item > .nav-trigger,
.nav-menu > a:not(.btn){
  /* sentence case, not caps. The .04em tracking existed to open up uppercase -
     at sentence case it reads loose, so it drops to .005em. 13px caps and 14px
     sentence case have about the same optical size. */
  font-family:var(--font-display); font-weight:500; font-size:16px;
  letter-spacing:.015em; color:#fff;
  white-space:nowrap;
}
/* six links + a bigger logo + the CTA all share one bar now, so the row has to
   tighten earlier than it used to. Measured: still one line at 1321px. */
@media (max-width:1600px){
  .nav-menu{ gap:22px; }
  .nav-island{ gap:20px; }
}
@media (max-width:1440px){
  .nav-menu{ gap:18px; }
  .nav-island{ gap:18px; }
}
/* The links are white the whole way down the page, so hover and the current page
   lift rather than darken. There is no second state to write. */
.nav-menu > .nav-item > .nav-trigger:hover,
.nav-menu > a:not(.btn):hover,
.nav-menu > a:not(.btn)[aria-current="page"]{ color:var(--azure-soft); }
/* CTA pill: the right track of the bar, not a separate island. It takes the
   standard 46px from .btn like every other button on the site. It used to be
   squashed to 40px so it would nest inside a 58px bar with an even 9px ring,
   but that only mattered while the bar was always there. With the bar now
   invisible at the top of the page it just read as a thinner button. The ring
   still comes out at 9px, because the logo is the tallest thing in the bar and
   sets the height. */
.nav-cta-mobile{ display:none; }
.nav-actions{ display:flex; align-items:center; gap:16px; }
.nav-toggle{
  display:none; align-items:center; justify-content:center;
  width:44px; height:44px; background:transparent; border:0; cursor:pointer; color:#fff;
}
.nav-toggle .ic-close{ display:none; }
.nav.is-open .nav-toggle .ic-open{ display:none; }
.nav.is-open .nav-toggle .ic-close{ display:block; }

@media (max-width:1320px){
  .nav-island{
    display:flex; justify-content:space-between; align-items:center;
    padding:0; min-height:0;
    flex:1;
  }
  .nav-brand{ background:none; -webkit-text-fill-color:#fff; color:#fff; }
  .nav-cta{ display:none; }
  .nav-toggle{ display:inline-flex; z-index:2; }
  .nav-menu{
    position:fixed; inset:0; z-index:1;
    flex-direction:column; align-items:center; justify-content:center; gap:24px;
    background:var(--navy); overflow-y:auto; padding:96px var(--gutter) 48px;
    transform:translateY(-100%); opacity:0; visibility:hidden;
    transition:transform var(--dur-state) var(--ease), opacity var(--dur-state) var(--ease), visibility var(--dur-state) var(--ease);
  }
  .nav.is-open .nav-menu{ transform:translateY(0); opacity:1; visibility:visible; }
  .nav-menu > .nav-item > .nav-trigger,
  .nav-menu > a:not(.btn){ font-size:22px; color:#fff; }
  .nav-menu > .nav-item > .nav-trigger:hover,
  .nav-menu > a:not(.btn):hover{ color:var(--azure); }
  .nav-cta-mobile{ display:inline-flex; margin-top:8px; }
}

/* ---------- MAP HOTSPOTS (DOM, over canvas) ---------- */
.map-frame{ position:relative; }
.map-hotspot{ position:absolute; transform:translate(-50%,-50%); z-index:2; }
.map-hotspot .dot{
  width:12px; height:12px; border-radius:50%; background:var(--red);
  box-shadow:0 0 0 4px rgba(229,57,53,.22); cursor:pointer;
}
.map-hotspot .map-tooltip{
  position:absolute; bottom:calc(100% + 8px); left:50%;
  transform:translateX(-50%) scale(0); transform-origin:bottom center;
  opacity:0; pointer-events:none; white-space:nowrap;
  background:var(--navy); color:#fff; border-radius:var(--radius-sm);
  padding:6px 10px; font-family:var(--font-display); font-weight:600; font-size:12px;
  transition:opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.map-hotspot:hover .map-tooltip,
.map-hotspot:focus-within .map-tooltip{ opacity:1; transform:translateX(-50%) scale(1); }

/* ---------- VIDEO POPUP ---------- */
.video-modal{
  position:fixed; inset:0; z-index:120; display:none;
  align-items:center; justify-content:center; padding:clamp(16px,4vw,48px);
  background:rgba(11,18,32,.85);
}
.video-modal.is-open{ display:flex; }
.video-modal video{ width:min(100%,1000px); max-height:82vh; border-radius:var(--radius); background:#000; }
.video-close{
  position:absolute; top:20px; right:20px; width:44px; height:44px;
  display:inline-flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.12); border:1px solid var(--ring-on-dark); border-radius:999px;
  color:#fff; cursor:pointer;
}

/* ---------- REVEAL SCAFFOLD ---------- */
html.gsap-ready [data-gsap]{ visibility:hidden; }
/* .gsap-mask lived here: the per-line clipping box for the SplitText rise, with
   the em padding/negative-margin pair that stopped ascenders and descenders being
   cropped by its own overflow:hidden. The reveal is a plain fade now, nothing is
   split into lines, and nothing wrote that class any more. */

/* ---------- A11Y ---------- */
:focus-visible{ outline:0px solid var(--azure); outline-offset:0px; }
.btn:focus-visible,.link-arrow:focus-visible{ outline-offset:4px; }
.visually-hidden{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}
.skip-link{
  position:absolute; left:16px; top:-64px; z-index:200;
  background:var(--white); color:var(--deep); padding:12px 18px; border-radius:var(--radius-sm);
  font-family:var(--font-display); font-weight:600; transition:top var(--dur-fast) var(--ease);
}
.skip-link:focus{ top:16px; }

/* ---------- RESPONSIVE ---------- */
@media (max-width:768px){
  .grid--3,.grid--2{ grid-template-columns:1fr; }
}
@media (max-width:620px){
  .section{ padding-block:clamp(56px,12vw,76px); }
}

/* ---------- REDUCED MOTION ---------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important; scroll-behavior:auto !important;
  }
  [data-parallax]{ transform:none !important; }
  [data-gsap]{ visibility:visible !important; opacity:1 !important; }
  .card--hover:hover{ transform:none; }
}

/* ===== section-scoped ===== */
/* nav */
.nav .nav-item{position:relative;display:flex;align-items:center}
.nav .nav-trigger{display:inline-flex;align-items:center;gap:.3em}
.nav .nav-chev{width:12px;height:12px;transition:transform var(--dur-state) var(--ease)}
.nav .nav-item:hover .nav-chev,.nav .nav-item:focus-within .nav-chev{transform:rotate(180deg)}
.nav .nav-dropdown{position:absolute;top:100%;left:0;margin-top:14px;min-width:232px;background:#fff;border:1px solid var(--line,#E6E9F0);border-radius:var(--radius);padding:10px;display:grid;gap:2px;box-shadow:0 22px 46px -20px rgba(20,33,61,.28);opacity:0;visibility:hidden;transform:translateY(8px);pointer-events:none;transition:opacity var(--dur-state) var(--ease), transform var(--dur-state) var(--ease), visibility var(--dur-state);z-index:5}
.nav .nav-dropdown::before{content:"";position:absolute;left:0;right:0;top:-16px;height:16px}
.nav .nav-item:hover>.nav-dropdown,.nav .nav-item:focus-within>.nav-dropdown{opacity:1;visibility:visible;transform:translateY(0);pointer-events:auto}
.nav .nav-dropdown a{display:block;padding:9px 12px;border-radius:var(--radius-sm);color:var(--navy,#14213D);font-family:'Montserrat',sans-serif;font-weight:500;font-size:14px;white-space:nowrap;transition:background var(--dur-fast), color var(--dur-fast)}
.nav .nav-dropdown a:hover{background:var(--mist,#F2F4F7);color:var(--azure,#2196F3)}
.nav .nav-dropdown--mega{grid-template-columns:1fr 1.35fr;gap:20px;padding:18px;width:clamp(320px,44rem,92vw)}
.nav .nav-dropdown--ops{grid-template-columns:.8fr 1.2fr;width:clamp(340px,42rem,92vw)}
.nav .nav-dropdown--sites{grid-template-columns:1fr;width:clamp(300px,25rem,92vw)}
.nav .nav-dropdown--right{left:auto;right:0}
.nav .nav-mega-label{margin:0 0 6px;padding:0 12px;font-family:'Montserrat',sans-serif;font-weight:700;font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--slate,#5C6B82)}
.nav .nav-sites-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.nav .nav-dropdown a.nav-site-card{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px;border:1px solid var(--line,#E6E9F0);border-radius:var(--radius-sm);color:var(--navy,#14213D);font-family:'Montserrat',sans-serif;font-weight:600;font-size:13px;white-space:normal;background:#fff;transition:transform var(--dur-state) var(--ease), box-shadow var(--dur-state), border-color var(--dur-state), background var(--dur-state)}
.nav .nav-dropdown a.nav-site-card:hover{transform:translateY(-3px);box-shadow:0 12px 24px -12px rgba(20,33,61,.22);border-color:transparent;background:var(--mist,#F2F4F7);color:var(--navy,#14213D)}
.nav .nav-site-card svg{width:16px;height:16px;flex:none;color:var(--deep-blue,#0D47A1);transition:transform var(--dur-fast)}
.nav .nav-site-card:hover svg{transform:translateX(3px)}
.nav .nav-featured{position:relative;border:1px solid var(--line,#E6E9F0);border-radius:var(--radius);overflow:hidden;background:#fff;transition:transform var(--dur-state) var(--ease), box-shadow var(--dur-state)}
.nav .nav-featured:hover{transform:translateY(-3px);box-shadow:0 16px 30px -14px rgba(20,33,61,.24)}
.nav .nav-featured-fig{position:relative;margin:0;aspect-ratio:16/10;background:linear-gradient(135deg,#14213D,#0D47A1);overflow:hidden}
.nav .nav-featured-fig img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform var(--dur) var(--ease)}
.nav .nav-featured-scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(11,18,32,.04),rgba(11,18,32,.46));z-index:1}
.nav .nav-featured-chip{position:absolute;left:12px;top:12px;z-index:2}
.nav .nav-featured-body{padding:14px}
.nav .nav-featured-title{margin:0 0 8px;font-family:'Montserrat',sans-serif;font-weight:700;font-size:16px;line-height:1.25;color:var(--navy,#14213D)}
.nav .nav-readmore{display:inline-flex;align-items:center;gap:.4em;font-family:'Montserrat',sans-serif;font-weight:600;font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--deep-blue,#0D47A1)}
.nav .nav-readmore svg{width:15px;height:15px;transition:transform var(--dur-fast)}
.nav .nav-featured:hover .nav-readmore svg{transform:translateX(3px)}
.nav .nav-dropdown a.nav-featured-link{position:absolute;inset:0;z-index:3;padding:0;border-radius:0;background:transparent}
.nav .nav-dropdown a.nav-featured-link:hover{background:transparent}
@media (max-width:1320px){
.nav .nav-item{flex-direction:column;align-items:stretch;width:100%}
.nav .nav-trigger{justify-content:center}
.nav .nav-chev{display:none}
.nav .nav-dropdown,.nav .nav-dropdown--mega,.nav .nav-dropdown--right{position:static;left:auto;right:auto;margin:4px 0 8px;min-width:0;width:auto;opacity:1;visibility:visible;transform:none;pointer-events:auto;background:transparent;border:none;box-shadow:none;padding:0;grid-template-columns:1fr;gap:2px}
.nav .nav-dropdown::before{display:none}
.nav .nav-dropdown a{color:rgba(255,255,255,.72);text-align:center;font-size:16px;padding:7px}
.nav .nav-dropdown a:hover{background:transparent;color:#fff}
.nav .nav-mega-label{color:rgba(255,255,255,.45);text-align:center;padding:0}
.nav .nav-sites-grid{grid-template-columns:1fr}
.nav .nav-dropdown a.nav-site-card{justify-content:center;border:none;background:transparent;color:rgba(255,255,255,.72);padding:7px}
.nav .nav-dropdown a.nav-site-card:hover{background:transparent;color:#fff;transform:none;box-shadow:none}
.nav .nav-site-card svg{display:none}
.nav .nav-featured{display:none}
}

/* 001-hero */
.s-hero{position:relative;min-height:100vh;min-height:100svh;display:grid;place-items:center;overflow:hidden;isolation:isolate;background:#14213D;color:#fff;text-align:center}
.s-hero__bg{position:absolute;inset:0;z-index:0;background:#14213D}
/* Lifted so the laptop sits higher in the frame. Two things worth knowing before
   anyone changes this.

   object-position does nothing here: cover crops this video horizontally at every
   viewport and never vertically, so there is no vertical slack to move into.

   No scale either. scale() grows from the centre, so pairing it with a lift pushes
   the BOTTOM down by half the scale while raising the top - the laptop ends up
   bigger and lower, which is the opposite of the point. Translate alone moves the
   whole frame up together.

   What this exposes along the bottom edge has to stay under .hero-glass, which is
   flush to the bottom and a FIXED 61-68px tall. A flat 5% grew past it on a large
   screen (72px against a 68px bar at 2560x1440), so the lift is capped: 5% of the
   hero, or 58px, whichever is smaller. If that bar ever shrinks, lower the 58. */
.s-hero__bg video{width:100%;height:100%;object-fit:cover;display:block;
  transform:translateY(calc(min(5%, 58px) * -1))}
.s-hero__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;background:linear-gradient(rgba(20, 33, 61, .93), rgb(12 18 33 / 85%) 60%, rgb(20 33 61 / 72%))}
.s-hero .container{position:relative;z-index:2}
.s-hero__content{max-width:68rem;margin:0 auto;padding-block:clamp(96px,18vh,176px) clamp(128px,20vh,200px)}
.s-hero__wordmark{font-family:Montserrat,sans-serif;font-weight:800;color:#fff;font-size:clamp(20px,3vw,30px);letter-spacing:.02em;margin:0 0 clamp(18px,3vh,28px)}
/* h1 sits on exactly 2 lines from 768px up: the measure is wide enough for the full
   mandated sentence at full size, and text-wrap:balance evens the two lines. The
   font ramp is slightly under the global .h1 clamp so 1024px stays at 2 lines too. */
.s-hero h1{color:#fff;max-width:none;margin:0 auto;font-size:clamp(28px,5.2vw,62px);text-wrap:balance}
.s-hero__h1-lead{display:block;font-size:clamp(44px,8.4vw,104px);line-height:.98;letter-spacing:var(--track-display)}
.s-hero__h1-sub{display:block;color:var(--azure-soft);font-size:clamp(20px,3.3vw,40px);line-height:1.1;letter-spacing:var(--track-lg);margin-top:clamp(6px,1.2vh,16px);text-wrap:balance}
/* 62rem, not a measure. This was 62ch (682px), which set its own comfortable
   line length and ignored the h1 above it; the two stacked centred blocks read
   as unrelated widths. 62rem lands at 992px, within a few px of where the h1's
   second line ends at full size, so the pair share an edge. It is a ~90ch line,
   past the usual 45-75 reading range — deliberate, and the reason it is written
   in rem rather than ch. */
.s-hero__subhead{color:rgba(255,255,255,.9);max-width:62rem;margin:clamp(16px,3vh,26px) auto 0;text-wrap:pretty}
.s-hero__cta{margin-top:clamp(24px,4vh,36px);display:flex;justify-content:center}
.s-hero__stats{display:grid;grid-template-columns:repeat(3,1fr);width:100%}
.s-hero__stat{display:flex;align-items:center;justify-content:center;gap:.5rem;padding:clamp(14px,2.4vh,20px) 16px;font-family:Montserrat,sans-serif;font-weight:700;font-size:clamp(13px,1.15vw,16px);letter-spacing:.05em;text-transform:uppercase;color:#fff;text-decoration:none;border-left:1px solid rgba(255,255,255,.22);transition:color var(--dur-state) var(--ease), background-color var(--dur-state) var(--ease)}
.s-hero__stat:first-child{border-left:0}
.s-hero__stat svg{width:18px;height:18px;flex:none;transition:transform var(--dur-fast) var(--ease)}
.s-hero__stat:hover,.s-hero__stat:focus-visible{color:var(--azure);background-color:rgba(11,18,32,.45)}
.s-hero__stat:hover svg,.s-hero__stat:focus-visible svg{transform:translateX(.25em)}
@media (max-width:620px){.s-hero__stat{flex-direction:column;gap:.3rem;font-size:13px}}

/* 002-who */
.s-who{min-height:78vh;display:flex;align-items:center}
.s-who>.container{position:relative;z-index:1;width:100%}
.s-who .parallax-inner video,.s-who .parallax-inner img{width:100%;height:100%;object-fit:cover}
/* Same three stops as .s-hero__scrim, so the two photographic bands on the home
   page read as one treatment. This was .82 navy fading to .74 deep blue: flatter,
   and the blue shift at the bottom made it a different surface to the hero rather
   than a reprise of it.
   Only the home page sees this. Every mast (.s-mast/.c-mast/.p-mast/.t-mast/
   .r-mast) reuses the class but replaces `background` outright in its own page
   stylesheet, so none of them inherit this. */
.s-who__scrim{position:absolute;inset:0;background:linear-gradient(rgba(20,33,61,.93),rgba(20,33,61,.78) 60%,rgba(20,33,61,.65))}
.s-who .grid--2{align-items:end;gap:clamp(28px,5vw,64px)}
.s-who__col{display:flex;flex-direction:column;align-items:flex-start;gap:clamp(20px,3vw,28px)}
@media(max-width:768px){.s-who{min-height:auto}}

/* 003-what */
.s-what .s-what-head{align-items:end;gap:clamp(20px,4vw,48px)}
.s-what .s-what-intro .lead{max-width:54ch}
/* reference .what-we-do-stack: sticky pinned cards that stack on top of one another on scroll */
.s-what .wwd-stack{display:flex;flex-direction:column;gap:clamp(20px,3vw,28px);margin-top:clamp(30px,4vw,46px)}
/* WHOLE-CARD CLICK TARGET
   The card's single link is stretched invisibly over the card with its own
   ::after, so the whole card is clickable and hovering anywhere lights the
   link. Still exactly ONE link per card - no nested anchors, no JS - so
   keyboard and screen-reader behaviour is unchanged.
   The link must stay position:static, and no wrapper between it and the card
   may be positioned, or the overlay measures against the wrapper instead. */
/* #F1F2F6, not --mist. The illustrations carry that exact colour baked in and
   the figure tile below matches it, so anything else leaves a faint edge where
   the picture meets the copy — --mist is only one level off, which is just
   enough to see. The three have to stay in step: change one, change all three. */
.s-what .wwd-card{cursor:pointer;display:grid;grid-template-columns:1.24fr .76fr;align-items:stretch;background:#F1F2F6}
.s-what .wwd-card-text{display:flex;flex-direction:column;align-items:flex-start;gap:18px;padding:clamp(28px,3.6vw,52px)}
.s-what .wwd-card-text .link-arrow{margin-top:auto}
.s-what .wwd-card .link-arrow::after{content:"";position:absolute;inset:0;z-index:3;border-radius:var(--radius)}
.s-what .wwd-figure{position:relative;margin:0;justify-self:center;align-self:center;width:80%;aspect-ratio:16/9;min-height:clamp(200px,24vw,300px);overflow:hidden;background:#F1F2F6}
.s-what .wwd-figure img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;transform:scale(1);transition:transform var(--dur) var(--ease)}
.s-what .card--hover:hover .wwd-figure img{transform:scale(1.04)}
@media (min-width:769px){
  /* pinned stack: each card sticks a touch lower so buried cards peek above the next (reference +~17px stagger) */
  .s-what .wwd-card{position:sticky;transform-origin:center top;will-change:transform;box-shadow:0 24px 60px -34px rgba(20,33,61,.5)}
  .s-what .wwd-card:nth-child(1){top:96px}
  .s-what .wwd-card:nth-child(2){top:114px}
  .s-what .wwd-card:nth-child(3){top:132px}
  .s-what .wwd-card:nth-child(4){top:150px}
  .s-what .wwd-card.card--hover:hover{transform:none}
}
@media (max-width:768px){
  .s-what .wwd-card{grid-template-columns:1fr}
  .s-what .wwd-figure{order:-1;min-height:220px}
}

/* 004-transition removed — the light→dark bridge is now carried by 005's own top edge. */

/* 005-why */
.s-why{position:relative;overflow:hidden}
.s-why .container{position:relative;z-index:1}
/* Was .84 → .66, which let about a sixth of the backdrop through and read as
   flat navy. Lifted so the grid behind it is actually visible; the copy still
   clears contrast against it. */
.s-why__scrim{position:absolute;inset:0;z-index:0;background:linear-gradient(160deg,rgba(20,33,61,.62),rgba(13,71,161,.44));pointer-events:none}
.s-why__head{align-items:end;gap:clamp(24px,4vw,48px)}
.s-why__lead{max-width:48ch}
.s-why__cards{margin-top:clamp(40px,5vw,64px)}
.s-why .why-card{display:flex;flex-direction:column;gap:14px;padding:clamp(24px,3vw,30px)}
.s-why .why-card .link-arrow{margin-top:auto}

/* 006-execution */
.s-execution{position:relative;display:flex;align-items:center;}
.s-execution .container{position:relative;z-index:1;width:100%}
.s-execution__glow{position:absolute;inset:0;z-index:0;pointer-events:none;background:radial-gradient(58% 54% at 26% 40%,rgba(33,150,243,.11),transparent 70%),radial-gradient(48% 50% at 82% 74%,rgba(13,71,161,.07),transparent 72%)}
.s-execution__top{align-items:center}
.s-execution__map{min-width:0}
/* 16/9 with a height floor, which is the site's shape for a photograph sitting
   beside copy - same as .s-what .wwd-figure above. It was 3/2 while this frame
   held a flat pipeline diagram, which is a diagram shape, not a photo one. */
.s-execution .map-frame{position:relative;width:100%;aspect-ratio:16/9;min-height:clamp(200px,24vw,300px);border-radius:var(--radius);background:rgba(255,255,255,.4);border:1px solid var(--line);overflow:hidden}
.s-execution .map-frame canvas{position:absolute;inset:0;display:block;width:100%;height:100%}
.s-execution__head .lead{margin-top:20px;max-width:52ch}
.s-execution__stats{display:flex;flex-wrap:wrap;gap:14px;margin-top:clamp(32px,5vw,52px)}
@media (min-width:769px){.s-execution__top{grid-template-columns:1.08fr .92fr;column-gap:clamp(28px,5vw,64px)}}

/* 007-news */
.s-news .news-head{display:flex;justify-content:space-between;align-items:flex-end;gap:24px 40px;flex-wrap:wrap;max-width:none}
.s-news .news-head-copy{flex:1 1 auto}
.s-news .news-head-cta{flex:0 0 auto}
/* reference .resources-card: light panel, borderless, 16:9 full-bleed cover, overlaid tag pill.
   Radius is the card value, not the small one — these are cards like any other on the site. */
.s-news .res-card{display:flex;flex-direction:column;text-decoration:none;color:inherit;background:var(--white);border-radius:var(--radius)}
/* No background here on purpose. The three images are exactly 16/9, opaque, and
   object-fit:cover - so they always cover this box completely and anything painted
   behind them can only ever appear as a seam. It used to carry a blue gradient, and
   on hover (card lifts 3px, image scales to 1.04) the antialiased clip edge let a
   hairline of it show along the bottom of the image. */
/* These are illustrations, not photographs, so they are placed rather than
   cropped: the graphic sits at 74% inside a shorter band with white around it.
   Filling a 16/9 box edge to edge made them the loudest thing on the card. */
.s-news .res-figure{position:relative;margin:0;overflow:hidden;aspect-ratio:2/1;
  display:grid;place-items:center;background:var(--white)}
.s-news .res-figure img{width:65%;margin-top:3em;height:auto;object-fit:contain;display:block;transition:transform var(--dur) var(--ease)}
.s-news .res-card:hover .res-figure img{transform:scale(1.04)}
/* tag overlay: reference slate@60% -> Prime navy@64%, light text, 4px, uppercase, no border/dot */
/* One colour per route, so three different offers stop reading as one label.
   They were all the same translucent navy, which said nothing about which is
   free, which is bought and which is a subscription. */
.s-news .res-chip{position:absolute;top:14px;left:14px;z-index:2;padding:6px 9px;gap:0;border:none;border-radius:var(--radius-sm);background:var(--glass-navy);color:var(--white);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}
.s-news .res-card:nth-child(1) .res-chip{background:var(--azure)}   /* free */
.s-news .res-card:nth-child(2) .res-chip{background:var(--deep)}    /* bought */
.s-news .res-card:nth-child(3) .res-chip{background:var(--navy)}    /* subscription */
.s-news .res-body{padding:20px;display:flex;flex-direction:column;gap:14px;flex:1 1 auto}
/* reference d5 title: display 600, ~20px, lh1.24, ls-.02em, navy, 2-line clamp */
.s-news .res-title{margin:0;font-family:var(--font-display);font-weight:600;font-size:clamp(18px,1.4vw,21px);line-height:1.24;letter-spacing:-.02em;color:var(--navy);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* 14.5px/1.58 is the site's card blurb size - same as .p-plan__blurb and
   .t-free-card__blurb. 13.5px is the smaller rail-note size and read too light
   sitting under a card title. */
.s-news .res-note{margin:0;font-family:var(--font-body);font-weight:400;
  font-size:14.5px;line-height:1.58;color:var(--slate)}
.s-news .res-more{margin-top:1em;}
/* .res-more is a span inside the card link, so it never gets :hover itself */
.s-news .res-card:hover .res-more{color:var(--azure)}
.s-news .res-card:hover .res-more svg{transform:translateX(3px)}
@media (max-width:768px){.s-news .news-head{align-items:flex-start}.s-news .news-head-cta{width:auto}}

/* 008-portal */
/* Surface comes from `section--mist` in the markup, same as every other grey band.
   It used to be a white->mist gradient here, which made it the only gradient
   background on the site and blurred the edge with the band below. */
/* 49rem, not 46: its centred heading was 31px short of fitting on one line. */
.s-portal__head{max-width:min(900px,100%);margin-inline:auto;text-align:center}
.s-portal__head h2{text-wrap:balance}
.s-portal__head .lead{margin-top:.85rem}
.s-portal__cta{margin-top:clamp(1.6rem,2.6vw,2.2rem);display:flex;justify-content:center}
.s-portal__mockup{margin:clamp(2.4rem,5vw,3.75rem) auto 0;max-width:min(900px,100%)}
.s-portal__frame{position:relative;border-radius:var(--radius);overflow:hidden;background:#14213D;border:1px solid #DCE1EA;box-shadow:0 34px 60px -34px rgba(13,71,161,.4),0 14px 30px -22px rgba(20,33,61,.35)}
.s-portal__chrome{display:flex;align-items:center;gap:8px;padding:12px 16px;background:#14213D}
.s-portal__dot{width:11px;height:11px;border-radius:50%;background:rgba(242,244,247,.28)}
.s-portal__dot--red{background:#E53935}
.s-portal__addr{flex:1;height:16px;margin-left:10px;border-radius:999px;background:rgba(242,244,247,.12)}
.s-portal__screen{position:relative;aspect-ratio:16/9;background:#F2F4F7}
.s-portal__screen>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.s-portal__fallback{position:absolute;inset:0;display:flex}
.s-portal__sidebar{width:24%;min-width:108px;background:#14213D;padding:18px 16px;display:flex;flex-direction:column;gap:14px}
.s-portal__brand{font-family:Montserrat,system-ui,sans-serif;font-weight:800;font-size:13px;line-height:1.2;color:#fff}
.s-portal__navitem{height:10px;border-radius:999px;background:rgba(242,244,247,.16)}
.s-portal__navitem--on{background:linear-gradient(135deg,#0D47A1,#2196F3)}
.s-portal__main{flex:1;min-width:0;padding:22px clamp(16px,3vw,30px);display:flex;flex-direction:column;gap:16px}
.s-portal__welcome{height:20px;width:52%;border-radius:var(--radius-sm);background:#DCE1EA}
.s-portal__tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.s-portal__tile{aspect-ratio:3/2;border-radius:var(--radius-sm);background:#fff;border:1px solid #E8ECF3}
.s-portal__panel{flex:1;min-height:70px;border-radius:var(--radius-sm);background:#fff;border:1px solid #E8ECF3}
@media(max-width:520px){.s-portal__brand{font-size:11px}.s-portal__tiles{gap:10px}}

/* 009-footer-cta */
.s-footcta .s-footcta__grid{clamp(18px,2.2vw,26px);}
.s-footcta__block{cursor:pointer;position:relative;display:flex;flex-direction:column;min-height:clamp(340px,42vw,460px);border-radius:var(--radius);overflow:hidden;background:var(--navy);isolation:isolate;transition:transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease)}
.s-footcta__block:hover{transform:translateY(-3px);box-shadow:0 16px 34px -20px rgba(0,0,0,.55)}
.s-footcta__title{text-wrap:nowrap}
/* Both cards carry the same navy grid field as the capabilities band, so the two
   read as one surface rather than two posters. The second one is mirrored, which
   throws its glow to the opposite side and stops the pair looking like a copy
   and paste. Mirroring the layer, not the card, so the content is unaffected. */
/* The one transition on the site that is deliberately off the rungs. 1.1s on its
   own curve is a slow drift on a background image, not a control responding to
   you, and normalising it to --dur would change the effect rather than tidy it. */
.s-footcta__bg{position:absolute;inset:0;z-index:-2;background-size:cover;background-position:center;transition:transform 1.1s cubic-bezier(.16,.84,.44,1)}
.s-footcta__block:nth-of-type(even) .s-footcta__bg{transform:scaleX(-1)}
.s-footcta__block:nth-of-type(even):hover .s-footcta__bg{transform:scaleX(-1) scale(1.05)}
.s-footcta__block:hover .s-footcta__bg{transform:scale(1.05)}
.s-footcta__scrim{position:absolute;inset:0;z-index:-1;background:linear-gradient(180deg,rgba(20,33,61,.15) 0%,rgba(20,33,61,.60) 52%,rgba(20,33,61,.88) 100%)}
.s-footcta__content{margin-top:auto;padding:clamp(24px,3vw,36px);max-width:58ch}
/* Whole-block click target. The button is taken out of the positioning flow so
   its ::after measures against the block, then stretched over the whole thing.
   There is still exactly ONE link in the block - no nested anchors, no JS - so
   keyboard and screen-reader behaviour is unchanged. Hovering anywhere is then
   literally hovering the link, which is what lights the button up.
   (.btn-outline inside these buttons is an unstyled leftover, so making the
   button static breaks nothing.) */
.s-footcta__block .btn{position:static}
.s-footcta__block .btn::after{content:"";position:absolute;inset:0;z-index:3;border-radius:var(--radius)}
/* reserve three lines so every block keeps the same shape, whatever the window width */
.s-footcta__content .lead{min-height:calc(3 * 1.6em)}
.s-footcta__block .eyebrow{color:var(--azure)}
.s-footcta__block .h3-lg{color:var(--white);margin:16px 0 0}
.s-footcta__block .lead{color:var(--mist);margin:12px 0 0}
.s-footcta__block .btn-ghost{margin-top:3em;}
@media (prefers-reduced-motion:reduce){.s-footcta__block:hover .s-footcta__bg,.s-footcta__block:hover{transform:none}}

/* 010-footer */
.s-footer{position:relative;background:var(--navy,#14213D);color:#fff;padding:clamp(44px,6vw,70px) 0 clamp(30px,4vw,44px);overflow:hidden}
.s-footer::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--prime-gradient);z-index:1}
.s-footer .container{position:relative}
.s-footer__top{display:grid;grid-template-columns:1.5fr 2.2fr;gap:clamp(48px,12vw,200px);align-items:end;}
.s-footer__wordmark{display:block;text-decoration:none}
.s-footer__wordmark img{width:min(300px,100%);height:auto}
/* The 24px of padding-top used to clear a red rule sitting at top:0. The rule is
   gone, so it is rolled into the margin - same gap, no padding holding nothing. */
.s-footer__tagline{margin:42px 0 0;max-width:min(430px,100%);font-family:Poppins,sans-serif;font-size:14px;line-height:1.65;color:rgba(255,255,255,.68)}
.s-footer__cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,2.4vw,38px)}
/* footer link labels stay on one line, they are short and known */
.s-footer__list a{white-space:nowrap}
.s-footer__col-title{margin:0 0 14px;font-family:Montserrat,sans-serif;font-weight:700;font-size:12px;letter-spacing:.11em;text-transform:uppercase;color:#fff}
.s-footer__list{list-style:none;margin:0;padding:0}
.s-footer__list li+li{margin-top:5px}
.s-footer__list a{font-family:Poppins,sans-serif;font-size:14.5px;line-height:1.4;color:rgba(255,255,255,.7);text-decoration:none;transition:color var(--dur-fast) var(--ease)}
.s-footer__list a:hover{color:#fff}
.s-footer__news-label{margin:14px 0 14px;font-family:Poppins,sans-serif;font-size:15px;line-height:1.45;color:rgba(255,255,255,.82)}
.s-footer__form{display:flex;flex-wrap:wrap;gap:10px}
.s-footer__form input{flex:1 1 190px;min-width:0;height:44px;padding:0 15px;font-family:Poppins,sans-serif;font-size:15px;color:#fff;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.14);border-radius:var(--radius-sm);transition:border-color var(--dur-fast) var(--ease)}
.s-footer__form input::placeholder{color:rgba(255,255,255,.46)}
.s-footer__form input:focus-visible{outline:2px solid var(--azure);outline-offset:2px;border-color:transparent}
.s-footer__msg{margin:12px 0 0;font-family:Poppins,sans-serif;font-size:14px}
.s-footer__msg--ok{color:var(--azure)}
.s-footer__msg--fail{color:var(--red,#E53935)}
.s-footer__credit{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:18px;margin-top:clamp(34px,4.4vw,52px);padding-top:24px;border-top:1px solid rgba(255,255,255,.14)}
.s-footer__copy{margin:0;font-family:Poppins,sans-serif;font-size:13.5px;color:rgba(255,255,255,.6)}
.s-footer__social{display:flex;gap:8px;margin:0;padding:0;list-style:none}
.s-footer__social a{display:grid;place-items:center;width:38px;height:38px;border-radius:var(--radius-sm);border:1px solid rgba(255,255,255,.14);color:rgba(255,255,255,.8);transition:color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease)}
.s-footer__social a:hover{color:var(--azure);border-color:var(--azure);background:rgba(255,255,255,.04)}
.s-footer__social svg{width:18px;height:18px}
.s-footer__legal{margin:0;display:flex;align-items:center;gap:10px;font-family:Poppins,sans-serif;font-size:13.5px}
.s-footer__legal a{color:rgba(255,255,255,.6);text-decoration:none;transition:color var(--dur-fast) var(--ease)}
.s-footer__legal a:hover{color:var(--azure)}
.s-footer__legal-sep{color:rgba(255,255,255,.28)}
@media (max-width:900px){.s-footer__top{grid-template-columns:1fr}}
@media (max-width:600px){.s-footer__top{grid-template-columns:1fr;gap:34px}.s-footer__cols{grid-template-columns:1fr 1fr}.s-footer__credit{justify-content:flex-start}}
@media (max-width:400px){.s-footer__cols{grid-template-columns:1fr}}
