/*
 * Pratimāna — shared design system.
 *
 * One stylesheet, byte-identical in pratimana-website, pratimana-consulting,
 * pratimana-security and pratimana-matrix. Matrix is the reference
 * implementation: every rule here was lifted from it so the four properties
 * read as one company rather than four separate builds.
 *
 * What lives here is the chrome every property shares — tokens, type,
 * buttons, the masthead, the hero and stats strip, section rhythm, the
 * console panel pattern, the CTA band, the footer, and both themes.
 * What stays in each page's own <style> is that property's editorial
 * layout: matrix keeps its program rows and payout table, consulting its
 * practice-area grid, security its service cards.
 *
 * Two rules of the road when editing:
 *   1. Change it in matrix first, then copy the file across. Divergence
 *      here is the thing this file exists to prevent.
 *   2. Nothing site-specific goes in. If only one property uses a rule,
 *      it belongs in that property's page, not in the kit.
 */

/* ============================================================
   1. TOKENS
   ============================================================ */
:root{
  /* Pure black ground, pure white type. Everything else is derived from
     those two anchors: surfaces lift off the ground by a few points of
     luminance rather than by hue, so nothing tints the black, and every
     muted tone is an alpha of the type colour rather than a grey picked
     by hand. That is what keeps the two themes symmetrical — swapping
     --ink-rgb and --ground-rgb inverts the entire system correctly. */
  --void:#000000;
  --panel:#0A0A0B;
  --panel-raised:#131316;
  --ink-rgb:255,255,255;
  --ground-rgb:0,0,0;
  --text:#FFFFFF;
  --text-dim:rgba(255,255,255,.72);
  --text-faint:rgba(255,255,255,.58);
  --hairline:rgba(255,255,255,.14);
  --hairline-soft:rgba(255,255,255,.07);
  --blue:#4ca2f3;
  --blue-dim:#3d5eef;

  /* Semantic colour is theme-aware, and has to be. Anchoring the page at
     pure #000 and pure #FFF puts the two grounds at opposite extremes, so
     no single mid-tone hue clears 4.5:1 against both — #62d66d reads at
     11.4:1 on black and 1.9:1 on white. Each meaning therefore carries a
     bright variant for the dark ground and a deep one for the light.
     The nine-stop brand spectrum above is untouched: it paints the logo
     and the gradient headlines, which are decorative, not informational. */
  --accent:#55c8b5;
  --ok:#7fcb8a;
  --warn:#fbc043;
  --danger:#e85862;
  --sev-high:#f18342;
  /* White type sits on this, so its lightest end must clear 4.5:1 against
     white. The old start (#55c8b5) gave 2.04:1 — the label on the primary
     gradient button was effectively unreadable. */
  --grad-btn:linear-gradient(90deg,#1a7266,#3d5eef);
  --g1:#e85862; --g2:#f18342; --g3:#fbc043; --g4:#ccda3f; --g5:#62d66d;
  --g6:#55c8b5; --g7:#4ca2f3; --g8:#3d5eef; --g9:#6a26f6;
  --header-bg: rgba(0,0,0,.72);
  --shadow-soft: rgba(0,0,0,.65);

  /* Pacing. A single cinematic curve — slow out, long settle — used for
     everything that moves, so the whole surface decelerates the same way
     instead of each component easing on its own terms. */
  --ease-cinematic:cubic-bezier(0.16, 1, 0.3, 1);
  --ease-gentle:cubic-bezier(0.25, 0.1, 0.25, 1);
  --transition-fast:300ms var(--ease-cinematic);
  --transition-base:600ms var(--ease-cinematic);
  --transition-slow:1200ms var(--ease-cinematic);

  /* Ambient depth, stacked in layers rather than one hard drop. On black
     a cast shadow is invisible, so dark mode carries its elevation as a
     hairline lift on the top edge and a deep pool beneath. */
  --shadow-sm:
    0 1px 2px rgba(0,0,0,.5),
    0 4px 12px rgba(0,0,0,.35),
    inset 0 1px 0 rgba(255,255,255,.045);
  --shadow-lg:
    0 2px 4px rgba(0,0,0,.4),
    0 12px 32px rgba(0,0,0,.45),
    0 32px 72px rgba(0,0,0,.55),
    inset 0 1px 0 rgba(255,255,255,.07);

  /* Section rhythm. One scale so vertical breathing is consistent
     everywhere and scales with the viewport rather than snapping at
     breakpoints. */
  --space-section:clamp(6rem, 11vw, 13rem);
  --measure:62ch;
  /* .brand-sub's tracking. The animated slot has to add this to its own
     width, because an inline-grid does not receive letter-spacing the way
     a real character does. Shared so the two can never disagree. */
  --brand-track:.12em;

  /* Brand-mark geometry, measured at 8x device scale rather than guessed.
     At font-size:1em this mono face carries .724em of cap ink while the
     emoji carry 1.114em and 1.125em — they overflow their own em box — so an
     emoji has to be set at .67em for its ink to stand exactly as tall as the
     MATRI beside it. The old .85em stood 28% taller than the letters. */
  --mx-cap:.724em;
  --mx-emoji:.67em;
  --mx-type-dur:1s;
  /* The cell and field widths are deliberately NOT tokens here. A custom
     property has its var()s substituted where it is declared, so a token
     defined on :root would freeze --brand-track at the root's value and a
     context that tightens the tracking — the dashboard sidebar does — would
     get a field wider than the text it is meant to clip. Written inline
     below, they resolve against whichever element is using them. */
  /* Every control in the masthead resolves to this height. Buttons are sized
     by padding and the toggles by fixed dimensions, so left alone they settle
     at 44.3px and 38px respectively and the bar reads as ragged. Body CTAs
     keep their own, more generous, padding-derived height. */
  --control-h:40px;

  /* "Noto Sans" sits in the stack ahead of the generic fallbacks so the
     Latin/Cyrillic subset i18n.js preloads is what renders for Spanish,
     German, French, Italian, Russian and Portuguese. The per-script Noto
     faces (Devanagari, Tamil, Arabic, …) are appended by i18n.js only
     once such a language is actually selected. */
  --font-display: -apple-system, "SF Pro Display", "SF Pro Text", "SF Pro", "Noto Sans", system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Consolas, monospace;
}

[data-theme="light"]{
  /* The exact inverse. Cards sit at pure white on a pure white page, so
     they are separated by shadow and hairline rather than by fill — which
     is the whole reason the ambient shadow scale exists. */
  --void:#FFFFFF;
  --panel:#FFFFFF;
  --panel-raised:#F7F7F8;
  --ink-rgb:0,0,0;
  --ground-rgb:255,255,255;
  --text:#000000;
  --text-dim:rgba(0,0,0,.64);
  --text-faint:rgba(0,0,0,.58);
  --hairline:rgba(0,0,0,.12);
  --hairline-soft:rgba(0,0,0,.06);
  --header-bg: rgba(255,255,255,.72);
  --shadow-soft: rgba(0,0,0,.10);
  --blue:#1668c7;
  --accent:#1a7266;
  --ok:#197a30;
  --warn:#8a6205;
  --danger:#c0212d;
  --sev-high:#b35309;
  --shadow-sm:
    0 1px 2px rgba(0,0,0,.02),
    0 4px 10px rgba(0,0,0,.03);
  --shadow-lg:
    0 2px 4px rgba(0,0,0,.02),
    0 12px 28px rgba(0,0,0,.04),
    0 24px 60px rgba(0,0,0,.05);
}

/* ============================================================
   2. RESET / BASE
   ============================================================ */
*{ box-sizing:border-box; }
/* Suppresses the colour transition on the very first paint, so a visitor
   whose saved theme is light doesn't watch the page fade in from dark. */
html[data-theme]{ transition:none; }
html{ scroll-behavior:smooth; }
body{
  margin:0; background:var(--void); color:var(--text);
  font-family:var(--font-display);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  font-feature-settings:"kern","liga","clig","calt";
  line-height:1.5;
  transition:background-color var(--transition-fast), color var(--transition-fast);
  /* German compounds ("Ticketwarteschlange") and several Indic
     translations run long enough to overflow a 360px viewport as an
     unbreakable string. */
  overflow-wrap:break-word;
}
a{ color:inherit; text-decoration:none; }
img,svg{ display:block; max-width:100%; }
::selection{ background:rgba(76,162,243,.3); }
:focus-visible{ outline:2px solid var(--blue); outline-offset:2px; }

/* ============================================================
   3. LAYOUT PRIMITIVES + TYPE
   ============================================================ */
.wrap{ max-width:1180px; margin:0 auto; padding:0 32px; position:relative; z-index:2; }
h1,h2,h3{ font-family:var(--font-display); font-weight:800; letter-spacing:-0.02em; margin:0;
  /* hyphens:auto reads the <html lang> that i18n.js sets on each change. */
  hyphens:auto; }
.eyebrow{
  font-family:var(--font-mono); font-size:11.5px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--text-faint); display:flex; align-items:center; gap:10px;
}
.eyebrow::before{ content:""; width:16px; height:1px; background:var(--g6); display:inline-block; }
.grad-text{
  background:linear-gradient(90deg,var(--g1),var(--g2),var(--g3),var(--g4),var(--g5),var(--g6),var(--g7),var(--g8),var(--g9));
  background-size:220% auto;
  -webkit-background-clip:text; background-clip:text; color:transparent;
  animation:shimmer 7s ease-in-out infinite;
}
@keyframes shimmer{
  0%,100%{ background-position:0% 50%; }
  50%{ background-position:100% 50%; }
}
/* The mono eyebrow wraps badly on a phone, leaving the leading rule
   floating beside a two-line block. */
