/* ═══════════════════════════════════════════════════════════════
   EHE 2027 — A DECADE OF STRENGTH
   Darkroom editorial system. Navy void, pearl type, platinum metal.
   ═══════════════════════════════════════════════════════════════ */

:root{
  /* ── Brand ── */
  --navy:        #192F58;
  --teal:        #008C95;
  --silver:      #C0C0C0;
  --mist:        #F2F5F6;

  /* ── Surfaces (0 → 3) ── */
  --void:        #070D19;   /* 0 · page canvas — navy at 6% lightness */
  --void-2:      #0B1526;   /* 0.5 · barely-raised band */
  --surface:     #192F58;   /* 1 · the one elevated solid (filled CTA) */
  --border:      #1E3050;   /* 2 · hairlines, dashed dividers */
  --driftwood:   #55688A;   /* mid-tone bridge, muted structure */

  /* ── Ink ── */
  --ink:         #F2F5F6;
  --ink-70:      rgba(242,245,246,.70);
  --ink-45:      rgba(242,245,246,.45);
  --ink-28:      rgba(242,245,246,.28);
  --accent:      #00B4BE;   /* teal, lifted for legibility on the void */

  /* ── Type ── */
  /* relative to THIS stylesheet, not the document; declared once so the
     build inlines it once */
  --logo: url(../img/ehe-logo.png);

  --font: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-legal: Arial, ui-sans-serif, system-ui, sans-serif;

  --t-display:    clamp(2.4rem, 7.4vw, 9.2rem);
  --t-heading:    clamp(2.1rem, 5.4vw, 4.6rem);
  --t-heading-sm: clamp(1.15rem, 1.65vw, 1.5rem);
  --t-body:       clamp(1.05rem, 1.55vw, 1.8rem);
  --t-sub:        clamp(.9rem, 1.02vw, 1.125rem);
  --t-micro:      12px;
  --t-legal:      9px;

  --lead-display: .9;
  --lead-heading: .9;
  --lead-body:    1.26;

  /* ── Space ── */
  --gap:    18px;
  --pad:    24px;
  --gut:    clamp(24px, 5.2vw, 68px);
  --sect:   clamp(96px, 13vh, 204px);

  /* ── Radius ── */
  --r-card: 12px;
  --r-pill: 36px;
  --r-ghost: 22.5px;

  --ease: cubic-bezier(.16,1,.3,1);
}

/* ── Reset ─────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth}
html.has-smooth{scroll-behavior:auto}
body{
  margin:0; background:var(--void-2); color:var(--ink);
  font-family:var(--font); font-weight:500; font-feature-settings:"ss01" on,"cv05" on;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
img{max-width:100%; height:auto; display:block}
a{color:inherit; text-decoration:none}
ul,ol{margin:0; padding:0; list-style:none}
p{margin:0}
h1,h2,h3{margin:0; font-weight:500}
button{font:inherit; color:inherit; background:none; border:0; cursor:pointer}
::selection{background:var(--teal); color:var(--mist)}

/* ═══ TYPE VOICES ══════════════════════════════════════════════
   Voice 1 — UPPERCASE 500 : everything.
   Voice 2 — mixed case 400 : the .body descriptive register only.
   ═════════════════════════════════════════════════════════════ */

.display{
  font-size:var(--t-display); line-height:var(--lead-display);
  /* the one place the system breaks from weight 500: the display statement
     is set bold, with a touch of negative tracking so the heavier forms
     don't read loose at this size */
  text-transform:uppercase; font-weight:700; letter-spacing:-.018em;
}
.heading{
  font-size:var(--t-heading); line-height:var(--lead-heading);
  text-transform:uppercase; font-weight:500;
}
.heading--center{text-align:center}
/* Shared first-line hierarchy, including the generated reveal/reel wrappers. */
.heading--tiered > :first-child{font-size:.78em; color:var(--accent)}
.heading--tiered > .line:first-child,.heading--tiered > .roll-line:first-child{margin-bottom:.14em}
.heading-sm{
  font-size:var(--t-heading-sm); line-height:1.09;
  text-transform:uppercase; font-weight:500;
}
.body{
  font-size:var(--t-body); line-height:var(--lead-body);
  font-weight:400; text-transform:none; color:var(--ink-70);
  max-width:34ch;
}
.body--sm{font-size:clamp(.95rem,1.15vw,1.15rem); max-width:60ch}
.body--wide{max-width:44ch}
.sub{
  font-size:var(--t-sub); line-height:1.5; font-weight:400;
  color:var(--ink-45); max-width:46ch;
}
.micro{
  font-size:var(--t-micro); line-height:1.2; font-weight:500;
  text-transform:uppercase; color:var(--ink-45);
}
.tag{
  font-size:11px; line-height:1.2; font-weight:500;
  text-transform:uppercase; color:var(--accent);
  margin-bottom:10px;
}
.legal{
  font-family:var(--font-legal); font-size:var(--t-legal); font-weight:500;
  line-height:1.2; text-transform:uppercase; color:var(--ink-28);
  letter-spacing:.04em;
}
.kicker{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  font-size:var(--t-micro); font-weight:500; text-transform:uppercase;
  color:var(--ink-70); margin-bottom:var(--gap);
}
.kicker__dot{width:4px;height:4px;border-radius:50%;background:var(--accent);display:inline-block}
/* .kicker is declared after .heading-sm, so a bare class on the element loses
   the cascade — the size has to be re-stated at equal-or-higher specificity */
.kicker--lead{
  font-size:var(--t-heading-sm); line-height:1.09; gap:14px;
  color:var(--ink); margin-bottom:calc(var(--gap) * .8);
}
.kicker--lead .kicker__dot{width:5px; height:5px}

.link{position:relative; text-transform:uppercase; font-size:var(--t-micro); font-weight:500}
.link::after{content:""; position:absolute; left:0; right:0; bottom:-3px; height:1px;
  background:currentColor; opacity:.4; transform-origin:0 50%; transition:opacity .3s}
.link:hover::after{opacity:1}

/* headings that FLIP from a full-bleed statement down into their resting
   position — the transform is written per frame by site.js */
[data-flip]{transform-origin:0 50%; will-change:transform}

/* ── Rules ─────────────────────────────────────────────────── */
.rule{display:block; width:100%; height:1px; border:0; border-top:1px dashed var(--border)}
.rule--dash{margin:var(--gap) 0}

/* ═══ BUTTONS ══════════════════════════════════════════════════ */
.btn{
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  overflow:hidden; text-transform:uppercase; font-weight:500; font-size:var(--t-micro);
  white-space:nowrap;
  transition:color .4s var(--ease), border-color .4s var(--ease),
             background-color .4s var(--ease);
}
.btn--fill{
  background:var(--surface); color:var(--mist);
  border:1px solid rgba(192,192,192,.34);
  border-radius:var(--r-pill); padding:14px 28px;
}
.btn--fill:hover{border-color:var(--silver)}
.btn--lg{padding:20px 44px; font-size:14px}
/* brushed-metal sweep — the only motion permitted on a surface */
.btn__sheen{
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background:linear-gradient(105deg, transparent 30%,
    rgba(242,245,246,.10) 42%, rgba(255,255,255,.34) 50%,
    rgba(242,245,246,.10) 58%, transparent 70%);
  transform:translateX(-120%); transition:transform .85s var(--ease);
}
.btn:hover .btn__sheen{transform:translateX(120%)}

/* ═══ FIXED CHROME ═════════════════════════════════════════════ */
.nav{
  position:fixed; inset:0 0 auto 0; z-index:60;
  display:flex; align-items:center; gap:var(--gut);
  padding:20px var(--gut);
  transition:background .5s var(--ease), backdrop-filter .5s var(--ease), padding .5s var(--ease);
}
.nav.is-stuck{
  background:rgba(7,13,25,.72); backdrop-filter:blur(14px) saturate(1.2);
  border-bottom:1px solid var(--border); padding:12px var(--gut);
}
/* The wordmark is painted as a mask filled with currentColor, not placed as
   an <img>: one asset then serves any ground — light on the dark page, navy
   over the pearl hero — with no second file and no filter hack. The text
   label stays in the DOM, visually hidden, so the link keeps its name. */
.nav__mark{
  display:block; position:relative; flex:none; margin-right:auto;
  width:72px; height:38px;
  background-color:currentColor; color:var(--mist);
  -webkit-mask:var(--logo) left center / contain no-repeat;
          mask:var(--logo) left center / contain no-repeat;
  transition:color .35s var(--ease);
}
.nav__mark > *{
  position:absolute; width:1px; height:1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
.nav__mark-rule{width:18px; height:1px; background:var(--driftwood); display:inline-block}
.nav__mark-yr{color:var(--ink-45)}
.nav__list{display:flex; gap:31px}
.nav__link{
  position:relative; font-size:var(--t-micro); text-transform:uppercase;
  color:var(--ink-70); transition:color .35s var(--ease); padding-bottom:5px;
}
.nav__link:hover,.nav__link.is-on{color:var(--mist)}
.nav__link::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:0;
  border-top:1px solid var(--accent); opacity:0;
  transform:scaleX(.4); transform-origin:0 50%;
  transition:opacity .35s var(--ease), transform .45s var(--ease);
}
.nav__link.is-on::after{opacity:1; transform:scaleX(1)}
/* The nav pill is the page's one teal solid; the closing CTA stays navy. A
   white label is what forces brand --teal here rather than --accent: #F2F5F6
   on #00B4BE measures 2.5:1, on #008C95 it is 4.1:1. Deeper still (#00818A)
   would clear AA outright for a label this small — worth knowing if the teal
   is ever allowed to darken. */
