/* ==========================================================================
   About / Founder — SAMPLE
   --------------------------------------------------------------------------
   THE ONE RULE THIS FILE IS BUILT ON
   Every moving thing here composes its transform from CSS custom properties
   (--py, --rx, --ry, --mx, --my, --p). JavaScript sets those properties and
   never writes `transform` itself. GSAP's reveal animations also write
   `transform`, and the last writer wins — a JS parallax that sets transform
   directly silently cancels every reveal on the same element. Composing in CSS
   means the two systems can both be right at once.

   Second rule: only tokens that exist in tokens.css. --s-9, --s-13 and --s-14
   are NOT in the scale, and an undefined custom property invalidates the whole
   declaration at computed-value time — silently.
   ========================================================================== */

.page-abx {
  --abx-hair:  rgb(255 255 255 / .09);
  --abx-pane:  rgb(255 255 255 / .035);
  --abx-pane2: rgb(255 255 255 / .06);
  --abx-glass: blur(16px) saturate(1.3);
  --abx-max:   1240px;
  background: var(--surface-0);
  overflow-x: clip;   /* 3D children can overhang; clip without killing sticky */
}

/* Perspective lives on the sections that own 3D children, not on a global
   wrapper — a page-wide perspective makes every fixed element drift. */
.page-abx [data-depth] { perspective: 1400px; perspective-origin: 50% 45%; }

/* ── AMBIENCE ──────────────────────────────────────────────────────────────
   A fixed grid floor and two slow orbs. Fixed, not scrolled, so it reads as
   the room the page is in rather than another layer of content. */
.abxSky { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.abxSky__grid {
  position: absolute; inset: -20%;
  background-image:
    linear-gradient(to right, rgb(240 198 74 / .05) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(240 198 74 / .05) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 40%, #000 20%, transparent 78%);
  transform: perspective(700px) rotateX(58deg) translateY(12%);
  transform-origin: 50% 100%;
  opacity: .5;
}
.abxSky__orb {
  position: absolute; border-radius: 50%; filter: blur(90px); opacity: .3;
  animation: abxDrift 26s var(--ease-inout) infinite alternate;
}
.abxSky__orb--1 { width: 42vw; height: 42vw; left: -10vw; top: -8vw; background: radial-gradient(circle, rgb(240 198 74 / .5), transparent 70%); }
.abxSky__orb--2 { width: 36vw; height: 36vw; right: -8vw; top: 42vh; background: radial-gradient(circle, rgb(232 84 74 / .32), transparent 70%); animation-delay: -13s; }
@keyframes abxDrift {
  from { translate: 0 0; }
  to   { translate: 4vw 6vh; }
}

.page-abx main > section { position: relative; z-index: 1; }

/* ── SHARED FURNITURE ──────────────────────────────────────────────────── */
.abxEyebrow {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase;
  color: var(--accent-ink); margin: 0 0 var(--s-4);
}
.abxEyebrow--sm { font-size: .68rem; margin-bottom: var(--s-2); }

.abxHead { max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter); margin-bottom: var(--s-12); }
.abxHead--center { text-align: center; }
.abxHead--center .abxHead__sub { margin-inline: auto; }
.abxHead__title {
  font-family: var(--font-display); font-size: var(--fs-h2);
  line-height: var(--lh-snug); letter-spacing: var(--tracking-hero);
  color: var(--text-hi); margin: 0 0 var(--s-4); font-weight: 500;
}
.abxHead__title .accent { color: var(--accent-ink); }
.abxHead__sub { max-width: 56ch; color: var(--text-mid); font-size: var(--fs-body-lg); line-height: var(--lh-body); margin: 0; }

.abxCrumbs ol { display: flex; flex-wrap: wrap; gap: var(--s-2); list-style: none; margin: 0 0 var(--s-6); padding: 0; font-size: var(--fs-sm); color: var(--text-lo); }
.abxCrumbs li + li::before { content: '/'; margin-right: var(--s-2); color: var(--surface-line); }
.abxCrumbs a { color: var(--text-lo); text-decoration: none; }
.abxCrumbs a:hover { color: var(--accent-ink); }

.abxBtn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: .85rem 1.5rem; border-radius: var(--radius-pill);
  font-size: var(--fs-sm); font-weight: 600; text-decoration: none;
  border: 1px solid transparent; cursor: pointer;
  transition: transform var(--dur) var(--ease-spring), box-shadow var(--dur) var(--ease-out),
              background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.abxBtn--primary { background: var(--y-yellow); color: var(--y-black); box-shadow: var(--glow-yellow); }
.abxBtn--primary:hover { transform: translateY(-2px); box-shadow: 0 0 0 1px rgb(240 198 74 / .5), 0 14px 44px -10px rgb(240 198 74 / .5); }
.abxBtn--ghost { border-color: var(--abx-hair); color: var(--text-hi); background: var(--abx-pane); backdrop-filter: var(--abx-glass); }
.abxBtn--ghost:hover { border-color: rgb(240 198 74 / .5); color: var(--accent-ink); transform: translateY(-2px); }
.abxBtn--lg { padding: 1.05rem 2rem; font-size: var(--fs-body); }
.abxBtn--sm { padding: .6rem 1.1rem; font-size: var(--fs-xs); }

/* ── IMAGE SLOTS ───────────────────────────────────────────────────────────
   These are placeholders, and they are designed on purpose. A grey rectangle
   reads as a page that failed to load; a composed slot that names the missing
   photograph reads as a page waiting for one specific thing. */
.abxPh {
  position: relative; margin: 0; overflow: hidden;
  aspect-ratio: var(--ratio, 4/3);
  border-radius: var(--radius-lg);
  border: 1px solid var(--abx-hair);
  background:
    radial-gradient(120% 90% at 20% 0%, rgb(240 198 74 / .14), transparent 60%),
    linear-gradient(160deg, var(--surface-1), var(--surface-0));
  isolation: isolate;
}
.abxPh__grid {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(to right, rgb(255 255 255 / .045) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(255 255 255 / .045) 1px, transparent 1px);
  background-size: 30px 30px;
  mask-image: radial-gradient(circle at 50% 50%, #000 20%, transparent 76%);
}
.abxPh__glow {
  position: absolute; inset: auto -30% -55% -30%; height: 70%;
  background: radial-gradient(ellipse at 50% 100%, rgb(240 198 74 / .3), transparent 68%);
  filter: blur(28px);
}
.abxPh__body {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2); text-align: center; padding: var(--s-5);
}
.abxPh__tag {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--text-lo);
  border: 1px dashed var(--abx-hair); border-radius: var(--radius-pill);
  padding: .2rem .7rem;
}
.abxPh__slot {
  font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 600;
  color: var(--accent-ink); word-break: break-word;
}
.abxPh__cap { font-size: var(--fs-xs); color: var(--text-lo); max-width: 30ch; }