@media (max-width:560px){
  .eyebrow{ font-size:10.5px; letter-spacing:.1em; gap:8px; }
  .eyebrow::before{ width:12px; }
}

/* ============================================================
   4. BUTTONS
   ============================================================ */
.btn{
  display:inline-flex; align-items:center; gap:8px; padding:13px 22px; border-radius:8px;
  font-weight:600; font-size:14.5px; cursor:pointer; border:1px solid transparent;
  transition:transform var(--transition-fast), background-color var(--transition-fast),
             border-color var(--transition-fast), box-shadow var(--transition-fast), filter var(--transition-fast);
}
.btn-primary{ background:var(--text); color:var(--void); }
.btn-primary:hover{ transform:translateY(-1px); box-shadow:var(--shadow-lg); }
.btn-ghost{ border-color:var(--hairline); color:var(--text); }
.btn-ghost:hover{ border-color:var(--text-dim); }
.btn-grad{ background:var(--grad-btn); color:#fff; }
.btn-grad:hover{ filter:brightness(1.08); transform:translateY(-1px); }
.btn:focus-visible{ outline:2px solid var(--blue); outline-offset:2px; }

/* ============================================================
   5. MASTHEAD
   Measured, not guessed. The width at which the bar stops fitting moves
   with the language — 999px in Chinese, 1173px in English, 1317px in
   French — so the collapse is driven by the .nav-collapsed class that
   nav.js sets from the bar's real content width. A width query cannot do
   this job, and would in fact zero out the measurement by hiding
   .nav-links before nav.js could read it.
   ============================================================ */
header{
  position:sticky; top:0; z-index:50;
  backdrop-filter:blur(14px) saturate(150%);
  -webkit-backdrop-filter:blur(14px) saturate(150%);
  background:var(--header-bg); border-bottom:1px solid var(--hairline);
  box-shadow:0 1px 0 rgba(255,255,255,.04) inset, 0 8px 24px -16px var(--shadow-soft);
  transition:box-shadow .3s ease, background .3s ease;
}
header.scrolled{ box-shadow:0 8px 24px rgba(0,0,0,.3); }
/* The masthead gets a wider container than the 1180px content column.
   Pinned at 1180px, seven languages — French, Russian, German, Tamil,
   Telugu, Portuguese, Spanish — dropped to the menu button even on a
   1920px display. */
nav.wrap{ display:flex; align-items:center; justify-content:flex-start; max-width:1360px; gap:26px; height:80px; }
/* Brand must never be squeezed by the actions on the right — before
   this, a narrow viewport collapsed the logo to zero width. */
nav.wrap > :first-child{ flex-shrink:0; }
/* The wordmark takes the page's text colour — white on the black ground,
   black on the white one — rather than the nine-stop brand spectrum it used
   to carry. currentColor is set on the <svg> and inherited by the <text>,
   so a single declaration covers the masthead and the footer alike.
   The spectrum still lives in .grad-text headlines and the hero gauge. */
.logo-mark{ height:26px; flex-shrink:0; color:var(--text); }
.logo-mark text{ fill:currentColor; }
.brand-sub{ font-family:var(--font-mono); font-size:11px; color:var(--text-faint); border-left:1px solid var(--hairline); padding-left:12px; letter-spacing:var(--brand-track); }
.nav-links{ display:flex; gap:21px; font-size:13.5px; font-weight:500; color:var(--text-dim); }
/* Nav labels must sit on one line — "For Hackers" and "Live Console"
   were wrapping mid-phrase and made the bar look ragged. */
.nav-links a{ white-space:nowrap; position:relative; padding-bottom:4px; transition:color .18s ease; }
.nav-links a:hover{ color:var(--text); }
.nav-links a::after{
  content:""; position:absolute; left:0; right:100%; bottom:0; height:2px;
  background:var(--blue);
  transition:right .2s cubic-bezier(.16,1,.3,1);
}
.nav-links a:hover::after{ right:0; }
.nav-actions{ display:flex; align-items:center; gap:10px; margin-left:auto; }
.nav-actions .btn{ padding:11px 15px; font-size:13.5px; }
header.nav-collapsed .nav-links{ display:none; }
header.nav-collapsed nav.wrap{ height:64px; gap:0; }
header.nav-collapsed .nav-actions{ gap:10px; }
header.nav-collapsed .nav-actions .btn{ display:none; }

/* One height for everything in the bar, and one vertical rhythm with it. */
header .btn,
header .theme-toggle,
header .lang-toggle,
header .acct-btn{
  height:var(--control-h);
  padding-top:0; padding-bottom:0;
  display:inline-flex; align-items:center; justify-content:center;
}
header .theme-toggle{ width:var(--control-h); }
@media (max-width:620px){
  .wrap{ padding:0 20px; }
  .logo-mark{ height:22px; }
}
@media (max-width:420px){
  .brand-sub{ display:none; }
  .nav-actions{ gap:6px; }
}

/* ============================================================
   6. HERO
   ============================================================ */
.hero{ position:relative; padding:96px 0 60px; overflow:hidden; }
.hero-glow{
  position:absolute; top:-260px; left:50%; width:900px; height:640px; transform:translateX(-50%);
  background:radial-gradient(ellipse at center, rgba(76,162,243,.18), rgba(76,162,243,.05) 42%, transparent 72%);
  filter:blur(10px); pointer-events:none; z-index:0; opacity:0;
  animation:glow-in 2.4s cubic-bezier(.16,1,.3,1) .2s forwards;
  transition:transform 1.2s cubic-bezier(.16,1,.3,1);
}
@keyframes glow-in{ from{ opacity:0; transform:translateX(-50%) scale(.85); } to{ opacity:1; transform:translateX(-50%) scale(1); } }
/* Centring is opt-in, not the default. pratimana.com, consulting and security
   all run a two-column hero — copy left, live gauge right — and their own
   `.hero-grid` rule (0,1,0) could never outrank a blanket `.hero .wrap`
   (0,2,0), so the kit was centring their body copy and squeezing a 1180px
   layout into 820px. Matrix's single-column hero opts in with `.center`, the
   same way `.section-head.center` already works. */
.hero .wrap{ z-index:2; }
.hero .wrap.center{ max-width:820px; text-align:center; }
.hero h1{ font-size:56px; line-height:1.04; margin:22px 0 22px; }
@media (max-width:600px){ .hero h1{ font-size:38px; } }
.hero p.lede{ font-size:17.5px; color:var(--text-dim); max-width:600px; margin:0 auto 32px; }
.hero-actions{ display:flex; gap:14px; flex-wrap:wrap; justify-content:center; }

/* ============================================================
   7. STATS STRIP
   ============================================================ */
.stats{ border-top:1px solid var(--hairline-soft); border-bottom:1px solid var(--hairline-soft); padding:34px 0; }
.stats .wrap{ display:grid; grid-template-columns:repeat(4,1fr); gap:20px; }
@media (max-width:760px){ .stats .wrap{ grid-template-columns:1fr 1fr; } }
.stat{ text-align:center; transition:transform .2s ease; cursor:default; }
.stat .num{ font-family:var(--font-mono); font-size:26px; font-weight:600; }
.stat .lbl{ font-family:var(--font-mono); font-size:11.5px; color:var(--text-faint); margin-top:4px; text-transform:uppercase; letter-spacing:.06em; }
.stat:hover{ transform:translateY(-3px); }
.stat:hover .num{
  background:linear-gradient(90deg,var(--g1),var(--g2),var(--g3),var(--g4),var(--g5),var(--g6),var(--g7),var(--g8),var(--g9));
  background-size:220% auto; -webkit-background-clip:text; background-clip:text; color:transparent;
  animation:shimmer 3s ease-in-out infinite;
}

/* ============================================================
   8. SECTION RHYTHM + SCROLL REVEAL
   ============================================================ */
section{ padding:96px 0; position:relative; z-index:2; }
.section-head{ max-width:640px; margin-bottom:52px; }
.section-head.center{ margin-left:auto; margin-right:auto; text-align:center; }
.section-head h2{ font-size:34px; margin-top:16px; }
.section-head p{ color:var(--text-dim); font-size:16px; margin-top:14px; }
.reveal{
  opacity:0; transform:translateY(22px);
  transition:opacity .7s cubic-bezier(.16,1,.3,1), transform .7s cubic-bezier(.16,1,.3,1);
}
.reveal.in{ opacity:1; transform:translateY(0); }

/* ============================================================
   9. TRIAD — the three-step standard, shared by all four properties
   ============================================================ */
.triad{ display:grid; grid-template-columns:repeat(3,1fr); gap:1px; background:var(--hairline); border:1px solid var(--hairline); border-radius:14px; overflow:hidden; }
@media (max-width:820px){ .triad{ grid-template-columns:1fr; } }
.triad-item{ background:var(--panel); padding:34px 30px; border:1px solid transparent;
  transition:transform .2s ease, border-color .2s ease, background-color .2s ease; }
.triad-item:hover{ transform:translateY(-3px); border-color:var(--hairline); background:var(--panel-raised); }
.triad-item .tag{ font-family:var(--font-mono); font-size:11px; letter-spacing:.1em; color:var(--accent); }
.triad-item h3{ font-size:21px; margin:12px 0 12px; }
.triad-item p{ color:var(--text-dim); font-size:14.5px; margin:0; }

/* ============================================================
   10. CONSOLE PANEL PATTERN
   The live-engagement panel every property shows: a titled chrome bar
   over a body of rows or a scrolling activity feed.
   ============================================================ */
.split{ display:grid; grid-template-columns:1fr 1fr; gap:20px; }
@media (max-width:900px){ .split{ grid-template-columns:1fr; } }
.panel-box{ background:var(--panel-raised); border:1px solid var(--hairline); border-radius:16px; overflow:hidden; }
.panel-bar{ display:flex; align-items:center; gap:8px; padding:14px 20px; border-bottom:1px solid var(--hairline); font-family:var(--font-mono); font-size:12px; color:var(--text-faint); }
.dot{ width:8px; height:8px; border-radius:50%; background:var(--hairline); }
.panel-inner{ padding:22px 24px; }
.feed-item{
  display:flex; gap:12px; padding:10px 0; border-bottom:1px solid var(--hairline-soft); font-size:13px;
  opacity:0; animation:rise .6s cubic-bezier(.16,1,.3,1) forwards;
  transition:background-color .18s ease, padding-left .18s ease;
}
@keyframes rise{ from{opacity:0; transform:translateY(8px);} to{opacity:1; transform:translateY(0);} }
.feed-item:last-child{ border-bottom:none; }
.feed-item:hover{ background:var(--panel-raised); padding-left:6px; }
.feed-time{ font-family:var(--font-mono); color:var(--text-faint); font-size:11.5px; flex-shrink:0; width:56px; padding-top:1px; }
.feed-dot{ width:6px; height:6px; border-radius:50%; margin-top:6px; flex-shrink:0; }
.feed-dot.live{ animation:pulse-dot 2s ease-out infinite; }
@keyframes pulse-dot{ 0%{box-shadow:0 0 0 0 rgba(98,214,109,.5);} 70%{box-shadow:0 0 0 7px rgba(98,214,109,0);} 100%{box-shadow:0 0 0 0 rgba(98,214,109,0);} }
.feed-txt strong{ color:var(--text); font-weight:600; }
.feed-txt span{ color:var(--text-dim); }

/* ============================================================
   11. CTA BAND
   ============================================================ */
.cta-band{
  background:var(--panel); border:1px solid var(--hairline); border-radius:20px; padding:64px 48px;
  text-align:center; position:relative; overflow:hidden;
}
.cta-band::after{
  content:""; position:absolute; left:50%; top:-40%; width:600px; height:300px; transform:translateX(-50%);
  background:radial-gradient(circle, rgba(76,162,243,.14), transparent 70%); pointer-events:none;
}
.cta-band h2{ font-size:36px; margin-bottom:16px; }
.cta-band p{ color:var(--text-dim); margin-bottom:30px; }
.cta-band .hero-actions{ position:relative; z-index:1; }
@media (max-width:620px){ .cta-band{ padding:44px 24px; } .cta-band h2{ font-size:27px; } }

/* ============================================================
   12. FOOTER
   ============================================================ */
footer{ border-top:1px solid var(--hairline-soft); padding:64px 0 32px; }
.footer-grid{ display:grid; grid-template-columns:1.4fr repeat(4,1fr); gap:32px; margin-bottom:56px; }
@media (max-width:820px){ .footer-grid{ grid-template-columns:repeat(2,1fr); } }
.footer-grid h4{ font-family:var(--font-mono); font-size:11px; text-transform:uppercase; letter-spacing:.08em; color:var(--text-faint); margin-bottom:16px; font-weight:600; }
.footer-grid ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:11px; }
.footer-grid a{ color:var(--text-dim); font-size:14px; }
.footer-grid a:hover{ color:var(--text); }
.footer-blurb{ color:var(--text-faint); font-size:13.5px; margin-top:16px; max-width:260px; line-height:1.6; }
.footer-bottom{ display:flex; justify-content:space-between; align-items:center; padding-top:28px; border-top:1px solid var(--hairline-soft); font-size:13px; color:var(--text-faint); flex-wrap:wrap; gap:12px; }
.footer-bottom .legal{ display:flex; gap:20px; flex-wrap:wrap; }
footer a{ transition:color .18s ease; }

