/* ============================================================
   TOKENS
   ============================================================ */
:root {
  --ink: #1B1220;
  --paper: #FBF9F6;
  --paper-2: #FBEFEF;
  --muted: #6B6070;
  --line: #E7DFE2;

  /* Brand ramp — matches the gradient baked into the Summit wordmark.
     Equivalent to the original #DF5830 / #AE1E50 / #511C49. */
  --bright: hsl(13.7deg 73.2% 53.1%);
  --mid: hsl(339.2deg 70.6% 40%);
  --dark: hsl(309deg 48.6% 21.4%);

  --grad: linear-gradient(45deg, var(--bright) 0%, var(--mid) 48%, var(--dark) 100%);
  --grad-soft: linear-gradient(45deg, rgba(223, 88, 48, 0.09) 0%, rgba(174, 30, 80, 0.07) 48%, rgba(81, 28, 73, 0.07) 100%);

  /* Fixed copy of the ramp for the wordmark and other brand furniture,
     so the logo does not drift in hue as the page scrolls. */
  --grad-brand: linear-gradient(45deg, #DF5830 0%, #AE1E50 48%, #511C49 100%);

  --success: #1C7C4D;

  --font-display: 'Fira Sans', 'Helvetica Neue', sans-serif;
  --font-body: 'Manrope', -apple-system, sans-serif;

  /* Type scale — four hierarchy tiers shared across every section so the
     section level always outranks the cards nested under it. Tier 1/2 are
     the section heading + intro; Tier 3/4 are the card heading + body.
     Cards also drop to weight 600 (vs the section title's 700) so the two
     levels separate by weight as well as size. */
  --fs-title: clamp(32px, 4.4vw, 44px);
  /* Tier 1 — section headings */
  --fs-lede: 16px;
  /* Tier 2 — section intro paragraph (Body Text: 16px mobile → 18px desktop) */
  --fs-card-title: 17px;
  /* Tier 3 — card headings */
  --fs-card-body: 16px;
  /* Tier 4 — card descriptions */

  --radius: 18px;
  --radius-sm: 10px;
  --shadow: 0 20px 50px -20px rgba(81, 28, 73, 0.28);
  --container: 1160px;
  --gutter: 32px;
  /* Edge inset for full-bleed bars (nav + footer) whose logos sit flush
     to the viewport corners rather than on the content column. */
  --edge-gutter: clamp(18px, 2.4vw, 30px);
  /* Single source of truth for the fixed nav height. The mobile drawer,
     hero offset and anchor scroll offset are all derived from it. */
  --nav-h: 88px;
}

@media (min-width: 1024px) {
  :root {
    --nav-h: 104px;
    --fs-lede: 18px;
    /* Tier 2 — section intro paragraph (desktop) */
    --fs-card-title: 20px;
    /* Tier 3 — card headings (desktop) */
    --fs-card-body: 18px;
    /* Tier 4 — card descriptions (desktop) */
  }
}

* {
  box-sizing: border-box;
}

/* The UA rule for [hidden] is display:none, but any author `display`
   declaration outranks it — which silently defeats the attribute. */
[hidden] {
  display: none !important;
}

html {
  /* Smooth scrolling is switched on only once the page has settled (see
     script.js). Enabling it up front lets any load-time scroll adjustment
     animate visibly, which fights the hero's entrance animation. */
  scroll-behavior: auto;
  /* keep anchor targets clear of the fixed nav */
  scroll-padding-top: calc(var(--nav-h) + 24px);
  /* Never scroll the page sideways. Decorative layers (line-motifs,
     rotator, waves) bleed a few percent past their sections' edges and
     rely on each section's own overflow:hidden — this is the backstop so
     any stray horizontal spill can't add a page-wide scrollbar. Safe here:
     no position:sticky and no transformed ancestors, so nothing that
     depends on the document being the scroll container breaks. */
  overflow-x: hidden;
}

html.is-ready {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {

  /* html.is-ready would otherwise out-specify a bare html selector */
  html,
  html.is-ready {
    scroll-behavior: auto;
  }

  /* Delays must be zeroed as well as durations — a staggered animation whose
     duration collapses to nothing still waits out its delay, leaving content
     invisible until it fires. */
  * {
    animation-duration: 0.001ms !important;
    animation-delay: 0s !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
  }
}

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ------------------------------------------------------------
   BRAND LOCKUPS

   The Summit wordmark ships as a Figma export whose colour
   variant paints every glyph with a `foreignObject` conic
   gradient and no SVG-native fallback — that does not render
   inside <img>. So we mask the plain white variant and paint
   the brand gradient through it instead: one reliable asset,
   correct colour, no dependency on foreignObject support.
   ------------------------------------------------------------ */
.brand-logo {
  display: block;
  aspect-ratio: 463 / 128;
  /* deliberately the fixed ramp — the mark should not shift with scroll */
  background: var(--grad-brand);
  -webkit-mask: url("assets/Logo (White).svg") no-repeat center / contain;
  mask: url("assets/Logo (White).svg") no-repeat center / contain;
}

/* Ashoka × CSBC lockup. Two variants of the same artwork: the dark one
   on light surfaces, the white one direct on the gradient hero. */
.powered-lockup {
  height: 54px;
  width: auto;
  display: block;
}

.powered-lockup--lg {
  height: 53px;
}

@media (max-width: 480px) {
  .powered-lockup {
    height: 38px;
  }

  .powered-lockup--lg {
    height: 38px;
  }
}

.eyebrow {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mid);
  margin: 0 0 14px;
  display: inline-block;
}

.eyebrow--light {
  color: rgba(255, 255, 255, 0.85);
}

.grad-text {
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.section__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-title);
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 20px;
  max-width: 16ch;
}

.section__title--light {
  color: #fff;
}

/* Some titles are full questions/sentences rather than 2–3 words, so
   they need more room than the default 16ch measure. */
.section__title--wide {
  max-width: 26ch;
}

.section__lede {
  font-size: var(--fs-lede);
  color: var(--muted);
  max-width: 640px;
  margin: 0 0 48px;
}

.section__lede--light {
  color: rgba(255, 255, 255, 0.78);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 15px;
  padding: 14px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, color 0.18s ease;
  white-space: nowrap;
  /* Hover lift and magnetic drift are separate variables composed into a
     single transform — if the lift were its own `transform` declaration
     it would overwrite the drift (and vice versa). Both default to 0,
     so this is an identity transform when no effect is running. */
  transform: translate3d(var(--mag-x, 0px), calc(var(--mag-y, 0px) + var(--btn-lift, 0px)), 0);
}

.btn--primary {
  background: var(--grad);
  color: #fff;
  box-shadow: 0 12px 30px -10px rgba(174, 30, 80, 0.55);
}

.btn--primary:hover {
  --btn-lift: -2px;
  box-shadow: 0 16px 36px -10px rgba(174, 30, 80, 0.6);
}

.btn--block {
  width: 100%;
}