.nav__cta{padding:11px 22px}
/* the register button wears the nav CTA's teal, so both Save the Dates match */
.nav__cta,.cta .btn--fill{
  background:var(--teal); color:var(--mist); border-color:var(--teal);
}
.nav__cta:hover,.cta .btn--fill:hover{background:#00A3AD; border-color:#00A3AD}
.nav__cta.shine-hover:hover,.cta .btn--fill.shine-hover:hover{background:var(--teal)}
/* Three 2px bars, not two hairlines: at 1px in a 22px box this read as a
   stray dash rather than a control. Padding gives a 44px touch target while
   negative margin keeps it from shifting the nav. */
.nav__toggle{
  display:none; flex-direction:column; align-items:flex-end; gap:5px;
  padding:14px 11px; margin:-14px -11px -14px 0;
}
.nav__toggle i{
  width:24px; height:2px; background:var(--mist); display:block; border-radius:2px;
  transition:transform .4s var(--ease), opacity .25s var(--ease);
}
.nav.is-open .nav__toggle i:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav.is-open .nav__toggle i:nth-child(2){opacity:0}
.nav.is-open .nav__toggle i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}


/* ── Cursor ────────────────────────────────────────────────── */

/* ═══ THE FIELD (hero WebGL) ═══════════════════════════════════ */
.field{
  position:fixed; inset:0; z-index:1; width:100%; height:100%;
  display:block; pointer-events:none;
}
/* The anniversary mark is a persistent layer: it never unmounts, and each
   staged section writes its pose. Sits above the hero's veil and below the
   content, so the staged sections must be transparent for it to show. */
.stage10{
  position:fixed; inset:0; z-index:3; width:100%; height:100%;
  display:block; pointer-events:none;
}
html.no-webgl .stage10{display:none}

/* the hero's own field must clear once we leave it, or it shows through the
   now-transparent sections below */
.field, .field__veil{transition:opacity .5s var(--ease)}
body:not(.hero-on) .field, body:not(.hero-on) .field__veil{opacity:0}

.field__veil{
  position:fixed; inset:0; z-index:2; pointer-events:none;
  /* Three darkening layers used to stack here and multiply out to ~99%
     opaque at the lower-left corner — which is exactly where the corner
     light now lives, behind the canvas. The scrim is kept for headline
     legibility but pulled up and right of the corner, and the heavy
     bottom band is gone: the next section is opaque and gives a clean
     edge on its own. */
  background:
    radial-gradient(54% 46% at 30% 62%, rgba(7,13,25,.58) 0%, rgba(7,13,25,.26) 52%, transparent 82%),
    radial-gradient(40% 44% at 86% 74%, rgba(7,13,25,.44) 0%, transparent 74%),
    linear-gradient(to bottom, rgba(7,13,25,.50) 0%, rgba(7,13,25,.05) 22%,
                    transparent 62%, transparent 100%);
}

/* ═══ LAYOUT ═══════════════════════════════════════════════════ */
#smooth{position:relative; z-index:4}
.wrap{padding:0 var(--gut); max-width:1680px; margin:0 auto; width:100%}

.hero{
  /* z-index:0 makes the hero its own stacking context, so .hero__mark's z-index
     is scoped INSIDE it. Without this the lagging lockup outranks #countdown —
     a later sibling at z-index auto — and paints over the band instead of
     sliding behind it. */
  position:relative; z-index:0; min-height:calc(100svh + 48px);
  display:flex; flex-direction:column; justify-content:flex-end;
  /* Reserve a footer lane for the EHE label and scroll cue below the dates
     and dashed divider. */
  padding:0 var(--gut) 120px;
}
.hero__grid{
  display:grid; grid-template-columns:minmax(0,1fr);
  gap:var(--gut); align-items:flex-end;
}
.hero__lead{width:max-content; max-width:100%}
/* The headline is deliberately NOT at display scale: 40px at 1600.
   "Celebrating 10 years" measures 11.572em unwrapped, so at this size it holds
   one line down to a ~370px viewport with room to spare. */
.hero__h1{
  margin:0; font-size:clamp(1.5rem, 2.5vw, 2.75rem);
  letter-spacing:0;
}
.hero__dates{display:flex; flex-direction:column; gap:4px;
  font-size:var(--t-micro); text-transform:uppercase; color:var(--ink-70)}
/* client: the hero date matches the "2027 EHE Conference" kicker size; the
   .cta reuse of .hero__dates keeps the small size */
.hero .hero__dates{font-size:var(--t-heading-sm); line-height:1.09; font-weight:500}

.card{
  background:rgba(25,47,88,.30);
  border:1px solid var(--border); border-radius:var(--r-card);
  padding:var(--pad); backdrop-filter:blur(12px) saturate(1.15);
  max-width:440px; justify-self:end;
}
.card__head{font-size:var(--t-heading-sm); line-height:1.09; text-transform:uppercase; font-weight:500}
.card__body{font-size:clamp(.95rem,1.1vw,1.1rem); line-height:1.5; font-weight:400; color:var(--ink-70); margin-bottom:var(--pad)}

.scrollcue{
  position:absolute; left:50%; bottom:clamp(14px,2.4vh,26px); transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:10px;
}
.scrollcue .legal{font-size:12px; color:#fff}
.scrollcue__line{width:1px; height:38px; background:linear-gradient(var(--driftwood), transparent);
  position:relative; overflow:hidden}
.scrollcue__line::after{content:""; position:absolute; inset:0; background:var(--silver);
  animation:cue 2.4s var(--ease) infinite}
@keyframes cue{0%{transform:translateY(-100%)}60%,100%{transform:translateY(100%)}}


/* ── Hero anniversary mark ─────────────────────────────────── */
/* Anchored to the ribbon bundle's vanishing point: the WebGL VP sits
   at 78% / 38.5% of the viewport, so these values must track it. The mark's
   ring centre sits ~4.5% above the container centre. */
.hero__mark{
  /* Anchor to the viewport so the extra footer space doesn't move the title. */
  position:absolute; left:78%; top:40svh;
  transform:translate(-50%,-50%);
  /* also bounded by viewport height: on a short laptop the caption ran
     under the hero card, which paints over it */
  width:min(clamp(232px, 24vw, 400px), 32vh);
  pointer-events:none; z-index:2;
}
.hero__mark-in{position:relative; margin:0}
/* The whole lockup hangs below the mark, centred on it as a group but set
   flush left inside itself: the rotating word lives in a box sized to the
   LONGEST word, so centring the line would leave a gap that changes with
   every word. Left-aligning makes the box's slack fall harmlessly to the
   right, where nothing sits. */
.hero__lockup{
  position:absolute; top:calc(100% + 20px); left:50%; transform:translateX(-50%);
  width:max-content; max-width:min(92vw, 640px);
  text-align:left; pointer-events:auto;
}
.hero__theme{
  font-size:clamp(11px, .92vw, 15px); font-weight:500;
  text-transform:uppercase; letter-spacing:.2em; color:var(--ink-70);
  white-space:nowrap;
}
/* a hairline that fades at both ends rather than butting into nothing */
.hero__sep{
  border-top:0; height:1px; width:100%; margin:14px 0;
  background:linear-gradient(90deg,
    transparent 0%, rgba(192,202,214,.46) 14%,
    rgba(192,202,214,.46) 86%, transparent 100%);
}
.hero__mark-slot{display:block; width:100%; aspect-ratio:760 / 643}
.hero__mark figcaption{
  position:absolute; top:calc(100% + 11px); left:50%; transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:9px; white-space:nowrap;
}
.hero__mark-kicker{
  font-size:10px; font-weight:500; text-transform:uppercase;
  letter-spacing:.16em; color:var(--ink-45);
}
.hero__mark-pillars{
  display:flex; align-items:center; gap:10px;
  font-size:var(--t-micro); text-transform:uppercase;
}
.hero__mark-pillars b{font-weight:500; color:var(--mist)}
.hero__mark-pillars i{width:4px; height:4px; border-radius:50%; background:var(--accent); flex:none}

/* ── Bands ─────────────────────────────────────────────────── */
.band{position:relative; background:transparent; padding:var(--sect) 0}
.band--tall{padding:calc(var(--sect) * 1.15) 0}
#countdown{
  background:var(--void);
  /* the sketch field is absolutely positioned inside this box and must not
     contribute to its height — the section stays sized by its own content */
  position:relative; overflow:hidden;
  padding-block:calc(var(--sect) * .35);
}
#countdown > .wrap{position:relative; z-index:1}

/* ── Sketch field ──────────────────────────────────────────────
   Line-art plates drift behind the countdown, blurred and dim, and
   come into focus along the pointer's trail. The <img> sources are
   in the DOM (not fetched from JS) so the build inlines them; the
   canvas reads them as textures. */