/* ============================================================
   13. THEME TOGGLE
   ============================================================ */
.theme-toggle{
  width:38px; height:38px; border-radius:9px; border:1px solid var(--hairline);
  background:transparent; display:inline-flex; align-items:center; justify-content:center;
  cursor:pointer; transition:border-color .2s ease, transform .2s ease, background-color .2s ease;
  color:var(--text); flex-shrink:0;
}
.theme-toggle:hover{ border-color:var(--text-dim); transform:translateY(-1px); background:var(--panel); }
.theme-toggle:active{ transform:translateY(0) scale(.94); }
.theme-toggle svg{ width:17px; height:17px; }
.theme-toggle .icon-moon{ display:none; }
.theme-toggle[data-current="light"] .icon-sun{ display:none; }
.theme-toggle[data-current="light"] .icon-moon{ display:block; }

/* ============================================================
   14. LIGHT MODE — frosted glass controls
   ============================================================ */
[data-theme="light"] .btn,
[data-theme="light"] .sso-btn,
[data-theme="light"] .theme-toggle{
  background:rgba(var(--ground-rgb),.5);
  backdrop-filter:blur(18px) saturate(180%);
  -webkit-backdrop-filter:blur(18px) saturate(180%);
  border:1.5px solid rgba(var(--ground-rgb),.65);
  box-shadow:0 4px 14px rgba(var(--ink-rgb),.05), inset 0 1px 0 rgba(var(--ground-rgb),.6);
  color:var(--text);
}
[data-theme="light"] .btn-primary{
  background:rgba(var(--ink-rgb),.88);
  color:#fff;
  border-color:rgba(var(--ink-rgb),.9);
}
[data-theme="light"] .btn-grad{
  background:var(--grad-btn);
  border-color:rgba(var(--ground-rgb),.5);
  color:#fff;
}
/* Hovering used to start a 3s rainbow border loop that ran for as long
   as the pointer rested on a button. A settled, single-step lift reads
   calmer and befits an assurance firm; the brand's spectrum still lives
   in the logo and the gradient headlines. */