/* ============================================================
   GRADIENT LINE MOTIFS

   Two long curved ribbons sweeping down the right of the hero.
   Deliberately faint — they should read as a tint in the gradient
   rather than as artwork. Tuning knobs are the per-variant
   --motif-opacity, top/right offsets and width below, plus the
   stop-opacity ramps inside the two SVGs themselves.
   ============================================================ */
/* Width sets the horizontal footprint; height is pinned to the full
   hero. <img> defaults to object-fit:fill, so giving both dimensions
   stretches the ribbon vertically rather than cropping it — which is
   what we want: the bands should run the whole height of the section. */
.line-motif {
  position: absolute;
  z-index: 0;
  top: 0;
  height: 100%;
  pointer-events: none;
  opacity: var(--motif-opacity);
  animation: lineMotifIn 1.6s ease 0.15s both;
}

/* warm band runs on the inside, cool band outside it — matching the
   pink-then-lavender pairing in the reference.

   Two dimmers stack: the SVG fills now fade 0.55 -> 0 along their own
   length, and --motif-opacity scales the whole thing on top. Peak alpha
   on the page is roughly the product of the two (~0.19 and ~0.15). */
.line-motif--1 {
  --motif-opacity: 0.55;
  right: 9%;
  width: 22%;
}

.line-motif--2 {
  --motif-opacity: 1;
  right: -4%;
  width: 19%;
}

@keyframes lineMotifIn {
  from {
    opacity: 0;
  }

  to {
    opacity: var(--motif-opacity);
  }
}

/* Scale up a little on phones — at 22% of a narrow viewport the bands
   are barely a stripe — and pull the opacity back so they stay a wash. */
@media (max-width: 700px) {
  .line-motif--1 {
    --motif-opacity: 0.26;
    width: 32%;
  }

  .line-motif--2 {
    --motif-opacity: 0.22;
    width: 28%;
  }
}

/* ============================================================
   ICONS
   ============================================================ */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  display: block;
}

/* sits in a heading's text flow, so it wraps and scales with the type */
.icon--inline {
  display: inline-block;
  width: 0.86em;
  height: 0.86em;
  vertical-align: -0.06em;
  margin-right: 0.32em;
  stroke-width: 1.9;
}

/* section eyebrows carry the same icon as their nav link */
.eyebrow--icon {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.eyebrow--icon .icon {
  width: 15px;
  height: 15px;
}

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  height: var(--nav-h);
  /* Solid, so the nav and the attribution strip below it read as one
     continuous bar — two translucent layers over the hero gradient
     showed a visible seam where their blur boundaries met. */
  background: var(--paper);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s ease;
}

/* the brand and partners sit flush to the viewport edges (the content
   below keeps the wider --gutter) */