.sketchfield{position:absolute; inset:0; z-index:0; pointer-events:none}
.sketchfield canvas{width:100%; height:100%; display:block}
.sketch-src{position:absolute; width:1px; height:1px; opacity:0; pointer-events:none}
.mark-logo-src{position:absolute; left:0; top:0; width:1px; height:1px; opacity:0; pointer-events:none}
html.no-webgl .sketchfield{display:none}

/* ── Countdown ─────────────────────────────────────────────── */
/* The section eyebrow: bolder, with a teal tick in front of it. Opted into by
   section rather than put on .kicker, because the hero and the drawers use the
   same class without the tick. */
#countdown .kicker,#about > .wrap > .kicker,#speakers > .wrap > .kicker,#agenda > .wrap > .kicker,.voices__head .voices__eyebrow{font-weight:700}
#countdown .kicker::before,#about > .wrap > .kicker::before,#speakers > .wrap > .kicker::before,#agenda > .wrap > .kicker::before,.voices__head .voices__eyebrow::before{
  content:""; display:inline-block; width:18px; height:1px;
  margin-right:10px; vertical-align:middle; background:var(--accent);
}
.count{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--gap); padding:26px 0}
.count li{display:flex; flex-direction:column; gap:6px; align-items:flex-start}
.count b{
  font-size:clamp(3.4rem,10.4vw,9.5rem); line-height:.86; font-weight:700;
  letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
  background:linear-gradient(168deg,#ffffff 0%,var(--silver) 30%,#7d8794 52%,#e8ecef 68%,var(--silver) 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
.count em{font-style:normal; font-size:14px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--accent); margin-left:12px;
  background:rgba(0,184,202,.13); box-shadow:0 0 0 4px rgba(0,184,202,.13)}

/* ═══ VOID-MODE REVEAL ═════════════════════════════════════════ */
.void{
  position:relative; overflow:hidden; min-height:100svh; background:transparent;
  display:grid; grid-template-columns:minmax(0,1.08fr) minmax(240px,32vw) minmax(0,1fr);
  gap:var(--gut); align-items:center; padding:var(--sect) var(--gut);
}
.void__col--l{justify-self:start}
.void__col--r{justify-self:start; max-width:34ch}
.void .heading{font-size:clamp(2rem,3.9vw,4rem)}
.void .body{max-width:30ch}
.void__object{margin:0; position:relative; display:flex; flex-direction:column;
  align-items:center; gap:var(--gap)}
.void__object{min-height:62vh; justify-content:flex-end}

/* ── v7: the decade section carries the full anniversary statement ──
   The right column went from one paragraph to roughly ninety words, so the
   grid is rebalanced away from the wide left column it no longer needs: the
   statement gets the space, and the copy sizes come down a step so the measure
   stays readable rather than filling the column edge to edge.

   Scoped to a min-width query on purpose: `.void` collapses to one column at
   1100px, and a `.void--decade` rule (or an #id) would out-specify that and
   leave the three columns wedged onto a phone. */
@media (min-width:1101px){
  .void--decade{grid-template-columns:minmax(0,1.05fr) minmax(200px,25vw) minmax(0,1.2fr)}
  /* The heading has to hold "2027 ISN’T JUST" on one line in the LEFT column at
     every width down to the 1101px breakpoint, not just at 1600 — at 2.6vw in
     a 28vw-middle grid it broke to four ragged lines on a 1280 screen. The
     middle column gives up the width, since the logo scales to whatever box
     it gets. */
  .void--decade .heading{font-size:clamp(1.5rem,2.7vw,2.9rem)}
  /* the shared 34ch cap was sized for one paragraph and squeezes the
     statement into a 355px ribbon inside a 491px column */
  .void--decade .void__col--r{max-width:none}
}
.decade__underline{
  position:relative; display:inline-block;
}
.decade__underline svg{
  position:absolute; left:-3%; top:100%; width:106%; height:.24em;
  overflow:visible; pointer-events:none;
}
.decade__underline path{
  stroke:var(--accent); stroke-width:10; stroke-linecap:round;
  stroke-dasharray:1; stroke-dashoffset:0;
}
.void--decade .heading .line:first-child{
  padding-bottom:.46em; margin-bottom:0;
}
.decade__lead{
  font-size:var(--t-micro); text-transform:uppercase; font-weight:500;
  color:var(--ink-45); margin-top:calc(var(--gap) * 1.7);
}
.decade__trio{display:flex; flex-direction:column; gap:11px; margin-top:13px}
.decade__trio li{
  display:flex; align-items:center; gap:13px;
  font-size:var(--t-heading-sm); line-height:1.1; font-weight:500;
  text-transform:uppercase; color:var(--mist);
}
.decade__trio li::before{content:""; width:18px; height:1px; background:var(--accent); flex:none}

/* the statement reads as lead → turn → three beats → signature, with air
   between them, rather than as one block of text */
.void--decade .decade__body{max-width:54ch}
.decade__pivot{
  font-size:clamp(1.08rem,1.32vw,1.38rem); line-height:1.36; font-weight:400;
  color:var(--mist); max-width:30ch; margin-top:calc(var(--gap) * 1.3);
}
.decade__list{display:flex; flex-direction:column; gap:15px; margin-top:calc(var(--gap) * 1.2)}
.decade__list li{
  position:relative; padding-left:20px; max-width:54ch;
  font-size:clamp(.96rem,1.06vw,1.06rem); line-height:1.58;
  font-weight:400; color:var(--ink-70);
}
.decade__list li::before{
  content:""; position:absolute; left:0; top:.68em;
  width:10px; height:1px; background:var(--driftwood);
}
.decade__list b{font-weight:500; color:var(--accent)}
/* two stacked lines, not one with a separator: the inline dot stranded itself
   at the end of the first line when the signature wrapped */
.decade__close{display:flex; flex-direction:column; gap:5px; color:var(--ink-70); margin-top:2px}
.decade__years{color:var(--ink-45)}

/* v9: the section remains transparent while this fixed layer follows its
   viewport bounds beneath the anniversary canvas. */
.decade-ground{
  position:fixed; left:0; top:0; width:100%; height:0; z-index:2;
  overflow:hidden;
  pointer-events:none; visibility:hidden; background:var(--void-2);
  box-shadow:inset 0 1px rgba(25,47,88,.08),inset 0 -1px rgba(25,47,88,.08);
  transition:background-color .85s var(--ease);
}
.decade-ground.is-light{background:#f6f0e8}
/* Soft gradient pools stay on the ground, below both the metal and copy.
   Blur merges their edges; the cream showing through keeps the result light. */
.decade-ground::before,.decade-ground::after{
  content:""; position:absolute; pointer-events:none;
  opacity:0; filter:blur(65px);
  transition:opacity .85s var(--ease);
}
.decade-ground::before{
  inset:-12% -8%;
  background:
    radial-gradient(ellipse at 18% 38%,rgba(112,207,218,.62),transparent 48%),
    radial-gradient(ellipse at 78% 68%,rgba(173,156,229,.52),transparent 46%),
    radial-gradient(ellipse at 92% 12%,rgba(255,190,149,.52),transparent 40%);
}
.decade-ground::after{
  width:65%; height:70%; left:20%; bottom:-28%;
  border-radius:43% 57% 64% 36% / 54% 39% 61% 46%;
  background:linear-gradient(125deg,rgba(144,190,236,.46),rgba(243,174,199,.44));
  transform:rotate(-14deg);
}
.decade-ground.is-light::before,.decade-ground.is-light::after{opacity:.7}
@media(max-width:860px){
  .decade-ground::before,.decade-ground::after{filter:blur(42px)}
  .decade-ground::before{
    background:
      radial-gradient(ellipse at 0% 20%,rgba(112,207,218,.56),transparent 42%),
      radial-gradient(ellipse at 100% 62%,rgba(173,156,229,.46),transparent 44%),
      radial-gradient(ellipse at 85% 0%,rgba(255,190,149,.46),transparent 34%);
  }
  .decade-ground::after{width:100%; left:0}
}
@media(prefers-reduced-motion:reduce){
  .decade-ground,.decade-ground::before,.decade-ground::after{transition:none}
}
.void--decade{transition:color .85s var(--ease)}
.void--decade.is-light{--ink:#192f58; --mist:#192f58; --accent:#008c95;
  --ink-70:rgba(25,47,88,.72); --ink-45:rgba(25,47,88,.52);
  --driftwood:#68788c; --border:rgba(25,47,88,.20)}
.void--decade .heading,.void--decade .decade__trio li,
.void--decade .decade__lead,.void--decade .decade__pivot,
.void--decade .decade__list li,.void--decade .decade__close,
.void--decade .decade__years,.void--decade .decade__list b,
.void--decade .rule--dash{transition:color .85s var(--ease),border-color .85s var(--ease)}
.void--decade.is-light .heading,.void--decade.is-light .decade__trio li{color:#192f58}
.void--decade.is-light .rule--dash{border-color:rgba(25,47,88,.24)}


.void--cta{grid-template-columns:1fr; place-items:center; text-align:center; min-height:92svh}
.cta{display:flex; flex-direction:column; align-items:center; gap:2px; max-width:min(1100px,92vw)}
.cta .kicker{justify-content:center}
.cta .rule{max-width:420px; margin:var(--gap) auto}
.cta .hero__dates{align-items:center; margin-bottom:calc(var(--gap) * 2)}
.display--cta{text-align:center}

/* ── Corner light ──────────────────────────────────────────────
   A large circle whose centre sits ON the section's lower-left corner,
   so only its top-right quadrant falls inside the frame. The radius is
   generous from the start; scroll drives brightness, not size, via
   --glow (written per frame by site.js). Falloff approximates the
   reference shader's pow(1 - d, 4): tight core, long faint tail.
   ───────────────────────────────────────────────────────────── */
.glow{
  --glow: .42;
  position:absolute; left:0; bottom:0;
  width:min(150vh, 1480px); aspect-ratio:1;
  z-index:0; pointer-events:none;
  /* centre on the corner → the visible slice is the top-right quarter */
  transform:translate(-50%, 50%) scale(calc(.97 + var(--glow) * .08));
  background:radial-gradient(circle closest-side,
    rgba(0,205,220,1) 0%,    rgba(0,184,202,.78) 18%,
    rgba(0,158,180,.46) 34%, rgba(0,134,160,.22) 56%,
    rgba(0,116,142,.08) 76%, transparent 100%);
  filter:blur(8px);
  /* the next section paints over everything past the boundary, and the
     boundary runs through this circle's centre — fade the core out just
     below it so the occlusion never shows as a step */
  -webkit-mask-image:linear-gradient(to top, transparent 46%, #000 57%);
          mask-image:linear-gradient(to top, transparent 46%, #000 57%);
  opacity:var(--glow);
  will-change:opacity, transform;
}
/* the hero light lives in the fixed stack BELOW the canvas (z-index 1), so
   the ribbons are lit by it and read as metal in front of a light source */
/* Anchored to the TOP-right corner, not the bottom-left. A fixed light at
   the bottom is eaten from below by the next section as it scrolls up, so it
   reads as fading out however hard the opacity ramps. Nothing occludes the
   top until the hero has almost gone. The centre sits a little outside the
   corner so the hottest part of the core stays off-screen and out of the
   nav's way; the visible slice is the circle's lower-left quadrant. */
/* The hero light is far wider than the viewport. Fixed or not, an element
   hanging off the right edge makes mobile Chrome widen the layout viewport
   (390px device reported innerWidth 635), which scales the whole page up.
   This layer clips it to the viewport. */
.glowlayer{position:fixed; inset:0; overflow:hidden; z-index:0; pointer-events:none}
.glow--hero{
  position:fixed; right:0; top:0; left:auto; bottom:auto; z-index:0;
  width:min(178vh, 1780px);
  transform:translate(50%, -50%) scale(calc(.97 + var(--glow) * .08));
  -webkit-mask-image:none; mask-image:none;
}
/* content rides above the light. NB: .hero__mark and .scrollcue are already
   absolutely positioned — only lift them, never restate `position`. */
.void__col, .void__object, .cta, .hero__grid{position:relative; z-index:1}
.hero__mark, .scrollcue{z-index:1}

/* ── Triptych ──────────────────────────────────────────────── */
#pillars,#speakers{
  overflow:hidden;
  background:
    radial-gradient(100% 145% at 0% 0%,#233848 0%,#101d2d 49%,#070d19 100%);
}
.pillars-cursor{position:absolute; inset:0; z-index:0; pointer-events:none; opacity:.65}
.pillars-cursor canvas{display:block; width:100%; height:100%}
#pillars::after,#speakers::after{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:linear-gradient(180deg,rgba(7,13,25,.25),rgba(7,13,25,.12) 45%,rgba(7,13,25,.45));
}
#pillars > .wrap,#speakers > .wrap{position:relative; z-index:1}
/* ═══ ABOUT — THE PROGRESSION RAIL ═════════════════════════════
   Goal → Strategy → Success is a sequence, not three equal boxes, so it is
   drawn as one line with three stations rather than the card grid the earlier
   version used. The spine borrows .rule's scaleX draw, so the line arrives the
   same way every other divider on the page does. */
#about > .wrap > .kicker{margin-bottom:24px}
#about > .wrap > .rail__kicker{margin:clamp(60px, 7.4vw, 112px) 0 0}
.lede{
  display:grid; grid-template-columns:minmax(0,1.08fr) minmax(0,1fr);
  gap:clamp(28px, 4.4vw, 80px); align-items:end;
}
.lede__body{max-width:48ch; padding-bottom:.4em}

.rail{
  position:relative; list-style:none; padding:0;
  margin:clamp(28px, 2.8vw, 40px) 0 0;
  display:grid; grid-template-columns:repeat(4, minmax(0,1fr));
  gap:clamp(22px, 3.2vw, 48px);
}
/* the spine runs along the top edge of the list, so every station sits on it */
.rail::before{
  content:""; position:absolute; left:0; right:0; top:0; height:1px;
  background:linear-gradient(90deg,
    var(--accent) 0%, rgba(0,180,190,.34) 62%, rgba(0,180,190,.08) 100%);
  transform:scaleX(0); transform-origin:0 50%;
  transition:transform 1.5s var(--ease) .12s;
}
html.js .rail.is-in::before{transform:scaleX(1)}
.rail__step{
  position:relative; min-width:0;
  padding:clamp(26px, 2.6vw, 40px) 0 0;
}
/* the station: a dot centred on the spine, plus a ::after ring that pings out
   of it. Both are gated on .is-in so the three stations pulse in sequence as
   the line draws, and both animate transform/opacity only — a box-shadow
   spread would repaint the section on every frame. */
.rail__step::before,.rail__step::after{
  content:""; position:absolute; top:-4px; left:0;
  width:9px; height:9px; border-radius:50%;
}
.rail__step::before{background:var(--accent); box-shadow:0 0 0 5px rgba(0,180,190,.12)}
/* a ring, not a filled disc: scaled to 4× a solid one reads as a blob over the
   icon behind it, where an outline reads as a ping. box-sizing is border-box
   globally, so the border does not change the 9px box the dot is centred in. */
.rail__step::after{border:1px solid var(--accent); opacity:0; pointer-events:none}
/* linear, not var(--ease): the page's ease is a hard ease-out, which spends
   the ring's whole budget in the first ~300ms and reads as a flick. Linear
   over the same window reads as a steady expansion. The opacity peak sits at
   8% so the ring fades UP out of the dot rather than appearing full strength. */
html.js .rail.is-in .rail__step::before{
  animation:railDot 4.24s linear infinite;
  animation-delay:calc(.6s + var(--i,0) * 1.06s);
}
html.js .rail.is-in .rail__step::after{
  animation:railPing 4.24s linear infinite;
  animation-delay:calc(.6s + var(--i,0) * 1.06s);
}
@keyframes railDot{
  0%,70%,100%{transform:scale(1)}
  7%{transform:scale(1.45)}
  22%{transform:scale(1)}
}
@keyframes railPing{
  0%{transform:scale(1); opacity:0}
  8%{transform:scale(1.25); opacity:.8}
  62%{transform:scale(4.2); opacity:0}
  100%{transform:scale(4.2); opacity:0}
}
@media(prefers-reduced-motion:reduce){
  .rail__step::before,.rail__step::after{animation:none !important}
  .rail__step::after{display:none}
}
/* Line-art plates in the countdown sketches' style: white strokes on a
   transparent ground, resting dim like the countdown plates.
   The ripple borrows the countdown's reveal (teal surge, white-hot core, gain
   that lifts the faint construction lines) but drives it from a ring that
   expands out of the centre, echoing the station's ping above it. It is CSS
   rather than a fourth WebGL context: the ring is a fixed shape, so nothing
   has to run between ripples. .rail__hot is a gradient band masked by a
   gain-lifted copy of the plate (gain-*-hot.webp), and the band's radius is
   the animated --rr. */
@property --rr{syntax:'<percentage>'; inherits:false; initial-value:-40%}
.rail__icon{
  position:relative; display:block; width:clamp(76px, 6.4vw, 108px);
  margin:0 0 clamp(12px, 1.2vw, 18px) -6px;
}
.rail__icon img{display:block; width:100%; height:auto; opacity:.62;
  transition:opacity .5s var(--ease)}
.rail__hot{
  position:absolute; inset:0; pointer-events:none;
  --rr:-40%;
  background:radial-gradient(circle closest-side,
    transparent calc(var(--rr) - 30%),
    rgba(0,226,240,.55) calc(var(--rr) - 14%),
    #fff calc(var(--rr) - 2%),
    rgba(0,226,240,.9) calc(var(--rr) + 4%),
    transparent calc(var(--rr) + 10%));
  -webkit-mask:var(--hot) center / contain no-repeat;
          mask:var(--hot) center / contain no-repeat;
  mix-blend-mode:plus-lighter;
}
@keyframes railRipple{
  0%{--rr:-12%; opacity:1}
  80%{opacity:1}
  100%{--rr:165%; opacity:0}
}
/* The title takes the same pass as a sweep: a teal-to-white band crosses the
   letters left to right as the ring expands, so icon and heading read as one
   event. The text is filled through background-clip, with the band position
   as the animated --tx; at rest the whole fill is the accent. */
@property --tx{syntax:'<percentage>'; inherits:false; initial-value:-30%}
.rail .tag{
  --tx:-30%;
  background:linear-gradient(90deg,
    var(--accent) calc(var(--tx) - 22%),
    #fff var(--tx),
    var(--accent) calc(var(--tx) + 22%)) 0 0 / 100% 100% no-repeat;
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent;
  display:inline-block;
}
@keyframes railSweep{
  0%{--tx:-30%}
  100%{--tx:130%}
}
/* pointer: the ripple fires once per entry (site.js adds .is-ripple to the
   step and drops it on animationend, so leaving mid-ripple does not cut it off) */
.rail__step:hover .rail__icon img{opacity:.92}
.rail__step.is-ripple .rail__hot{animation:railRipple 1.15s linear}
.rail__step.is-ripple .tag{animation:railSweep .95s linear .12s}
/* touch: no hover, so the ripple rides the station's 4.24s ping instead */
@media (hover:none), (pointer:coarse){
  html.js .rail.is-in .rail__hot{
    animation:railRippleBeat 4.24s linear infinite;
    animation-delay:calc(.6s + var(--i,0) * 1.06s);
  }
  html.js .rail.is-in .tag{
    animation:railSweepBeat 4.24s linear infinite;
    animation-delay:calc(.72s + var(--i,0) * 1.06s);
  }
  @keyframes railSweepBeat{
    0%{--tx:-30%}
    23%,100%{--tx:130%}
  }
  @keyframes railRippleBeat{
    0%{--rr:-12%; opacity:1}
    22%{opacity:1}
    28%,100%{--rr:165%; opacity:0}
  }
}
@media(prefers-reduced-motion:reduce){
  .rail__hot{display:none}
  .rail .tag{animation:none !important}
  .rail__icon img{opacity:.8}
}
.rail .tag{margin-bottom:12px}
.rail .sub{max-width:38ch}

/* The steps rise as units rather than carrying [data-reveal] each: the spine
   is one observer, and a station that waited for its own threshold would
   arrive after the line had already run past it. */
html.js .rail__step{
  opacity:0; transform:translateY(20px);
  transition:opacity .9s var(--ease), transform 1.05s var(--ease);
  transition-delay:calc(.3s + var(--i,0) * 150ms);
}
html.js .rail.is-in .rail__step{opacity:1; transform:none}

@media (max-width:860px){
  .lede{grid-template-columns:1fr; gap:var(--gap); align-items:start}
  .lede__body{padding-bottom:0}
  /* the rail turns on its side: spine down the left, stations beside it */
  .rail{grid-template-columns:1fr; gap:0; padding-left:26px}
  .rail::before{
    top:0; bottom:0; left:0; right:auto; width:1px; height:auto;
    background:linear-gradient(180deg,
      var(--accent) 0%, rgba(0,180,190,.34) 62%, rgba(0,180,190,.08) 100%);
    transform:scaleY(0); transform-origin:50% 0;
    transition:transform 1.5s var(--ease) .12s;
  }
  html.js .rail.is-in::before{transform:scaleY(1)}
  .rail__step{padding:0 0 clamp(28px, 6.4vw, 44px)}
  .rail__step::before,.rail__step::after{top:9px; left:-30px}   /* the ping ring follows its dot */
  .rail__icon{width:72px; margin-bottom:8px}
  .rail .sub{max-width:none}
}

.triptych{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--gap); margin-top:calc(var(--gap) * 2)}
.triptych article{
  position:relative; min-width:0; padding:clamp(24px,2.4vw,36px);
  border:1px solid rgba(192,202,214,.24);
  background:radial-gradient(80% 95% at 25% 0%,rgba(192,202,214,.12),transparent 80%),rgba(7,13,25,.42);
}
.triptych article:nth-child(2){margin-top:24px}
.triptych article:nth-child(3){margin-top:48px}
.triptych article::before{
  content:"+"; position:absolute; top:-13px; left:-7px;
  font-size:19px; line-height:1; font-weight:400; color:var(--accent);
}
.triptych .tag{margin-bottom:clamp(36px,5vw,70px); font-size:14px; letter-spacing:.08em; color:var(--mist)}
.triptych .body{max-width:34ch}
.triptych--tight .sub{max-width:38ch}
#pillars .heading--center{margin-bottom:var(--gap)}

/* ── Gain ──────────────────────────────────────────────────── */
/* ── Speakers ──────────────────────────────────────────────── */
.roll-line{display:block}
.roll-word{display:inline-block; vertical-align:top; white-space:nowrap}
.roll-cell{position:relative; display:inline-block; vertical-align:top}
.roll-measure{display:block; visibility:hidden}
.roll-clip{position:absolute; inset:0; overflow:hidden}
.roll-reel{display:block; will-change:transform}
.roll-reel > span{display:block; height:.9em; line-height:.9}
[data-roll].is-in .roll-reel{
  animation:letterRoll var(--duration) cubic-bezier(.16,1,.3,1) var(--delay) both;
}
@keyframes letterRoll{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,var(--travel),0)}}
.speakers__intro{
  max-width:none; margin:var(--gap) 0 calc(var(--gap) * 1.5); color:var(--ink-70);
  font-size:clamp(1rem,1.35vw,1.25rem); line-height:1.5;
}
#speakers > .wrap > .kicker{margin-bottom:18px; line-height:1}
#speakers > .wrap > .heading{margin-top:0}
.speakers{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--gut); margin-top:calc(var(--gap) * 2.5)}
.spk{border-top:1px solid var(--border); padding-top:var(--pad)}
#speakers .spk{
  position:relative;
  container-type:inline-size;
  display:flex; flex-direction:column; align-items:center; text-align:center;
  min-width:0;
  border:1px solid rgba(192,202,214,.24); padding:clamp(24px,2.4vw,36px);
  background:radial-gradient(80% 95% at 25% 0%,rgba(192,202,214,.12),transparent 80%),rgba(7,13,25,.42);
  transition:opacity .9s var(--ease) calc(var(--i,0) * 90ms),
    transform 1.1s var(--ease) calc(var(--i,0) * 90ms),
    border-color .75s cubic-bezier(.4,0,.2,1);
}
@media (hover:hover) and (prefers-reduced-motion:no-preference){
  #speakers .spk:hover{border-color:rgba(0,180,190,.65)}
}
#speakers .spk:focus-within{border-color:var(--accent)}
.spk .tag{margin-bottom:28px; color:var(--accent)}
.spk .heading-sm{margin-bottom:10px}
.spk .sub{
  max-width:none; color:var(--ink-70); white-space:nowrap;
  font-size:clamp(.625rem,4.6cqw,.9rem);
}
.spk--profile{position:relative}
.spk__thumb{
  width:min(200px,100%); height:auto; aspect-ratio:1; margin:0 0 24px; object-fit:cover;
  border-radius:50%; filter:grayscale(1); border:1px solid var(--border);
  transition:filter .65s cubic-bezier(.4,0,.2,1);
}
.spk__thumb--placeholder{filter:none}
.spk--profile:hover .spk__thumb,
.spk--profile:focus-within .spk__thumb{filter:grayscale(0)}
.spk__bio-trigger{
  display:inline-flex; align-items:center; gap:14px; margin-top:24px;
  padding:0 0 7px; border-bottom:1px solid var(--accent);
  color:var(--accent); font-size:11px; font-weight:700;
  letter-spacing:.12em; text-transform:uppercase;
}
.spk__bio-trigger span{font-size:18px; line-height:.7; font-weight:400}
/* Extend the native button's hit area over the card, preserving keyboard use. */
.spk__bio-trigger::after{content:""; position:absolute; inset:0; cursor:pointer}
.spk__bio-trigger:focus-visible::after{outline:2px solid var(--accent); outline-offset:4px}
.spk__bio-trigger:hover,.spk__bio-trigger:focus-visible{color:var(--mist); border-color:var(--mist)}