[data-theme="light"] .btn:hover,
[data-theme="light"] .sso-btn:hover,
[data-theme="light"] .theme-toggle:hover{
  transform:translateY(-1px);
  border-color:rgba(var(--ink-rgb),.22);
  box-shadow:0 8px 20px -8px rgba(var(--ink-rgb),.16), inset 0 1px 0 rgba(var(--ground-rgb),.6);
}
[data-theme="light"] .btn:not(.btn-primary):not(.btn-grad):hover,
[data-theme="light"] .sso-btn:hover,
[data-theme="light"] .theme-toggle:hover{
  background:rgba(var(--ground-rgb),.68);
}
[data-theme="light"] .btn-primary:hover{ background:rgba(var(--ink-rgb),1); }
[data-theme="light"] .btn-grad:hover{ filter:brightness(1.1); }
/* The masthead keeps a calmer hover than the body's CTAs — a masthead
   shouldn't shimmer. */
[data-theme="light"] header .btn:hover,
[data-theme="light"] header .theme-toggle:hover{
  animation:none;
  transform:translateY(-1px);
  border-color:rgba(var(--ink-rgb),.28);
  box-shadow:0 6px 18px -6px rgba(var(--ink-rgb),.13);
}

/* ============================================================
   15. PERFORMANCE + MOTION PREFERENCES
   ============================================================ */