.nav__inner {
  max-width: none;
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--edge-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.nav.is-scrolled {
  border-bottom-color: var(--line);
}

/* Brand and partners take equal flex space so the link group lands
   optically centred without absolute positioning (which would let it
   overlap the logos at awkward widths). */
.nav__brand {
  display: flex;
  align-items: center;
  flex: 1 1 0;
  min-width: 0;
}

/* Nav uses the star mark alone (square), flush left. */
.nav__brand .brand-logo {
  aspect-ratio: 1 / 1;
  height: 46px;
  -webkit-mask-image: url("assets/Logo Star (White).svg");
  mask-image: url("assets/Logo Star (White).svg");
}

@media (min-width: 1024px) {
  .nav__brand .brand-logo {
    height: 56px;
  }
}

/* Stays in the layout at every width even when the logos themselves are
   hidden — it is the right-hand counterweight that keeps the link group
   optically centred. Hiding the container instead let the links drift. */
.nav__partners {
  display: flex;
  flex: 1 1 0;
  justify-content: flex-end;
  align-items: center;
  min-width: 0;
}

.nav__partners img {
  display: block;
  height: 38px;
  width: auto;
  /* The flex-shrinking parent (.nav__partners, flex: 1 1 0) collapses its
     width as the viewport narrows. Without these two the global
     `img { max-width: 100% }` would then cap the logo to that shrunken
     width while the fixed height held, distorting the lockup. */
  max-width: none;
  flex-shrink: 0;
}

/* larger once the desktop link row appears; the mid height keeps it
   clear of the links in the tighter 1024–1180 band */
@media (min-width: 1024px) {
  .nav__partners img {
    height: 44px;
  }
}

@media (min-width: 1180px) {
  .nav__partners img {
    height: 56px;
  }
}

.nav__links {
  display: none;
  align-items: center;
  flex: 0 0 auto;
  /* Fluid so the row tightens as it nears the 1024px switch (where the
     overlap was) and relaxes to full size by ~1180px. */
  gap: clamp(18px, 2.7vw, 32px);
  font-size: clamp(14px, 1.4vw, 16.5px);
  font-weight: 600;
}

.nav__links a {
  color: var(--ink);
  transition: color 0.2s ease;
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.nav__links a:hover {
  color: var(--mid);
}

.nav__links .icon {
  color: var(--mid);
}

/* Solid gradient pill, set apart from the plain text links.
   Selector must out-specify `.nav__links a`, which sets a solid colour. */
.nav__links a.nav__cta {
  font-weight: 600;
  color: #fff;
  background: var(--grad);
  padding: 9px 20px;
  border-radius: 999px;
  box-shadow: 0 10px 24px -12px rgba(174, 30, 80, 0.6);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.nav__links a.nav__cta:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 14px 28px -12px rgba(174, 30, 80, 0.7);
}

@media (min-width: 1024px) {
  .nav__links {
    display: flex;
  }
}

.nav__burger {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
}

.nav__burger span {
  width: 20px;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
}

@media (min-width: 1024px) {
  .nav__burger {
    display: none;
  }
}

.nav__mobile {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  right: 0;
  z-index: 49;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  transform: translateY(-120%);
  transition: transform 0.28s ease, visibility 0.28s;
  visibility: hidden;
}

.nav__mobile-inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: 12px var(--gutter) 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.nav__mobile.is-open {
  transform: translateY(0);
  visibility: visible;
}

.nav__mobile a {
  padding: 12px 0;
  font-weight: 500;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 10px;
}

.nav__mobile-partners {
  display: flex;
  justify-content: center;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.nav__mobile-partners img {
  height: 42px;
  width: auto;
}

.nav__mobile a .icon {
  color: var(--mid);
}

.nav__mobile .btn {
  margin-top: 14px;
  border-bottom: none;
  justify-content: center;
}

@media (min-width: 1024px) {
  .nav__mobile {
    display: none;
  }
}

/* ============================================================
   TOPLINE — attribution strip attached under the nav

   Fixed just below the nav and styled to match it. It slides up behind
   the nav and fades the moment the page is scrolled, driven off the
   nav's own .is-scrolled class via a general-sibling selector.
   ============================================================ */
.topline {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  right: 0;
  z-index: 48;
  /* Same solid surface as the nav so the two form one seamless bar,
     with a single hairline at the base of the whole thing. */
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  transition: transform 0.32s ease, opacity 0.32s ease, visibility 0.32s;
}

.topline__text {
  max-width: var(--container);
  margin: 0 auto;
  padding: 9px var(--gutter);
  text-align: center;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--mid);
  line-height: 1.4;
}

.nav.is-scrolled~.topline {
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
}

@media (max-width: 600px) {
  .topline__text {
    font-size: 11px;
    letter-spacing: 0.09em;
    padding: 8px var(--gutter);
  }
}

/* ============================================================
   HERO
   ============================================================ */
.hero {
  /* --hero-veil  How far the brand gradient is pulled back behind the
                  copy. 0 = the ramp at full energy (as it appears in the
                  pull-quote and footer); higher settles it into
                  atmosphere and lifts contrast under the white type. */
  --hero-veil: 0.12;
  /* --hero-pad-bottom  Clearance under the action group. Must stay above
                        .hero__arc's height or the copy lands on the white
                        curve and disappears — that was the bug here. */
  --hero-pad-bottom: 200px;
  /* --hero-arc-h  Height of the decorative white curve at the section
                   foot. Added to the section's min-height so the copy area
                   is a full viewport tall *above* the arc, not behind it. */
  --hero-arc-h: 120px;

  position: relative;
  padding: calc(var(--nav-h) + 96px) var(--gutter) var(--hero-pad-bottom);
  overflow: hidden;
  isolation: isolate;
  /* Veil first (top layer), brand ramp underneath — the gradient itself
     is untouched, so the hero still matches the wordmark. */
  background:
    linear-gradient(rgba(27, 18, 32, var(--hero-veil)), rgba(27, 18, 32, var(--hero-veil))),
    var(--grad);
}

.hero__arc {
  position: absolute;
  left: 50%;
  bottom: -1px;
  transform: translateX(-50%);
  width: 145%;
  height: var(--hero-arc-h);
  background: var(--paper);
  border-radius: 50% 50% 0 0;
  z-index: 0;
}

/* ---- HERO_GLOW ----------------------------------------------------
   A soft pool of warm light on the hero's gradient that follows the
   cursor. Kept at z-index 0, below both .hero__content and
   .hero__stage, so it lights the gradient only and never washes over
   the hero graphic.

   Softness comes from wide, low-alpha gradient stops rather than a
   `filter: blur()` — blurring a near-viewport-sized layer on every
   pointer move is far too expensive. */
.fx-heroglow .hero__glow {
  /* ------- TUNING KNOBS: everything about the glow's look is here -------
     --hg-size     Radius of the pool, in px. `circle` keeps it a true
                   1:1 circle at any viewport size — a percentage radius
                   would stretch into an ellipse on a wide hero.
     --hg-opacity  Master dimmer. 1 = as authored, 0.5 = half strength,
                   1.5 = brighter. Scales all three stops together.
     --hg-soft     How gradually it fades. 1 = as authored; higher pushes
                   the colour stops outward for a softer, more diffuse
                   edge; lower tightens it into a more defined disc.
     Movement smoothing is not here — that is HERO_GLOW_EASE in script.js.
     -------------------------------------------------------------------- */
  --hg-size: 540px;
  --hg-opacity: 0.6;
  --hg-soft: 1.3;

  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(circle var(--hg-size) at var(--hgx, 30%) var(--hgy, 42%),
      rgba(255, 202, 156, calc(0.30 * var(--hg-opacity))) 0%,
      rgba(255, 160, 150, calc(0.16 * var(--hg-opacity))) calc(34% * var(--hg-soft)),
      rgba(255, 128, 176, calc(0.07 * var(--hg-opacity))) calc(58% * var(--hg-soft)),
      transparent calc(82% * var(--hg-soft)));
}

/* No cursor to follow (touch, or reduced motion): fall back to two
   fixed blobs drifting slowly, which keeps the gradient alive without
   needing any pointer input or per-frame work in JS.
   Same --hg-size / --hg-opacity knobs apply. */
.fx-heroglow-idle .hero__glow {
  background:
    radial-gradient(circle var(--hg-size) at 26% 30%,
      rgba(255, 196, 148, calc(0.28 * var(--hg-opacity))) 0%,
      transparent calc(74% * var(--hg-soft))),
    radial-gradient(circle calc(var(--hg-size) * 0.84) at 72% 62%,
      rgba(255, 128, 176, calc(0.20 * var(--hg-opacity))) 0%,
      transparent calc(76% * var(--hg-soft)));
  animation: heroGlowDrift 22s ease-in-out infinite alternate;
}

@keyframes heroGlowDrift {
  from {
    transform: translate3d(-2%, -1.5%, 0) scale(1);
  }

  to {
    transform: translate3d(3%, 2%, 0) scale(1.09);
  }
}

.hero__stars {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* position is set once in JS; only `transform` is animated so the drift
   stays on the compositor */
.hero__stars span {
  position: absolute;
  width: 4px;
  height: 4px;
  background: rgba(255, 255, 255, 0.42);
  border-radius: 50%;
  will-change: transform;
}

/* Two columns from 940px up: all copy on the left, the right half left
   empty for the hero graphic. Below that the stage collapses and the
   content takes the full width — still left-aligned. */
.hero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--container);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 40px;
  align-items: center;
}

/* ---- Desktop: the landing is exactly one viewport tall ------------
   .hero__inner takes the leftover height so .hero__content can be a
   full-height flex column; the auto top margin on the separator then
   pushes the action group to the foot of it. The tagline keeps its own
   bottom margin so that when the copy is taller than the viewport (a
   short laptop window) the auto margin collapses to 0 and the two
   groups still clear each other. */
@media (min-width: 940px) {
  .hero {
    /* the arc only needs its own height; the desktop landing trades the
       rest of the old 200px for copy height */
    --hero-pad-bottom: 140px;

    /* one viewport *plus* the arc, so the copy area clears the white
       curve entirely instead of sharing the bottom 120px with it */
    min-height: calc(100vh + var(--hero-arc-h));
    min-height: calc(100svh + var(--hero-arc-h));
    display: flex;
    flex-direction: column;
  }

  .hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 48px;
    flex: 1;
    width: 100%;
    align-items: stretch;
  }

  /* Centre the copy in its full-height cell so it sits with equal
     breathing room top and bottom, rather than being pinned to the foot. */
  .hero__content {
    justify-content: center;
  }
}

/* Reserved for the hero graphic. Sits above .hero__glow so whatever
   goes in here is not tinted by the glow passing underneath. */
.hero__stage {
  position: relative;
  z-index: 1;
  display: none;
}

@media (min-width: 940px) {
  .hero__stage {
    display: block;
    min-height: 420px;
  }
}

