/* ───────────────── Reactorfield design tokens ───────────────── */
:root {
  --nav-h: 64px;

  /* Base surfaces (warm paper) */
  --bg-0: #FAF9F6;            /* page */
  --bg-nav: rgba(250, 249, 246, 0.85);  /* translucent sticky nav */
  --bg-1: #F3F2EE;            /* section alternate */
  --bg-2: #FFFFFF;            /* card / panel */
  --bg-3: #F0EFEB;            /* raised / hover */
  --bg-4: #F7F7F3;            /* tinted panel (pillars) */

  /* Foreground scale (dark ink). Every step clears WCAG AA (4.5:1) against
     --bg-0, so the ramp bottoms out at "quiet", not at "unreadable": the
     faint step is still legible at the 12px it is usually set in. Contrast
     against #FAF9F6 is noted per step. */
  --fg-1: #111512;            /* 18.0:1 */
  --fg-2: #424A45;            /*  8.7:1 */
  --fg-3: #5C6560;            /*  5.7:1 */
  --fg-4: #6B736E;            /*  4.7:1 */

  /* The same four ink roles in reverse, for the inverted bands and the
     footer. No component ever names these: the .invert / .foot-merged scope
     below rebinds --text-* onto them, so a rule asks for "muted ink" and
     gets whichever surface it happens to be sitting on. */
  --fg-1-inv: #FAF9F6;
  --fg-2-inv: rgba(250, 249, 246, 0.88);
  --fg-3-inv: rgba(250, 249, 246, 0.62);
  --fg-4-inv: rgba(250, 249, 246, 0.45);

  /* Hairlines & grid rules */
  --hairline: rgba(17, 24, 20, 0.10);
  --hairline-strong: rgba(17, 24, 20, 0.20);
  --crosshair: rgba(17, 24, 20, 0.40);
  --hairline-inv: rgba(250, 249, 246, 0.15);
  --hairline-strong-inv: rgba(250, 249, 246, 0.30);
  --crosshair-inv: rgba(250, 249, 246, 0.35);
  --grid-size: 56px;

  /* Accent: tech green */
  --accent: #00945C;
  --accent-bright: #00B36F;
  --accent-dim: #006B42;
  --accent-glow: rgba(0, 148, 92, 0.12);
  --accent-ink: #FFFFFF;
  --scrim: 11, 15, 12;   /* footer photo scrim, as RGB channels for rgba() */

  /* Semantic aliases */
  --surface-card: var(--bg-2);
  --text-heading: var(--fg-1);
  --text-body: var(--fg-2);
  --text-muted: var(--fg-3);
  --text-faint: var(--fg-4);
  --selection-bg: rgba(0, 148, 92, 0.20);

  /* Type */
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-display: "SF Pro Display", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  --font-serif: "Instrument Serif", Georgia, "Times New Roman", serif;
  /* Type scale. Eleven tokens, and nothing outside them except the four
     documented exceptions: the hero headline, the Manifesto's serif voice,
     the wordmark lockup, and the two display glyphs (the FAQ toggle and the
     team monogram, which are drawn shapes rather than read text). Each carries
     weight / size / line-height / family, so a component picks a role and
     never a measurement. Anything that wants a size not on this list is
     asking the wrong question - pick the nearest role instead. */
  --type-display-xl: 500 clamp(36px, 6vw, 72px) / 1.04 var(--font-display);
  --type-h1: 500 clamp(30px, 4vw, 40px) / 1.15 var(--font-display);
  --type-h2: 500 clamp(24px, 2.8vw, 28px) / 1.2 var(--font-display);
  --type-h3: 500 20px / 1.3 var(--font-sans);
  --type-body-lg: 400 clamp(16px, 1.6vw, 18px) / 1.7 var(--font-sans);
  --type-body: 400 16px / 1.6 var(--font-sans);
  --type-small: 400 15px / 1.5 var(--font-sans);
  --type-ui-lg: 500 16px / 1 var(--font-sans);
  --type-ui-sm: 500 13px / 1 var(--font-sans);
  --type-mono: 400 12px / 1.5 var(--font-mono);
  --type-label: 500 12px / 1.2 var(--font-mono);
  --ls-display: -0.03em;
  --ls-heading: -0.02em;
  --ls-label: 0.08em;

  /* Layout, radii, rules */
  --content-max: 1080px;
  /* One radius. On a page built from hairlines, corner crosshairs and a
     triangle lattice, 2px reads as square and anything larger reads as a
     mistake - so there is nothing to choose between. */
  --radius: 2px;
  --rule: 1px solid var(--hairline);
  --shadow-glow: 0 0 0 1px rgba(0, 148, 92, 0.35), 0 0 24px rgba(0, 148, 92, 0.18);

  /* Spacing scale: 4 8 12 16 20 24 32 40 48 56 64 96. Every padding, margin
     and gap in the sheet is one of these. Two exemptions, both deliberate:
     1-3px optical nudges that align a glyph rather than space a block, and
     the 13px on .team-socials a, which is computed (18px icon + 2x13 = 44px
     touch target) and breaks if it is rounded.

     Spacing roles. Filled panels - cards, bands, persona rows, pillar cells -
     all share one inset; tight cells nested inside a panel share another.
     --space-lead is the gap between a section headline and the first block
     under it, so that rhythm is set in one place instead of drifting per
     section. All three shrink together at the 900px breakpoint. */
  --pad-panel: 32px;
  --pad-panel-tight: 20px;
  --space-lead: 56px;

  /* Section tiers. Three vertical weights - a full section, an inverted band,
     a logo strip - sharing one horizontal gutter, so a new section picks a
     tier instead of inventing a padding. */
  --pad-section: 96px;
  --pad-band: 64px;
  --pad-strip: 32px;
  --pad-gutter: 48px;

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 120ms;
}

/* ───────────────── Base ───────────────── */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
html, body { margin: 0; padding: 0; }
body {
  background-color: var(--bg-0);
  /* Field-triangle lattice - the page is a charted field of triangular wedges. */
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='56'%20height='56'%20viewBox='0%200%2056%2056'%3E%3Cpath%20d='M29.63%2026.37%20L28.21%2030.33%20L25.67%2027.79%20Z'%20fill='rgba(17,24,20,0.16)'/%3E%3C/svg%3E");
  background-size: var(--grid-size) var(--grid-size);
  color: var(--text-body);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-tap-highlight-color: transparent;
  /* clip the decorative corner crosshairs that bleed ~9px past the frame edge;
     `clip` (not `hidden`) avoids creating a scroll container that breaks the sticky nav */
  overflow-x: clip;
}
::selection { background: var(--selection-bg); color: var(--fg-1); }
h1, h2, h3, h4, p, ul, blockquote { margin: 0; }
a { color: inherit; }
:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }

/* ───────────────── Surface context ─────────────────
   Dark surfaces rebind the semantic names rather than restating colours.
   Everything downstream - headings, copy, hairlines, --rule, the corner
   crosshairs - keeps asking for the same role and resolves against whichever
   surface it is on. Custom properties are substituted at the point of use, so
   --rule picks up the inverted hairline here without being redeclared. */
section.framed.invert,
footer .foot-merged {
  --text-heading: var(--fg-1-inv);
  --text-body: var(--fg-2-inv);
  --text-muted: var(--fg-3-inv);
  --text-faint: var(--fg-4-inv);
  --hairline: var(--hairline-inv);
  --hairline-strong: var(--hairline-strong-inv);
  --crosshair: var(--crosshair-inv);
  color: var(--text-body);
}

/* ───────────────── Text styles ─────────────────
   A heading or a paragraph is fully dressed - size, tracking, colour - before
   any component rule runs. Components below set position and spacing only,
   and reach for a type token only when they genuinely want a different role
   from the element default. */