.triad-item, .stat, .panel-box{ will-change:transform; }
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  .btn{ transition:none; }
  .grad-text{ animation:none; }
  .stat:hover .num{ animation:none; }
  .hero-glow{ animation:none; opacity:1; }
  .reveal{ opacity:1 !important; transform:none !important; transition:none !important; }
  .feed-item{ opacity:1 !important; animation:none !important; transform:none !important; }
  .feed-dot.live{ animation:none; }
  header{ transition:none; }
}

/* ============================================================
   17. LINE BREAKING
   Headings balance their lines instead of leaving a long first line and a
   short second one; body copy avoids an orphan final line. The section
   intros were wrapping to "…public programs anyone can research," followed
   by a stubby "private ones by invite.", which reads as a ragged block
   rather than a deliberate measure. Browsers without text-wrap ignore both
   keywords and fall back to normal wrapping.
   ============================================================ */
.hero h1,
.section-head h2,
.cta-band h2{ text-wrap:balance; }
.hero p.lede,
.section-head p,
.cta-band p,
.footer-blurb{ text-wrap:pretty; }

/* ============================================================
   16. NO-JAVASCRIPT FALLBACK
   nav.js never runs, so there is no drawer to collapse into. Let the
   bar wrap onto a second line instead — every link stays reachable.
   ============================================================ */