/* A real photograph in the same box. The caption sits ON the image as a
   bottom scrim rather than below it, so swapping a placeholder for a photo
   never changes the height of the card it lives in — which would otherwise
   re-flow the whole grid the moment the first image decoded. */
.abxPh--real { background: var(--surface-1); }
.abxPh--real picture, .abxPh--real img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.abxPh__realcap {
  position: absolute; inset: auto 0 0 0; z-index: 1;
  padding: var(--s-8) var(--s-4) var(--s-3);
  font-size: var(--fs-xs); color: var(--y-white);
  background: linear-gradient(to top, rgb(0 0 0 / .82), rgb(0 0 0 / .45) 45%, transparent);
  text-shadow: 0 1px 3px rgb(0 0 0 / .6);
}

/* ══════════════════════════════════════════════════════════════════════════
   01 · HERO
   ══════════════════════════════════════════════════════════════════════════ */
.abxHero {
  min-height: min(100svh, 60rem);
  display: grid; align-content: center;
  padding: calc(var(--s-24) + 2rem) 0 var(--s-20);
}
.abxHero__inner {
  width: 100%; max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(var(--s-8), 4vw, var(--s-16)); align-items: center;
}

.abxHero__name {
  font-family: var(--font-display); font-size: var(--fs-hero);
  line-height: var(--lh-tight); letter-spacing: var(--tracking-hero);
  margin: 0 0 var(--s-5); font-weight: 500; color: var(--text-hi);
  display: flex; flex-wrap: wrap; gap: 0 .3em;
}
/* Staggered rise, per word. `--i` carries the index so one rule covers both
   lines instead of an :nth-child for each. */
.abxHero__line {
  display: inline-block;
  animation: abxRise .9s var(--ease-out) backwards;
  animation-delay: calc(var(--i) * 110ms + 120ms);
}
.abxHero__line--accent { color: var(--accent-ink); }
@keyframes abxRise {
  from { opacity: 0; translate: 0 .45em; filter: blur(8px); }
  to   { opacity: 1; translate: 0 0;     filter: blur(0); }
}

.abxHero__sub {
  max-width: 48ch; color: var(--text-mid);
  font-size: var(--fs-body-lg); line-height: var(--lh-body); margin: 0 0 var(--s-6);
  animation: abxRise .9s var(--ease-out) 360ms backwards;
}

/* The rotating credential. Every item is stacked in the same grid cell so the
   line never changes height as it cycles. */
.abxTicker {
  display: grid; margin: 0 0 var(--s-8); min-height: 3.2rem;
  animation: abxRise .9s var(--ease-out) 460ms backwards;
}
.abxTicker__item {
  grid-area: 1 / 1; display: flex; flex-direction: column; gap: .1rem;
  opacity: 0; translate: 0 .5rem; pointer-events: none;
  transition: opacity .5s var(--ease-out), translate .5s var(--ease-out);
}
.abxTicker__item.is-on { opacity: 1; translate: 0 0; }
.abxTicker__item b { font-size: var(--fs-body-lg); color: var(--text-hi); font-weight: 600; }
.abxTicker__item i {
  font-style: normal; font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: .08em; text-transform: uppercase; color: var(--accent-ink);
}

.abxHero__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); animation: abxRise .9s var(--ease-out) 560ms backwards; }

/* --- the portrait stack ------------------------------------------------- */
.abxHero__stage { display: grid; place-items: center; }

.abxPortrait {
  position: relative; width: min(100%, 27rem);
  transform-style: preserve-3d;
  /* --rx/--ry are written by the tilt handler; the fallbacks keep the element
     flat and valid before any pointer has moved. */
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform .5s var(--ease-out);
  animation: abxRise 1.1s var(--ease-out) 240ms backwards;
}
.abxPortrait.is-moving { transition: none; }   /* no lag while actively tracking */