h1, h2, h3, h4 { color: var(--text-heading); }
h1 { font: var(--type-h1); letter-spacing: var(--ls-heading); }
h2 { font: var(--type-h2); letter-spacing: var(--ls-heading); }
h3 { font: var(--type-h3); }
p, li { color: var(--text-body); }

/* Component copy: the 14px voice used inside cards, grids and panels, as
   opposed to the 16px page voice on body. Declared once for every component
   that speaks it. */
.card p,
.grid-pillars p,
.wf-body p,
.faq details p,
.persona-row .story p,
.wk-acts li,
.wk-note { font: var(--type-small); }

/* ...and the two that also share their spacing. */
.card p,
.grid-pillars p { margin-top: 12px; text-wrap: pretty; }

/* Every uppercase mono micro-label - section labels, nav, column heads,
   accordion labels, the deadline note - is one voice declared once. Each
   selector below keeps only what makes it different. */
.label,
.navlink,
.wf-lab,
.closes,
.badge {
  font: var(--type-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.fine { font: var(--type-mono); color: var(--text-faint); }

/* ───────────────── Framed sections ───────────────── */
section.framed { position: relative; border-top: var(--rule); }
/* The alternate surface carries the same field-triangle lattice as the page -
   a flat fill here read as a hole in the pattern the rest of the site is
   charted on. Both sections that use .alt pick this up at once. */
section.framed.alt {
  background-color: var(--bg-1);
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='56'%20height='56'%20viewBox='0%200%2056%2056'%3E%3Cpath%20d='M29.63%2026.37%20L28.21%2030.33%20L25.67%2027.79%20Z'%20fill='rgba(17,24,20,0.16)'/%3E%3C/svg%3E");
  background-size: var(--grid-size) var(--grid-size);
}
section.framed > .frame {
  position: relative;
  max-width: var(--content-max);
  margin: 0 auto;
  border-left: var(--rule);
  border-right: var(--rule);
  padding: var(--pad-section) var(--pad-gutter);
}
section.framed[id] { scroll-margin-top: calc(var(--nav-h) + 32px); }

/* The "+" mark at grid-line intersections */
.x {
  position: absolute;
  width: 9px; height: 9px;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.x::before, .x::after { content: ""; position: absolute; background: var(--crosshair); }
.x::before { left: 0; top: 4px; width: 9px; height: 1px; }
.x::after  { left: 4px; top: 0; width: 1px; height: 9px; }
.x-tl { top: -0.5px; left: -0.5px; }
.x-tr { top: -0.5px; right: -9.5px; }

.accent-text { color: var(--accent); }

.h-section {
  font: var(--type-h1);
  max-width: 640px;
  text-wrap: balance;
}
.lead {
  font: var(--type-body);
  color: var(--text-body);
  max-width: 600px;
  margin-top: 20px;
  text-wrap: pretty;
}

/* ───────────────── Buttons & badges ───────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.btn-sm { font: var(--type-ui-sm); padding: 8px 16px; }
/* both buttons hold a 2:1 horizontal:vertical inset so they scale as one shape */
.btn-lg { font: var(--type-ui-lg); padding: 16px 32px; }
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:active { background: var(--accent-dim); transform: translateY(1px); }
.btn-glow { box-shadow: var(--shadow-glow); }
/* Every affordance arrow shares one transition; the hover block below gives
   each its own direction. */
.arr { transition: transform var(--dur-fast) var(--ease-out); }

.badge {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 3px 8px;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius);
  white-space: nowrap;
}
.badge-accent { color: var(--accent-dim); border-color: var(--accent-dim); background: var(--accent-glow); }

/* ───────────────── Cards ───────────────── */
.card {
  position: relative;
  background: var(--surface-card);
  border: var(--rule);
  border-radius: var(--radius);
  padding: var(--pad-panel);
}

/* ───────────────── Brand assets ───────────────── */
.brand-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 300px));
  gap: 24px;
  margin-top: var(--space-lead);
}
.brand-tile { padding: 0; overflow: hidden; }
.brand-swatch {
  display: flex; align-items: center; justify-content: center;
  height: 240px;
  text-decoration: none;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.brand-swatch img { width: 120px; height: 120px; }
.brand-swatch-light { background: #FAF9F5; }
.brand-swatch-dark { background: #121512; }
.brand-tile-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--pad-panel-tight);
  border-top: var(--rule);
}
.brand-dl {
  font: var(--type-ui-sm);
  color: var(--accent-dim);
  text-decoration: none;
  display: inline-flex; align-items: center; gap: 8px;
}
.brand-back { color: var(--accent-dim); text-decoration: none; font: var(--type-small); }
.brand-back-top { margin-bottom: 24px; text-align: left; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ───────────────── Speaker cards ─────────────────
   A speaker session reads as a broadcast still: a dark tile sitting on the
   warm page, with green dot rails down both edges fading inward, holding the
   portrait in its own colour. The ground is the one
   tools/make_card.py prints onto the 1080x1350 social plate, so the page and
   the announcement asset stand on the same field. Each card is a dark surface,
   so it rebinds the inverted ink roles the same way .invert does - everything
   inside keeps asking for --text-heading / --text-muted and resolves against
   the tile.

   Two documented type exceptions live here, both drawn rather than read: the
   italic SPEAKER SESSION kicker and the poster-weight name, which is set at
   700 and tracked tight so a long surname still carries the tile edge to
   edge. The fill stays flat ink: the page has no other gradient letterform,
   and the PNG is shot from this same card, so a fancier fill here would either
   read as WordArt on the page or make the click swap one name for another. */
/* Keep the announced speakers on screen at once; newly announced guests join
   the same grid through one explicit disclosure below it. Three across at full
   width, then two, then one. */
.speakers-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
  margin-top: var(--space-lead);
}
.speakers-more {
  margin: 24px auto 0;
  padding: 12px 14px 12px 16px;
  display: flex;
  align-items: center;
  gap: 16px;
  border: var(--rule);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-heading);
  font: 600 13px/1 var(--font-mono);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.speakers-more[hidden] { display: none; }
.speakers-more-close { display: none; }
.speakers-more[aria-expanded="true"] .speakers-more-open { display: none; }
.speakers-more[aria-expanded="true"] .speakers-more-close { display: inline; }
.speakers-more-mark {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 18px;
  transition: transform var(--dur-fast) var(--ease-out);
}
.speakers-more[aria-expanded="true"] .speakers-more-mark { transform: rotate(45deg); }
.speakers-more:hover { border-color: var(--accent); color: var(--accent-dim); }
.speakers-more:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 3px; }
.js .speakers-grid:not(.is-expanded) .speaker-card-more { display: none; }
.speakers-grid.is-expanded .speaker-card-more { animation: speaker-card-reveal 260ms var(--ease-out) both; }
.speakers-grid.is-expanded .speaker-card-more:nth-last-child(1) { animation-delay: 45ms; }
@keyframes speaker-card-reveal {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
.speaker-card {
  /* Type inside sizes off the card, not the viewport, which is what lets one
     component hold together at 260px in a three-up row and at 380px in the
     export. */
  container-type: inline-size;
  /* The name is nowrap, so its min-content width would otherwise stop a grid
     column from ever shrinking below it - and the card would overrun a phone
     before the fit script had a chance to scale the name down. */
  min-width: 0;
  --text-heading: var(--fg-1-inv);
  --text-body: var(--fg-2-inv);
  --text-muted: var(--fg-3-inv);
  --text-faint: var(--fg-4-inv);
  --hairline: var(--hairline-inv);
  /* The rail is both the dot band and the content inset, so the field has
     always died before it reaches the photo frame or the copy - nothing in
     the band ever touches the portrait. */
  --spk-rail: clamp(28px, 8%, 52px);
  /* The source card sets rail 86px, pitch 15px, dot radius 2.6px on a 1080
     canvas. Deriving pitch and dot from the rail keeps those three in the same
     proportion at any card width, so the grid reads at the poster's density
     instead of drifting coarse on wide cards and dense on narrow ones. */
  --spk-pitch: calc(var(--spk-rail) / 5.73);
  --spk-dot: calc(var(--spk-pitch) * 0.173);
  color: var(--text-body);
  position: relative;
  /* The site's own dark ink, the same one the inverted bands and the footer
     use, so the tile is of the page rather than a colour of its own. */
  background: var(--fg-1);
  overflow: hidden;
}
/* The dot rails, at the source card's own metrics: one grid on a 15px pitch
   scaled to the web card, each rail as wide as the content inset and masked
   solid for its first quarter, then falling to nothing by the inner edge - so
   the field is spent before it reaches the portrait or the copy. Artwork only;
   see --spk-artwork above. The rail still sets the content inset either way,
   so hiding the dots does not move anything. */
.speaker-card::before,
.speaker-card::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: var(--spk-rail);
  display: var(--spk-artwork, none);
  background-image: radial-gradient(var(--accent-bright) var(--spk-dot), transparent calc(var(--spk-dot) + 0.1px));
  background-size: var(--spk-pitch) var(--spk-pitch);
  pointer-events: none;
}
.speaker-card::before {
  left: 0;
  -webkit-mask-image: linear-gradient(to right, #000 25%, transparent 100%);
  mask-image: linear-gradient(to right, #000 25%, transparent 100%);
}
.speaker-card::after {
  right: 0;
  -webkit-mask-image: linear-gradient(to left, #000 25%, transparent 100%);
  mask-image: linear-gradient(to left, #000 25%, transparent 100%);
}
.spk-inner {
  position: relative;
  height: 100%;
  padding: 20px var(--spk-rail) 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* Artwork-only chrome. The kicker, the wordmark and the dot rails belong to the
   shareable poster, not to the page: the section heading already says "Guest
   speakers", the nav already carries the mark, and on a page charted with its
   own lattice the rails are one pattern too many. All three hang off a single
   --spk-artwork switch that only tools/shoot_cards.py sets, so the page and its
   PNG differ in exactly one declaration. */
.spk-head {
  display: var(--spk-artwork, none);
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.spk-kicker {
  font: italic 700 clamp(13px, 4.6cqw, 19px) / 1 var(--font-display);
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--text-heading);
}
.spk-mark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* the nav's wordmark lockup, sized to the card */
  font: 500 clamp(12px, 4.2cqw, 17px) / 1 var(--font-sans);
  letter-spacing: -0.01em;
  color: var(--text-heading);
}
.spk-mark-glyph {
  /* a background, not an <img>: the card must hold no image of its own, or a
     right-click offers that instead of the whole-tile link */
  display: block;
  width: clamp(18px, 6.4cqw, 26px);
  aspect-ratio: 1;
  flex-shrink: 0;
  background: url("assets/brand/reactorfield-mark-on-dark.svg") center / contain no-repeat;
}
/* Portrait: the photo in its own colour, untreated, inside the poster's green
   frame. The broadcast wash and scanlines that used to sit over it are gone on
   both the page and the export - the shareable PNG is shot from this same
   card, and the two must not differ over the face itself. */
.spk-photo {
  position: relative;
  /* A fixed square, never a filler. Letting the photo absorb leftover height
     made its size depend on how many lines a speaker's title happened to take,
     so one card's portrait grew taller than its neighbours'. */
  aspect-ratio: 1 / 1;
  flex: none;
  overflow: hidden;
  background-color: #07271B;
  border: 3px solid var(--accent-bright);
}
/* The portrait carries the speaker's name as alt text, so the section reads as
   people rather than as decoration. A per-card object-position in the markup
   nudges the crop when a face sits high or low in its photo. */
.spk-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 18%;
}
/* On hover the portrait brightens a step - the only cue the tile needs that
   it opens at full size. Nothing moves and no chrome appears, so the grid
   stays still under a wandering pointer. */
.spk-photo img { transition: filter 0.2s ease; }
.spk-open:hover .spk-photo img { filter: brightness(1.07); }
.spk-foot { display: grid; gap: 8px; }
/* The speaker's own profiles, pinned bottom right. They sit outside the card's
   own link - an <a> inside an <a> is invalid - and above it, so the icon wins
   the click while the rest of the tile still opens the card. The title reserves
   room for them so a two-line job title never runs underneath. */
.spk-links {
  position: absolute;
  right: calc(var(--spk-rail) - 8px);
  bottom: 8px;
  display: flex;
  gap: 4px;
  z-index: 1;
}
.spk-links a {
  display: inline-flex;
  /* 16px of glyph in a 32px target: the icons are small, the tap area is not */
  padding: 8px;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-out);
}
.spk-links a:hover,
.spk-links a:focus-visible { color: var(--accent-bright); }
.spk-links svg { width: 16px; height: 16px; fill: currentColor; display: block; }
.speaker-card:has(.spk-links) .spk-role { padding-right: 72px; }
.spk-name {
  font: 700 clamp(24px, 11cqw, 46px) / 1 var(--font-display);
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  /* The one-line rule is applied by the fit script, not here: a `nowrap` in the
     stylesheet would set a min-content floor on the column before any measuring
     happened, and the card could never be narrower than its longest name. */
  color: var(--text-heading);
}
/* Inline inside .spk-name, so it trails the last word instead of dropping to
   its own line. Its own colour opts it out of the name's gradient fill. */
.spk-suffix {
  font: 500 clamp(12px, 4cqw, 17px) / 1 var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--accent-bright);
  white-space: nowrap;
}
/* The whole tile opens its shareable PNG in the article lightbox - the same
   component the blog uses for figures. No separate download control: at full
   size the card is a plain image, so saving or posting it is whatever the
   reader's device already does with an image. */
.spk-open {
  display: block;
  height: 100%;
  text-decoration: none;
  color: inherit;
}
.spk-open:focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 3px; }
/* The name holds one line as a lockup; the title is a caption and wraps like
   the sentence it is - forcing it onto one line scaled a long job title down to
   the point of being unreadable.

   Two lines are reserved whether or not the title needs them, so the name sits
   at the same height on every card and a short title cannot shorten its card.
   A title that would run to three is clamped rather than allowed to shift the
   layout; tools/render_speakers.py refuses one long enough for that to bite.

   The reservation is a page concern - cards aligning across a row. A shot
   card stands alone, so tools/shoot_cards.py zeroes it and a one-line title
   does not drag a band of empty tile beneath it into the export. */