@media (max-width:1200px){
  nav.wrap{ height:auto; flex-wrap:wrap; gap:14px; padding-top:14px; padding-bottom:14px; }
  .nav-links{ order:3; width:100%; flex-wrap:wrap; gap:18px; }
}

/* ============================================================
   18. EDITORIAL MEASURE + LUXURY SURFACES
   The house style, applied to the components that already exist rather
   than bolted on as utilities nobody calls. Three ideas do most of the
   work: copy gets a measure so no line runs past the eye's comfortable
   saccade, surfaces gain layered ambient depth instead of an outline,
   and everything that moves shares one long-settling curve.
   ============================================================ */

/* --- Measure ------------------------------------------------------------
   Body copy is capped in characters, not pixels, so the limit holds at any
   font size and in any script. Left-aligned by default: centred paragraphs
   restart every line at a different x and the eye has to hunt for the next
   one. Centring stays opt-in via .center, as it already was for the hero. */
.hero p.lede,
.section-head p,
.cta-band p,
.footer-blurb,
.triad-item p,
.panel-box p{
  max-width:var(--measure);
  line-height:1.75;
}
.section-head:not(.center) p,
.hero .wrap:not(.center) p.lede{ margin-left:0; margin-right:0; }

/* --- Section rhythm ----------------------------------------------------- */
.lux-section{ padding-block:var(--space-section); }

/* --- Animated underline -------------------------------------------------
   Wipes in from the left and out to the right, so the line travels with
   the pointer rather than blinking. Applied to footer navigation, which
   was a plain colour change. */
.lux-link,
.footer-grid a{
  position:relative; display:inline-block; padding-bottom:3px;
  text-decoration:none;
}
.lux-link::after,
.footer-grid a::after{
  content:""; position:absolute; left:0; bottom:0;
  width:100%; height:1px; background-color:currentColor;
  transform:scaleX(0); transform-origin:right center;
  transition:transform var(--transition-base);
}
.lux-link:hover::after,
.footer-grid a:hover::after{ transform:scaleX(1); transform-origin:left center; }