.abxPortrait__halo {
  position: absolute; inset: -14%; border-radius: 50%;
  background: radial-gradient(circle, rgb(240 198 74 / .3), transparent 66%);
  filter: blur(48px); translate: 0 0 -60px;
}
.abxPortrait__frame {
  position: relative; border-radius: calc(var(--radius-lg) + 6px);
  border: 1px solid var(--abx-hair); overflow: hidden;
  background: var(--surface-1); box-shadow: var(--shadow-card);
  translate: 0 0 40px;
}
.abxPortrait__frame .abxPh { border: 0; border-radius: 0; }

/* A slow scan line — one moving element, not a loop of them. */
.abxPortrait__scan {
  position: absolute; inset-inline: 0; height: 28%; top: -28%;
  background: linear-gradient(to bottom, transparent, rgb(240 198 74 / .16), transparent);
  animation: abxScan 5.5s var(--ease-inout) infinite;
  pointer-events: none;
}
@keyframes abxScan { 0% { top: -30%; } 60%, 100% { top: 110%; } }

.abxChip {
  position: absolute; z-index: 2;
  display: flex; flex-direction: column; gap: .05rem;
  padding: .6rem .9rem; border-radius: var(--radius);
  border: 1px solid var(--abx-hair); background: rgb(22 22 26 / .78);
  backdrop-filter: var(--abx-glass); box-shadow: var(--shadow-card);
  font-size: var(--fs-xs); color: var(--text-mid); white-space: nowrap;
  animation: abxFloat 7s var(--ease-inout) infinite alternate;
}
.abxChip b { font-size: var(--fs-sm); color: var(--text-hi); }
.abxChip--1 { left: -8%;  top: 14%;  translate: 0 0 90px;  }
.abxChip--2 { right: -10%; top: 46%; translate: 0 0 120px; animation-delay: -2.4s; }
.abxChip--3 { left: -4%;  bottom: 10%; translate: 0 0 70px; animation-delay: -4.8s; }
@keyframes abxFloat { from { margin-top: -6px; } to { margin-top: 6px; } }

.abxScrollCue {
  position: absolute; left: 50%; bottom: var(--s-6); translate: -50% 0;
  display: grid; justify-items: center; gap: var(--s-2);
  text-decoration: none; color: var(--text-lo); font-size: var(--fs-xs);
  font-family: var(--font-mono); letter-spacing: .1em; text-transform: uppercase;
}
.abxScrollCue:hover { color: var(--accent-ink); }
.abxScrollCue__rail {
  width: 1px; height: 3rem; background: linear-gradient(to bottom, transparent, var(--abx-hair));
  position: relative; overflow: hidden;
}
.abxScrollCue__rail i {
  position: absolute; inset-inline: 0; height: 40%;
  background: var(--y-yellow); animation: abxCue 2.2s var(--ease-inout) infinite;
}
@keyframes abxCue { 0% { top: -40%; } 100% { top: 100%; } }

/* ══════════════════════════════════════════════════════════════════════════
   02 · LEDGER MARQUEE
   ══════════════════════════════════════════════════════════════════════════ */
.abxLedger {
  border-block: 1px solid var(--abx-hair);
  background: rgb(255 255 255 / .02);
  padding: var(--s-5) 0; overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
.abxLedger__track { display: flex; width: max-content; animation: abxMarquee 34s linear infinite; }
.abxLedger:hover .abxLedger__track { animation-play-state: paused; }
.abxLedger__row { display: flex; gap: var(--s-10); list-style: none; margin: 0; padding: 0 var(--s-5); }
.abxLedger__item { display: grid; gap: .1rem; white-space: nowrap; }
.abxLedger__kind {
  font-family: var(--font-mono); font-size: .58rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent-ink);
}
.abxLedger__item b { font-size: var(--fs-body); color: var(--text-hi); font-weight: 600; }
.abxLedger__item i { font-style: normal; font-size: var(--fs-xs); color: var(--text-lo); }
/* Translating by exactly -50% works because the row is rendered twice. */
@keyframes abxMarquee { from { translate: 0 0; } to { translate: -50% 0; } }

/* ══════════════════════════════════════════════════════════════════════════
   03 · STORY — scrolling chapters against a sticky stage
   ══════════════════════════════════════════════════════════════════════════ */
.abxStory { padding-block: var(--section-y); }
/* One column since the sticky picture stage was removed.

   Capping the RAIL alone was not enough: the section container is still
   1240px, so a 62ch rail inside it left ~600px of dead canvas down the right
   and the story read as if something had failed to load. The whole section —
   heading and chapters together — is narrowed instead, so the empty space
   becomes page margin on both sides rather than a hole beside the text. */
.abxStory > .abxHead,
.abxStory__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
}

.abxStory__grid {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(var(--s-8), 5vw, var(--s-16)); align-items: start;
}