/* Full hero height. The canvas is square (1080x1080 buffer); height:100%
   makes it as tall as the hero and the intrinsic ratio makes it equally
   wide. The shape fills the frame vertically, so it spans top-to-bottom;
   the transparent side padding harmlessly overlaps the empty right half.
   --rotator-x centres it within that half. */
.hero-rotator {
  position: absolute;
  top: 0;
  height: 100%;
  width: auto;
  left: var(--rotator-x, 74%);
  transform: translateX(-50%);
  z-index: 0;
  pointer-events: none;
}

@media (max-width: 939px) {
  .hero-rotator {
    display: none;
  }
}

/* Four groups — identity / idea / logistics / action — separated by
   space rather than by extra type treatments. The only rule is that
   --hero-group-gap must stay comfortably larger than --hero-item-gap,
   or the groups stop reading as groups. */
.hero__content {
  --hero-item-gap: 14px;
  --hero-group-gap: clamp(28px, 3.4vw, 42px);
  /* One size for the tagline, the date/time/venue lines, the support
     copy and the CTA label. Weight and opacity do the ranking. */
  --hero-fs: 18px;

  position: relative;
  z-index: 1;
  max-width: 620px;
  margin: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* ---- first-load entrance -------------------------------------
   Pure CSS with `both` fill so it plays on load without waiting
   for JS. Children animate in source order.
   -------------------------------------------------------------- */
@keyframes heroRise {
  from {
    opacity: 0;
    transform: translate3d(0, 24px, 0);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes heroLogoIn {
  from {
    opacity: 0;
    transform: translate3d(0, 24px, 0) scale(0.94);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes heroArcIn {
  from {
    transform: translateX(-50%) translateY(60px);
  }

  to {
    transform: translateX(-50%) translateY(0);
  }
}

.hero__content>* {
  animation: heroRise 0.85s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

.hero__content>.hero__logo {
  animation: heroLogoIn 0.95s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  animation-delay: 0.18s;
}

.hero__content>.hero__tagline {
  animation-delay: 0.42s;
}

.hero__content>.hero__divider {
  animation-delay: 0.54s;
}

.hero__content>.hero__support {
  animation-delay: 0.66s;
}

/* the button and the when/where lines ride in on their shared row */
.hero__content>.hero__action {
  animation-delay: 0.9s;
}

.hero__arc {
  animation: heroArcIn 1.1s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

.hero__stars {
  animation: heroRise 1.4s ease both;
  animation-delay: 0.3s;
}

/* ---- Group 1: identity ------------------------------------------
   The summit's horizontal lockup (star + wordmark) as one asset. The
   <h1> keeps the summit name in the document outline; the SVG carries it
   as alt text. Scales with the copy column, capped at its natural width. */
.hero__logo {
  margin: 0 0 calc(var(--hero-item-gap) + 16px);
}

.hero__logo img {
  display: block;
  width: min(100%, 463px);
  height: auto;
}

/* Belongs to the identity group — it names what the summit is, so it
   travels with the title rather than sitting off on its own. One line,
   always, at its natural width: it never wraps and is never scaled, so
   it is free to run past the copy column rather than be squeezed by
   it. Group spacing comes from the tagline's own bottom margin on
   desktop, not from a margin here. */
.hero__tagline {
  color: rgba(255, 255, 255, 0.9);
  font-size: var(--hero-fs);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0.005em;
  white-space: nowrap;
  width: max-content;
  max-width: none;
  margin: 0;
}

/* Horizontal hairline dividing the identity group from the action group.
   Spans the copy column (align-self:stretch, since the column itself is
   flex-start-aligned) and owns the group gap on both sides. */
.hero__divider {
  align-self: stretch;
  height: 1px;
  background: rgba(255, 255, 255, 0.22);
  margin: var(--hero-group-gap) 0;
}

/* Hairline between the hero's three groups — identity / logistics /
   action. Full width of the copy column, and it owns the spacing on
   both sides so no group needs its own group-margin. */
/* ---- Group 3: action --------------------------------------------- */
/* Who the room is for. The content carries the weight; the type stays
   quiet and unemphasised. */
.hero__support {
  color: #fff;
  font-size: var(--hero-fs);
  font-weight: 400;
  line-height: 1.45;
  max-width: 560px;
  margin: 0;
}

/* ---- logistics, inside the action row ---------------------------
   Two stacked lines — when, then where — each led by its own icon,
   sitting to the right of the CTA button. Everything here is
   --hero-fs; the lead item of each line carries the display face at
   600 and the trailing detail drops to 500 and 72% opacity, so the two
   rank without a second size. */
.hero__event {
  --evt-gap: 8px;

  display: flex;
  flex-direction: column;
  gap: var(--evt-gap);
  /* Fixed, one step below the surrounding hero copy — 16px desktop,
     14px on phones (see the max-width:700px block). Icons are em-sized,
     so they track it. */
  font-size: 16px;
  color: #fff;
}

.hero__event-line {
  display: flex;
  align-items: baseline;
  gap: 0 8px;
  margin: 0;
  line-height: 1.25;
}

/* Stacks the two parts of a line — date over time, venue over format —
   to the right of the icon, so the second row aligns under the first
   rather than under the icon. */
.hero__event-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Sized off the line's own font-size so it tracks --hero-fs. `baseline`
   on the row would sit an SVG on its bottom edge, so it is nudged into
   optical alignment with translateY instead. */
.hero__icon {
  width: 1.05em;
  height: 1.05em;
  flex: 0 0 auto;
  transform: translateY(0.15em);
  opacity: 0.75;
}

.hero__event-date,
.hero__event-venue {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.01em;
}

.hero__event-time,
.hero__event-address {
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.72);
}

/* The button leads and the when/where lines sit beside it, split by a
   hairline. width:max-content keeps the row at its natural size rather
   than letting the copy column compress it; on phones (below) it is
   allowed to wrap into a stack instead of running off-screen.
   ---- tuning knobs ----
   --act-gap   space between button, rule and the when/where block */
.hero__action {
  --act-gap: 24px;

  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px var(--act-gap);
  width: max-content;
  margin-top: 16px;
}

/* only as tall as the when/where block, hence align-self:stretch on a
   row whose items are centred */
.hero__action-rule {
  width: 1px;
  align-self: stretch;
  background: rgba(255, 255, 255, 0.22);
}

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 14px;
  margin: 0;
}

/* The strongest interactive contrast on the page, but quiet enough not
   to compete with the title: off-white pill, dark-pink label, and only
   enough shadow to lift it off the gradient.
   Beats .btn--primary:hover on specificity, so the hover shadow has to
   be restated here rather than inherited. */
.hero .btn--primary {
  background: var(--paper);
  color: var(--mid);
  font-size: var(--hero-fs);
  padding: 14px 30px;
  box-shadow: 0 6px 18px -10px rgba(0, 0, 0, 0.4);
}

.hero .btn--primary:hover {
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, 0.45);
}

/* ---- HERO on phones ----------------------------------------------
   The copy stays left-aligned at every width; on phones the column just
   goes full-bleed and the desktop rotator is dropped in favour of the
   HERO_MARQUEE cylinder, which sits inline within the copy. */
@media (max-width: 700px) {

  /* The topline attribution strip wraps to two lines on phones, so the
     hero needs more top clearance than the desktop +56px or the logo
     crowds it. */
  .hero {
    padding-top: calc(var(--nav-h) + 96px);
  }

  /* Body copy drops to the mobile size (tagline, date/time/venue,
     support line, CTA label all read off --hero-fs). */
  .hero__content {
    --hero-fs: 16px;
  }

  /* No cursor to follow — drop the hero glow entirely on phones. */
  .hero__glow {
    display: none;
  }

  /* The one-line-at-natural-width rule runs the tagline off-screen on a
     phone. Let it wrap inside the copy column instead. */
  .hero__tagline {
    white-space: normal;
    width: auto;
    max-width: 100%;
  }

  /* Drop the identity/action divider on phones; the support copy owns the
     group gap that the divider's margins used to provide, so the marquee
     and the copy stay a group-step apart with no line between them. */
  .hero__divider {
    display: none;
  }

  .hero__support {
    margin-top: var(--hero-group-gap);
  }

  .hero__event {
    font-size: 14px;
  }

  /* No room for button-beside-lines on a phone: stack the button over the
     when/where lines as a plain column with an even gap. */
  .hero__action {
    width: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }

  /* The in-row hairline between the button and the date/time/venue is
     dropped on phones — the stacked block reads as one unit without it. */
  .hero__action-rule {
    display: none;
  }
}

/* ---- HERO_MARQUEE (mobile) ---------------------------------------
   A flat ribbon of summit photos scrolling sideways, between the
   tagline and the date. Hidden by default; the module unhides it (via
   .fx-hero-marquee) on phones. The track holds two copies of the image
   set, so the -50% slide loops seamlessly.
   ---- tuning knobs ----
   --marquee-height  height of the ribbon; this is the size control.
                     Card widths follow from it via the 3:2 aspect.
   --marquee-gap     space between cards
   --marquee-speed   auto-drift duration — seconds for one copy to pass
                     (higher = slower); read by hero-marquee.js, which
                     owns the scroll so the ribbon can also be dragged
   --marquee-tint    strength of the brand tint over the photos (0..1) */
.hero-marquee {
  display: none;
}

@media (max-width: 700px) {
  .fx-hero-marquee .hero-marquee {
    --marquee-height: 148px;
    --marquee-gap: 14px;
    --marquee-speed: 50s;
    --marquee-tint: 0.5;

    display: block;
    width: 100%;
    height: var(--marquee-height);
    /* the separator below opens the gap under it, so only the top one
       is set here — same group step as everything else */
    margin: var(--hero-group-gap) 0 0;
    /* slot the entrance between the tagline (0.42s) and the date (0.62s) */
    animation-delay: 0.52s;
    overflow: hidden;
    /* horizontal drags swipe the ribbon (handled in JS); vertical drags
       still scroll the page */
    touch-action: pan-y;
    cursor: grab;
    /* dissolve the ribbon ends into the hero gradient */
    -webkit-mask: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
    mask: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  }

  .fx-hero-marquee .hero-marquee.is-dragging {
    cursor: grabbing;
  }

  /* The scroll transform is driven by hero-marquee.js (one rAF loop shared
     by the auto-drift and the drag), so there is no CSS animation here. */
  .fx-hero-marquee .hero-marquee__track {
    display: flex;
    gap: var(--marquee-gap);
    width: max-content;
    height: 100%;
    will-change: transform;
    user-select: none;
  }

  /* Height drives the card; width follows from the 3:2 aspect. */
  .fx-hero-marquee .hero-marquee__item {
    flex: none;
    position: relative;
    height: 100%;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.18);
  }

  .fx-hero-marquee .hero-marquee__item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    -webkit-user-drag: none;
    user-select: none;
  }

  /* Brand tint so the photos live inside the gradient rather than
     fighting it. soft-light keeps the image legible while warming it. */
  .fx-hero-marquee .hero-marquee__item::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--grad);
    mix-blend-mode: soft-light;
    opacity: var(--marquee-tint);
    pointer-events: none;
  }
}