/* The speaker bio lives outside #smooth so its modal layer stays fixed even
   while the page's smooth-scroll shell is translated. */
.speaker-drawer{
  position:fixed; inset:0 0 0 auto; width:min(760px,100vw); height:100dvh;
  max-width:none; max-height:none; margin:0; padding:0; border:0;
  overflow-y:auto; background:var(--void-2); color:var(--ink);
  box-shadow:-24px 0 80px rgba(0,0,0,.35);
  transform:translateX(100%); transition:transform .58s var(--ease);
}
.speaker-drawer.is-open{transform:none}
.speaker-drawer::backdrop{background:rgba(0,0,0,.66)}
.speaker-drawer__inner{padding:clamp(28px,4.5vw,66px)}
.speaker-drawer__top{display:flex; justify-content:space-between; align-items:center; gap:20px; padding-bottom:18px; border-bottom:1px solid var(--border)}
.speaker-drawer__top .tag{color:var(--accent)}
.speaker-drawer__close{
  flex:none; display:grid; place-items:center; width:42px; height:42px;
  border:1px solid var(--border); border-radius:50%;
  font-size:30px; line-height:1; font-weight:300;
}
.speaker-drawer__close:hover,.speaker-drawer__close:focus-visible{border-color:var(--accent); color:var(--accent)}
.speaker-drawer__hero{display:grid; grid-template-columns:minmax(0,180px) minmax(0,1fr); gap:clamp(22px,3vw,40px); align-items:end; margin:40px 0}
.speaker-drawer__hero img{width:100%; aspect-ratio:1; object-fit:cover; filter:grayscale(1); border-radius:var(--r-card)}
.speaker-drawer__hero .kicker{margin-bottom:12px; color:var(--accent)}
.speaker-drawer__hero .heading{font-size:clamp(2rem,4vw,3.6rem); margin-bottom:14px}
.speaker-drawer__hero .sub{color:var(--ink-70)}
.speaker-drawer__copy{display:flex; flex-direction:column; gap:20px; padding-bottom:40px; font-size:clamp(1rem,1.2vw,1.16rem); line-height:1.65; font-weight:400; color:var(--ink-70)}
.speaker-drawer__lead{font-size:clamp(1.2rem,1.8vw,1.6rem); line-height:1.4; color:var(--mist)}
.speaker-drawer__signoff{margin-top:12px; padding-top:20px; border-top:1px solid var(--border); color:var(--accent); font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase}
@media (min-width:900px){
  /* The desktop profile uses half the viewport, giving the long biography
     enough line length to settle into one screen in normal laptop heights. */
  .speaker-drawer{width:50vw; overflow-y:auto}
  .speaker-drawer__inner{padding:clamp(28px,3vw,48px)}
  .speaker-drawer__hero{grid-template-columns:minmax(0,150px) minmax(0,1fr); gap:28px; margin:28px 0}
  .speaker-drawer__hero .heading{font-size:clamp(2rem,3vw,3rem)}
  .speaker-drawer__copy{gap:14px; padding-bottom:20px; font-size:1rem; line-height:1.5}
  .speaker-drawer__lead{font-size:clamp(1.15rem,1.5vw,1.4rem); line-height:1.35}
}
@media (max-width:560px){
  .speaker-drawer__hero{grid-template-columns:1fr; align-items:start}
  .speaker-drawer__hero img{width:150px}
}