.spk-role {
  font: 400 clamp(12px, 3.4cqw, 15px) / 1.35 var(--font-sans);
  color: var(--text-body);
  text-wrap: pretty;
  min-height: var(--spk-role-min, 2.7em);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blog-feature {
  margin-top: var(--space-lead);
  padding: var(--pad-panel);
  background: var(--bg-1);
  border: var(--rule);
}
.blog-feature-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  align-items: center;
  margin-bottom: 16px;
}
.blog-feature time {
  font: var(--type-mono);
  color: var(--text-muted);
}
.blog-feature h2 a,
.blog-read {
  color: inherit;
  text-decoration: none;
}
.blog-feature p {
  max-width: 720px;
  margin-top: 16px;
  color: var(--text-body);
  text-wrap: pretty;
}
.blog-read {
  display: inline-flex;
  gap: 8px;
  margin-top: 20px;
  font: var(--type-ui-sm);
  color: var(--accent-dim);
}
.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  max-width: 760px;
}
.article-title {
  font: var(--type-display-xl);
  letter-spacing: var(--ls-heading);
  text-wrap: balance;
}
.article-body { margin-top: var(--space-lead); }
.article-body h2 { margin: 48px 0 16px; }
.article-body p,
.article-body li {
  font: var(--type-body-lg);
  color: var(--text-body);
  text-wrap: pretty;
}
.article-body p + p { margin-top: 24px; }
.article-body ul,
.article-body ol {
  margin: 24px 0 0;
  padding-left: 24px;
}
.article-body li + li { margin-top: 12px; }
.article-body a { color: var(--accent-dim); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.article-image {
  margin: 32px 0;
}
.article-image a {
  display: block;
  cursor: zoom-in;
  text-decoration: none;
}
.article-image img {
  display: block;
  width: 100%;
  height: auto;
  border: var(--rule);
  background: var(--bg-2);
}
.article-footer-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 56px;
  padding-top: 20px;
  border-top: var(--rule);
  font: var(--type-mono);
  color: var(--text-muted);
}
.has-image-lightbox {
  overflow: hidden;
}
.image-lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 32px;
  background: rgba(8, 9, 8, 0.88);
}
.image-lightbox[hidden] {
  display: none;
}
.image-lightbox img {
  max-width: min(100%, 1400px);
  max-height: calc(100vh - 96px);
  width: auto;
  height: auto;
  border: var(--rule);
  background: var(--bg-2);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.42);
}
.image-lightbox-close {
  position: fixed;
  top: 20px;
  right: 20px;
  min-height: 40px;
  padding: 0 16px;
  border: var(--rule);
  border-radius: var(--radius);
  background: var(--bg-0);
  color: var(--text-heading);
  font: var(--type-mono);
  cursor: pointer;
}