/* ============================================================
   SECTIONS (shared)
   ============================================================ */
.section {
  padding: 108px 0;
}

.section--tint {
  background: var(--paper-2);
}

/* ------------------------------------------------------------
   SECTION EDGES

   Soft inset shadows along the top and bottom of the surfaces that
   differ in tone from the page, so the joins between sections read
   as deliberate seams rather than an abrupt colour change. Delete
   this block to remove the effect — nothing else depends on it.
   ------------------------------------------------------------ */

.footer {
  box-shadow:
    inset 0 11px 22px -14px rgba(81, 28, 73, 0.30),
    inset 0 -11px 22px -14px rgba(81, 28, 73, 0.30);
}

/* the footer only needs the top seam — nothing sits below it */
.footer {
  box-shadow: inset 0 11px 22px -14px rgba(81, 28, 73, 0.26);
}

/* the invite band is dark, so its seam reads better as a light lip */
.invite {
  box-shadow: inset 0 11px 24px -14px rgba(0, 0, 0, 0.38);
}

/* ============================================================
   WHY ATTEND
   ============================================================ */
/* Two-column body copy on wide screens, collapsing to one below. */
/* Full container width so the two text columns line up with the total
   span of the three pillar cards below. */
.why__copy {
  columns: 2;
  column-gap: 24px;
  /* extra bottom room so the "1" above the first card clears the copy */
  margin: 0 0 130px;
}

.why__copy p {
  font-size: var(--fs-lede);
  color: var(--muted);
  margin: 0;
  /* keep a paragraph from splitting across the two columns */
  break-inside: avoid;
}

.why__copy p+p {
  margin-top: 18px;
}

@media (max-width: 720px) {
  .why__copy {
    columns: 1;
    margin-bottom: 40px;
  }
}

/* Three pillar cards — the summit's argument in brief. */
/* Staircase: each card steps down from the one before it. align-items:
   start keeps the offsets intact rather than stretching all three to a
   common height. */
.pillars {
  counter-reset: pillar;
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
  gap: 24px;
}

.pillar:nth-child(2) {
  margin-top: 64px;
}

.pillar:nth-child(3) {
  margin-top: 128px;
}

@media (max-width: 760px) {
  .pillars {
    grid-template-columns: 1fr;
  }

  /* stacked single column — drop the staircase offsets and its steps */
  .pillar:nth-child(2),
  .pillar:nth-child(3) {
    margin-top: 0;
  }

  .pillar::before {
    display: none;
  }

  /* Move the number off the top of the card and inline it into the
     title instead: "1. Think in Moonshots". */
  .pillar::after {
    display: none;
  }

  .pillar__title::before {
    content: counter(pillar) ". ";
    color: var(--mid);
  }

  /* Without the desktop staircase's big number propping up the title, the
     pillar heading must drop to the shared card tier (weight 600) so it
     stays clearly below the section title on narrow screens. */
  .pillar__title {
    font-weight: 600;
    font-size: var(--fs-card-title);
  }
}