/* ── Agenda ────────────────────────────────────────────────── */
#agenda > .wrap > .kicker{margin-bottom:24px}
#agenda > .wrap > .heading{margin-top:0}
/* Keep the two conference days equally sized in a compact day picker. */
.tabs{
  display:grid; grid-template-columns:repeat(2, minmax(0, 1fr));
  gap:10px; max-width:min(100%, 640px);
  margin:calc(var(--gap) * 2) 0 var(--gap);
}
.tab{
  display:flex; flex-direction:column; gap:6px; align-items:flex-start;
  border:1px solid var(--border); border-radius:6px;
  padding:13px 20px; text-align:left;
  transition:border-color .4s var(--ease), background .4s var(--ease);
}
.tab b{font-size:14px; text-transform:uppercase; font-weight:500; color:var(--ink-70); transition:color .4s var(--ease)}
.tab em{font-style:normal; font-size:18px; font-weight:700; line-height:1.3; color:var(--mist)}
.tab:hover{border-color:var(--driftwood)}
.tab.is-on{background:rgba(0,180,190,.08); border-color:var(--accent)}
.tab.is-on b{color:var(--mist)}
.tab.is-on em{color:var(--mist)}

.panel{display:none}
.panel.is-on{display:block; animation:panelIn .7s var(--ease) both}
@keyframes panelIn{from{opacity:0; transform:translateY(14px)}to{opacity:1; transform:none}}
.agenda__day{
  display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap;
  gap:12px 24px; padding:8px 0 24px;
}
.agenda__day .tag{color:var(--accent); white-space:nowrap}