/* Persona rows (02 / Who should join) - flat horizontal cards: filled panels
   with no rules, the inverse of the program's open ruled grid. Each row reads
   who you are → your world → what AI changes; the AI line is always green. */
.persona-rows { display: grid; gap: 24px; margin-top: var(--space-lead); }
.persona-row {
  background: var(--bg-1);
  padding: var(--pad-panel);
  display: grid;
  grid-template-columns: minmax(180px, 25%) 1fr;
  gap: 16px 56px;
  transition: background var(--dur-fast) var(--ease-out);
}
.persona-row .tick {
  display: block;
  width: 24px; height: 2px;
  background: var(--accent);
  margin-bottom: 16px;
  transition: width var(--dur-fast) var(--ease-out);
}
.persona-row .story p { text-wrap: pretty; }
.persona-row .world,
.persona-row .ai { margin-top: 12px; }
.persona-row:active { background: var(--bg-3); }

/* Team (06 / Team) - compact horizontal card: small 1:1 photo box (holding a
   monogram placeholder; a real <img> can later replace .team-monogram inside
   .team-photo) sits left of a text column. */
#team .card {
  background: var(--bg-1);
  border: none;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 20px;
  align-items: start;
  padding: var(--pad-panel);
  transition: background var(--dur-fast) var(--ease-out);
}
#team .card:hover { background: var(--bg-3); }
.team-aside {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.team-photo {
  position: relative;
  width: 120px;
  height: 120px;
  background: var(--bg-2);
  display: grid;
  place-items: center;
  overflow: hidden;
}
.team-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.team-text { min-width: 0; }
/* `font: var(--font-mono)` was invalid shorthand - no size, so the whole
   declaration dropped and the monogram never rendered in mono at all. */
.team-monogram {
  font: 400 24px / 1 var(--font-mono);
  color: var(--accent);
  letter-spacing: var(--ls-label);
}
.team-socials {
  display: flex;
  gap: 16px;
  margin-top: 16px;
}
.team-socials a {
  display: inline-flex;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-out);
}
.team-socials a:hover,
.team-socials a:focus-visible { color: var(--accent); }
.team-socials svg { width: 18px; height: 18px; fill: currentColor; display: block; }
.card .tick {
  display: block;
  width: 24px; height: 2px;
  background: var(--accent);
  margin: 16px 0;
  transition: width var(--dur-fast) var(--ease-out);
}
.card:hover .tick { width: 48px; }

/* ───────────────── Nav ───────────────── */
header.nav {
  position: sticky; top: 0; z-index: 10;
  background: var(--bg-nav);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: var(--rule);
}
header.nav .frame {
  max-width: var(--content-max);
  margin: 0 auto;
  border-left: var(--rule);
  border-right: var(--rule);
  padding: 0 var(--pad-gutter);
  height: var(--nav-h);
  display: flex; align-items: center; justify-content: space-between;
}
header.nav nav { display: flex; align-items: center; gap: 32px; }
.mobile-menu { display: none; }
.navlink {
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
.navlink:active { color: var(--text-heading); }

.logo { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
/* Declared inline rather than routed through a token: this 15px is a lockup
   measurement optically matched to the mark beside it, not a text size. It
   coincides with --type-small today, but it is not that role - if the copy
   scale moves again, the wordmark should not follow it. */
.logo .wordmark {
  font: 500 15px / 1 var(--font-sans);
  letter-spacing: -0.01em; color: var(--text-heading);
}

/* ───────────────── Hero ───────────────── */
.hero {
  position: relative;
  /* lift headline + wormhole together so the throat keeps framing the h1;
     footnote is absolutely positioned and stays put */
  --hero-lift: 44px;
  --hero-floor: 720px;
  min-height: calc(100vh - var(--nav-h)); /* fallback for older browsers */
  /* fill the fold, but never squeeze below the content floor - short
     windows scroll a little instead of overlapping */
  min-height: max(var(--hero-floor), calc(100svh - var(--nav-h)));
  display: grid;
  /* empty spacer rows stay exactly equal at every viewport height,
     pinning .inner to the wormhole throat */
  grid-template-rows: 1fr auto 1fr;
  justify-items: center;
  padding: 96px 48px 64px;
  overflow: hidden;
}
.hero svg.wormhole {
  position: absolute;
  /* shifted up via inset (not transform - the breathe animation owns that) */
  inset: calc(-1 * var(--hero-lift)) 0 var(--hero-lift);
  width: 100%; height: 100%;
  pointer-events: none;
  opacity: 0.88;
  transform-origin: 50% 50%;
  animation: rf-wormhole-breathe 10s var(--ease-out) infinite alternate;
}
.hero svg.wormhole polyline { stroke-opacity: 0.72; }
.hero svg.wormhole .rf-meridian { stroke-opacity: 0.52; }
.hero svg.wormhole .rf-orbit { stroke-opacity: 0.62; }
.hero .inner {
  position: relative;
  grid-row: 2;
  translate: 0 calc(-1 * var(--hero-lift));
  width: min(980px, 100%);
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 32px;
}
.hero h1 {
  font: var(--type-display-xl);
  letter-spacing: var(--ls-display);
  width: 100%;
  max-width: 900px;
  text-wrap: balance;
}
.hero h1 .ai { color: var(--accent); }
.hero h1 sup.fn-ref {
  font: var(--type-mono);
  font-size: 0.30em;
  line-height: 0;
  color: var(--text-heading);
  margin-left: 1px;
}
.hero .footnote {
  /* out of flow so it never skews the throat-centering above */
  position: absolute;
  left: 50%;
  bottom: 48px;
  transform: translateX(-50%);
  z-index: 1;
  width: min(680px, calc(100% - 96px));
  text-align: left;
}
.hero .footnote::before {
  content: "";
  display: block;
  width: 80px;
  height: 1px;
  background: var(--hairline-strong);
  margin-bottom: 16px;
}
.hero .footnote p { font: var(--type-body); color: var(--text-muted); text-wrap: pretty; margin: 0; }
.hero .footnote p + p { margin-top: 12px; }
.hero .footnote .fn-num {
  font: var(--type-mono);
  color: var(--text-heading);
  margin-right: 2px;
}
@keyframes rf-orbit { to { stroke-dashoffset: var(--c); } }
@keyframes rf-wormhole-breathe {
  from { transform: scale(1) rotate(-0.5deg); }
  to { transform: scale(1.075) rotate(0.5deg); }
}
.rf-orbit { animation: rf-orbit linear infinite; }

/* ───────────────── Statement band (inverted) ───────────────── */
section.framed.invert {
  background-color: var(--fg-1);
  /* White NE-triangle field - inverted twin of the page lattice. */
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='56'%20height='56'%20viewBox='0%200%2056%2056'%3E%3Cpath%20d='M29.63%2026.37%20L28.21%2030.33%20L25.67%2027.79%20Z'%20fill='rgba(250,249,246,0.14)'/%3E%3C/svg%3E");
  background-size: var(--grid-size) var(--grid-size);
}
section.framed.invert > .frame { text-align: center; }
.statement h2 {
  /* Same token as every other section headline: the band earns its emphasis
     from the inverted field, the centring and the accent word, not from
     being a size nothing else on the page uses. */
  font: var(--type-h1);
  max-width: 760px;
  margin: 0 auto;
  text-wrap: balance;
}
.statement h2 .next { color: var(--accent-bright); }
section.framed.statement > .frame { padding: var(--pad-band) var(--pad-gutter); }

/* ───────────────── Video ───────────────── */
.video-embed {
  position: relative;
  aspect-ratio: 16 / 9;
  margin-top: var(--space-lead);
  border: var(--rule);
  background: #000;
  overflow: hidden;
}
.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}

/* ───────────────── Partner strip (inverted) ───────────────── */
section.framed.partners > .frame { padding: var(--pad-strip) var(--pad-gutter); }
.partners .label {
  font: var(--type-h3);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--text-faint);
}
.partners .logo-row {
  display: flex;
  flex-wrap: wrap;
  gap: 20px 48px;
  justify-content: center;
  align-items: center;
  margin-top: 16px;
  max-width: 1000px;
  margin-inline: auto;
}
/* Forced wrap only: 3 featured logos on top, the remaining logos centered below. */
.partners .logo-row-break {
  flex-basis: 100%;
  height: 0;
}
.partners .partner-logo {
  height: var(--partner-logo-height);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  opacity: 0.85;
  transition: opacity 0.15s ease;
}
.partners .partner-logo:focus-visible {
  opacity: 1;
}
.partners .partner-logo img {
  display: block;
  height: 100%;
  width: auto;
}
/* Heights tuned to equal optical text cap-height, not box height:
   marks (Prime swoosh, Rowan flower, Muni dot-cluster) inflate the box, so
   their boxes run taller; ASI is a two-line lockup sized as a tamed block. */