.pillar {
  position: relative;
  counter-increment: pillar;
  background: #fff;
  border: 1px solid var(--line);
  /* a brand-coloured accent along the top edge marks these as the pillars */
  border-top: 3px solid var(--mid);
  border-radius: var(--radius);
  padding: 30px 28px;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* The "step" each card stands on — same width as the card, dropping to a
   common floor so the three steps decrease in height (tallest under the
   highest card). --step-h is set per card by initPillarSteps(). */
.pillar::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 10px);
  height: var(--step-h, 40px);
  border-radius: var(--radius);
  background: rgba(27, 18, 32, 0.05);
}

/* the column number, sitting clearly above each card and stepping down
   with it (its box height is 1em, so -1em lifts it fully clear). */
.pillar::after {
  content: counter(pillar);
  position: absolute;
  top: calc(-1em - 10px);
  left: 2px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: clamp(42px, 5.2vw, 58px);
  line-height: 1;
  color: rgba(27, 18, 32, 0.16);
  pointer-events: none;
}

.pillar:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 34px -20px rgba(81, 28, 73, 0.4);
}

.pillar__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-card-title);
  margin: 0 0 12px;
}

.pillar__desc {
  color: var(--muted);
  font-size: var(--fs-card-body);
  line-height: 1.55;
  margin: 0;
}

/* ============================================================
   PULL-QUOTE
   Two identical bands: one between the hero and Why Attend, one
   between Why Attend and the agenda.
   ============================================================ */
.pullquote {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--paper);
  /* --pq-pad  vertical breathing room around the quote; the band has no
               other content, so this is the whole size control */
  /* --pq-pad: 40px;
  padding: var(--pq-pad) 0; */
}

/* Two subtle flowing waves behind the quote, drawn in quote-waves.js. */
.pullquote__waves {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
  opacity: 0.16;
}

.pullquote .container {
  position: relative;
  z-index: 1;
}

/* Full-width, balanced lines, flanked by a gradient quote mark on each
   side. The marks are inline pseudo-elements so they hug the text at any
   line count rather than hanging in a fixed margin. */
.pullquote__text {
  font-family: var(--font-display);
  font-weight: 600;
  /* Higher floor so the quote stays a consistent header size on phones
     rather than collapsing toward body text. */
  font-size: var(--fs-title);
  line-height: 1.28;
  letter-spacing: -0.01em;
  margin: 0;
  text-align: center;
  text-wrap: balance;
}

/* The accent half of the quote sits on its own line — a single line on
   wide screens, wrapping only where it would otherwise overflow. It keeps
   the size and weight of the line above so the whole quote is one size
   with one bold treatment; only the gradient fill sets it apart. */
.pullquote__accent {
  /* Inline, not block: keeping it inline lets `text-wrap: balance` on the
     parent balance the black and gradient text together as one run instead
     of balancing each separately. */
  font-size: inherit;
  font-weight: inherit;
}

.pullquote__text::before,
.pullquote__accent::after {
  font-size: 1.3em;
  line-height: 0;
  vertical-align: -0.34em;
  background: var(--grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.pullquote__text::before {
  content: "\201C";
  margin-right: 0.08em;
}

.pullquote__accent::after {
  content: "\201D";
  margin-left: 0.06em;
}

@media (max-width: 620px) {
  .pullquote {
    --pq-pad: 30px;
  }
}

/* ============================================================
   SPEAKERS
   ============================================================ */
  
/* This override is only required if there is no pull-quote at the top of the section */
#speakers{
  padding-top: 0px;
}

/* Explicit column counts rather than auto-fill: with 6 speakers the
   auto-fill track resolved to 4 columns and left a ragged half-row. */
.speakers-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
}

@media (min-width: 600px) {
  .speakers-grid {
    gap: 24px;
  }
}

@media (min-width: 940px) {
  .speakers-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
  }
}

.speaker-card {
  text-align: center;
}

/* The photo arrives with its frame already baked in — no masking here. */
.speaker-card__photo {
  width: 100%;
  height: auto;
  margin-bottom: 20px;
}

.speaker-card__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-card-title);
  margin: 0 0 4px;
}

.speaker-card__role {
  font-size: var(--fs-card-body);
  color: var(--muted);
  margin: 0;
  line-height: 1.45;
}

/* the one-line role each speaker plays in the evening */
.speaker-card__summit {
  font-size: 12.5px;
  font-style: italic;
  color: var(--mid);
  margin: 8px 0 0;
  line-height: 1.4;
}

/* On phones each card reveals by swinging in from edge-on to flat,
   hinged on its left edge. This reuses the scroll-reveal machinery:
   the JS still tags the card with data-reveal / is-visible, we just
   swap the position-based translate for a rotateY. is-visible's
   transform:translate3d(0,0,0) is the flat resting state, so the
   card animates the whole way to face-on. Higher specificity than
   the generic [data-reveal] rules, so it overrides them; the
   reduced-motion `transform:none !important` still wins over this. */
@media (max-width: 700px) {
  .js-reveal .speaker-card[data-reveal] {
    transform: perspective(1100px) rotateY(82deg);
    transform-origin: center center;
  }

  /* Matching transform-function list for the resting state, so the
     browser interpolates a clean hinge instead of decomposing a
     mismatched translate. Beats the generic is-visible rule on
     specificity. */
  .js-reveal .speaker-card[data-reveal].is-visible {
    transform: perspective(1100px) rotateY(0deg);
  }
}

/* ============================================================
   AGENDA
   ============================================================ */
/* Timeline rail running down the left of the cards, with a node per stop. */
.agenda-list {
  --rail-x: 8px;
  --rail-gap: 40px;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-left: var(--rail-gap);
}

/* top / bottom are set by initAgendaRail() so the rail starts at the
   first node and ends at the last; the fallbacks apply before JS runs. */
.agenda-list::before {
  content: "";
  position: absolute;
  left: var(--rail-x);
  top: var(--rail-top, 12px);
  bottom: var(--rail-bottom, 12px);
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--bright), var(--mid) 45%, var(--dark));
  opacity: 0.4;
}

.agenda-item::before {
  content: "";
  position: absolute;
  /* `left` targets the rail's centre line, then translate centres the dot
     on it. The card's 1px border cancels out because absolute offsets are
     measured from its padding box — which is also where --rail-gap lands.
     box-sizing is set explicitly: the `*` reset does not match pseudo-elements,
     so the border would otherwise sit outside the declared width. */
  box-sizing: border-box;
  left: calc(var(--rail-x) - var(--rail-gap));
  transform: translateX(-50%);
  top: 28px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid var(--mid);
  box-shadow: 0 0 0 4px var(--paper);
}

