/* ============================================================================
   Omni-Channel — Design System
   Built on the Apple web system: photography-first, chrome recedes, one accent,
   one shadow, alternating light/dark full-bleed tiles.

   Two deliberate departures from the source spec, both for the same reason:

   1. ACCENT. The spec names Action Blue #0066cc. Ours is #2F6BFF, because the
      eight film clips are rendered with #2F6BFF data cubes. The spec's real rule
      is "one interactive colour, non-negotiable" — honouring the rule matters
      more than the hex, and two near-identical blues on one screen is exactly
      what that rule exists to prevent.
   2. TYPE. SF Pro is Apple's and cannot be served. It resolves natively via
      system-ui/-apple-system on Apple platforms; everywhere else the stack
      falls to Inter, which the spec names as the closest open equivalent.
      Per the spec's substitution note: display tracking is nudged -0.01em and
      body leading tightened 0.03, because Inter runs wider and taller than SF.
   ============================================================================ */

/* Rubik: rounded terminals, which is the same language as the clay world in
   the film — every form in those renders has a softened corner. It also
   carries Arabic, which the system stack does not do consistently. */
/* The font is requested from the document head, not from here. An @import is
   only discovered once this file has been fetched and parsed, which serialises
   two round trips before a single glyph can load — and on a slow or blocked
   connection the page renders the whole way through in a fallback face. The
   <link> in each page starts the fetch alongside this file instead. */

:root{
  /* ---- accent: one colour, every interactive element ------------------ */
  /* Primary is the darkest blue in the logo. It clears 9.54:1 on white, so
     unlike the old #2F6BFF it is safe for small text as well as for fills —
     one colour now covers both jobs instead of needing a darker twin. */
  --c-accent:        #204289;   /* every link, every pill CTA, every focus root */
  --c-accent-text:   #204289;   /* 9.54:1 on white */
  --c-accent-focus:  #204289;
  --c-accent-on-dark:#6DCFF6;   /* the logo cyan: 5.41:1 on the primary tile */
  --c-accent-deep:   #163268;   /* pressed state */
  /* the film's data cubes are #2F6BFF and stay that way — it is an illustrative
     colour inside the renders, not an interface colour */
  --c-film-blue:     #2F6BFF;
  --c-whatsapp:      #25D366;   /* only ever on WhatsApp itself */
  --c-whatsapp-ink:  #14793C;   /* one step darker than Meta's own: #178A45 lands at 4.41:1 */

  /* ---- surface -------------------------------------------------------- */
  --canvas:          #FFFFFF;
  --canvas-parchment:#F5F5F7;   /* the alternating light tile, and the footer */
  --surface-pearl:   #FAFAFC;   /* ghost-button fill: reads as a button on parchment */
  /* A slate with a navy cast rather than near-black: #272729 read as a hole in
     the page, and this sits close enough to the brand to belong to it. White
     clears 9.7:1 on it and the cyan accent 5.5:1, so nothing had to move. */
  --surface-tile-1:  #3A4257;   /* primary dark tile */
  --surface-tile-2:  #414A61;   /* one micro-step lighter, for dark-on-dark */
  --surface-tile-3:  #333B4E;   /* one micro-step darker, for the bottom of a stack */
  --surface-black:   #000000;   /* true void only: video frames, the top nav */
  /* the clips' own background, sampled from the renders — the page must match
     it exactly or a seam shows at the film's edge */
  --film-bg:         #FEFEFE;

  /* ---- ink ------------------------------------------------------------ */
  --ink:             #1D1D1F;   /* near-black, not black: keeps the page photographic */
  --ink-muted-80:    #333333;
  /* #7A7A7A measured 3.94:1 behind 14px text, under the 4.5 floor. This is the
     lightest grey that still clears it on white, so every caption, tier note
     and fine print inherits a passing value instead of being fixed one by one. */
  --ink-muted-48:    #6E6E6E;
  --on-dark:         #FFFFFF;
  --on-dark-muted:   #CCCCCC;

  /* ---- hairlines ------------------------------------------------------ */
  --hairline:        #E0E0E0;
  --divider-soft:    rgba(0,0,0,.04);

  /* ---- type ----------------------------------------------------------- */
  /* system-ui first: on Apple platforms this IS SF Pro. Cairo carries Arabic. */
  --font-display: "Rubik", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-text:    "Rubik", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;

  /* the ladder is 300 / 400 / 600 / 700. 500 is deliberately absent. */
  --t-hero:      600 clamp(34px,4.6vw,56px)/1.07 var(--font-display);
  --t-display-lg:600 clamp(28px,3.2vw,40px)/1.10 var(--font-display);
  --t-display-md:600 clamp(24px,2.6vw,34px)/1.2  var(--font-text);
  --t-lead:      400 clamp(21px,2.1vw,28px)/1.14 var(--font-display);
  --t-lead-airy: 300 clamp(19px,1.8vw,24px)/1.55 var(--font-text);
  --t-tagline:   600 21px/1.19 var(--font-display);
  --t-body-strong:600 17px/1.24 var(--font-text);
  --t-body:      400 17px/1.52 var(--font-text);
  --t-dense-link:400 17px/2.41 var(--font-text);
  --t-caption:   400 14px/1.43 var(--font-text);
  --t-caption-strong:600 14px/1.29 var(--font-text);
  --t-button:    400 17px/1 var(--font-text);
  --t-button-utility:400 14px/1.29 var(--font-text);
  --t-fine:      400 12px/1 var(--font-text);
  --t-nav:       400 12px/1 var(--font-text);

  /* negative tracking at display sizes is the whole signature */
  --track-hero:   -.72px;   /* Rubik runs wide; display needs a tighter pull */
  --track-md:     -.46px;
  --track-body:   -.374px;
  --track-caption:-.224px;
  --track-fine:   -.12px;

  /* ---- space: 8px base ------------------------------------------------ */
  --s-xxs:4px; --s-xs:8px; --s-sm:12px; --s-md:17px;
  --s-lg:24px; --s-xl:32px; --s-xxl:48px;
  /* 80px above and below is right beside a wide column and far too much stacked
     on a phone, where the two together were spending 160px — a fifth of the
     screen — on the join between two sections. The ramp reaches 80px by 1000px
     wide, so every desktop width keeps the spacing it already had. */
  --s-section: clamp(56px, 8vw, 80px);
  --nav-h: 76px;              /* the film sits under this, never behind it */
  --pad-x: clamp(22px,5vw,80px);
  --maxw:  1440px;
  --maxw-text: 980px;

  /* ---- radius --------------------------------------------------------- */
  --r-none:0; --r-xs:5px; --r-sm:8px; --r-md:11px; --r-lg:18px; --r-pill:9999px;

  /* ---- elevation: exactly one shadow, and it is for imagery only ------- */
  --shadow-product: rgba(0,0,0,.22) 3px 5px 30px 0;

  /* ---- motion --------------------------------------------------------- */
  --dur-1:.16s; --dur-2:.28s; --dur-3:.5s; --dur-4:.8s;
  --ease:     cubic-bezier(.22,.68,.28,1);
  --ease-out: cubic-bezier(.16,.84,.36,1);

  /* ---- z scale: named, never arbitrary -------------------------------- */
  --z-base:0; --z-sticky:30; --z-nav:40; --z-overlay:50; --z-toast:60;

  /* aliases kept so existing markup keeps working */
  --bg:var(--canvas); --bg-sunken:var(--canvas-parchment); --bg-raised:var(--canvas);
  --ink-2:var(--ink-muted-80); --ink-3:var(--ink-muted-48);
  --line:var(--hairline); --line-soft:var(--divider-soft);
  --font:var(--font-text);
}