.partners .partner-logo-anthropic { --partner-logo-height: 18px; }
.partners .partner-logo-nvidia { --partner-logo-height: 24px; }
.partners .partner-logo-asi { --partner-logo-height: 45px; }
.partners .partner-logo-muni { --partner-logo-height: 33px; }
.partners .partner-logo-rowan { --partner-logo-height: 32px; }
.partners .partner-logo-prime-intellect { --partner-logo-height: 22px; }
.partners .partner-logo-fulcrum { --partner-logo-height: 38px; }
.partners .partner-logo-answerthis { --partner-logo-height: 22px; }
.partners .partner-logo-tuva { --partner-logo-height: 29px; }

/* ───────────────── Cohort strip (twin of partners) ─────────────────
   Fifteen organizations share one deliberate 5 × 3 field. Each tile owns the
   same hit area while its mark gets an optically tuned height. The tooltip is
   the one expressive layer: a compact research note revealed on hover/focus. */
.participants .cohort-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: center;
  gap: 32px 40px;
  max-width: 1040px;
  margin: 32px auto 0;
}
.participants .cohort-logo {
  --cohort-logo-height: 34px;
  position: relative;
  width: 100%;
  height: 64px;
  justify-self: stretch;
  opacity: 0.78;
  outline-offset: 6px;
  z-index: 1;
}
.participants .cohort-logo:hover,
.participants .cohort-logo:focus-visible { z-index: 20; }
.participants .cohort-logo img {
  width: auto;
  height: var(--cohort-logo-height);
  max-width: 148px;
  object-fit: contain;
  filter: brightness(0) invert(1);
}
.cohort-wordmark {
  color: var(--fg-1-inv);
  font: 700 18px / 1 var(--font-sans);
  letter-spacing: -0.03em;
  white-space: nowrap;
}
.participants .cohort-logo-retro,
.participants .cohort-logo-kopra,
.participants .cohort-logo-mass { gap: 9px; }
.participants .cohort-logo-retro      { --cohort-logo-height: 25px; }
.participants .cohort-logo-merge      { --cohort-logo-height: 20px; }
.participants .cohort-logo-strand     { --cohort-logo-height: 26px; }
.participants .cohort-logo-xilio      { --cohort-logo-height: 30px; }
.participants .cohort-logo-kopra      { --cohort-logo-height: 32px; }
.participants .cohort-logo-episteme   { --cohort-logo-height: 25px; }
.participants .cohort-logo-marathon   { --cohort-logo-height: 36px; }
.participants .cohort-logo-northwell  { --cohort-logo-height: 15px; }
.participants .cohort-logo-northwell img { max-width: 200px; }
.participants .cohort-logo-mayo       { --cohort-logo-height: 72px; }
.participants .cohort-logo-harvard    { --cohort-logo-height: 27px; }
.participants .cohort-logo-alga       { --cohort-logo-height: 60px; }
.participants .cohort-logo-mass       { --cohort-logo-height: 20px; gap: 6px; }
.participants .cohort-logo-mass .cohort-wordmark { font-size: 16px; }
.participants .cohort-logo-radify     { --cohort-logo-height: 15px; }
.participants .cohort-logo-lume       { --cohort-logo-height: 16px; }
.participants .cohort-logo-foundation { --cohort-logo-height: 32px; }
.cohort-tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% - 2px);
  width: max-content;
  max-width: min(280px, calc(100vw - 48px));
  padding: 12px 14px;
  border: 1px solid rgba(17, 21, 18, 0.24);
  background: var(--bg-0);
  color: var(--fg-1);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.24);
  font: 500 13px / 1.4 var(--font-sans);
  letter-spacing: 0;
  text-align: left;
  text-transform: none;
  white-space: normal;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translate(-50%, 8px);
  transition: opacity var(--dur-fast) ease, transform var(--dur-fast) var(--ease-out), visibility 0s linear var(--dur-fast);
}
.cohort-tooltip::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: 9px;
  height: 9px;
  background: var(--bg-0);
  border-right: 1px solid rgba(17, 21, 18, 0.24);
  border-bottom: 1px solid rgba(17, 21, 18, 0.24);
  transform: translate(-50%, -5px) rotate(45deg);
}
.cohort-tooltip strong {
  display: block;
  margin-bottom: 3px;
  color: var(--accent);
  font-weight: 700;
}
.cohort-logo:hover .cohort-tooltip,
.cohort-logo:focus-visible .cohort-tooltip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition-delay: 0s;
}
.cohort-more {
  width: max-content;
  margin: 32px auto 0;
  padding: 6px 0 5px;
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid currentColor;
  color: var(--text-muted);
  font: 600 12px/1 var(--font-mono);
  letter-spacing: var(--ls-label);
  text-decoration: none;
  text-transform: uppercase;
  transition: color var(--dur-fast) var(--ease-out);
}
.cohort-more-arrow {
  font-size: 16px;
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease-out);
}
.cohort-more:hover { color: var(--text-heading); }
.cohort-more:hover .cohort-more-arrow { transform: translateX(2px); }
.cohort-more:focus-visible { outline: 1px solid currentColor; outline-offset: 8px; }

/* ───────────────── Cohort directory ───────────────── */
.cohort-directory { overflow-x: clip; }
.cohort-directory > .frame { min-height: 72vh; }
.cohort-directory-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
  padding-bottom: 48px;
}
.cohort-directory h1 {
  max-width: 760px;
  font: var(--type-display-xl);
  letter-spacing: var(--ls-display);
  text-wrap: balance;
}
/* The cohort switch is a tab bar that owns the table's top rule: tabs sit on
   the same 2px ink line the roster hangs from, so the control and the content
   read as one object. The current tab carries the accent rule; the rest are
   muted labels that darken on hover. */