/* --- Media frame --------------------------------------------------------
   A slow, barely-perceptible push-in. The frame clips, the image scales,
   so nothing reflows. */
.lux-media-frame{ overflow:hidden; position:relative; }
.lux-media-frame img{
  width:100%; height:100%; object-fit:cover;
  transform:scale(1); transition:transform var(--transition-slow);
}
.lux-media-frame:hover img{ transform:scale(1.035); }

/* --- Surfaces -----------------------------------------------------------
   The card treatment, given to the panels and triad items the properties
   already use. Depth comes from stacked ambient shadow; the hairline is
   there to hold the edge in light mode, where card and page are both
   pure white. */
.lux-card,
.panel-box,
.triad-item{
  background:var(--panel);
  border:1px solid var(--hairline);
  box-shadow:var(--shadow-sm);
  transition:box-shadow var(--transition-base),
             transform var(--transition-base),
             border-color var(--transition-base);
}
.lux-card:hover,
.panel-box:hover,
.triad-item:hover{
  box-shadow:var(--shadow-lg);
  transform:translateY(-2px);
  border-color:rgba(var(--ink-rgb),.2);
}
.lux-card{ padding:clamp(2rem, 4vw, 3.5rem); }

/* --- Frosted masthead ---------------------------------------------------
   Saturation is lifted as well as blurred: without it, colour passing
   under a translucent bar goes flat and grey. */
header{
  backdrop-filter:blur(16px) saturate(140%);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
  border-bottom:1px solid var(--hairline-soft);
}

/* --- Motion preferences -------------------------------------------------
   Everything added above is decoration. None of it survives a request for
   reduced motion. */
@media (prefers-reduced-motion: reduce){
  .lux-link::after,
  .footer-grid a::after,
  .lux-media-frame img,
  .lux-card, .panel-box, .triad-item{ transition:none; }
  .lux-media-frame:hover img{ transform:none; }
  .lux-card:hover, .panel-box:hover, .triad-item:hover{ transform:none; }
}

/* ============================================================
   19. ALIGNMENT — headings may centre, running copy may not
   A centred heading is a poster; a centred paragraph is a chore. Every
   line of centred body copy starts at a different x, so the eye has to
   re-find the left edge on every return sweep. The compromise used here
   is the editorial one: the block is centred, the text inside it is not.
   Headings, eyebrows and button rows keep their centring.
   ============================================================ */
.section-head.center p,
.cta-band p,
.hero .wrap.center p.lede{
  text-align:left;
  max-width:var(--measure);
  margin-inline:auto;
}

/* List items carry running copy too, and were reaching ~100 characters a
   line on a wide viewport — well past the 45–75 the eye tracks comfortably. */
.policy-list li,
.panel-box li,
.section-head + ul li{
  max-width:var(--measure);
  line-height:1.75;
}

/* Right-to-left scripts flip the reading direction, so "left" becomes
   "start". Arabic and Urdu would otherwise get a ragged right edge. */
[dir="rtl"] .section-head.center p,
[dir="rtl"] .cta-band p,
[dir="rtl"] .hero .wrap.center p.lede{ text-align:right; }

/* ============================================================
   20. BRAND MARK — [MATRIX] types itself in, then the X cycles
   ============================================================
   Two animations with different lifetimes.

   The reveal runs ONCE — iteration-count 1, fill:both — so the word types
   itself on load and on reload and never again. No JavaScript: a script
   would have to observe the element, and this is a wordmark in a sticky
   header, not a feature.

   The cycle runs forever, one second a frame:
       X → █ → 🤖 → █ → X → █ → 👤 → █
   The block between every glyph is what makes it read as a loop rather than
   a list; the seven-frame version wrapped 👤 straight to X with no beat and
   that one seam was visible once a cycle. It waits out the reveal, and frame
   1 carries opacity:1 as its resting state so the X is already in place as
   the reveal uncovers it.

   Four decisions, each from a measurement rather than a guess:

   - The bracket travels by transform, not by the text growing. A width
     animation would widen the mark as it typed and push the nav links
     around during page load; overflow:hidden would also move the
     inline-block's baseline off the text baseline. With a transform the
     mark's box is a constant 217.422px through every frame.
   - The reveal is clip-path, not width. Same reason: clip does not affect
     layout. The field is 147.297px and the text inside it is 147.297px, so
     the six clip steps land exactly on character boundaries and never
     mid-glyph.
   - The bracket steps with jump-none, not end. With steps(6, end) the final
     value overshoots by one cell and the mark settles as [MATRIX ].
   - Emoji sit at var(--mx-emoji). See the token block for the measurement.

   Frames cross-fade on opacity rather than animating the `content`
   property, which Chrome animates and Firefox and Safari do not.
   ============================================================ */