/* The table had the full 1464px to itself, so the toggle sat some 600px clear
   of the longest title with nothing in between. A measure plus a duration
   column closes that gap and gives the space a job. */
.agenda__note{
  display:flex; flex-direction:column; gap:5px;
  max-width:640px; margin:0 0 calc(var(--gap) * 1.5); padding:16px 20px;
  border-left:3px solid var(--accent); border-radius:0 6px 6px 0;
  background:rgba(0,180,190,.09); line-height:1.5;
}
.agenda__note strong{font-size:17px; font-weight:700; color:var(--mist)}
.agenda__note span{font-size:14px; color:var(--ink-70)}
.panels{max-width:min(100%, 1180px)}
.row{border-top:1px dashed var(--border)}
.row:last-child{border-bottom:1px dashed var(--border)}
.row summary{
  display:grid; grid-template-columns:104px minmax(0,1fr) auto 22px;
  gap:clamp(18px, 2.4vw, var(--gut));
  align-items:center; padding:var(--pad) 0; cursor:pointer; list-style:none;
  transition:color .4s var(--ease), background .4s var(--ease);
}
/* a quiet full-row hover, so a wide row still reads as one target */
.row summary:hover{background:linear-gradient(90deg, rgba(25,47,88,.30), rgba(25,47,88,.06) 72%, transparent)}
.row__dur{
  font-size:var(--t-micro); color:var(--ink-45); white-space:nowrap;
  font-variant-numeric:tabular-nums; justify-self:end;
}
.row summary::-webkit-details-marker{display:none}
.row__t{font-size:var(--t-micro); text-transform:uppercase; color:var(--ink-45); font-variant-numeric:tabular-nums}
.row__h{font-size:clamp(1rem,1.45vw,1.35rem); line-height:1.15; text-transform:uppercase; font-weight:500}
.row--key .row__h{color:var(--mist)}
.row--key .row__t{color:var(--accent)}
/* session eyebrow ("Keynote • Purpose"), styled like the drawer's speaker kicker */
.row__eyebrow{
  display:flex; align-items:center; gap:8px; margin-bottom:8px;
  font-size:var(--t-micro); line-height:1; font-weight:500; color:var(--accent);
}
.row__x{position:relative; width:14px; height:14px; justify-self:end}
.row__x::before,.row__x::after{
  content:""; position:absolute; left:0; top:6px; width:14px; height:1px; background:var(--driftwood);
  transition:transform .45s var(--ease), background .45s var(--ease);
}
.row__x::after{transform:rotate(90deg)}
.row[open] .row__x::after{transform:rotate(0)}
.row[open] .row__x::before,.row[open] .row__x::after{background:var(--silver)}
.row:hover .row__h{color:var(--mist)}
.row__body{
  padding:0 0 var(--pad) calc(110px + var(--gut));
  display:flex; flex-direction:column; gap:12px;
  animation:panelIn .55s var(--ease) both;
}
.row__body .tag{margin-bottom:0}
.obj{display:flex; flex-direction:column; gap:8px}
.obj li{
  position:relative; padding-left:20px;
  font-size:var(--t-sub); line-height:1.5; font-weight:400; color:var(--ink-45); max-width:80ch;
}
.obj li::before{content:""; position:absolute; left:0; top:.68em; width:10px; height:1px; background:var(--driftwood)}

/* ── Voices ────────────────────────────────────────────────────
   A horizontal track of quotes driven by vertical scroll. The stage is
   pinned in JS rather than with position:sticky — sticky does not pin
   inside the fixed+transformed smooth-scroll wrapper (verified: the probe
   scrolled away instead of holding). Only the active quote is framed, by
   a dashed marquee drawn on canvas. */
.voices{position:relative; background:transparent; height:340svh}
.voices__stage{
  position:absolute; inset:0 0 auto 0; height:100svh;
  display:flex; flex-direction:column; justify-content:center;
  gap:calc(var(--gap) * 2); will-change:transform;
  /* The headline sweeps in from beyond the right edge. Anything hanging off
     that edge makes mobile Chrome widen the LAYOUT viewport (390px device
     reported scrollWidth 407), which rescales the page and desynchronises the
     WebGL canvases from the boxes they are keyed to. Clip it here. */
  overflow:hidden;
}
/* Touch scrolling runs ahead of JavaScript; let the browser pin the stage.
   overflow:clip prevents horizontal spill without creating a scroll ancestor. */
html:not(.has-smooth) body{overflow-x:clip}
html:not(.has-smooth) .voices__stage{
  position:sticky; top:0; transform:none; will-change:auto;
}
.voices__head{--voices-title-size:clamp(1.7rem,5.4vw,4.6rem);
  display:flex; flex-direction:column; align-items:flex-start; gap:clamp(6px,.6vw,10px);
  position:relative; z-index:1}
.voices__head .kicker{
  margin-bottom:0; will-change:transform;
  font-size:calc(var(--voices-title-size) * .78); line-height:1.1;
  font-weight:500; letter-spacing:-.02em; color:var(--accent);
}
/* the small section eyebrow, matching Agenda — undo the oversized lead kicker */
.voices__head .voices__eyebrow{
  font-size:var(--t-micro); line-height:1; letter-spacing:normal;
  color:var(--ink-70); will-change:auto; margin-bottom:14px;
  transition:opacity .6s ease;
}
/* set by the flip loop until "Ten Years." lands; never set without JS
   or under reduced motion, so the eyebrow stays visible there */