.cohort-directory-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0 32px;
  border-bottom: 2px solid var(--text-heading);
}
.cohort-directory-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-bottom: -2px;
  padding: 12px 0;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font: var(--type-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.cohort-directory-link:hover { color: var(--text-heading); }
.cohort-directory-link[aria-current] {
  color: var(--text-heading);
  border-bottom-color: var(--accent);
}
.cohort-directory-link:focus-visible {
  outline: 2px solid var(--accent-dim);
  outline-offset: 4px;
}
.cohort-directory-nav + .cohort-panel .cohort-table-wrap { border-top: 0; }
.cohort-panel, .cohort-apply { scroll-margin-top: calc(var(--nav-h) + 24px); }
.cohort-apply {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 32px;
  margin-top: 48px;
  padding: clamp(24px, 4vw, 48px);
  background: var(--bg-1);
  border-top: 2px solid var(--accent);
}
.cohort-apply > div { flex: 1 1 360px; }
.cohort-apply h2 {
  max-width: 580px;
  margin-top: 0;
  font: var(--type-h1);
  letter-spacing: var(--ls-heading);
  text-wrap: balance;
}
.cohort-apply p {
  max-width: 520px;
  margin-top: 16px;
  font: var(--type-body);
  color: var(--text-body);
}
.cohort-apply p.cohort-apply-note { font: var(--type-small); color: var(--text-muted); }
.cohort-apply .btn { flex-shrink: 0; }
@media (max-width: 600px) {
  .cohort-apply .btn { width: 100%; }
}
.cohort-table-wrap { border-top: 2px solid var(--text-heading); }
.cohort-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}
.cohort-table th {
  padding: 16px 12px;
  border-bottom: var(--rule);
  color: var(--text-muted);
  font: var(--type-label);
  letter-spacing: var(--ls-label);
  text-align: left;
  text-transform: uppercase;
}
.cohort-table th:first-child,
.cohort-table td:first-child { width: 25%; padding-left: 0; }
.cohort-table th:nth-child(2),
.cohort-table td:nth-child(2) { width: 21%; }
.cohort-table td {
  padding: 20px 12px;
  border-bottom: var(--rule);
  vertical-align: top;
}
.cohort-company-link {
  color: var(--text-heading);
  font: var(--type-ui-lg);
  text-decoration-color: transparent;
  text-underline-offset: 4px;
  transition: color var(--dur-fast) var(--ease-out), text-decoration-color var(--dur-fast) var(--ease-out);
}
.cohort-table-description {
  color: var(--text-body);
  font: var(--type-small);
  text-wrap: pretty;
}
.cohort-table-representatives {
  color: var(--text-heading);
  font: var(--type-small);
}
.cohort-table-representatives span { display: block; }
.cohort-table-representatives span + span { margin-top: 5px; }
.cohort-company-link:hover,
.cohort-company-link:focus-visible { color: var(--accent-dim); text-decoration-color: currentColor; }

/* ───────────────── Manifesto (inverted, serif) ───────────────── */
section.framed.manifesto > .frame { text-align: left; }
.manifesto h2 {
  font: italic 400 clamp(36px, 5.4vw, 64px) / 1.02 var(--font-serif);
  letter-spacing: 0;
  max-width: 760px;
  text-wrap: balance;
}
/* The one section that leads wider than --space-lead: the Manifesto is meant
   to sit apart from the page's rhythm, like its serif. */
.manifesto .cols {
  display: grid; grid-template-columns: 1fr 1fr; gap: 56px;
  margin-top: 64px;
}
.manifesto .cols p {
  font: 400 22px / 1.35 var(--font-serif);
  letter-spacing: 0;
  text-wrap: pretty;
}
.manifesto .cols p + p { margin-top: 24px; }
.manifesto .dropcap::first-letter {
  font: 400 92px / 0.78 var(--font-serif);
  color: var(--accent-bright);
  float: left;
  padding: 8px 16px 0 0;
}
.manifesto .foryou { color: var(--accent-bright); }

/* ───────────────── Spec-sheet section & pillared grid ───────────────── */
section.framed.sheet { background: transparent; }
.grid-pillars {
  display: grid; grid-template-columns: repeat(3, 1fr);
  column-gap: 20px;
  margin-top: var(--space-lead);
}
.grid-pillars .pillar {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  background: var(--bg-4);
  border: var(--rule);
  overflow: hidden;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.grid-pillars .pillar:hover { border-color: var(--accent-dim); }
.grid-pillars .pillar:hover .label { color: var(--accent-bright); }
.grid-pillars .pillar > div {
  padding: var(--pad-panel);
  border-bottom: var(--rule);
}
.grid-pillars .pillar > div:last-child { border-bottom: none; }
.grid-pillars .label { transition: color var(--dur-fast) var(--ease-out); }
.grid-pillars p { margin-top: 12px; text-wrap: pretty; }
.grid-pillars .pillar-head p { font: var(--type-small); color: var(--text-muted); margin-top: 8px; }
.pillar-label { font-weight: 700; }
.field-deployment-band {
  margin-top: 24px;
  padding: var(--pad-panel);
  background: var(--bg-4);
  border: var(--rule);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  align-items: start;
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.field-deployment-band:hover { border-color: var(--accent-dim); background: var(--bg-0); }
.field-deployment-band .badge { width: fit-content; margin-bottom: 16px; }
.field-deployment-band p {
  font: var(--type-small);
  color: var(--text-body);
  margin-top: 12px;
  max-width: 680px;
  text-wrap: pretty;
}

/* ───────────────── Section grids ───────────────── */
.cards-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: var(--space-lead); }

/* ───────────────── Use cases ───────────────── */
.wf-intro {
  font: var(--type-body);
  color: var(--text-body);
  max-width: 680px;
  margin-top: 16px;
  text-wrap: pretty;
}
.wf-cases { border-top: var(--rule); margin-top: var(--space-lead); }
.wf-chev-sm { width: 14px; height: 14px; color: var(--text-faint); flex-shrink: 0; transition: transform var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
details.wf[open] .wf-chev-sm { transform: rotate(180deg); color: var(--accent); }
details.wf summary:hover .wf-chev-sm { color: var(--accent); }
details.wf { border-bottom: var(--rule); }
details.wf summary {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 20px 0;
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-fast) var(--ease-out);
}
details.wf summary::-webkit-details-marker { display: none; }
/* Same trigger type as .faq summary - both are accordion headers, so they
   read at one size rather than two that differ by 3px for no reason. */
.wf-name {
  font: var(--type-h3);
  color: var(--text-heading);
  transition: color var(--dur-fast) var(--ease-out);
}
details.wf summary:hover .wf-name { color: var(--accent-dim); }
.wf-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  padding: 4px 0 24px;
}
.wf-col { min-width: 0; }
.wf-lab { display: block; color: var(--text-faint); margin-bottom: 8px; }
.wf-lab.ai { color: var(--accent-dim); }
.wf-body p {
  font: var(--type-small);
  color: var(--text-body);
  margin: 0;
  text-wrap: pretty;
}

/* ───────────────── FAQ ───────────────── */
.faq-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  margin-top: var(--space-lead);
}
.faq { border-top: var(--rule); }
.faq details { border-bottom: var(--rule); }
.faq summary {
  display: flex; justify-content: space-between; align-items: center; gap: 24px;
  min-height: 100px;
  padding: 24px 0;
  cursor: pointer;
  list-style: none;
  font: var(--type-h3);
  color: var(--text-heading);
  transition: color var(--dur-fast) var(--ease-out);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--accent-dim); }
.faq summary::after {
  content: "+";
  font: 400 24px / 1 var(--font-sans);
  color: var(--text-muted);
  flex-shrink: 0;
}
.faq details[open] summary::after { content: "-"; color: var(--accent); }
.faq details p {
  font: var(--type-small); color: var(--text-body);
  margin: -8px 48px 32px 0; max-width: 680px; text-wrap: pretty;
}