/* ============================ primitives ================================ */
*,*::before,*::after{ box-sizing:border-box }
html{ -webkit-text-size-adjust:100% }  /* no scroll-behavior: site.js owns the glide */
body{
  margin:0; background:var(--canvas); color:var(--ink);
  font:var(--t-body); letter-spacing:var(--track-body);
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
a{ color:inherit }
/* Form controls do not inherit type from their parent — they fall back to the
   UA default (Arial/Times). Without this the design system silently stops at
   the edge of every input, select, textarea and button on the site. */
button, input, select, textarea, optgroup{
  font:inherit; letter-spacing:inherit; color:inherit;
}
img{ max-width:100%; display:block }
::selection{ background:var(--c-accent); color:#fff }
:focus-visible{ outline:2px solid var(--c-accent-focus); outline-offset:3px; border-radius:var(--r-sm) }

/* ============================ layout ==================================== */
.wrap{ max-width:var(--maxw); margin-inline:auto; padding-inline:var(--pad-x) }
.section{ padding-block:var(--s-section) }
/* Two section skins. Both were used throughout the page but never defined here,
   so every section rendered as the same flat white sheet with no divisions —
   the alternation IS the page's rhythm, so it has to exist. */
.section--sunken{ background:var(--canvas-parchment) }
.section--hair{ border-top:1px solid var(--divider-soft) }
/* two sunken sections in a row would read as one long block */
.section--sunken + .section--sunken{ border-top:1px solid var(--hairline) }
.stack > * + *{ margin-top:var(--s-md) }
.grid{ display:grid; gap:var(--s-lg) }
.grid--2{ grid-template-columns:repeat(auto-fit,minmax(320px,1fr)) }
.grid--3{ grid-template-columns:repeat(auto-fit,minmax(260px,1fr)) }
.grid--4{ grid-template-columns:repeat(auto-fit,minmax(212px,1fr)) }
.split{ display:grid; grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
        gap:clamp(32px,5vw,80px); align-items:center }
@media (max-width:834px){ .split{ grid-template-columns:1fr; gap:var(--s-xl) } }

/* ---- tiles: the colour change IS the divider. No borders, no shadows. --- */
.tile{ padding-block:var(--s-section); padding-inline:var(--pad-x) }
.tile--light{ background:var(--canvas); color:var(--ink) }
.tile--parchment{ background:var(--canvas-parchment); color:var(--ink) }
.tile--dark{ background:var(--surface-tile-1); color:var(--on-dark) }
.tile--dark-2{ background:var(--surface-tile-2); color:var(--on-dark) }
.tile--dark-3{ background:var(--surface-tile-3); color:var(--on-dark) }
.tile--dark .lede, .tile--dark-2 .lede, .tile--dark-3 .lede{ color:var(--on-dark-muted) }
.tile--dark a:not(.btn), .tile--dark-2 a:not(.btn), .tile--dark-3 a:not(.btn){ color:var(--c-accent-on-dark) }

/* ============================ type ====================================== */
.display,.hero-display{ font:var(--t-hero); letter-spacing:var(--track-hero); margin:0; text-wrap:balance }
.h1{ font:var(--t-display-lg); letter-spacing:0; margin:0; text-wrap:balance }
.h2{ font:var(--t-display-lg); letter-spacing:0; margin:0; text-wrap:balance }
.h3{ font:var(--t-display-md); letter-spacing:var(--track-md); margin:0 }
.tagline{ font:var(--t-tagline); letter-spacing:.231px; margin:0 }
.lead,.lede{ font:var(--t-lead); letter-spacing:.196px; color:var(--ink-muted-80); margin:0; max-width:52ch }
.lead--airy{ font:var(--t-lead-airy); letter-spacing:0 }
.body-strong{ font:var(--t-body-strong); letter-spacing:var(--track-body) }
.small,.caption{ font:var(--t-caption); letter-spacing:var(--track-caption); color:var(--ink-muted-80); margin:0 }
.fine{ font:var(--t-fine); letter-spacing:var(--track-fine); color:var(--ink-muted-48) }
.measure{ max-width:46ch }
p{ text-wrap:pretty }

/* One named kicker, used sparingly — not above every section. */
.eyebrow{
  font:var(--t-caption-strong); letter-spacing:var(--track-caption);
  color:var(--c-accent-text); margin:0 0 var(--s-sm);
}
.eyebrow--quiet{ color:var(--ink-muted-48) }

/* ============================ button ==================================== */
/* Two grammars only: a pill for actions, a small rect for utility. */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-xs);
  font:var(--t-button); letter-spacing:var(--track-body);
  text-decoration:none; white-space:nowrap; cursor:pointer;
  padding:11px 22px; border-radius:var(--r-pill); border:1px solid transparent;
  background:var(--c-accent); color:#fff;
  transition:transform var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.btn:active{ transform:scale(.95) }   /* the system-wide press micro-interaction */
.btn--accent{ background:var(--c-accent) }
/* ghost pill: the second CTA when two sit together */
.btn--ghost{ background:transparent; color:var(--c-accent-text); border-color:currentColor }
.tile--dark .btn--ghost{ color:var(--c-accent-on-dark) }
.btn--pearl{ background:var(--surface-pearl); color:var(--ink-muted-80);
             border-radius:var(--r-md); padding:8px 14px; font:var(--t-caption);
             border:3px solid var(--divider-soft) }
.btn--utility{ background:var(--ink); color:var(--on-dark); border-radius:var(--r-sm);
               padding:8px 15px; font:var(--t-button-utility); letter-spacing:var(--track-caption) }
.btn--link{ background:none; color:var(--c-accent-text); padding:0; border:0; border-radius:0 }
/* nowrap is right for a pill until the label is a sentence: "Everything a
   product page records →" measured 305px inside a 261px column on a 320px
   phone and carried the page 22px past its own right edge. Below the narrowest
   common phone the label wraps, so the pill grows downwards instead of pushing
   sideways. Wide enough to hold one line, nothing changes. */
@media (max-width:420px){
  .btn{ white-space:normal; max-width:100%; text-align:center }
}
.btn--link:active{ transform:none }
.btn--lg{ font:300 18px/1 var(--font-text); padding:14px 28px }

/* ============================ chip / pill =============================== */
.pill{
  display:inline-flex; align-items:center; gap:var(--s-xxs);
  font:var(--t-caption); letter-spacing:var(--track-caption); color:var(--ink);
  padding:12px 16px; border-radius:var(--r-pill);
  border:1px solid var(--hairline); background:var(--canvas); white-space:nowrap;
}
.pill--selected{ border:2px solid var(--c-accent-focus) }
.pill--accent{ color:var(--c-accent-text); border-color:var(--c-accent-text) }
.pill--wa{ color:var(--c-whatsapp-ink); border-color:rgba(37,211,102,.4) }
.pills{ display:flex; flex-wrap:wrap; gap:var(--s-xs) }

/* ============================ card ====================================== */
/* Hairline border, no shadow. Elevation is a surface change, not a shadow. */
.card{
  background:var(--canvas); border:1px solid var(--hairline);
  border-radius:var(--r-lg); padding:var(--s-lg);
  transition:transform var(--dur-2) var(--ease);
}
.card--link{
  text-decoration:none; display:grid; align-content:start;
  transition:transform var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease);
}
/* A card that is a link has to answer the pointer. Lift, a border that commits
   to the accent, and the arrow stepping forward — three cheap signals, one
   gesture. */