.voices__head .voices__eyebrow.is-waiting{opacity:0; transition-duration:.2s}
.voices__title{
  position:relative;
  font-size:var(--voices-title-size); line-height:1.02; font-weight:500;
  letter-spacing:-.02em; color:var(--ink); margin:0;
  width:max-content; max-width:100%;
}
/* The ghost sizes the box the ink is painted into. Both must be nowrap: if one
   wraps while the other does not, the sweep is measured against a box that is
   not the text, and the tail is clipped by .voices__stage's overflow:hidden. */
.voices__title-size{visibility:hidden; white-space:nowrap}
.voices__title-ink{
  position:absolute; left:0; top:50%; transform:translateY(-50%);
  width:max-content; white-space:nowrap; line-height:inherit;
}
/* The reference blooms a light behind the headline at its peak, which is what
   turns "big type" into a moment. Driven by the same sweep/land windows. */
.voices__bloom{
  position:absolute; left:0; top:50%; transform:translateY(-50%);
  width:100%; height:64svh; z-index:0; opacity:0; pointer-events:none;
  background:
    radial-gradient(52% 46% at 38% 50%, rgba(242,245,246,.13) 0%, transparent 72%),
    radial-gradient(38% 34% at 62% 50%, rgba(0,180,190,.12) 0%, transparent 76%);
  filter:blur(18px);
}
.voices__seam{transform-origin:50% 50%}

.voices__viewport{position:relative; z-index:2; width:100%; overflow:hidden; padding:48px 0}
/* A static box at the centre of the viewport: the quotes centre themselves on
   it, and the headline rides its axis while it is large. It draws nothing. */
.voices__slot{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(560px, 68vw); height:300px; pointer-events:none;
}
.voices__track{
  display:flex; align-items:center; gap:clamp(40px, 6vw, 96px);
  will-change:transform;
}

/* v10: the carousel now carries three impact metrics rather than five quotes.
   The box metrics are unchanged — voices() centres cards by their measured
   width, so the stepping maths carries over untouched. */
.stat{
  /* fixed height, not content height: the stage parks a constant rectangle at
     the centre, so every card has to occupy the same box */
  flex:none; width:min(560px, 68vw); height:300px;
  display:flex; flex-direction:column; justify-content:center; gap:14px;
  padding:34px 38px; border-radius:0;
  opacity:.34; filter:blur(3px); transform:scale(.88);
  transform-origin:50% 50%;
  transition:opacity .55s var(--ease), filter .55s var(--ease),
             transform .55s var(--ease);
}
.stat.is-active{opacity:1; filter:blur(0); transform:scale(1)}

/* Each card carries its own ground, drawn from the brand palette and kept
   dark enough that the figures hold contrast. */