.apply-form + .closes, .lead + .closes { margin-top: 24px; }
.apply-form {
  margin-top: 40px;
  max-width: 760px;
}
.apply-form .closes { margin-bottom: 20px; }
.apply-form iframe {
  display: block;
  width: 100%;
  /* guard against collapse/jump before Tally's dynamicHeight resize lands */
  min-height: 636px;
}

/* ───────────────── Footer (content over Solvay halftone) ───────────────── */
footer .foot-merged {
  position: relative;
  min-height: clamp(480px, 50vw, 640px);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  /* fallback if the photo fails: light text must never sit on cream */
  background-color: rgb(var(--scrim));
}
.science-ai-footer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center bottom;
}
.foot-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    rgba(var(--scrim), 0) 32%,
    rgba(var(--scrim), 0.72) 62%,
    rgba(var(--scrim), 0.96) 100%);
}
/* No padding here: the footer is a section.framed, so `section.framed > .frame`
   (0,2,1) outranks `footer .frame` (0,1,1) and the section tier governs. A
   padding declared here would silently never apply - it did not, for a while. */
footer .frame {
  position: relative;
  width: 100%;
}
footer .foot-merged > .frame {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  border-left-color: var(--hairline-strong);
  border-right-color: var(--hairline-strong);
}
.foot-top { display: flex; justify-content: flex-end; align-items: center; flex-wrap: wrap; gap: 24px; }
.foot-top nav { display: flex; gap: 24px; flex-wrap: wrap; }
.foot-merged .navlink { color: var(--text-heading); text-shadow: 0 1px 8px rgba(var(--scrim), 0.8); }
.foot-merged .navlink:hover { color: var(--accent-bright); }
.foot-merged :focus-visible { outline: 2px solid var(--accent-bright); outline-offset: 3px; }
.foot-fine {
  border-top: 1px solid var(--hairline-strong);
  margin-top: 40px; padding-top: 20px;
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px;
}
.foot-merged .fine { color: var(--text-body); text-shadow: 0 1px 8px rgba(var(--scrim), 0.8); }
.foot-fine-text { display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: baseline; }
.foot-socials { margin-top: 0; align-items: center; }
.foot-socials a { color: var(--text-body); filter: drop-shadow(0 1px 8px rgba(var(--scrim), 0.8)); }
.foot-socials a:hover,
.foot-socials a:focus-visible { color: var(--accent-bright); }

/* ───────────────── Responsive ───────────────── */

/* ───────────────── Schedule ─────────────────
   A left rail of accent dots carries the week rows; each row is
   when / what you do / what you leave with. The outcome cell is tinted because
   it is the actual sell.

   The table runs a three-step ramp: .label (12px) for the scaffolding - column
   headers and dates - --wk-size for the week name, which is the only thing in
   a row allowed to sit above body size, and --type-small for every sentence,
   matching the FAQ answers and card copy elsewhere on the page. Emphasis in
   the outcome cell comes from tint, rule and ink colour, never from weight:
   nothing here is running copy set at heading weight. The column template is
   shared by the header and the rows, so they can never drift apart. */
.schedule {
  --wk-cols: minmax(145px, 0.6fr) minmax(0, 2fr) minmax(0, 1.4fr);
  --wk-gap: 24px;
  --wk-size: 16px;
  --wk-rail: 30px;   /* indent that clears the rail and its dots */
  --wk-pad: 22px;    /* row padding; the dot and rail insets follow from it */
}

.wk-head {
  display: grid;
  grid-template-columns: var(--wk-cols);
  gap: var(--wk-gap);
  margin-top: var(--space-lead);
  padding: 0 0 12px var(--wk-rail);
}
.weeks {
  list-style: none;
  padding: 0 0 0 var(--wk-rail);
  margin: 0;
  position: relative;
  border-top: var(--rule);
}
/* rail: inset top and bottom so it starts and ends at the first and last dot */
.weeks::before {
  content: "";
  position: absolute;
  left: 4px;
  top: calc(var(--wk-pad) + 6px);
  bottom: calc(var(--wk-pad) + 6px);
  width: 1px;
  background: var(--hairline-strong);
}
.wk {
  position: relative;
  display: grid;
  grid-template-columns: var(--wk-cols);
  gap: var(--wk-gap);
  align-items: start;
  padding: var(--wk-pad) 0;
  border-bottom: var(--rule);
}
.wk-when { position: relative; }
.wk-dot {
  position: absolute;
  left: calc(var(--wk-rail) * -1);
  top: 5px;   /* centres on the date's line box */
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  /* halo punches the rail out behind the dot so it reads as a station on the line */
  box-shadow: 0 0 0 4px var(--bg-0);
}
.wk-date { display: block; color: var(--accent-dim); }
.wk-when h3 {
  font: 500 var(--wk-size) / 1.4 var(--font-sans);
  margin-top: 8px;
}

.wk-acts { list-style: none; padding: 0; margin: 0; }
/* the activity lines and the closing row's sentence are the same voice, so
   they share one declaration */
.wk-acts li,
.wk-note { text-wrap: pretty; }
.wk-acts li { position: relative; padding-left: 20px; }
.wk-acts li + li { margin-top: 12px; }
.wk-acts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 5px;
  height: 5px;
  background: var(--fg-4);
}

.wk-out {
  background: var(--accent-glow);
  border-left: 2px solid var(--accent);
  padding: var(--pad-panel-tight);
}
/* the column header names this cell on desktop; the in-cell label is the
   mobile fallback, where the header is gone */
.wk-out .label { display: none; color: var(--accent-dim); }
.wk-out p {
  font: var(--type-small);
  color: var(--text-heading);
  margin-top: 8px;
  text-wrap: pretty;
}

/* Closing row: keeps the timeline format on the left, but merges the activities
   and outcome columns into one cell so the text can run as a sentence rather
   than a bulleted list. Its ::after masks the rail below the final dot. */
.wk-end { border-bottom: none; }
.wk-end .wk-when::after {
  content: "";
  position: absolute;
  left: calc((var(--wk-rail) - 4px) * -1);
  top: 16px;      /* just below the dot */
  height: 60px;
  width: 1px;
  background: var(--bg-0);
}
.wk-note { max-width: 620px; }
/* the closing row's note is a single line against a two-line week block, so the
   row's cells centre on each other rather than hanging from the top */
.wk-end > * { align-self: center; }

/* ───────────────── Hover ─────────────────
   Every pointer-only affordance, in one place. Hover states were previously
   spread over six identical media blocks. */
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover { background: var(--accent-bright); }
  .btn:hover .arr { transform: translateX(2px); }
  a.brand-swatch:hover { opacity: 0.85; }
  .brand-dl:hover .arr { transform: translateY(2px); }
  .brand-back:hover { color: var(--accent-bright); }
  .persona-row:hover { background: var(--bg-3); }
  .persona-row:hover .tick { width: 48px; }
  .navlink:hover { color: var(--text-heading); }
  .partners .partner-logo:hover { opacity: 1; }
}