/* ── The zigzag timeline ───────────────────────────────────────────────────
   Chapters alternate sides of a centre spine: odd to the left, even to the
   right. Each .abxChapter is its own 3-column grid — [body | spine | body] —
   so the numbered marks all land on the same vertical axis without any of
   them needing to know how tall its neighbours are.

   The spine is drawn once, on the rail, as a ::before rather than as a border
   on each chapter. Per-chapter borders leave a gap at every `gap: var(--s-20)`
   seam, so the line would arrive as eleven dashes instead of one run.

   Body text stays LEFT-aligned on both sides. Mirroring it to the right on the
   left-hand chapters is the conventional timeline move and it looks tidy in a
   mockup, but a right-ragged paragraph is measurably slower to read — and
   these are paragraphs, not captions. */
.abxStory__rail {
  display: grid; gap: var(--s-20);
  position: relative;
}

.abxStory__rail::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; transform: translateX(-.5px);
  background: linear-gradient(to bottom, transparent, var(--abx-hair) 6%, var(--abx-hair) 94%, transparent);
  pointer-events: none;
}

.abxChapter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  column-gap: var(--s-6);
  align-items: start;
}

/* The mark always sits in the middle column, on the spine. */
.abxChapter__mark { grid-column: 2; display: grid; justify-items: center; gap: var(--s-3); }

/* Odd chapters take the left cell, even ones the right. */
.abxChapter:nth-child(odd)  .abxChapter__body { grid-column: 1; }
.abxChapter:nth-child(even) .abxChapter__body { grid-column: 3; }
.abxChapter__n {
  display: grid; place-items: center; width: 2.9rem; height: 2.9rem;
  border-radius: 50%; border: 1px solid var(--abx-hair);
  font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--text-lo);
  background: var(--surface-1);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.abxChapter.is-on .abxChapter__n {
  color: var(--y-black); background: var(--y-yellow);
  border-color: transparent; box-shadow: var(--glow-yellow);
}
/* Superseded by .abxStory__rail::before, which runs the whole column. */
.abxChapter__line { display: none; }

.abxChapter__title {
  font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  line-height: var(--lh-snug); color: var(--text-hi); margin: 0 0 var(--s-3);
  transition: color var(--dur) var(--ease-out);
}
.abxChapter__text { color: var(--text-mid); line-height: var(--lh-body); margin: 0; max-width: 46ch; }
/* Inactive chapters recede rather than disappear — a dimmed paragraph still
   reads if somebody wants to read ahead. */
.abxChapter { opacity: .42; transition: opacity var(--dur-slow) var(--ease-out); }
.abxChapter.is-on { opacity: 1; }



/* ══════════════════════════════════════════════════════════════════════════
   04 · MOAT
   ══════════════════════════════════════════════════════════════════════════ */
.abxMoat { padding-block: var(--section-y); }
.abxMoat__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-5);
  perspective: 1200px;
}
.abxMoat__card {
  position: relative; overflow: hidden; isolation: isolate;
  padding: var(--s-8) var(--s-6) var(--s-6);
  border-radius: var(--radius-lg); border: 1px solid var(--abx-hair);
  background: linear-gradient(165deg, var(--abx-pane2), var(--abx-pane));
  backdrop-filter: var(--abx-glass);
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform .5s var(--ease-out), border-color var(--dur) var(--ease-out);
  transform-style: preserve-3d;
}
.abxMoat__card.is-moving { transition: border-color var(--dur) var(--ease-out); }
.abxMoat__card:hover { border-color: rgb(240 198 74 / .38); }
.abxMoat__n {
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--accent-ink);
  letter-spacing: .12em;
}
.abxMoat__card h3 {
  font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500;
  color: var(--text-hi); margin: var(--s-3) 0 var(--s-3); line-height: var(--lh-snug);
}
.abxMoat__card p { color: var(--text-mid); margin: 0; font-size: var(--fs-sm); line-height: var(--lh-body); }
/* Pointer-tracked sheen. --mx/--my are percentages written by the tilt
   handler; without a pointer the gradient sits off-card and is invisible. */
.abxMoat__sheen {
  position: absolute; inset: 0; z-index: -1; opacity: 0;
  background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), rgb(240 198 74 / .16), transparent 55%);
  transition: opacity var(--dur) var(--ease-out);
}
.abxMoat__card:hover .abxMoat__sheen { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   05 · THE TURN
   ══════════════════════════════════════════════════════════════════════════ */
.abxTurn { padding-block: var(--section-y); }
/* Single column since the right-hand media plate was removed.

   The GRID keeps the full container width and the COPY inside it is capped at
   measure — not the other way round. Narrowing the grid itself centred the
   paragraphs and left them starting 170px right of the .abxBuilt cards
   directly underneath, which share this section and are still full width. */
.abxTurn__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(var(--s-8), 5vw, var(--s-16)); align-items: center;
}

.abxTurn__copy { max-width: 64ch; }
.abxTurn__copy p { color: var(--text-mid); line-height: var(--lh-body); margin: 0 0 var(--s-4); max-width: 54ch; }
.abxTurn__copy em { color: var(--text-hi); font-style: italic; }