.card--link:hover{
  transform:translateY(-3px);
  border-color:color-mix(in srgb,var(--c-accent) 38%,transparent);
  box-shadow:0 14px 30px -22px rgba(10,16,32,.34);
}
.card--link:hover .btn--link{ color:var(--c-accent-deep) }
/* the arrow steps forward on transform rather than on a width change */
.card--link .btn--link{ display:inline-block }
.card--link:hover .btn--link{ transform:translateX(4px) }
.card--link .btn--link{ transition:color var(--dur-1) var(--ease), transform var(--dur-2) var(--ease) }
.card--link:focus-visible{ transform:translateY(-3px) }
.card--link:active{ transform:scale(.99) }
@media (prefers-reduced-motion:reduce){
  .card--link, .card--link .btn--link, .card--link .btn--link::after{ transition:none }
  .card--link:hover{ transform:none }
}
.card--flush{ padding:0; overflow:hidden }
.card img{ border-radius:var(--r-sm) }
/* the step number on a process card: a counter, not a heading */
.card__num{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:30px; height:30px; padding-inline:8px; margin-bottom:var(--s-sm);
  border-radius:var(--r-pill); background:#EBF1FF; color:var(--c-accent);
  font:var(--t-caption-strong); letter-spacing:var(--track-caption);
}

/* the one shadow in the system, and it is only ever for product imagery */
.product-shot{ box-shadow:var(--shadow-product) }

/* ============================ stat ====================================== */
.stats{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:var(--s-xl) }
.stat b{ display:block; font:var(--t-display-lg); letter-spacing:0; margin-bottom:var(--s-xs) }
.stat span{ font:var(--t-caption); letter-spacing:var(--track-caption); color:var(--ink-muted-80) }
.tile--dark .stat span{ color:var(--on-dark-muted) }

/* ============================ list ====================================== */
.ticks{ list-style:none; margin:0; padding:0; display:grid; gap:var(--s-sm) }
.ticks li{ font:var(--t-body); letter-spacing:var(--track-body); padding-left:1.3rem; position:relative }
.ticks li::before{
  content:''; position:absolute; left:0; top:.62em; width:5px; height:5px;
  border-radius:1px; background:var(--c-accent);
}
.links{ list-style:none; margin:0; padding:0 }
.links a{ font:var(--t-dense-link); color:var(--ink-muted-80); text-decoration:none }
.links a:hover{ text-decoration:underline }

/* ============================ nav =======================================
   One glass treatment, used by the masthead and by the channel subnav, so the
   two never drift apart. Real glass needs four things working together, not
   just a blur: a translucent fill so colour comes through, saturation boost so
   what shows through stays lively instead of going grey, a hairline at the
   bottom edge, and a top inner highlight that reads as the lit edge of a pane.

   The fill has to stay translucent — an opaque bar with a blur behind it is
   just an opaque bar. That is the usual mistake. */
.glass{
  /* explicit rgba, not color-mix: the mixed value animates through oklab and
     the alpha stops resolving cleanly across the state change */
  background:rgba(255,255,255,.60);
  -webkit-backdrop-filter:saturate(185%) blur(22px);
  backdrop-filter:saturate(185%) blur(22px);
  border-bottom:1px solid rgba(10,16,32,0);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.55);
  transition:background var(--dur-3) var(--ease),
             border-bottom-color var(--dur-3) var(--ease),
             box-shadow var(--dur-3) var(--ease);
}
/* once the page has moved under it, the pane commits: denser fill, visible edge */
.glass.is-stuck{
  background:rgba(255,255,255,.82);
  border-bottom-color:rgba(10,16,32,.10);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.75), 0 1px 14px -8px rgba(10,16,32,.22);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{ background:rgba(255,255,255,.94) }
  .glass.is-stuck{ background:#fff }
}

/* Three columns, not two: the outer pair take equal free space so the links
   land on the page's true centre no matter how wide the logo or the button is.
   A flex row with space-between centres nothing — it only pushes things apart. */
.mast{
  position:fixed; inset:0 0 auto 0; z-index:var(--z-nav);
  /* auto, not 1fr, for the outer tracks. With `1fr auto 1fr` the nav claimed the
     middle and the logo's track was squeezed to whatever was left — 66px at
     768 and 0 at 375 — so the global img{max-width:100%} crushed the wordmark
     to a quarter of its width. Sizing the outer tracks to their content means
     the logo is never the thing that gives way. */
  display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  column-gap:var(--s-md);
  min-height:76px; padding:var(--s-md) var(--pad-x);
}
.mast__nav{ justify-self:center }
.mast > .btn{ justify-self:end }
/* max-width:none defeats the global img rule: this is a fixed-height wordmark
   with a 7:1 ratio, and letting it be clamped by its box distorts it rather
   than scaling it. */
.mark{ flex-shrink:0 }
.mark__logo{ height:38px; width:auto; max-width:none; display:block }
.mark{ display:inline-flex; align-items:center; text-decoration:none }
.mast nav{ display:flex; align-items:center; gap:var(--s-xl) }
/* The page you are on is stated in the accent; the one you are not is held
   back. Both stay links — quiet is not disabled, so the muted one still clears
   contrast and still answers the pointer. */
.mast nav a:not(.btn){
  font:600 14px/1.29 var(--font-text); letter-spacing:var(--track-caption);
  color:var(--ink-muted-48); text-decoration:none; position:relative;
  transition:color var(--dur-1) var(--ease);
}
.mast nav a:not(.btn)::after{
  content:''; position:absolute; left:0; right:0; bottom:-6px; height:1.5px;
  background:var(--c-accent); border-radius:2px;
  transform:scaleX(0); transform-origin:50% 50%;
  transition:transform var(--dur-2) var(--ease-out);
}
.mast nav a:not(.btn):hover{ color:var(--ink) }
.mast nav a:not(.btn):hover::after,
.mast nav a[aria-current]::after{ transform:scaleX(1) }
.mast nav a[aria-current]{ color:var(--c-accent-text) }
.mast .btn{ padding:8px 16px; font:var(--t-button-utility) }

/* On a finger, the masthead links are 18px tall and the button 36 — both under
   the 44px target. The links grow their HIT area rather than their box, through
   an invisible overlay: padding would drag the hover underline away from the
   text it belongs to. */
@media (pointer:coarse){
  .mast nav a:not(.btn){ position:relative }
  .mast nav a:not(.btn)::before{
    content:''; position:absolute; left:-10px; right:-10px; top:50%;
    height:44px; transform:translateY(-50%);
  }
  .mast .btn{ padding-block:12px }
}

/* ---- section rhythm ----------------------------------------------------
   One ladder everywhere, so no section invents its own gaps:
     eyebrow -> heading   12   tight: they are one unit
     heading -> lede      17   a breath, still the same thought
     lede    -> content   48   a real break: the argument stops, evidence starts */
.eyebrow{ margin-block:0 var(--s-sm) }
.h2 + .lede, .h1 + .lede, h2 + .lede,
.display + .lede, .hero-display + .lede{ margin-top:var(--s-md) }
.lede + .grid, .lede + .loop, .lede + .cardset, .lede + .tiers,
.h2 + .grid, .h2 + .loop, .h2 + .cardset, .h2 + .tiers,
h2 + .grid, h2 + .loop, h2 + .cardset, h2 + .tiers{ margin-top:var(--s-xxl) }
.card .pill + *{ margin-top:var(--s-md) }
.card .h3 + *,  .card h3 + *{ margin-top:var(--s-xs) }
.card p + .btn, .card p + .btn--link{ margin-top:var(--s-md) }

/* ---- primary tile ------------------------------------------------------
   Inverting here is what makes a section read as a panel laid over the film
   rather than another white sheet continuing it. Every text role has to be
   restated: .lede and .caption carry dark ink by default and would otherwise
   land at ~1.3:1 on the navy, which is invisible, not subtle. */