/* ───────────────── Responsive: tablet & down ───────────────── */
@media (max-width: 900px) {
  /* The spacing roles step down once here; the components that used to
     restate a mobile padding now just follow. */
  :root {
    --pad-panel: 24px;
    --pad-panel-tight: 16px;
    --space-lead: 40px;
    --pad-section: 56px;
    --pad-band: 48px;
    --pad-strip: 24px;
    --pad-gutter: 24px;
  }
  .manifesto .cols { grid-template-columns: 1fr; gap: 32px; margin-top: 48px; }
  /* Mobile hero: one vertically-centered column - badge, headline, CTA, then
     footnote - so breathing room distributes evenly instead of pooling into a
     void between the CTA and a bottom-pinned footnote. The landscape wormhole
     can't frame a portrait throat, so the composition carries itself. */
  .hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    /* wider svh-scaled gap drops the footnotes lower and lets the hero
       breathe; svh keeps the rhythm consistent across phone sizes and
       browser-chrome states */
    gap: clamp(32px, 7svh, 64px);
    min-height: calc(100svh - var(--nav-h));
    padding: clamp(24px, 6svh, 56px) 24px clamp(24px, 5svh, 48px);
  }
  /* throat (SVG vertical center) is anchored onto the headline by JS, which
     sets --wormhole-shift; we move via `top`/inset because the breathe
     animation owns `transform`. Falls back to filling the hero (0) without JS. */
  .hero svg.wormhole { inset: var(--wormhole-shift, 0px) 0 auto 0; }
  .hero .inner {
    translate: none;
    grid-row: auto;
    width: 100%;
    gap: clamp(20px, 3svh, 24px);
  }
  .hero h1 {
    width: 100%;
    max-width: 100%;
    font: 500 clamp(32px, 10vw, 48px) / 1.05 var(--font-display);
    text-wrap: balance;
  }
  .hero .footnote {
    position: static;
    transform: none;
    left: auto;
    bottom: auto;
    width: 100%;
    max-width: 480px;
  }
  .hero .footnote::before { margin-bottom: 12px; }
  .hero .footnote p { font: var(--type-small); }
  .hero .footnote p + p { margin-top: 12px; }
  .hero .badge { max-width: 100%; white-space: normal; justify-content: center; text-align: center; }
  .cards-2 { grid-template-columns: 1fr; }
  .persona-row { grid-template-columns: 1fr; gap: 20px; }
  .grid-pillars { grid-template-columns: 1fr; row-gap: 20px; }
  .grid-pillars .pillar { grid-template-rows: auto; grid-row: auto; }
  .field-deployment-band { grid-template-columns: 1fr; }
  .faq summary { min-height: 72px; }
  details.wf summary { flex-direction: column; align-items: flex-start; gap: 12px; }
  .wf-body { grid-template-columns: 1fr; gap: 20px; }
  footer .foot-merged { min-height: clamp(400px, 88vw, 500px); }
  .foot-top { flex-direction: column; align-items: flex-start; }
  .foot-fine { margin-top: 24px; }
  /* Stack to cards: the week identity heads each card and the outcome closes it
     as a full-bleed accent band, so the payoff is still what the eye lands on. */
  .schedule { --wk-rail: 24px; }
  .wk-head { display: none; }
  .weeks { border-top: none; }
  .wk {
    grid-template-columns: minmax(0, 1fr);
    gap: 20px;
    padding: 24px 0 24px;
  }
  .wk-out { margin-top: 2px; }
  .wk-out .label { display: block; }
  .wk-out p { margin-top: 8px; }
  .cohort-directory-head { padding-bottom: 40px; }
}

/* ───────────────── Responsive: phone ───────────────── */
@media (max-width: 720px) {
  /* One card at a time, swiped. A phone has room for exactly one of these, and
     stacking three or six of them turns the section into a scroll of its own;
     side by side they stay one gesture. Native scroll-snap does the moving, so
     there is nothing to drive and no chrome to draw - the card peeking past the
     frame edge is the whole affordance. */
  .speakers-grid {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* bleed past the frame so the next card is visibly cut off */
    margin-right: calc(var(--pad-gutter) * -1);
    padding-right: var(--pad-gutter);
    scrollbar-width: none;
  }
  .speakers-grid::-webkit-scrollbar { display: none; }
  .js .speakers-grid:not(.is-expanded) .speaker-card-more { display: block; }
  .speaker-card {
    flex: 0 0 84%;
    scroll-snap-align: start;
  }
  .speakers-more { display: none; }

  .partners .logo-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 32px;
    max-width: 300px;
  }
  .partners .logo-row-break {
    display: none;
  }
  .partners .partner-logo {
    justify-self: center;
  }
  .partners .partner-logo-anthropic { --partner-logo-height: 15px; }
  .partners .partner-logo-nvidia { --partner-logo-height: 21px; }
  .partners .partner-logo-asi { --partner-logo-height: 38px; }
  .partners .partner-logo-muni { --partner-logo-height: 29px; }
  .partners .partner-logo-rowan { --partner-logo-height: 27px; }
  .partners .partner-logo-prime-intellect { --partner-logo-height: 19px; }
  .partners .partner-logo-fulcrum { --partner-logo-height: 33px; }
  .partners .partner-logo-answerthis { --partner-logo-height: 19px; }
  .partners .partner-logo-tuva { --partner-logo-height: 26px; }
  /* Keep the affiliation proof compact: the institutions hold one row, while
     the university lockups form a deliberate two-by-two rather than a stack. */
  .participants .logo-row {
    display: grid;
  }
  .participants .logo-band-institutions {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    max-width: 340px;
  }
  .participants .logo-band-universities {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 32px;
    max-width: 300px;
  }
  .participants .cohort-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 32px;
    max-width: 400px;
  }
  .participants .cohort-logo-mass { --cohort-logo-height: 14px; gap: 4px; }
  .participants .cohort-logo-mass .cohort-wordmark { font-size: 13px; }
  /* Touch-target floor (44px) - grow hit areas, keep visuals unchanged */
  .btn { min-height: 44px; }
  .logo { min-height: 44px; }
  .team-socials { gap: 0; margin-top: 3px; }
  /* off-scale on purpose: 18px icon + 13px x2 = the 44px minimum touch
     target; the negative margin keeps the row's visual height unchanged */
  .team-socials a { padding: 13px; margin: -13px 0; }
  .foot-top nav .navlink { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ───────────────── Responsive: small phone ───────────────── */
@media (max-width: 600px) {
  .brand-grid { grid-template-columns: 1fr; }
  .image-lightbox { padding: 16px; }
  .image-lightbox img { max-height: calc(100vh - 88px); }
  .cohort-table thead { display: none; }
  .cohort-table,
  .cohort-table tbody,
  .cohort-table tr,
  .cohort-table td { display: block; width: 100%; }
  .cohort-table tr {
    padding: 24px 0;
    border-bottom: var(--rule);
  }
  .cohort-table td { padding: 0; border: 0; }
  .cohort-table td:first-child { width: 100%; }
  .cohort-table td:nth-child(2) { width: 100%; margin-top: 12px; }
  .cohort-table td:nth-child(3) { width: 100%; margin-top: 16px; }
  .cohort-table-representatives::before {
    content: "Represented by";
    display: block;
    margin-bottom: 5px;
    color: var(--text-muted);
    font: var(--type-label);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
  }
  .cohort-company-link { font: var(--type-h3); }
}

/* ───────────────── Reduced motion ─────────────────
   Last in the sheet so it wins over every animation and transition above. */
@media (prefers-reduced-motion: reduce) {
  .speakers-grid.is-expanded .speaker-card-more { animation: none; }
  .speakers-more-mark,
  .cohort-more-arrow { transition: none; }
  .persona-row, .persona-row .tick { transition: none; }
  .cohort-tooltip { transition: none; }
  .team-socials a { transition: none; }
  #team .card, .card .tick { transition: none; }
  .hero svg.wormhole,
  .rf-orbit { animation: none; }
}

/* Switch to the compact menu before the expanded navigation gets crowded. */
@media (max-width: 1000px) {
  header.nav .navlink { display: none; }
  .mobile-menu {
    display: block;
    position: relative;
  }
  .mobile-menu summary {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    cursor: pointer;
    list-style: none;
    font: var(--type-label);
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .mobile-menu summary::-webkit-details-marker { display: none; }
  .mobile-menu[open] summary span { transform: rotate(45deg); }
  .mobile-menu-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 11;
    display: grid;
    min-width: 184px;
    padding: 8px 16px;
    background: var(--bg-0);
    border: var(--rule);
    box-shadow: 0 12px 28px rgba(var(--scrim), 0.12);
  }
  header.nav .mobile-menu-panel .navlink {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
}