.abxBuilt {
  max-width: var(--abx-max); margin: var(--s-16) auto 0; padding-inline: var(--gutter);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5); list-style: none;
}
.abxBuilt__item {
  padding: var(--s-6); border-radius: var(--radius);
  border: 1px solid var(--abx-hair); background: var(--abx-pane);
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.abxBuilt__item:hover { border-color: rgb(240 198 74 / .35); transform: translateY(-3px); }
.abxBuilt__item h3 { font-size: var(--fs-body); color: var(--text-hi); margin: 0 0 var(--s-2); font-weight: 600; }
.abxBuilt__item p { font-size: var(--fs-sm); color: var(--text-mid); margin: 0; line-height: var(--lh-body); }

/* ══════════════════════════════════════════════════════════════════════════
   06 · TROPHY WALL
   ══════════════════════════════════════════════════════════════════════════ */
.abxWins { padding-block: var(--section-y); }
.abxWins__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-6);
  perspective: 1300px;
}
.abxWin {
  display: grid; grid-template-rows: auto 1fr; overflow: hidden;
  border-radius: var(--radius-lg); border: 1px solid var(--abx-hair);
  background: linear-gradient(170deg, var(--abx-pane2), transparent 65%), var(--surface-1);
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform .5s var(--ease-out), border-color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
  transform-style: preserve-3d;
}
.abxWin.is-moving { transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out); }
.abxWin:hover { border-color: rgb(240 198 74 / .4); box-shadow: var(--glow-yellow); }
.abxWin__media .abxPh { border: 0; border-radius: 0; border-bottom: 1px solid var(--abx-hair); }
.abxWin__body { padding: var(--s-6); display: grid; align-content: start; gap: var(--s-2); }
.abxWin__kind {
  justify-self: start;
  font-family: var(--font-mono); font-size: .58rem; letter-spacing: .16em; text-transform: uppercase;
  padding: .2rem .6rem; border-radius: var(--radius-pill);
  border: 1px solid currentColor; color: var(--text-lo);
}
.abxWin__kind[data-kind="won"]       { color: var(--y-yellow); background: rgb(240 198 74 / .12); }
.abxWin__kind[data-kind="pitched"]   { color: var(--y-red-300); background: rgb(232 84 74 / .12); }
.abxWin__kind[data-kind="invited"],
.abxWin__kind[data-kind="presented"] { color: var(--text-mid); }
.abxWin__where { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500; color: var(--text-hi); margin: 0; line-height: var(--lh-snug); }
.abxWin__what { font-size: var(--fs-sm); color: var(--accent-ink); margin: 0; }
.abxWin__b { font-size: var(--fs-sm); color: var(--text-mid); margin: var(--s-2) 0 0; line-height: var(--lh-body); }

/* --- the card is a button ------------------------------------------------
   The <button> sits inside the <h3> and stretches its hit area over the whole
   card with ::after. That way the click target is the card, while the
   accessible name stays the name of the place — a page with six links all
   reading "read more" is unusable from a screen reader's link list. */
.abxWin__open {
  all: unset; cursor: pointer; display: inline;
  color: inherit; font: inherit;
}
.abxWin__open::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.abxWin { position: relative; }
.abxWin__open:focus-visible { outline: none; }
.abxWin:has(.abxWin__open:focus-visible) {
  outline: 2px solid var(--y-yellow); outline-offset: 3px;
}

.abxWin__more {
  margin-top: var(--s-4); font-size: var(--fs-xs); font-family: var(--font-mono);
  letter-spacing: .1em; text-transform: uppercase; color: var(--text-lo);
  display: inline-flex; align-items: center; gap: .5ch;
  transition: color var(--dur) var(--ease-out);
}
.abxWin__more i { font-style: normal; transition: translate var(--dur) var(--ease-out); }
.abxWin:hover .abxWin__more { color: var(--accent-ink); }
.abxWin:hover .abxWin__more i { translate: 4px 0; }

/* A hint that the picture is the thing being opened. */
.abxWin__media { position: relative; overflow: hidden; }
.abxWin__media .abxPh--real img { transition: scale var(--dur-slow) var(--ease-out); }
.abxWin:hover .abxWin__media .abxPh--real img { scale: 1.04; }

/* ══════════════════════════════════════════════════════════════════════════
   06b · THE STORY DIALOG
   Native <dialog>. showModal() supplies the top layer, the backdrop, Esc, the
   inert background and the focus trap — all four of which hand-rolled modals
   routinely get wrong.
   ══════════════════════════════════════════════════════════════════════════ */
.abxTale {
  width: min(64rem, calc(100vw - 2rem));
  max-height: min(46rem, calc(100dvh - 2rem));
  padding: 0; border: 1px solid var(--abx-hair);
  border-radius: var(--radius-lg);
  background: var(--surface-1); color: var(--text-mid);
  box-shadow: 0 40px 120px -30px rgb(0 0 0 / .85);
  overflow: hidden;
}
.abxTale::backdrop {
  background: rgb(6 6 9 / .72);
  backdrop-filter: blur(6px);
}
/* `display` is what <dialog> toggles, so the entry animation has to be keyed
   off [open] rather than a transition on opacity — a transition never runs on
   an element that was display:none a frame ago. */
.abxTale[open] { animation: abxTaleIn .42s var(--ease-out); }
.abxTale[open]::backdrop { animation: abxFade .42s var(--ease-out); }
@keyframes abxTaleIn {
  from { opacity: 0; translate: 0 2rem; scale: .96; }
  to   { opacity: 1; translate: 0 0;    scale: 1; }
}
@keyframes abxFade { from { opacity: 0; } to { opacity: 1; } }