.stat:nth-child(1){background:linear-gradient(152deg, #172C50 0%, #101E38 100%)}
.stat:nth-child(2){background:linear-gradient(152deg, #0D3742 0%, #0B2430 100%)}
.stat:nth-child(3){background:linear-gradient(152deg, #1D2A3E 0%, #131C2B 100%)}

.stat__label{
  font-size:11px; font-weight:500; letter-spacing:.16em;
  text-transform:uppercase; color:var(--accent);
}
/* the same brushed-metal treatment the countdown gives its figures, so the
   two number moments on the page read as one family */
.stat__value{
  font-size:clamp(3rem, 5.6vw, 4.8rem); line-height:.92; font-weight:700;
  letter-spacing:-.03em; font-variant-numeric:tabular-nums;
  color:var(--silver);
}
/* the cents-sized tail keeps 3,173.34 from reading as a bigger round number */
.stat__frac{font-size:.52em; letter-spacing:-.01em}
.stat__of{font-size:.42em; font-weight:500; letter-spacing:.02em; margin-left:.12em}
.stat__value{position:relative}
.stat__placeholder{display:none; position:absolute; left:0; top:0; color:var(--silver)}
.metric-tickers-ready .stat:not([data-metric-revealed]) .stat__placeholder{display:inline}
.metric-tickers-ready .stat:not([data-metric-revealed]) .stat__static{opacity:0}
.stat__static{
  background:linear-gradient(168deg,#ffffff 0%,var(--silver) 30%,#7d8794 52%,#e8ecef 68%,var(--silver) 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
.stat__value.is-rolling .stat__static{opacity:0}
.stat__rolling{position:absolute; left:0; top:0; display:flex; align-items:baseline; white-space:nowrap; color:#e8ecef}
.stat__rolling number-flow{display:inline-block; line-height:.92; --number-flow-mask-height:0em; --number-flow-mask-width:.04em}
.stat__note{
  font-size:clamp(.95rem, 1.1vw, 1.08rem); line-height:1.45;
  font-weight:400; color:var(--ink-70);
}

/* Two identical star rows: the lit one is clipped to the score, so the
   partial fifth star is the real 0.79 rather than a rounded-up whole. */
.stat__stars{position:relative; display:block; width:min(190px, 52%); line-height:0}
.stat__stars-svg{display:block; width:100%; height:auto}
.stat__stars-bed .stat__stars-svg{fill:rgba(192,202,214,.30)}
.stat__stars-lit{position:absolute; inset:0; clip-path:inset(0 calc(100% - var(--fill)) 0 0)}
.stat__stars-lit .stat__stars-svg{fill:var(--accent)}

/* a directive, not fine print — matches the hero's scroll cue */
.voices__cue{text-align:center; font-size:12px; color:#fff}

@media (max-width:860px){
  .voices{height:300svh}
  .stat{width:82vw; height:264px; padding:26px 28px; gap:10px}
  .voices__slot{width:82vw; height:264px}
  .voices__track{gap:32px}
}

/* ── Footer ────────────────────────────────────────────────── */
.foot{position:relative; z-index:4; background:var(--void); padding:0 var(--gut) var(--gut)}
.foot__row{display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr); align-items:start; gap:var(--gut); padding:var(--gap) 0}
.foot__credential-mark{width:200px; max-width:100%; height:auto}
.foot__contact{text-align:left}
.foot__terms{justify-self:center}
.foot__credentials{text-align:right}
.foot__credential-mark{margin-left:auto}
.foot .legal{max-width:80ch; padding-top:var(--gap)}
/* all footer copy is white, links included */
.foot .micro,.foot .legal,.foot .link{color:#fff}
.foot__credentials .legal{margin-left:auto; text-align:right}

/* ═══ REVEAL CHOREOGRAPHY ══════════════════════════════════════ */
html.js [data-reveal]{
  opacity:0; transform:translateY(22px);
  transition:opacity .9s var(--ease), transform 1.1s var(--ease);
  transition-delay:calc(var(--i,0) * 90ms);
}
html.js [data-reveal].is-in{opacity:1; transform:none}
html.js .rule[data-reveal]{transform:scaleX(0); transform-origin:0 50%; opacity:1}
html.js .rule[data-reveal].is-in{transform:scaleX(1)}

.line{display:block; overflow:hidden}
.line > span{
  display:block; transform:translateY(115%);
  transition:transform 1.15s var(--ease);
  transition-delay:calc(var(--i,0) * 85ms);
}
[data-split].is-in .line > span{transform:none}

/* ── The cycling word ───────────────────────────────────────────
   After x.ai's hero rotator, measured from the live page: the word's box
   ANIMATES ITS WIDTH between words while each character fades, un-blurs and
   rises on a stagger; a rounded 3px rule sits under the slot and stretches
   with it. The width is what makes it feel like type rather than a ticker —
   nothing jumps.

   An off-flow ghost carries the incoming word purely to be measured; the slot
   then gets that width in px, because `width: auto` does not transition. */
/* "of" stays at headline size; the rotating word is the same size and differs
   only in colour. Deliberately inline rather than flex: inline flow already
   sits it on the line's baseline, and `.line > span{display:block}`
   out-specifies a `.line__row` flex anyway. `position: relative` matters —
   the underline is absolutely placed against THIS box, so without it the rule
   stretches to the whole headline. */
.cycle{
  display:inline-block; position:relative; font-style:normal;
  vertical-align:baseline; color:var(--accent);
  margin-left:.28em;   /* one word space */
}
.cycle__slot{
  display:inline-block; position:relative; width:0;
  transition:width .58s var(--ease);
  /* horizontal clip with vertical bleed, so letters are masked at the sides
     but ascenders and descenders are never cropped */
  clip-path:inset(-.35em 0px);
}
.cycle__ghost{
  position:absolute; left:0; top:0; visibility:hidden;
  white-space:nowrap; pointer-events:none;
}
.cycle__word{display:flex; white-space:nowrap}
.cycle__ch{
  display:inline-block; opacity:1; filter:blur(0); transform:none;
  transition:opacity .40s var(--ease), filter .40s var(--ease),
             transform .48s var(--ease);
  transition-delay:calc(var(--i,0) * 28ms);
}
/* the exit runs on a tighter stagger than the entrance: the whole word has to
   clear inside the swap gap, or its tail is still on screen when the next one
   is rendered over it */
.cycle__ch.is-out{
  opacity:0; filter:blur(4px); transform:translateY(-.30em);
  transition-duration:.30s, .30s, .34s;
  transition-delay:calc(var(--i,0) * 16ms);
}
/* the pre-roll state is set without a transition, or the letter travels
   through the slot on its way to the start mark */
.cycle__ch.is-pre{
  opacity:0; filter:blur(4px); transform:translateY(.30em); transition:none;
}
/* The rule x.ai runs under the word, and its shimmer, both measured off the
   live page. The trick is a 400%-wide gradient carrying a narrow colour band
   across its middle third: at background-position 0 the band sits off the
   right edge, so the bar reads as a plain neutral hairline, and animating the
   position from 125% to 0 sweeps the band in from the left and out the right.
   Theirs is a rainbow; this is the brand's own run — teal through silver into
   navy. (x.ai measured: size 400%, position 121.67% → -4.99%.) */
.cycle__rule{
  position:absolute; left:0; right:0; bottom:-.18em; height:3px;
  border-radius:999px; pointer-events:none;
  /* five clearly separated steps, not four shades of one hue: the previous
     run was three teals and a blue and read as a single teal smear */
  background-image:linear-gradient(90deg,
    rgba(192,202,214,.26) 0%,  rgba(192,202,214,.26) 35%,
    #192F58 40%, #4C6FCB 45%, #00B4BE 50%, #9FD9DE 55%, #E8EDF2 60%,
    rgba(192,202,214,.26) 65%, rgba(192,202,214,.26) 100%);
  background-size:400% 100%;
  background-position:0 50%;
}
/* x.ai's timing exactly: 3s, LINEAR, 150% → -150%. Two details fall out of
   those numbers and neither is obvious.

   The range spans 300% of (W - 4W) = 9W of travel, while the gradient repeats
   every 4W — so the band crosses the bar TWICE in one run, not once. That is
   the double flash per word. (Solve for it: the band is on screen while
   position sits in [13.3%, 86.7%] and again in [-120%, -46.7%].)

   And it must be linear. An eased curve spends its time at the ends, which is
   exactly where the band is off-frame. */
.cycle__rule.is-sweep{animation:ruleSweep 3.3s linear .16s}
@keyframes ruleSweep{
  from{background-position:150% 50%}
  to{background-position:-150% 50%}
}
.sr-only{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}

/* per-glyph reveal — each character rotates up from below on a stagger */
.ch{
  /* <i> is italic by default and .kicker never resets it */
  display:inline-block; font-style:normal; transform-origin:0 100%;
  transform:translateY(.55em) rotate(9deg); opacity:0;
  transition:transform .85s var(--ease), opacity .5s var(--ease);
  transition-delay:calc(var(--i,0) * 24ms);
}
.ch--sp{transition:none}
[data-chars].is-in .ch{transform:none; opacity:1}
html:not(.js) .ch{transform:none; opacity:1}

/* ═══ RESPONSIVE ═══════════════════════════════════════════════ */
@media (max-width:1100px){
  .void{grid-template-columns:1fr; justify-items:start; gap:calc(var(--gap) * 2); min-height:auto}
  .void__object{order:-1; width:min(420px,72vw); align-self:center; justify-self:center}
  .void__col--r{max-width:46ch}
  .speakers{grid-template-columns:1fr; gap:calc(var(--gut) * 1.6)}
  .spk .tag{margin-bottom:24px}
  .triptych{grid-template-columns:1fr; gap:var(--gap)}
  .triptych article:nth-child(2),.triptych article:nth-child(3){margin-top:0}
  .hero__grid{grid-template-columns:1fr}
  .card{justify-self:start; max-width:520px}
  /* The mark takes the full column here and the SLOT carries the object's
     size, so the lockup below it is not boxed into the 10's width — at 268px
     "Celebrating 10 years" wraps and the whole point of the line is lost. */
  .hero__mark{position:static; transform:none; width:100%;
    align-self:flex-start; margin-bottom:calc(var(--gap) * 2)}
  .hero__mark-slot{width:min(268px,58vw)}
  .hero__lockup{position:static; transform:none; width:100%; max-width:none;
    margin-top:18px}
}
@media (max-width:860px){
  :root{--t-body:clamp(1.05rem,3.4vw,1.35rem)}
  /* at this size the line wraps mid-phrase and strands the dot at the end of
     the first row — stack the two parts instead and drop the separator */
  /* nowrap matters: .kicker sets flex-wrap:wrap, and with column direction
     that wraps into COLUMNS — "Virtual" lands beside "Conference" */
  .kicker--lead{flex-direction:column; flex-wrap:nowrap;
    align-items:flex-start; gap:2px}
  .kicker--lead .kicker__dot{display:none}
  /* let the hero run to its natural height rather than crushing into 100svh */
  .hero{min-height:auto; padding-top:132px; padding-bottom:56px}
  .hero__grid{gap:calc(var(--gap) * 2)}
  .scrollcue{display:none}
  .card{padding:18px; max-width:none}
  .card__body{font-size:.95rem; margin-bottom:18px}
  .void{padding-top:calc(var(--sect) * .8); padding-bottom:calc(var(--sect) * .8)}
  .nav__list{
    position:fixed; inset:0 0 auto 0; flex-direction:column; gap:var(--gap);
    background:rgba(7,13,25,.96); backdrop-filter:blur(18px);
    padding:96px var(--gut) var(--gut); border-bottom:1px solid var(--border);
    transform:translateY(-102%); transition:transform .7s var(--ease); z-index:-1;
  }
  .nav.is-open .nav__list{transform:none}
  .nav__list .nav__link{font-size:24px}
  .nav__toggle{display:flex}
  .nav__cta{margin-left:auto; margin-right:14px}
  .count{grid-template-columns:repeat(2,1fr); gap:var(--pad)}
  .row summary{grid-template-columns:minmax(0,1fr) 20px; gap:12px}
  .row__dur{display:none}          /* the duration is in the open row's body */
  .tabs{grid-template-columns:1fr; max-width:none}
  .row__t{grid-column:1/-1; margin-bottom:2px}
  .row__body{padding-left:0}
  .foot__row{grid-template-columns:1fr; gap:24px}
  .foot__contact{text-align:left}
  .foot__terms{justify-self:start}
  .foot__credentials{text-align:left}
  .foot__credentials .legal{margin-left:0; text-align:left}
  .foot__credential-mark{margin-left:0}
}
@media (max-width:860px){
  /* the metal needs a heavier scrim behind a narrow column of type */
  .field__veil{
    background:
      radial-gradient(140% 38% at 34% 47%, rgba(7,13,25,.80) 0%, rgba(7,13,25,.36) 60%, transparent 90%),
      linear-gradient(to bottom, rgba(7,13,25,.52) 0%, rgba(7,13,25,.04) 22%,
                      rgba(7,13,25,.44) 72%, var(--void) 100%);
  }
}
@media (max-width:520px){
  .nav__cta{padding:10px 18px}
  .void__object canvas{max-height:44vh}
}

/* ── Reduced motion ────────────────────────────────────────── */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important}
  [data-reveal],.line > span{opacity:1 !important; transform:none !important}
  .glow{--glow:.62}
  .scrollcue__line::after{display:none}
}

/* ═══ SMOOTH SCROLL SHELL ══════════════════════════════════════ */
html.has-smooth #smooth{
  position:fixed; top:0; left:0; width:100%;
  will-change:transform; backface-visibility:hidden;
}
/* masked-line reveals need a little room for the 0.9 leading */
.line{padding:.14em 0 .17em; margin:-.14em 0 -.17em}
html.no-webgl .field{display:none}
html.no-webgl .field__veil{
  background:
    radial-gradient(90% 60% at 22% 46%, rgba(192,192,192,.20), transparent 62%),
    radial-gradient(70% 50% at 74% 30%, rgba(0,140,149,.14), transparent 60%),
    linear-gradient(to bottom, #0d1730 0%, var(--void) 88%);
}
html.no-webgl .void__object canvas{display:none}

/* A polished silver reflection that travels across the brand-colored face.
   No idle motion; text stays above the reflection. Shared by v10 and React. */
.shine-hover {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.shine-hover::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: linear-gradient(110deg,
    transparent 30%,
    rgba(107, 124, 145, .22) 39%,
    rgba(228, 237, 245, .65) 44%,
    rgba(255, 255, 255, .95) 48%,
    rgba(125, 144, 166, .45) 52%,
    rgba(245, 250, 255, .8) 56%,
    rgba(207, 220, 234, .28) 62%,
    transparent 70%);
  background-size: 250% 100%;
  background-position: 200% 0;
  background-repeat: no-repeat;
  transition: none;
}
.shine-hover__label { position: relative; z-index: 1; }
.shine-hover:focus-visible {
  outline: 2px solid #f2f5f6;
  outline-offset: 4px;
}
.shine-hover:focus-visible::before {
  background-position: -100% 0;
  transition: background-position 1.15s cubic-bezier(.22, .61, .36, 1);
}
@media (hover: hover) {
  .shine-hover:hover {
    border-color: #dce5ef;
    box-shadow: inset 0 1px 0 #ffffff40, inset 0 -1px 0 #aabbd333, 0 3px 12px #0002;
  }
  .shine-hover:hover::before {
    background-position: -100% 0;
    transition: background-position 1.15s cubic-bezier(.22, .61, .36, 1);
  }
  .shine-hover:hover .shine-hover__label { text-shadow: 0 1px 3px #07182bc9; }
}
@media (prefers-reduced-motion: reduce) {
  .shine-hover::before { display: none; }
  .shine-hover { transition: none; }
}