.tile--primary{ background:var(--c-accent); color:var(--on-dark) }
.tile--primary .h2, .tile--primary h2,
.tile--primary .h3, .tile--primary h3,
.tile--primary .display{ color:var(--on-dark) }
.tile--primary .lede,
.tile--primary .caption,
.tile--primary .small,
.tile--primary > p, .tile--primary .interlude__in > p{ color:#C6D2EC }
.tile--primary .eyebrow{ color:var(--c-accent-on-dark) }
/* the cards stay white: solid panels reading on top of the tile */
.tile--primary .card{ background:var(--canvas); color:var(--ink) }
.tile--primary .card .h3, .tile--primary .card h3{ color:var(--ink) }
.tile--primary .card .caption,
.tile--primary .card .small,
.tile--primary .card p{ color:var(--ink-muted-80) }
.tile--primary .card__n{ color:var(--c-accent); background:#EBF1FF }
.tile--primary a:not(.btn){ color:var(--c-accent-on-dark) }
.tile--primary .btn{ background:var(--on-dark); color:var(--c-accent) }
.tile--primary .btn:hover{ background:#EDF1FA }

/* ============================ footer ====================================
   Navy, matching the primary tile, so the page closes on the brand rather than
   fading out on white. Columns collapse without a breakpoint. */
.site-foot{
  background:var(--c-accent); color:var(--on-dark);
  padding:var(--s-section) var(--pad-x) var(--s-xl);
}
.site-foot__in{ max-width:var(--maxw); margin-inline:auto }
.site-foot__grid{
  display:grid; grid-template-columns:1.6fr 1fr 1.3fr 1fr;
  gap:clamp(28px,3.4vw,60px); align-items:start;
}
.site-foot__brand .mark__logo{ height:44px; filter:brightness(0) invert(1) }
.site-foot__brand p{
  margin-top:var(--s-lg); max-width:32ch;
  font:var(--t-body); color:#C6D2EC;
}
.site-foot h3{
  font:var(--t-caption-strong); letter-spacing:var(--track-caption);
  color:var(--on-dark); margin:0 0 var(--s-md);
}
.site-foot ul{ list-style:none; margin:0; padding:0; display:grid; gap:var(--s-sm) }
.site-foot li{ font:var(--t-caption); color:#C6D2EC; line-height:1.5 }
.site-foot li b{ display:block; color:var(--on-dark); font-weight:600; margin-bottom:2px }
.site-foot a{
  color:#C6D2EC; text-decoration:none;
  transition:color var(--dur-1) var(--ease);
}
.site-foot a:hover{ color:var(--on-dark) }
.site-foot__legal{
  margin-top:var(--s-section); padding-top:var(--s-lg);
  border-top:1px solid rgba(255,255,255,.16);
  display:flex; flex-wrap:wrap; gap:var(--s-md) var(--s-lg);
  align-items:center; justify-content:space-between;
}
/* 12px on navy needs more than the column-heading tint carries: #8FA6D4 is
   3.89:1 there, which is under the floor for text this small. */
.site-foot__legal p, .site-foot__legal a{ font:var(--t-fine); color:#AFC2E7; margin:0 }
@media (max-width:1000px){
  .site-foot__grid{ grid-template-columns:1fr 1fr }
  .site-foot__brand{ grid-column:1 / -1 }
}
/* A footer link is one 17px line of type with 12px of air under it — fine to
   click, and about a third of what a finger needs. The type does not change;
   the link becomes a block and grows its own padding, and the list gives back
   most of the gap it no longer has to provide. Gated on the pointer rather
   than the width, because it is the input that is coarse, not the screen. */
@media (pointer:coarse){
  .site-foot li a,
  .site-foot__legal a{ display:inline-block; padding-block:11px }
  .site-foot ul{ gap:var(--s-xxs) }
}
@media (max-width:560px){
  .site-foot__grid{ grid-template-columns:1fr }
  /* The wordmark is 7:1, so 44px of height is 307px of width — wider than the
     276px a 320px screen leaves after padding. It used to be clamped by the
     global img{max-width:100%}, which distorted it; now that the mark keeps its
     proportions it has to be scaled instead, or it grows the footer track and
     carries every column in it past the edge of the screen. */
  .site-foot__brand .mark__logo{ height:32px }
}

/* ============================ rise ======================================
   Everything after the film arrives the way the film's second half does: it
   climbs up over what is behind it with a rounded leading edge, instead of
   simply scrolling into place. Three things are needed for that to read as one
   panel covering another rather than as a gap:

   - an opaque surface, so the section genuinely covers what it passes;
   - a rounded top edge with a shadow above it, so you see an edge arriving;
   - a negative top margin equal to the radius, so the next edge tucks under
     the last one instead of leaving a sliver of the page showing through.

   Unlike the pinned Tool panel these are normal-flow elements, so they CAN be
   driven by a view() timeline — a pinned element never travels, which is why
   that panel needs script and this does not. */
.rise{
  position:relative; z-index:1;
  background:var(--canvas);
  border-radius:26px 26px 0 0;
  /* just enough to read as an edge arriving. Anything heavier turns into a
     grey band across the page and the sections stop looking connected. */
  box-shadow:0 -8px 22px -18px rgba(10,16,32,.16);
}
.rise.section--sunken{ background:var(--canvas-parchment) }
.rise.section--hair{ border-top:0 }        /* the edge IS the divider now */
.rise + .rise, .rise + .site-foot{ margin-top:-26px }
/* .rise sets an opaque light surface; the footer keeps its own */
.site-foot.rise{ background:var(--c-accent); border-radius:26px 26px 0 0 }

@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    .rise{
      animation:rise-in linear both;
      animation-timeline:view();
      /* the first sixth of its travel: long enough to read, short enough that
         the section is settled well before you are reading it */
      animation-range:cover 0% cover 15%;
      transform-origin:50% 100%;
      will-change:transform;
    }
    @keyframes rise-in{
      from{ transform:translateY(7vh) scale(.955); border-radius:44px 44px 0 0 }
      to  { transform:none;                        border-radius:26px 26px 0 0 }
    }
  }
}

/* ============================ reveal ====================================
   Motion is an enhancement, never a gate. The default state of every element
   here is VISIBLE; only when JS is alive (html.js) does it start hidden and
   animate in. A dead script, a headless render or a reduced-motion setting
   therefore ships readable content instead of a blank section — which is the
   failure mode this file previously had. */
.reveal > *{ opacity:1; transform:none }
.js .reveal > *{
  opacity:0; transform:translateY(18px);
  transition:opacity var(--dur-4) var(--ease-out), transform var(--dur-4) var(--ease-out);
  transition-delay:calc(var(--i,0) * 70ms);
}
.js .reveal.is-in > *{ opacity:1; transform:none }

/* ---- portal section: argument beside evidence -------------------------
   The picture is not a band above the text, it is the evidence for the claim
   the section makes, so it sits beside the whole argument — headline, lede and
   the detail block together in one column — rather than above a wall of it.

   The media rail is a FIXED-HEIGHT box, and that is what fixes the rhythm.
   These twenty-seven pictures run from 0.64 to 2.54 in aspect, so sizing them
   by width gave every row a different height and the pages read as unaligned;
   one row was 62% of the viewport tall. A constant box with the image
   contained inside it gives every row the same footprint whatever shape the
   picture is, and nothing can tower again. The pictures are cut out, so the
   slack inside the box is invisible — it simply reads as a smaller picture. */
.psec{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.08fr);
  gap:clamp(30px,4vw,72px);
  align-items:start;
}
/* Alternation is written into the markup rather than derived from :nth-child —
   these pages carry a different number of sections each, and a positional rule
   restarts the rhythm every time one is added or removed. */
/* The tracks swap with the content. Reordering alone moved the column into the
   NARROW track on flipped rows, so every other section handed the picture more
   width than the argument it was supporting. */
.psec--flip{ grid-template-columns:minmax(0,1.08fr) minmax(0,1fr) }

/* One row's art is far wider than the rest — 2.54 against a 1.26-1.54 norm — so
   it hits the rail's width limit while it is still only half the box tall, and
   reads as the small one on the page. Extra height cannot help a width-limited
   picture; it needs a wider track. The copy column drops under the 470px
   container breakpoint here, so this row's list stacks into one column — the
   right trade for a section whose evidence is the picture. */
.psec--mediawide{ grid-template-columns:minmax(0,1fr) minmax(0,1.9fr) }
.psec--mediawide.psec--flip{ grid-template-columns:minmax(0,1.9fr) minmax(0,1fr) }
@media (max-width:900px){
  .psec--mediawide, .psec--mediawide.psec--flip{ grid-template-columns:1fr }
}
.psec--flip .psec__col{ order:2 }

/* The column, not the viewport, is what the blocks inside it must respond to:
   .pgrid and .pcase hold three columns and only collapse at a 900px VIEWPORT,
   so in a 670px column at desktop width they stayed three across and crushed
   their cards to ~210px. Container queries put that decision where it belongs. */
.psec__col{ min-width:0; container-type:inline-size }
.psec__col .h2{ max-width:20ch }
.psec__col .lede{ max-width:52ch; margin-top:var(--s-lg) }

.psec__media{
  margin:0; min-width:0;
  height:clamp(380px,55vh,620px);
  display:flex; align-items:center; justify-content:center;
  position:sticky; top:calc(var(--nav-h) + 32px);
}
.psec__media img{
  max-width:100%; max-height:100%; width:auto; height:auto;
  object-fit:contain; display:block;
  filter:drop-shadow(0 22px 34px rgba(10,16,32,.16));
}
.psec__media figcaption{
  margin-top:var(--s-md); text-align:center;
  font:var(--t-fine); color:var(--ink-muted-48);
}

/* The first block after the lede owns the gap, so the column keeps one rhythm
   whichever block a section happens to carry. */
.psec__col > :is(.pvs,.pgrid,.pcols,.pcase,.pflow,.pwire,.pbody){
  margin-top:clamp(30px,3.4vw,48px);
}

/* Every selector here is scoped through .psec__col deliberately. A container
   query adds no specificity of its own, and the block definitions it is meant
   to override live ~300 lines further down the file — unscoped, these lost on
   source order and .pgrid stayed three across at 178px a card. */
@container (max-width:1000px){
  .psec__col .pgrid,
  .psec__col .pcase{ grid-template-columns:repeat(2,minmax(0,1fr)) }
}
@container (max-width:470px){
  .psec__col .pgrid,
  .psec__col .pcase,
  .psec__col .pvs,
  .psec__col .pcols{ grid-template-columns:1fr; gap:var(--s-lg) }
  .psec__col .pflow__row{ grid-template-columns:1fr; gap:var(--s-xs); padding:var(--s-md) 0 }
  .psec__col .pwire__row{ grid-template-columns:1fr; gap:var(--s-xs) }
}

/* ---- entrance ----------------------------------------------------------
   The hidden state is scoped to :not(.is-in) so that arriving is the absence of
   a rule rather than a rule that has to out-rank one. The first version fought
   its own cascade: the flipped variant and the stacked-layout override both
   carry the same specificity as .is-in and sit below it in the file, so a
   flipped row below the breakpoint stayed offset after it had been revealed. */
.js .reveal > .psec{ opacity:1; transform:none; transition:none }

.js .psec.reveal > .psec__media{
  transition:opacity .86s var(--ease-out), transform .86s var(--ease-out);
  transition-delay:140ms;
}
.js .psec.reveal:not(.is-in) > .psec__media{
  opacity:0; transform:translate3d(34px, 24px, 0) scale(.962);
}
.js .psec.psec--flip.reveal:not(.is-in) > .psec__media{
  transform:translate3d(-34px, 24px, 0) scale(.962);
}

@media (max-width:900px){
  .psec{ grid-template-columns:1fr; gap:var(--s-xl) }
  .psec--flip .psec__col{ order:0 }
  /* Sticky is pointless once the rail sits above the text it belonged beside,
     and it would pin the picture over the reading. */
  .psec__media{ position:static; height:auto }
  /* Beside the copy a fixed-height slot is what keeps the column from jumping
     as the picture changes. Stacked under it there is nothing to line up with,
     and these cutouts are wide and shallow — 167 to 252px tall in a 379px slot,
     so every one of them sat on up to 200px of white. The box takes the
     picture's height now, and the cap moves onto the picture itself. */
  /* width:100%, not auto, so the box exists before the bytes do. With auto on
     both axes an unloaded lazy image measures nothing, the auto-height figure
     collapses to zero — and a picture with no height is a picture the loader
     never reaches and the reveal below never fires for. Every one of these
     sources is far wider than a phone column, so max-width was already pinning
     them to 100%; naming it changes no rendered size, it only lets the height
     be computed from the intrinsic ratio while the file is still in flight. */
  .psec__media img{ width:100%; height:auto; max-height:clamp(300px,46vh,460px) }

  /* The picture takes its own cue once it is stacked. Beside the copy the two
     arrive together, so the section's reveal is the right trigger for both.
     Stacked, the picture sits 600 to 900px BELOW the top of the section — the
     section is already revealed by the time the reader is anywhere near it, so
     the entrance had always finished before it could be seen. Keyed off the
     picture's own .is-in, which site.js gives it at this width only. */
  .js .psec.reveal > .psec__media:not(.is-in){
    opacity:0; transform:translate3d(0, 22px, 0) scale(.975);
  }
  .js .psec.reveal > .psec__media.is-in{ opacity:1; transform:none }
}

/* ---- per-letter -------------------------------------------------------- */
[data-letters]{ opacity:1 }
.js [data-letters] .wrd{ display:inline-block }
.js [data-letters] .ltr{
  display:inline-block; white-space:pre;
  opacity:0; transform:translateY(.38em);
  /* No blur. A filter on a letter span is cheap; a filter on three thousand of
     them is not — the page carried 3,790 filtered elements and a single style
     pass cost 76ms against a 16.7ms frame. Opacity and travel read as the same
     move and composite for free. */
  transition:opacity .62s var(--ease-out),
             transform .62s var(--ease-out);
  transition-delay:calc(var(--l,0) * var(--step,22ms));
  /* NO will-change. It promotes every letter to its own compositor layer, and
     features.html splits into 3,789 of them — 3,789 layers, each holding a 5px
     blur. That is what made the page crawl. The transition composites fine
     without the hint. */
}
.js [data-letters].is-in .ltr{ opacity:1; transform:none }

@media (prefers-reduced-motion:reduce){
  .js .reveal > *,
  .js [data-letters] .ltr{
    opacity:1 !important; transform:none !important; transition:none !important;
  }
}

/* ============================ trust & clients ===========================
   Badges and client marks arrive in every shape and weight there is. Two rules
   keep a wall of them from looking like a jumble: give every mark the same
   optical height rather than the same box, and let the grid breathe. */
/* A certification badge is evidence, so it has to be legible — at 76px these
   read as coloured smudges. Each one now sits in its own fixed-height frame:
   the frame equalises five wildly different aspect ratios (Cvent is 2:1, the
   eWizard editor badge is taller than it is wide) while the artwork inside
   is free to be as large as its own shape allows. The source files were also
   cropped to their ink, so none of the height is spent on transparent margin. */
/* The copy is centred above the evidence rather than beside it, which lets the
   badges run the full width and be read at a size worth reading.

   Two grids, not one flowing grid: relying on the column count to break between
   the platform badges and the three Meta ones works at exactly one width and
   interleaves them at every other. Each family gets its own row. */
.trust__head{ text-align:center; max-width:var(--maxw-text); margin-inline:auto }
/* Every section after the film is centred: heading, lede and any note under
   them. The evidence below each one keeps its own alignment — a centred
   heading over a left-aligned table is fine, a left-aligned heading over a
   centred row of logos is not. */
.rise .wrap > .h1,
.rise .wrap > .h2,
.rise .wrap > .lede,
.rise .wrap > .small,
.clients__head{ text-align:center; margin-inline:auto }
.rise .wrap > .h2.measure{ max-width:24ch }
.rise .wrap > .lede{ max-width:62ch }
.rise .wrap > .pills{ justify-content:center }
.clients__head .h2{ max-width:24ch; margin-inline:auto }
.clients__head .lede{ max-width:60ch; margin-inline:auto }
.trust__head .h2{ max-width:22ch; margin-inline:auto }
.trust__head .lede{ max-width:62ch; margin-inline:auto }
.certs-set{ display:grid; gap:var(--s-lg); margin-top:var(--s-section);
            max-width:1180px; margin-inline:auto }
.certs--meta{ grid-template-columns:repeat(3,minmax(0,1fr)) }
.certs{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:var(--s-lg); align-items:stretch;
}
/* the name sits with the badge, not in a list somewhere else that the reader
   has to map back onto seven near-identical seals */
.cert{
  margin:0; display:grid; grid-template-rows:1fr auto; justify-items:center;
  gap:var(--s-md); padding:var(--s-lg);
  min-height:190px; border:1px solid var(--hairline); border-radius:var(--r-lg);
  background:var(--canvas);
  transition:border-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.cert:hover{ border-color:color-mix(in srgb,var(--c-accent) 34%,transparent); transform:translateY(-2px) }
.cert img{ max-height:148px; max-width:100%; width:auto; object-fit:contain; align-self:center }
.cert figcaption{ text-align:center; display:grid; gap:3px; max-width:26ch }
.cert figcaption b{ font:600 13px/1.3 var(--font-text); letter-spacing:var(--track-caption); color:var(--ink) }
.cert figcaption span{ font:400 12px/1.4 var(--font-text); color:var(--ink-muted-48) }
/* the wide marks would otherwise be capped by height and end up half the size */
.cert--wide img{ max-height:98px }
@media (prefers-reduced-motion:reduce){ .cert{ transition:none } .cert:hover{ transform:none } }

/* Client marks stay in their own colours — a pharma wall greyed out reads as a
   list of logos, in colour it reads as a list of companies. The one thing that
   IS equalised is optical height, since these are drawn at wildly different
   densities. */
/* A moving band rather than a static wall. Two things make it work:

   1. The row is duplicated. The track holds two identical copies and travels
      exactly one copy's width, so the moment it would show the seam it is
      already back where it started — the loop has no visible jump and needs no
      JS to reset it.
   2. The edges are masked, not cut. A logo sliding under a hard container edge
      reads as broken; fading it out reads as a band that continues past the
      page. The mask is on the frame, the animation on the track, so the two
      never fight.

   Hovering pauses it, because a mark you are trying to read should hold still.
   The marks stay in their own colours — a pharma wall greyed out reads as a
   list of logos; in colour it reads as a list of companies. */
.marquee{
  position:relative; overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
          mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
}
.marquee__track{
  display:flex; width:max-content;
  animation:marquee-right var(--marquee-dur,46s) linear infinite;
}
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track{ animation-play-state:paused }
.marquee__row{
  display:flex; align-items:center; flex:0 0 auto;
  gap:clamp(44px,5vw,88px); list-style:none; margin:0;
  padding:var(--s-xs) clamp(22px,2.5vw,44px) var(--s-xs) 0;
}
.marquee img{
  max-height:44px; max-width:224px; width:auto; object-fit:contain;
  transition:transform var(--dur-2) var(--ease);
}
.marquee img:hover{ transform:scale(1.06) }
/* travels one full copy, so the second copy lands exactly where the first was */
@keyframes marquee-right{ from{ transform:translate3d(-50%,0,0) } to{ transform:translate3d(0,0,0) } }
@media (prefers-reduced-motion:reduce){
  .marquee{ -webkit-mask-image:none; mask-image:none; overflow-x:auto }
  .marquee__track{ animation:none }
  .marquee img{ transition:none }
}

/* the static wall is kept for anywhere a band would be wrong (print, a short
   list that would loop too obviously) */
.logowall{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:clamp(30px,3.6vw,64px) clamp(34px,4.4vw,74px);
}
.logowall img{
  max-height:44px; max-width:min(100%,224px); width:auto; object-fit:contain;
  transition:transform var(--dur-2) var(--ease);
}
.logowall img:hover{ transform:scale(1.05) }
@media (prefers-reduced-motion:reduce){ .logowall img{ transition:none } .logowall img:hover{ transform:none } }

/* ============================ portal pages ==============================
   Three sections a channel page needs that the channels overview does not:
   the case for the channel, one worked example, and the questions a buyer
   actually asks. Each is a plain reading layout — the mockups above them are
   doing the showing, so these are here to be read. */
.pcase{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(24px,3vw,48px); margin-top:var(--s-xxl);
}
.pcase h3{ font:var(--t-body-strong); letter-spacing:var(--track-body); margin:0 0 var(--s-xs) }
.pcase p{ font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80); margin:0 }
@media (max-width:900px){ .pcase{ grid-template-columns:1fr; gap:var(--s-lg) } }

/* the worked example. The time marker sits in its own column so the sequence
   reads down the page, and what is recorded at each step is set apart from
   what happened — they are two different claims. */
.pflow{ margin-top:var(--s-xxl) }
.pflow__row{
  display:grid; grid-template-columns:minmax(96px,120px) minmax(0,1.15fr) minmax(0,.85fr);
  gap:clamp(18px,2.4vw,40px); padding:var(--s-lg) 0; border-top:1px solid var(--hairline);
  align-items:start;
}
.pflow__row:last-child{ border-bottom:1px solid var(--hairline) }
.pflow__row b{ font:var(--t-caption-strong); letter-spacing:.04em; color:var(--c-accent-text) }
.pflow__row p{ font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink); margin:0 }
.pflow__row span{ display:block; font:var(--t-caption); letter-spacing:var(--track-caption);
                  color:var(--ink-muted-80) }
.pflow__row span i{
  display:block; font:var(--t-fine); font-style:normal; font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-muted-48);
  margin-bottom:4px;
}
@media (max-width:820px){
  .pflow__row{ grid-template-columns:1fr; gap:var(--s-xs); padding:var(--s-md) 0 }
  .pflow__row span{ padding-top:var(--s-xs) }
}