.abxTale__inner {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  max-height: inherit;
}
.abxTale__media {
  background: #07080c; display: grid; place-items: center;
  min-height: 0; overflow: hidden;
}
/* `contain`, not `cover`. This is the view where the whole photograph should
   be visible — it is the answer to a crop having cut somebody's face off. */
.abxTale__media img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; max-height: min(46rem, calc(100dvh - 2rem));
}
.abxTale__slot {
  display: grid; gap: var(--s-2); place-items: center; padding: var(--s-16) var(--s-6);
  text-align: center;
}

.abxTale__body {
  padding: var(--s-8) var(--s-8) var(--s-6);
  overflow-y: auto; min-height: 0;
  display: grid; align-content: start; gap: var(--s-3);
}
.abxTale__title {
  font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 500;
  color: var(--text-hi); margin: 0; line-height: var(--lh-snug);
  letter-spacing: var(--tracking-hero);
}
.abxTale__what { margin: 0; color: var(--accent-ink); font-size: var(--fs-body); }

.abxTale__meta {
  /* --cols comes from PHP: one column when an event has no date on record,
     so a single row fills the width instead of leaving a gap where the
     missing one would have been. */
  display: grid; grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
  gap: var(--s-4); margin: var(--s-3) 0;
  padding: var(--s-4) 0; border-block: 1px solid var(--abx-hair);
}
.abxTale__meta dt {
  font-family: var(--font-mono); font-size: .6rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--text-lo); margin-bottom: .25rem;
}
.abxTale__meta dd { margin: 0; color: var(--text-hi); font-size: var(--fs-sm); }

.abxTale__p { margin: 0; line-height: var(--lh-body); color: var(--text-mid); }

.abxTale__nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); margin-top: var(--s-5);
  padding-top: var(--s-5); border-top: 1px solid var(--abx-hair);
}
.abxTale__count { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-lo); }

.abxTale__x {
  position: absolute; top: var(--s-3); right: var(--s-3); z-index: 3;
  width: 2.2rem; height: 2.2rem; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer; font-size: 1.4rem; line-height: 1;
  border: 1px solid var(--abx-hair); background: rgb(13 13 15 / .72);
  backdrop-filter: blur(8px); color: var(--text-hi);
  transition: background-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.abxTale__x:hover { background: var(--y-red); color: var(--y-white); transform: rotate(90deg); }

.theme-light .abxTale { background: var(--surface-1); }
.theme-light .abxTale__x { background: rgb(255 255 255 / .84); }
.theme-light .abxTale::backdrop { background: rgb(28 32 48 / .5); }

@media (max-width: 56rem) {
  /* One column, and the picture capped by height — otherwise a tall phone
     photo shown `contain` takes the entire dialog and the story is below the
     fold of a box that is already scrolling. */
  .abxTale__inner { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
  .abxTale { max-height: calc(100dvh - 1.5rem); width: calc(100vw - 1.5rem); }
  .abxTale__media { max-height: 40dvh; }
  .abxTale__media img { max-height: 40dvh; }
  .abxTale__body { padding: var(--s-6) var(--s-5); }
}
@media (max-width: 30rem) {
  .abxTale__meta { grid-template-columns: minmax(0, 1fr); gap: var(--s-3); }
  .abxTale__nav { flex-wrap: wrap; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .abxTale[open], .abxTale[open]::backdrop { animation: none; }
  .abxWin:hover .abxWin__media .abxPh--real img { scale: 1; }
}

/* ══════════════════════════════════════════════════════════════════════════
   07 · THE QUOTE
   ══════════════════════════════════════════════════════════════════════════ */
.abxQuote {
  padding-block: calc(var(--section-y) * 1.5);
  background:
    radial-gradient(ellipse 60% 70% at 50% 40%, rgb(240 198 74 / .1), transparent 70%),
    linear-gradient(to bottom, transparent, rgb(255 255 255 / .022), transparent);
  border-block: 1px solid var(--abx-hair);
}
.abxQuote__inner { max-width: 60rem; margin-inline: auto; padding-inline: var(--gutter); text-align: center; position: relative; }
.abxQuote__mark {
  display: block; font-family: var(--font-display); font-size: 7rem; line-height: .6;
  color: var(--y-yellow); opacity: .28; margin-bottom: var(--s-4);
}
.abxQuote__text { margin: 0; }
.abxQuote__text p {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.5rem, 1rem + 2.4vw, 2.6rem);
  line-height: var(--lh-snug); letter-spacing: -0.02em;
  color: var(--text-hi); margin: 0;
}
.abxQuote__by {
  margin: var(--s-6) 0 0; font-family: var(--font-mono); font-size: var(--fs-xs);
  letter-spacing: .14em; text-transform: uppercase; color: var(--accent-ink);
}

/* ══════════════════════════════════════════════════════════════════════════
   08 · NUMBERS
   ══════════════════════════════════════════════════════════════════════════ */
.abxStats { padding-block: var(--section-y); }
.abxStats__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px; background: var(--abx-hair);
  border: 1px solid var(--abx-hair); border-radius: var(--radius-lg); overflow: hidden;
}
.abxStat { background: var(--surface-0); padding: var(--s-8) var(--s-6); display: grid; gap: var(--s-1); }
.abxStat__n {
  font-family: var(--font-display); font-size: clamp(2rem, 1.4rem + 2vw, 3rem);
  font-weight: 500; color: var(--text-hi); margin: 0; line-height: 1;
  display: flex; align-items: baseline; gap: .05em;
}
.abxStat__n i { font-style: normal; color: var(--accent-ink); font-size: .55em; }
.abxStat__l { margin: var(--s-3) 0 0; color: var(--text-hi); font-size: var(--fs-body); font-weight: 600; }
.abxStat__s { margin: 0; color: var(--text-lo); font-size: var(--fs-sm); }

/* ══════════════════════════════════════════════════════════════════════════
   09 · STUDIO
   ══════════════════════════════════════════════════════════════════════════ */
.abxStudio { padding-block: var(--section-y); }
.abxStudio__grid {
  max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr) minmax(0, .7fr);
  grid-template-areas: "a copy b";
  gap: clamp(var(--s-6), 3vw, var(--s-10)); align-items: center;
}
.abxStudio__media--a { grid-area: a; translate: 0 var(--py, 0px); }
.abxStudio__media--b { grid-area: b; translate: 0 var(--py, 0px); }
.abxStudio__copy { grid-area: copy; }
.abxStudio__copy p { color: var(--text-mid); line-height: var(--lh-body); margin: 0 0 var(--s-4); }
.abxStudio__team { padding: var(--s-4); border-left: 2px solid var(--y-yellow); background: var(--abx-pane); border-radius: 0 var(--radius) var(--radius) 0; }
.abxStudio__team b { color: var(--text-hi); }