.agenda-item {
  position: relative;
  display: grid;
  grid-template-columns: 132px 1fr;
  gap: 4px 24px;
  align-items: start;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px 30px;
  box-shadow: 0 2px 10px -7px rgba(81, 28, 73, 0.25);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.agenda-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 28px -16px rgba(81, 28, 73, 0.35);
}

/* the session's format, sitting in the left column where the time was */
.agenda-item__tag {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mid);
  line-height: 1.35;
  margin: 0;
  padding-top: 4px;
}

.agenda-item__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-card-title);
  margin: 0 0 6px;
}

.agenda-item__desc {
  color: var(--muted);
  font-size: var(--fs-card-body);
  margin: 0;
  max-width: 70ch;
}

/* closing line under the timeline: the label sits at the left, then a
   rule fills the remaining width to the right in the same colour. */
.agenda-foot {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 26px 0 0;
  padding-left: 40px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  color: var(--mid);
}

.agenda-foot::after {
  content: "";
  flex: 1;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

@media (max-width: 620px) {
  .agenda-foot {
    padding-left: 26px;
  }
}

@media (max-width: 620px) {
  .agenda-list {
    --rail-gap: 26px;
    --rail-x: 5px;
  }

  .agenda-item {
    grid-template-columns: 1fr;
    padding: 20px 22px;
  }

  .agenda-item__tag {
    padding-top: 0;
  }

  .agenda-item::before {
    top: 24px;
    width: 12px;
    height: 12px;
    border-width: 2.5px;
  }
}

/* ---- TIMELINE_GLOW (mobile) --------------------------------------
   A ring + inner glow drawn over the centred card as the timeline
   scrolls. --glow (0..1) is set per card in timeline-glow.js from how
   near the card is to the viewport centre, so the light lerps between
   cards. Masked to the card's rounded box via border-radius: inherit
   and the inset glow; the outer shadow adds a soft bloom.
   box-sizing is explicit because the `*` reset skips pseudo-elements. */
.fx-timeline-glow .agenda-item {
  --glow: 0;
  /* Shimmer palette — override these to recolour the focused-card text.
     --shimmer-base is the resting text colour; --shimmer-hi is the
     travelling highlight. --shimmer-speed sets the loop duration. */
  --shimmer-base: var(--ink);
  --shimmer-hi: var(--bright);
  --shimmer-speed: 4.5s;
}

.fx-timeline-glow .agenda-item::after {
  content: "";
  position: absolute;
  box-sizing: border-box;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  border: 1.5px solid var(--mid);
  box-shadow:
    0 10px 34px -10px rgba(174, 30, 80, 0.55),
    inset 0 0 26px rgba(223, 88, 48, 0.18);
  opacity: var(--glow);
  transition: opacity 0.12s linear;
}

/* The centred card's title shimmers: a highlight band drifts across the
   text via a clipped, animated gradient. Colours come from the variables
   above so the effect can be recoloured in one place. */
.fx-timeline-glow .agenda-item.is-focused .agenda-item__title {
  background: linear-gradient(100deg,
      var(--shimmer-base) 0%,
      var(--shimmer-base) 42%,
      var(--shimmer-hi) 50%,
      var(--shimmer-base) 58%,
      var(--shimmer-base) 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: timelineShimmer var(--shimmer-speed) linear infinite;
}

@keyframes timelineShimmer {
  from {
    background-position: 200% 0;
  }

  to {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fx-timeline-glow .agenda-item.is-focused .agenda-item__title {
    animation: none;
    background: none;
    -webkit-background-clip: initial;
    background-clip: initial;
    color: var(--shimmer-base);
  }
}

/* ============================================================
   ABOUT
   ============================================================ */
/* Two stacked blocks now: the summit overview, then the CSBC panel. */
.about-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
}

.about-col h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-title);
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 20px;
}

.about-col p {
  color: var(--muted);
  font-size: var(--fs-lede);
  margin: 0 0 14px;
}

.about-col p:last-child {
  margin-bottom: 0;
}

.org-logos {
  display: flex;
  align-items: center;
  margin-top: 24px;
}

@media (max-width: 760px) {
  .about-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }
}

/* ============================================================
   INVITE
   ============================================================ */
.invite {
  position: relative;
  background: var(--ink);
  /* the deeper bottom padding is the band the corner motifs sit in —
     without it they collide with the form card, and at <=900px the
     form goes full width so they'd land behind it */
  padding: 100px 0 148px;
  overflow: hidden;
  isolation: isolate;
}

.invite__arc {
  position: absolute;
  inset: 0;
  background: var(--grad);
  opacity: 0.92;
  z-index: -1;
}

.invite__container {
  /* keep the form and intro above the corner motifs */
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: 60px;
  align-items: start;
}

@media (max-width: 900px) {
  .invite__container {
    grid-template-columns: 1fr;
  }
}

.invite__facts {
  list-style: none;
  margin: 36px 0 0;
  padding: 24px 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.25);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.invite__facts li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #fff;
  font-size: var(--fs-card-body);
  gap: 16px;
}

/* icon + label group on the left of each fact row */
.invite__fact-label {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  flex: none;
}

.invite__facts .icon {
  width: 16px;
  height: 16px;
  flex: none;
  color: rgba(255, 255, 255, 0.65);
}

.invite__facts strong {
  font-weight: 600;
  color: rgba(255, 255, 255, 0.7);
}

.invite__facts li>span:last-child {
  text-align: right;
}

.invite__form {
  background: #fff;
  border-radius: var(--radius);
  padding: 36px;
  box-shadow: 0 30px 70px -20px rgba(0, 0, 0, 0.4);
}

.field {
  margin-bottom: 18px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* Honeypot — pulled off-screen for people, still in the DOM for bots.
   Not display:none/visibility:hidden, which sophisticated bots ignore. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

@media (max-width: 520px) {
  .field-row {
    grid-template-columns: 1fr;
  }
}

.field label {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.field .optional {
  font-weight: 400;
  color: var(--muted);
}

.field input,
.field textarea {
  font-family: var(--font-body);
  font-size: 15px;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
  resize: vertical;
}

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--mid);
  box-shadow: 0 0 0 3px rgba(174, 30, 80, 0.15);
}

/* Invalid field, set by invite-form.js on a failed submit. */
.field input.is-invalid,
.field textarea.is-invalid {
  border-color: #c62f3a;
}

.field input.is-invalid:focus,
.field textarea.is-invalid:focus {
  border-color: #c62f3a;
  box-shadow: 0 0 0 3px rgba(198, 47, 58, 0.15);
}

/* Consent checkbox flagged the same way — recolour the control itself. */
.field .check input.is-invalid {
  outline: 2px solid #c62f3a;
  outline-offset: 1px;
}

/* consent row — a checkbox and its wrapping label, laid out inline.
   Extra bottom margin sets it apart from the submit button below. */
.field--check {
  margin-top: 6px;
  margin-bottom: 24px;
}