/* Screen readers get the word, not the animation. */
.sr-only{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* Always six cells wide, whatever is revealed inside it. */
.brand-sub .mx-field{
  position:relative;
  display:inline-block;
  width:calc(6ch + 6 * var(--brand-track));   /* M A T R I + the slot */
  vertical-align:baseline;
}
.brand-sub .mx-text{
  display:inline-block;
  clip-path:inset(0 0 0 0);
  animation:mx-reveal var(--mx-type-dur) steps(6, end) 1 both;
}
@keyframes mx-reveal{
  from{ clip-path:inset(0 100% 0 0); }
  to  { clip-path:inset(0 0 0 0); }
}

/* The closing bracket rides one cell right of the caret and lands home as
   the X is uncovered. */
.brand-sub .mx-close{
  display:inline-block;
  animation:mx-bracket var(--mx-type-dur) steps(6, jump-none) 1 both;
}
@keyframes mx-bracket{
  from{ transform:translateX(calc(-5 * (1ch + var(--brand-track)))); }
  to  { transform:translateX(0); }
}

/* The caret rides the insertion point and switches off exactly as the X
   arrives, so the cursor appears to become the letter. */
.brand-sub .mx-caret{
  position:absolute;
  left:0;
  top:50%;
  width:1ch;
  height:var(--mx-cap);
  background:currentColor;
  /* the field's box centres on the line box, the letters on their ink */
  transform:translateY(-50%) translateY(-.055em);
  opacity:0;
  animation:mx-ride var(--mx-type-dur) steps(6, end) 1 both,
            mx-caret-on var(--mx-type-dur) steps(1, end) 1 both;
}
@keyframes mx-ride{
  from{ left:0; }
  to  { left:calc(6ch + 6 * var(--brand-track)); }
}
@keyframes mx-caret-on{
  from{ opacity:1; }
  to  { opacity:0; }
}

/* The slot the last glyph cycles in. An inline-grid does not receive the
   parent's letter-spacing, so the cell has to carry it itself or the
   closing bracket sits 4.17px tight. Grid items also default to
   align-items:stretch, which left each frame at the top of a full-height
   box using its OWN line-height — a smaller emoji got a shorter line box
   and floated up. line-height:1 removes that variable, align-items:center
   settles what is left. */
.brand-sub .mx-slot{
  display:inline-grid;
  justify-items:center;
  align-items:center;
  vertical-align:baseline;
  width:calc(1ch + var(--brand-track));
}
.brand-sub .mx-slot i{
  grid-area:1/1;
  font-style:normal;
  line-height:1;
  opacity:0;
  animation:mx-cycle 8s steps(1, end) infinite;
}
/* Frames 2-8 ship with the HTML `hidden` attribute so that a page which
   loads before this stylesheet — a stale CDN copy, a failed request —
   renders a plain static [MATRIX] instead of every frame at once. This is
   the rule that un-hides them once the stylesheet is actually here. */
.brand-sub .mx-slot i[hidden]{ display:block; }

.brand-sub .mx-slot i.mx-emoji{ font-size:var(--mx-emoji); line-height:1; }

/* Drawn, not typed. U+2588 carries 1.19em of ink, is missing from some mono
   faces, and its height varies by vendor; a painted box is exactly the cap
   height everywhere. Being empty, it also contributes nothing to the
   no-stylesheet fallback. */
.brand-sub .mx-block{
  display:inline-block;
  width:1ch;
  height:var(--mx-cap);
  background:currentColor;
  vertical-align:baseline;
  transform:translateY(.018em);
}

/* Frame 1 rests visible so the X is there as the reveal uncovers it, and
   the cycle starts only once the reveal is done. */
.brand-sub .mx-slot i:nth-child(1){ opacity:1; animation-delay:var(--mx-type-dur); }
.brand-sub .mx-slot i:nth-child(2){ animation-delay:calc(var(--mx-type-dur) + 1s); }
.brand-sub .mx-slot i:nth-child(3){ animation-delay:calc(var(--mx-type-dur) + 2s); }
.brand-sub .mx-slot i:nth-child(4){ animation-delay:calc(var(--mx-type-dur) + 3s); }
.brand-sub .mx-slot i:nth-child(5){ animation-delay:calc(var(--mx-type-dur) + 4s); }
.brand-sub .mx-slot i:nth-child(6){ animation-delay:calc(var(--mx-type-dur) + 5s); }
.brand-sub .mx-slot i:nth-child(7){ animation-delay:calc(var(--mx-type-dur) + 6s); }
.brand-sub .mx-slot i:nth-child(8){ animation-delay:calc(var(--mx-type-dur) + 7s); }

@keyframes mx-cycle{
  0%,      12.5% { opacity:1; }
  12.6%, 100%    { opacity:0; }
}

/* A masthead that never stops moving is a masthead nobody stops looking at.
   Reduced motion gets the word and nothing else. */
@media (prefers-reduced-motion: reduce){
  .brand-sub .mx-slot i{ animation:none; opacity:0; }
  .brand-sub .mx-slot i:first-child{ opacity:1; }
  .brand-sub .mx-text{ animation:none; clip-path:none; }
  .brand-sub .mx-close{ animation:none; transform:none; }
  .brand-sub .mx-caret{ animation:none; display:none; }
}