/* ══════════════════════════════════════════════════════════════════════════
   10 · WHAT'S NEXT
   ══════════════════════════════════════════════════════════════════════════ */
.abxNext { padding-block: var(--section-y); }
.abxNext__inner { max-width: var(--abx-max); margin-inline: auto; padding-inline: var(--gutter); }
/* `align-items: start` let every card size to its own text, so the one with a
   button stood taller than its neighbours and the row read as broken. Cards
   stretch to a common height instead, and each is a flex column whose button
   is pushed down by `margin-top: auto` — so the buttons sit on one line across
   the row no matter how the paragraphs above them wrap. */
.abxNext__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); align-items: stretch; }
.abxNext__card {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: var(--s-8) var(--s-6); border-radius: var(--radius-lg);
  border: 1px solid var(--abx-hair); background: var(--abx-pane);
  /* backdrop-filter REMOVED, and it has to stay removed.
     .abxBtn--primary:hover applies `transform: translateY(-2px)`, which
     promotes the button to its own compositing layer. In Chrome, a composited
     descendant of a backdrop-filter element gets re-sampled by that filter and
     its text disappears — the button went solid yellow and empty on hover.
     Nothing is lost visually: the card sits on a flat dark section, so the
     filter was blurring an almost solid colour. --abx-pane does the tint. */
  background-color: var(--abx-pane);
}

/* The button is the only thing that should absorb the slack. `z-index` keeps
   it painted above the card's own background in every state, so the hover lift
   cannot tuck it underneath. */
.abxNext__card .abxBtn {
  margin-top: auto;
  align-self: center;
  position: relative; z-index: 1;
}
.abxNext__card h3 { font-family: var(--font-display); font-size: var(--fs-h3); font-weight: 500; color: var(--text-hi); margin: 0; line-height: var(--lh-snug); }
.abxNext__card p { color: var(--text-mid); font-size: var(--fs-sm); line-height: var(--lh-body); margin: 0; }
.abxNext__card--hot {
  border-color: rgb(240 198 74 / .42);
  background: linear-gradient(165deg, rgb(240 198 74 / .1), var(--abx-pane));
  box-shadow: var(--glow-yellow);
}

/* ══════════════════════════════════════════════════════════════════════════
   11 · CLOSING
   ══════════════════════════════════════════════════════════════════════════ */
.abxEnd { padding-block: calc(var(--section-y) * 1.3); text-align: center; }
.abxEnd__inner { max-width: 48rem; margin-inline: auto; padding-inline: var(--gutter); }
.abxEnd h2 { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: 500; color: var(--text-hi); margin: 0 0 var(--s-3); letter-spacing: var(--tracking-hero); }
.abxEnd p { color: var(--text-mid); margin: 0 0 var(--s-8); font-size: var(--fs-body-lg); }
.abxEnd__cta { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center; }

/* ══════════════════════════════════════════════════════════════════════════
   LIGHT THEME
   The page is dark by default. It still has to survive the site-wide toggle,
   and the failure mode is specific: every rgb(255 255 255 / …) pane becomes
   invisible on a white ground, so the panes invert rather than the text.
   ══════════════════════════════════════════════════════════════════════════ */
.theme-light .page-abx,
.theme-light.page-abx {
  --abx-hair:  rgb(28 28 28 / .12);
  --abx-pane:  rgb(28 28 28 / .028);
  --abx-pane2: rgb(28 28 28 / .05);
}
.theme-light .abxSky__grid { opacity: .32; }
.theme-light .abxSky__orb { opacity: .18; }
.theme-light .abxChip { background: rgb(255 255 255 / .88); }
.theme-light .abxStat { background: var(--surface-1); }
.theme-light .abxPh {
  background:
    radial-gradient(120% 90% at 20% 0%, rgb(240 198 74 / .22), transparent 60%),
    linear-gradient(160deg, var(--surface-2), var(--surface-1));
}
.theme-light .abxPh__grid { background-image:
  linear-gradient(to right, rgb(28 28 28 / .05) 1px, transparent 1px),
  linear-gradient(to bottom, rgb(28 28 28 / .05) 1px, transparent 1px); }