.pqa{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(26px,3.2vw,56px); margin-top:var(--s-xxl);
}
.pqa h3{ font:var(--t-body-strong); letter-spacing:var(--track-body); margin:0 0 var(--s-xs);
         max-width:34ch }
.pqa p{ font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80);
        margin:0; max-width:52ch }
@media (max-width:820px){ .pqa{ grid-template-columns:1fr; gap:var(--s-lg) } }

/* The way into a channel's own page belongs to the last feature, not to a
   strip of its own underneath. On its own it read as a stray button after the
   content had ended; sitting under the final feature's copy it reads as the
   next thing to do once you have finished reading. */
.step__more{ margin-top:var(--s-lg); align-self:start }


/* ---- a channel page opens on itself ---------------------------------
   These are standalone pages, not a chapter lifted out of the overview, so
   each one opens on its own full-bleed head rather than on a section heading
   that only makes sense in a longer sequence. */
.phero{
  background:
    radial-gradient(115% 85% at 78% 12%,
      color-mix(in srgb, var(--c-accent) 74%, #fff) 0%, transparent 62%),
    linear-gradient(158deg,
      color-mix(in srgb, var(--c-accent) 88%, #fff) 0%,
      var(--c-accent) 44%,
      color-mix(in srgb, var(--c-accent) 80%, #000) 100%);
  color:var(--on-dark);
  padding:clamp(140px,19vh,206px) var(--pad-x) clamp(64px,9vh,104px);
}
.phero__in{
  max-width:var(--maxw); margin-inline:auto;
  display:grid; grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
  gap:clamp(26px,3.6vw,60px); align-items:center;
}
/* No ch cap on the hero headline. 15ch computes to 400px while "Email carries
   the depth." needs 465px and its column offers 565px, so the cap — not the
   space — was breaking every one of these headlines onto two lines. The column
   is already the measure: .phero__in is capped at --maxw and the copy track is
   .92fr of it, so the line can never exceed ~565px however wide the screen is.
   Headlines that genuinely outrun that still wrap, and text-wrap:balance keeps
   the two lines even rather than leaving one word stranded. */
.phero__copy .h1{ color:var(--on-dark) }
.phero__copy .lede{ color:#C6D2EC; max-width:46ch; margin-top:var(--s-lg) }
.phero__copy .eyebrow{ color:var(--c-accent-on-dark) }
.phero__copy .btn{ margin-top:var(--s-xl) }
.phero .showcase{ margin:0 }
.phero .showcase img{
  border-radius:var(--r-lg);
  box-shadow:0 40px 90px -40px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.3);
}
/* Email has no showcase render, so its hero was a narrow column of copy with
   half the bay empty beside it. With nothing to sit next to, the copy takes
   the width and the measure opens up instead of leaving a hole. */
.phero__in:not(:has(.showcase)){ grid-template-columns:minmax(0,1fr) }
.phero__in:not(:has(.showcase)) .h1{ max-width:26ch }
.phero__in:not(:has(.showcase)) .lede{ max-width:62ch }

@media (max-width:900px){
  .phero__in{ grid-template-columns:1fr; gap:var(--s-lg) }
}

/* The feature heading types itself out on every swap, so its letters lean a
   little further than the one-shot headings do — they are read as an arrival,
   not as a page settling. */
.js .step h3 .ltr{ transition-duration:.5s }


/* six items, so the bar may wrap rather than push the button off the end */
.mast__nav{ flex-wrap:wrap; row-gap:2px }
/* The button exists only for the panel, so it is absent wherever the panel is.
   Declared before the query and re-declared there through `.mast` — at equal
   specificity source order decides, and a base rule sitting after the query
   would have hidden the button at every width. */
.mast__menu,
.mast__nav-cta{ display:none }

/* `.mast .mast__nav`, not `.mast__nav`: the base rule is `.mast nav` at (0,1,1)
   and a lone class is (0,1,0), so the tighter gap never applied and the sixth
   link wrapped to its own row at 1024, standing the bar up to 103px. */
@media (max-width:1180px){
  .mast .mast__nav{ gap:var(--s-lg) }
  .mast .mast__nav a{ font-size:13px }
}

/* ---- masthead below the desktop nav's real minimum ----------------------
   The breakpoint is measured, not chosen: wordmark 265 + six links and their
   gaps ~500 + the CTA 130 + page padding ~88 needs about 1020px. The old
   860px cut left a band where all three fought for the same row.

   The hide it used to do was dead anyway — `.mast__nav` is (0,1,0) and lost to
   `.mast nav` at (0,1,1), so the desktop nav rendered at every width and simply
   wrapped, standing the fixed header up to 203px on a phone. Every selector
   below therefore carries `.mast` to clear that bar, and the nav becomes a real
   panel rather than disappearing: hiding it would leave a phone with no way to
   reach five of the six pages. */
@media (max-width:1023px){
  .mast{ grid-template-columns:auto 1fr auto auto; column-gap:var(--s-sm) }
  .mast .mast__menu{
    display:inline-flex; align-items:center; justify-content:center;
    grid-column:3; justify-self:end;
    width:46px; height:46px; margin-inline:calc(var(--s-xs) * -1) 0;
    padding:0; border:0; background:none; color:inherit; cursor:pointer;
    border-radius:var(--r-sm);
  }
  .mast__menu svg{ width:24px; height:24px; display:block }
  .mast__menu path{
    stroke:currentColor; stroke-width:1.6; stroke-linecap:round; fill:none;
    transform-origin:12px 12px;
    transition:transform var(--dur-2) var(--ease-out), opacity var(--dur-1) linear;
  }
  .mast.is-open .mast__menu-top{ transform:translateY(5px) rotate(45deg) }
  .mast.is-open .mast__menu-mid{ opacity:0 }
  .mast.is-open .mast__menu-bot{ transform:translateY(-5px) rotate(-45deg) }

  .mast > .btn{ grid-column:4 }

  .mast .mast__nav{
    grid-column:1 / -1;
    position:absolute; inset:100% 0 auto 0;
    display:grid; gap:0; justify-self:stretch;
    padding:var(--s-xs) var(--pad-x) var(--s-md);
    background:var(--canvas);
    border-top:1px solid var(--divider-soft);
    box-shadow:0 26px 44px -30px rgba(10,16,32,.45);
    /* closed: taken out of the tab order as well as out of sight */
    visibility:hidden; opacity:0; transform:translateY(-10px);
    transition:opacity var(--dur-2) var(--ease-out),
               transform var(--dur-2) var(--ease-out),
               visibility 0s var(--dur-2);
  }
  .mast.is-open .mast__nav{
    visibility:visible; opacity:1; transform:none; transition-delay:0s;
  }
  /* 52px rows: the desktop links were 18px tall, well under the 44px a finger
     needs, and a phone is the one place they are only ever touched. */
  .mast .mast__nav a:not(.btn){
    display:flex; align-items:center; min-height:52px;
    font:600 17px/1.3 var(--font-text); letter-spacing:var(--track-body);
    border-bottom:1px solid var(--divider-soft);
  }
  .mast .mast__nav a:not(.btn):last-child{ border-bottom:0 }
  /* the desktop underline is a hover affordance; a touch list does not need it */
  .mast .mast__nav a:not(.btn)::after{ display:none }

  /* The CTA lives in the panel, not the bar, once the bar cannot hold it.
     Measured at 375: wordmark 265 + button 46 + CTA 130 plus padding needs
     ~495px of a 331px row, so something had to leave — and a CTA squeezed to a
     70px pill would have been worth less than this full-width one. */
  .mast .mast__nav-cta{
    display:flex; align-items:center; justify-content:center;
    margin-top:var(--s-md); min-height:52px; width:100%;
  }
}

@media (min-width:600px) and (max-width:1023px){
  .mast .mast__nav-cta{ display:none }   /* the bar still has room for it here */
}
@media (max-width:599px){
  .mast > .btn{ display:none }
  /* 38px of wordmark is 265px of width at a 7:1 ratio, and with the button and
     the page padding that overruns a 320px row. Scaling the mark is the right
     give here — it stays legible and stays in proportion, which is what the
     previous max-width clamp destroyed. */
  .mark__logo{ height:30px }
  .mast{ grid-template-columns:auto 1fr auto }
  .mast .mast__menu{ grid-column:3 }
}

@media (max-width:1023px) and (prefers-reduced-motion:reduce){
  .mast .mast__nav{ transition:none }
  .mast__menu path{ transition:none }
}


/* ============================ behind a head ============================
   Three soft masses of light drifting slowly toward the same point and easing
   apart again. It carries the page's argument — separate things gathering —
   without drawing a diagram of it, which is what the previous version did and
   why it read as stray hairlines over the gradient.

   Everything here is transform and opacity on three elements, so it composites
   and costs nothing. Blur lives on the element, not on a backdrop filter: a
   backdrop filter over a full-bleed gradient repaints the whole head. */
.phero, .bk-head, .head{ position:relative; overflow:hidden }
.phero__in, .bk-head__in, .head__in{ position:relative; z-index:1 }

.headfx{
  position:absolute; inset:-18%; z-index:0; pointer-events:none;
  contain:strict;
}
.headfx i{
  position:absolute; display:block; border-radius:50%;
  filter:blur(70px); opacity:.42; will-change:transform;
  animation:head-drift 26s cubic-bezier(.45,0,.55,1) infinite alternate;
}
.headfx i:nth-child(1){
  width:46%; aspect-ratio:1; left:6%; top:2%;
  background:radial-gradient(circle, color-mix(in srgb,#fff 62%,transparent) 0%, transparent 68%);
  animation-duration:24s;
}
.headfx i:nth-child(2){
  width:38%; aspect-ratio:1; left:44%; top:44%;
  background:radial-gradient(circle, color-mix(in srgb,#7FA8FF 70%,transparent) 0%, transparent 66%);
  animation-duration:31s; animation-delay:-9s; opacity:.34;
}
.headfx i:nth-child(3){
  width:30%; aspect-ratio:1; left:66%; top:8%;
  background:radial-gradient(circle, color-mix(in srgb,#fff 48%,transparent) 0%, transparent 70%);
  animation-duration:38s; animation-delay:-17s; opacity:.3;
}
/* toward the same point, then away — never returning to the same place twice
   in a row, which is what makes a loop of this length read as drift */
@keyframes head-drift{
  0%   { transform:translate3d(0,0,0) scale(1) }
  50%  { transform:translate3d(14%, 8%, 0) scale(1.14) }
  100% { transform:translate3d(-8%, 14%, 0) scale(.94) }
}
@media (prefers-reduced-motion:reduce){
  .headfx i{ animation:none }
}

/* ---- the four section types the channel pages needed --------------------
   Built from the same tokens as everything else: the accent, the hairline, the
   type scale. Nothing here introduces a colour, a radius or a shadow the rest
   of the site does not already use. */

/* Prose sat as a 734px column in a 1265px bay — too wide to read comfortably
   at 86 characters, and too narrow to look deliberate beside the full-width
   sections either side of it. Set beside its own heading it does both: the
   claim holds the left, the argument runs at a proper measure on the right. */
.psplit{
  display:grid; grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(28px,4vw,84px); align-items:start;
}
.psplit .phead .h2{ max-width:16ch }
.psplit .phead .lede{ max-width:34ch }
/* 62ch measured at 79 characters a line — ch is the width of a zero, which in
   a proportional face is wider than the average letter, so the unit overstates
   the measure by about a sixth. 55ch lands at roughly 70. */
.pbody{ max-width:55ch; margin-top:0 }
@media (max-width:900px){
  .psplit{ grid-template-columns:1fr; gap:var(--s-lg) }
  .psplit .phead .h2, .psplit .phead .lede{ max-width:none }
}
.pbody .body{ font:var(--t-body); letter-spacing:var(--track-body);
              color:var(--ink-muted-80); margin:0 0 var(--s-md) }
.pbody .body:last-child{ margin-bottom:0 }

/* a capability set. Six of them, so three across rather than the three-up
   grid the argument sections use. */
.pgrid{
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(22px,2.6vw,40px); margin-top:var(--s-xxl);
}
.pgrid h3{ font:var(--t-body-strong); letter-spacing:var(--track-body); margin:0 0 var(--s-xs) }
.pgrid p{ font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80); margin:0 }
@media (max-width:900px){ .pgrid{ grid-template-columns:1fr; gap:var(--s-lg) } }

/* Two columns of equal weight. Deliberately NOT a winner-and-loser table:
   both sides carry the same rule, the same type and the same width, because
   the argument is a division of labour rather than a comparison. */
.pvs{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(24px,3vw,56px); margin-top:var(--s-xxl);
}
.pvs__col b{
  display:block; padding-bottom:var(--s-sm); margin-bottom:var(--s-md);
  border-bottom:2px solid var(--c-accent);
  font:var(--t-caption-strong); letter-spacing:.06em; text-transform:uppercase;
  color:var(--c-accent-text);
}
.pvs__col ul{ list-style:none; margin:0; padding:0; display:grid; gap:var(--s-md) }
.pvs__col li{
  font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80);
  padding-left:var(--s-md); position:relative;
}
.pvs__col li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:5px; height:5px; border-radius:50%; background:var(--c-accent);
}
@media (max-width:820px){ .pvs{ grid-template-columns:1fr; gap:var(--s-xl) } }

/* what passes between this channel and each of the others */
/* two reported columns. Its own name rather than .cols: .cols belongs to the
   channels page, and stripping that class off these pages by name took this
   section with it the first time. */
.pcols{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(26px,3.4vw,64px); margin-top:var(--s-xxl);
}
.pcols h3{
  font:var(--t-caption-strong); letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-muted-48); margin:0 0 var(--s-md);
  padding-bottom:var(--s-sm); border-bottom:1px solid var(--hairline);
}
.pcols ul{ list-style:none; margin:0; padding:0; display:grid; gap:var(--s-sm) }
.pcols li{
  font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80);
  padding-left:var(--s-md); position:relative;
}
.pcols li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:5px; height:5px; border-radius:50%; background:var(--c-accent);
}
@media (max-width:820px){ .pcols{ grid-template-columns:1fr; gap:var(--s-xl) } }

.pwire{ margin-top:var(--s-xxl) }
.pwire__row{
  display:grid; grid-template-columns:minmax(140px,220px) minmax(0,1fr);
  gap:clamp(18px,2.4vw,44px); align-items:start;
  padding:var(--s-lg) 0; border-top:1px solid var(--hairline);
}
.pwire__row:last-child{ border-bottom:1px solid var(--hairline) }
.pwire__row b{ font:var(--t-body-strong); letter-spacing:var(--track-body); color:var(--ink) }
.pwire__row p{ font:var(--t-body); letter-spacing:var(--track-body);
               color:var(--ink-muted-80); margin:0; max-width:55ch }
@media (max-width:760px){
  .pwire__row{ grid-template-columns:1fr; gap:var(--s-xs); padding:var(--s-md) 0 }
}

/* Questions. <details> rather than a scripted accordion: it opens without
   JavaScript, it is keyboard-operable for free, and the answer is in the DOM
   whether or not it is open — which matters for anyone reading the page with
   assistive tech, and for anything indexing it. */
/* Two columns across the full bay. A grid and not CSS columns: multi-column
   reflows its content across the column boundary when a height changes, so
   opening one question made items jump from one side to the other — measured at
   five moved. In a grid each item keeps its cell; opening one only pushes what
   is beneath it. */
/* One column, every question the full width of the wrap. Two columns put the
   answers on different baselines and made opening one shove its neighbour
   down; a single stack reads top-to-bottom the way a question list should. */
.pfaq{ margin-top:var(--s-xl); max-width:none; display:block }
.pfaq__q{ border-bottom:1px solid var(--hairline) }
.pfaq__q summary{
  cursor:pointer; list-style:none; padding:var(--s-lg) var(--s-xl) var(--s-lg) 0;
  font:var(--t-body-strong); letter-spacing:var(--track-body); color:var(--ink);
  position:relative; transition:color var(--dur-2) var(--ease);
}
.pfaq__q summary::-webkit-details-marker{ display:none }
.pfaq__q summary:hover{ color:var(--c-accent-text) }
.pfaq__q summary:focus-visible{ outline:2px solid var(--c-accent); outline-offset:3px }
.pfaq__q summary::after{
  content:''; position:absolute; right:4px; top:50%; width:11px; height:11px;
  margin-top:-6px;
  border-right:2px solid var(--ink-muted-48); border-bottom:2px solid var(--ink-muted-48);
  transform:rotate(45deg) translateY(-2px);
  transition:transform var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.pfaq__q[open] summary::after{
  transform:rotate(225deg) translateY(-2px);
  border-color:var(--c-accent);
}
.pfaq__q p{
  margin:0 0 var(--s-lg); max-width:none;
  font:var(--t-body); letter-spacing:var(--track-body); color:var(--ink-muted-80);
}
@media (prefers-reduced-motion:reduce){ .pfaq__q summary::after{ transition:none } }

}