.field .check {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
  font-size: 15px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.5;
  cursor: pointer;
}

.field .check input {
  flex: none;
  width: 17px;
  height: 17px;
  padding: 0;
  margin-top: 1px;
  border-radius: 5px;
  accent-color: var(--mid);
  cursor: pointer;
}

.invite__status {
  min-height: 20px;
  font-size: 15px;
  margin: 14px 0 0;
  font-weight: 600;
  /* Each validation message is on its own line (joined with \n in JS). */
  white-space: pre-line;
  line-height: 1.5;
}

.invite__status.is-error {
  color: #c62f3a;
}

/* ---- submitted state ---------------------------------------- */
.invite__success {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 30px 16px 22px;
}

.invite__success:focus {
  outline: none;
}

.invite__success-mark svg {
  width: 68px;
  height: 68px;
  display: block;
  margin-bottom: 22px;
}

.invite__success-mark circle,
.invite__success-mark path {
  fill: none;
  stroke: var(--success);
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* dasharray ≈ each shape's own length, so the offset animation draws it on */
.invite__success-mark circle {
  stroke-width: 3;
  stroke-dasharray: 151;
  /* 2πr, r = 24 */
  stroke-dashoffset: 151;
  animation: drawCircle 0.5s ease forwards;
}

.invite__success-mark path {
  stroke-width: 4;
  stroke-dasharray: 32;
  stroke-dashoffset: 32;
  animation: drawCheck 0.32s 0.42s ease forwards;
}

@keyframes drawCircle {
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes drawCheck {
  to {
    stroke-dashoffset: 0;
  }
}

.invite__success-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-card-title);
  margin: 0 0 8px;
}

.invite__success-text {
  color: var(--muted);
  font-size: var(--fs-card-body);
  margin: 0;
}

/* ============================================================
   FOOTER
   ============================================================ */
.footer {
  background: var(--paper-2);
  padding: 56px 0 24px;
  border-top: 1px solid var(--line);
}

/* Full-bleed like the nav: the brand sits flush left, the partner lockup
   flush right, both to the viewport edges rather than the content column.
   Overrides the .container the element also carries. */
.footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  justify-content: space-between;
  align-items: flex-start;
  max-width: none;
  margin: 0;
  padding-left: var(--edge-gutter);
  padding-right: var(--edge-gutter);
  padding-bottom: 32px;
}

.footer__brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.footer__brand .brand-logo {
  height: 46px;
}

/* Same wording as the hero tagline, tuned for the light footer.
   Uniform treatment — no emphasised or recoloured parts. */
.footer__tagline {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
  max-width: 44ch;
}

.org-logos--footer {
  margin-top: 14px;
}

.footer__legal {
  border-top: 1px solid var(--line);
  max-width: none;
  margin: 0;
  padding-left: var(--edge-gutter);
  padding-right: var(--edge-gutter);
  padding-top: 20px;
  font-size: 12.5px;
  color: var(--muted);
}

/* ============================================================
   SCROLL REVEAL

   Direction is decided in JS from each element's real horizontal
   position, so left-hand content slides in from the left, right-hand
   content from the right, and full-width blocks simply lift.

   Everything is gated behind .js-reveal (set by script.js) so the
   page is fully visible if JS never runs.
   ============================================================ */
.js-reveal [data-reveal] {
  opacity: 0;
  will-change: opacity, transform;
  transition:
    opacity 0.7s cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}

.js-reveal [data-reveal="left"] {
  transform: translate3d(-38px, 0, 0);
}

.js-reveal [data-reveal="right"] {
  transform: translate3d(38px, 0, 0);
}

.js-reveal [data-reveal="up"] {
  transform: translate3d(0, 28px, 0);
}

/* opt-in via data-reveal-mode="scale" — used by the corner motifs */
.js-reveal [data-reveal="scale"] {
  transform: scale(0.72);
}

/* used for a clean re-measure on resize */
.js-reveal [data-reveal].is-measuring {
  transform: none !important;
  transition: none !important;
}

.js-reveal [data-reveal].is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  will-change: auto;
}

/* Reduced motion: no travel, just a gentle fade. */
@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] {
    transform: none !important;
  }
}

/* ============================================================
   INTERACTION EFFECTS

   Every rule here is gated behind an `fx-*` class that script.js
   adds to <html> only when the matching flag in its EFFECTS object
   is true. Turning a flag off therefore switches off the styling
   too — see the comment block at the top of script.js.

   Custom properties are registered with @property so they can be
   transitioned/animated; without it they still work, they just jump
   instead of easing.
   ============================================================ */
/* ---- MAGNETIC_BUTTONS --------------------------------------------
   The transform itself lives on `.btn` (it composes the drift with the
   hover lift). Nothing more is needed here beyond a slightly snappier
   return once the pointer leaves. */
.fx-magnetic .btn {
  transition: transform 0.25s cubic-bezier(0.22, 0.61, 0.36, 1),
    box-shadow 0.18s ease, background 0.18s ease, color 0.18s ease;
}

/* ---- SCRAMBLE_TEXT ------------------------------------------------
   Height is locked inline by script.js for the duration; overflow is
   clipped so that if the substituted glyphs do wrap to an extra line
   mid-flight, the page below still cannot shift. */
.fx-scramble .is-scrambling {
  overflow: hidden;
  font-variant-ligatures: none;
}

/* ---- FLOATING_CTA -------------------------------------------------
   Fixed pill that mirrors the nav's Request-Invite action. Hidden by
   default (faded, nudged down, non-interactive); .is-visible is added
   by floating-cta.js only when the hero is past and the invite section
   is off screen. Sits below the nav (50) and mobile drawer. */
.fx-floating-cta .floating-cta {
  position: fixed;
  right: clamp(16px, 4vw, 32px);
  bottom: clamp(16px, 4vw, 32px);
  z-index: 45;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 22px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 15px;
  color: #fff;
  text-decoration: none;
  background: var(--grad);
  box-shadow: 0 14px 34px -10px rgba(174, 30, 80, 0.6);
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px);
  transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
}

.fx-floating-cta .floating-cta .icon {
  width: 18px;
  height: 18px;
  color: #fff;
}

.fx-floating-cta .floating-cta.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.fx-floating-cta .floating-cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 40px -10px rgba(174, 30, 80, 0.66);
}

/* Desktop already shows the nav's Request-Invite link (the burger folds
   out below this same 1024px breakpoint), so the floating pill would only
   duplicate it — hide it once the full nav is visible. */
@media (min-width: 1024px) {
  .fx-floating-cta .floating-cta {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fx-floating-cta .floating-cta {
    transition: opacity 0.2s linear, visibility 0.2s;
    transform: none;
  }

  .fx-floating-cta .floating-cta.is-visible,
  .fx-floating-cta .floating-cta:hover {
    transform: none;
  }
}

/* focus visibility */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
  outline: 2px solid var(--mid);
  outline-offset: 2px;
}