.theme-light .abxWin__kind[data-kind="won"] { color: var(--accent-ink); }
.theme-light .abxWin__kind[data-kind="pitched"] { color: var(--y-red-700); }
.theme-light .abxQuote__mark { opacity: .4; color: var(--accent-ink); }

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   Each breakpoint restates grid-template-areas wherever it changes the column
   count. An element left holding an area name that no longer exists is
   auto-placed into an implicit row — which looks like a random gap and gives
   no error anywhere.
   ══════════════════════════════════════════════════════════════════════════ */

/* --- tablet / small laptop --------------------------------------------- */
@media (max-width: 64rem) {
  .abxHero { min-height: 0; padding-top: calc(var(--s-16) + 2rem); }
  .abxHero__inner { grid-template-columns: minmax(0, 1fr); gap: var(--s-12); }
  /* Deliberately NOT `order: -1`. Floating the portrait above the copy puts
     an image slot above the breadcrumb and the founder's own name, so the
     first thing on a phone is a picture with no caption yet. Reading order
     stays name → face, which is also the DOM order a screen reader gets. */
  .abxPortrait { width: min(100%, 22rem); }
  .abxScrollCue { display: none; }

  .abxMoat__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .abxWins__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .abxBuilt { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .abxNext__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .abxStats__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .abxTurn__grid { grid-template-columns: minmax(0, 1fr); gap: var(--s-8); }


  /* Three columns to one: the areas MUST be restated or `copy`, `a` and `b`
     all still resolve against a template that no longer exists. */
  .abxStudio__grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "copy" "a" "b";
    gap: var(--s-8);
  }
  .abxStudio__media--b { max-width: 22rem; }
}

/* --- the zigzag stops paying for itself --------------------------------- */
@media (max-width: 56rem) {
  .abxStory__grid { grid-template-columns: minmax(0, 1fr); }
  .abxStory__rail { gap: var(--s-12); }

  /* Two columns of ~20ch each is a newspaper, not a page. Below this the
     timeline straightens: the spine moves to the left edge and every chapter
     takes the one remaining column, whichever side it was on above. */
  .abxStory__rail::before { left: 1.45rem; }
  .abxChapter { grid-template-columns: auto minmax(0, 1fr); column-gap: var(--s-5); }
  .abxChapter__mark { grid-column: 1; }
  .abxChapter:nth-child(odd)  .abxChapter__body,
  .abxChapter:nth-child(even) .abxChapter__body { grid-column: 2; }
  .abxChapter__text { max-width: none; }
}

/* --- phone -------------------------------------------------------------- */
@media (max-width: 46rem) {
  .abxHero { padding-block: calc(var(--s-12) + 2rem) var(--s-12); }
  .abxHero__name { font-size: clamp(2.5rem, 1.6rem + 6vw, 3.4rem); }
  .abxPortrait { width: min(100%, 17rem); }
  /* The chips are absolutely positioned outside the frame — at this width
     that means off the screen. Reflow them under the portrait as a row. */
  .abxPortrait { display: grid; gap: var(--s-3); }
  .abxChip { position: static; translate: none; animation: none; white-space: normal; }
  .abxPortrait__halo { display: none; }

  .abxMoat__grid,
  .abxWins__grid,
  .abxBuilt,
  .abxNext__grid { grid-template-columns: minmax(0, 1fr); }
  .abxStats__grid { grid-template-columns: minmax(0, 1fr); }

  .abxChapter { grid-template-columns: minmax(0, 1fr); gap: var(--s-3); }
  .abxChapter__mark { justify-items: start; }
  .abxChapter__line { display: none; }

  .abxQuote__mark { font-size: 4rem; }
  .abxLedger__row { gap: var(--s-6); }

  /* Tilt is a pointer affordance. On a touch screen it never fires, and the
     rotate variables would simply sit at 0 — but clearing them removes the
     transform entirely so the compositor has one less layer to keep. */
  .abxMoat__card, .abxWin, .abxPortrait { transform: none; }
}

@media (max-width: 24rem) {
  .abxBtn { width: 100%; }
  .abxHero__cta, .abxEnd__cta { flex-direction: column; }
  .abxStat { padding: var(--s-6) var(--s-5); }
}

/* ── REDUCED MOTION ────────────────────────────────────────────────────────
   tokens.css already forces [data-reveal] visible and kills durations. What
   it cannot know about is this page's infinite loops and its parallax, both
   of which have to be switched off at the source. */
@media (prefers-reduced-motion: reduce) {
  .abxSky__orb, .abxPortrait__scan, .abxChip,
  .abxScrollCue__rail i, .abxLedger__track { animation: none; }
  .abxHero__line, .abxHero__sub, .abxTicker, .abxHero__cta, .abxPortrait { animation: none; }
  .abxStudio__media--a, .abxStudio__media--b { translate: none; }
  .abxChapter { opacity: 1; }
  .abxTicker__item { opacity: 1; translate: none; grid-area: auto; }
  .abxTicker { min-height: 0; }
}
