/* AQcredix — founder portfolio.
   Theme tokens throughout; no hardcoded colour inside any media query. */

/* ---------------------------------- hero ---------------------------------- */

.fp-hero {
  background: transparent;
  color: var(--fg);
  padding: 64px 0 48px;
  position: relative;
  overflow: hidden;
}
/* Stacked and centred: portrait, then name, then the rest. A max-width on the text keeps
   the measure readable — centred prose running the full page width is hard to track,
   because the eye loses the start of each line. */
.fp-hero-grid {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 22px;
}
/* The text block must CENTRE ITSELF as well as centre its text. As a flex child it is
   sized to content, so text-align alone left the block flush left and only centred the
   lines within their own box — which is what the screenshot showed. width:100% plus auto
   margins puts the box in the middle; text-align on the children does the rest.
   Everything is forced because .eyebrow is inline-flex and .fp-role/.fp-loc carry their
   own alignment from the shared stylesheet. */
.fp-hero-text {
  max-width: 62ch;
  width: 100%;
  margin-inline: auto;
  text-align: center;
}
.fp-hero-text > *,
.fp-hero-text h1,
.fp-hero-text .fp-role,
.fp-hero-text .fp-loc,
.fp-hero-text .fp-affil { text-align: center; }

/* The eyebrow is inline-flex with a leading dash rule, so it needs centring as a box. */
.fp-hero-text .eyebrow { display: inline-flex; justify-content: center; }

/* The post-nominals sit on their own centred line under the name rather than trailing it. */
.fp-hero-text h1 .fp-post { display: block; white-space: normal; margin-top: 4px; }

.fp-photo {
  width: 190px;
  height: 190px;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid var(--accent-bright);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, .05);
  transition: transform 220ms ease;
  will-change: transform;
}
.fp-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fp-initials {
  font-family: var(--font-display);
  font-size: 58px;
  font-weight: 700;
  color:var(--accent-text);
  letter-spacing: .02em;
}

.fp-hero h1 {
  font-size: clamp(28px, 4.4vw, 46px);
  letter-spacing: -.03em;
  margin: 10px 0 8px;
  line-height: 1.08;
}
.fp-post {
  font-size: .52em;
  font-weight: 600;
  color:var(--accent-text);
  letter-spacing: 0;
  white-space: nowrap;
}
.fp-role { font-size: 16px; color: var(--fg-muted); margin-bottom: 4px; }
.fp-loc { font-size: 13.5px; color: var(--fg-faint); margin-bottom: 16px; }

.fp-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 20px; justify-content: center; }
.fp-tag {
  font-size: 11.5px;
  font-weight: 600;
  padding: 6px 12px;
  line-height: 1.45;
  /* 16px, NOT 999px — and the difference only shows when a chip wraps.
     A radius is clamped to half the box, so on a single line (27px tall) 16px still resolves
     to a full stadium and nothing changes. On a chip that has wrapped to three lines, 999px
     curves the ends into a 32px arc that cuts across the first and last lines of text, which
     is the "letters coming out of the circular space". At 16px a wrapped chip is a rounded
     rectangle, which is what it should have been all along. Some labels — "Consultant —
     Clinical Documentation Designer (NABH, JCI)" — wrap even in a 213px rail at 1440px, so
     wrapping is not an edge case to design around, it is the normal case. */
  border-radius: 16px;
  /* TOKENS, NOT HARD-CODED WHITE. These were rgba(255,255,255,.08) on a
     rgba(255,255,255,.14) border, which is a faint light chip on a dark page
     and completely invisible on a white one — in the light theme the tags read
     as bare text with no chip at all. The surface tokens already resolve to
     the right thing in both themes. */
  background: var(--surface-1);
  border: 1px solid var(--border);
  color: var(--fg);
}
.fp-links { display: flex; gap: 9px; flex-wrap: wrap; justify-content: center; }

/* Counters. Tabular figures so the width does not jitter while the number climbs. */
.fp-statband {
  width: 100%;
  text-align: center;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-top: 42px;
  padding-top: 28px;
  border-top: 1px solid rgba(255, 255, 255, .12);
}
.fp-stat .n {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(30px, 4vw, 44px);
  font-weight: 700;
  color:var(--accent-text);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.fp-stat .l {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--fg-muted);
  margin-top: 7px;
  display: block;
}

/* ------------------------------ scrolly card ------------------------------ */

.fp-lens { background: transparent; color: var(--fg); padding: 70px 0; }
.fp-card {
  border: 1px solid rgba(255, 255, 255, .14);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, .04);
  padding: 28px;
  position: relative;
  min-height: 300px;
}
/* Faces share one box and cross-fade, so the pinned card never changes height. */
.fp-face {
  opacity: 0;
  visibility: hidden;
  position: absolute;
  inset: auto 28px auto 28px;
  transition: opacity 460ms ease;
}
[data-stage="0"] .fp-face[data-face="0"],
[data-stage="1"] .fp-face[data-face="1"],
[data-stage="2"] .fp-face[data-face="2"] {
  opacity: 1;
  visibility: visible;
  position: relative;
  inset: auto;
}
.fp-card h4 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color:var(--accent-text);
  margin-bottom: 12px;
}
.fp-card p { font-size: 14.5px; line-height: 1.65; color: var(--fg-muted); }
.fp-face-list { list-style: none; padding: 0; margin: 16px 0 0; display: grid; gap: 8px; }
.fp-face-list li {
  font-size: 13.5px;
  padding-left: 18px;
  position: relative;
  color: var(--fg);
}
.fp-face-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background:var(--accent-solid);
}

/* ---------------------------- centre timeline ----------------------------
   Three columns: card | spine | card. Entries alternate sides so the section fills the
   full width rather than hugging one margin, and the travelling light runs up the middle.
   The side is set by a class from JS, not :nth-child — the education list restarts the
   sequence and CSS counting would put two entries on the same side across the two lists. */

/* THE ENTRANCE MUST NOT WIDEN THE PAGE.
   Each card starts 26px to one side and slides in. For the right-hand column that start
   position is 26px past the content edge, which the document dutifully counts as width — so
   the whole page scrolled sideways by 11px until the cards had animated, on every viewport
   narrow enough that the margin could not absorb it. An animation's off-screen start is not
   content and should not be measurable; clip is the right tool because, unlike hidden, it
   does not turn this into a scroll container and so cannot trap the sticky header. */
.fp-timeline { position: relative; padding: 8px 0; overflow-x: clip; }

/* The unlit track. The lit portion is .fp-spine, drawn over it by founder-motion.js. */
.fp-timeline::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--border);
}

.fp-item {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 64px 1fr;
  align-items: center;
  margin-bottom: 34px;
}

/* Each entry occupies one side; the opposite cell stays empty so the spine is never
   crowded and the eye can follow it straight down. */
.fp-item.is-right .fp-item-card { grid-column: 3; text-align: left; }
.fp-item.is-right .fp-item-year { grid-column: 1; text-align: right; }
.fp-item.is-left  .fp-item-card { grid-column: 1; text-align: right; }
.fp-item.is-left  .fp-item-year { grid-column: 3; text-align: left; }
.fp-node { grid-column: 2; display: flex; justify-content: center; }

.fp-item-card {
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-elevated);
  /* Entries fade in as they reach the reading line and fade back as they leave, so the
     eye is always drawn to the one the light is passing. */
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 620ms cubic-bezier(.22,.61,.36,1),
              transform 620ms cubic-bezier(.22,.61,.36,1),
              border-color 400ms ease;
}
.fp-item.is-right .fp-item-card { transform: translate(26px, 22px); }
.fp-item.is-left  .fp-item-card { transform: translate(-26px, 22px); }
.fp-item.is-lit .fp-item-card {
  opacity: 1;
  transform: none;
  border-color:var(--brand-2);
}
/* Seen but no longer at the reading line: still legible, no longer competing. */
.fp-item.is-seen .fp-item-card { opacity: .55; transform: none; }

.fp-item-card h4 { margin: 0 0 3px; font-size: 16px; }
.fp-when { font-family: var(--font-mono); font-size: 11.5px; color: var(--fg-faint); margin-bottom: 5px; }
.fp-now { font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: var(--brand-tint); color:var(--accent-text); margin-left: 5px; }
.fp-org { font-size: 14px; color:var(--accent-text); font-weight: 600; }
.fp-type { font-size: 12.5px; color: var(--fg-faint); margin-top: 2px; }
.fp-item-card p { font-size: 13.5px; color: var(--fg-muted); margin-top: 8px; line-height: 1.6; }

.fp-item-year {
  font-family: var(--font-display);
  font-size: clamp(26px, 4vw, 44px);
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--border-strong, var(--border));
  line-height: 1;
  padding: 0 22px;
  transition: color 420ms ease, opacity 420ms ease;
  opacity: .5;
}
.fp-item.is-lit .fp-item-year { color:var(--accent-text); opacity: 1; }
.fp-item.is-seen .fp-item-year { opacity: .45; }

.fp-dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--border);
  transition: background 320ms ease, border-color 320ms ease, box-shadow 320ms ease,
              transform 320ms cubic-bezier(.34,1.56,.64,1);
}
.fp-item.is-lit .fp-dot {
  background:var(--accent-solid);
  border-color:var(--brand-2);
  box-shadow: 0 0 16px 3px var(--accent-bright);
  transform: scale(1.35);
}
.fp-item.is-seen .fp-dot { background:var(--accent-solid); border-color:var(--brand-2); }

/* ------------------------------ peer review ------------------------------ */

.fp-review { background: var(--brand-tint); }
.fp-review-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.fp-journal-card {
  padding: 26px;
  border-radius: var(--r-lg);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  position: relative;
}
.fp-q {
  position: absolute;
  top: 20px;
  right: 20px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 13px;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--ok-tint, var(--brand-tint));
  color: var(--ok, var(--brand-2));
}
.fp-journal-card h3 { font-size: 18px; margin: 0 0 6px; padding-right: 54px; }
.fp-pub-by { font-size: 13px; color:var(--accent-text); font-weight: 700; margin-bottom: 10px; }
.fp-journal-card p { font-size: 14px; color: var(--fg-muted); line-height: 1.6; }

/* ----------------------------- publications ----------------------------- */

.fp-pubs { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.fp-pub {
  padding: 22px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-elevated);
  position: relative;
}
.fp-pub-n {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg-faint);
  margin-bottom: 8px;
}
.fp-pub h4 { font-size: 15.5px; line-height: 1.4; margin: 0 0 8px; }
.fp-journal { font-size: 13px; color:var(--accent-text); font-weight: 600; }
.fp-date { color: var(--fg-faint); font-weight: 400; }
.fp-pub p { font-size: 13.5px; color: var(--fg-muted); margin-top: 10px; line-height: 1.6; }

.fp-project {
  margin-top: 22px;
  padding: 26px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--accent-bright-tint);
}
.fp-project h4 { font-size: 17px; margin: 10px 0 10px; }
.fp-project p { font-size: 14px; line-height: 1.7; color: var(--fg-muted); }

/* ---------------------------- certifications ---------------------------- */

.fp-certs-band { background: var(--bg-elevated); }
.fp-certs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.fp-cert {
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
}
.fp-cert h5 { font-size: 14px; margin: 0 0 6px; line-height: 1.4; }
.fp-issuer { font-size: 12.5px; color:var(--accent-text); font-weight: 600; }
.fp-cert-meta { font-size: 11.5px; color: var(--fg-faint); margin-top: 5px; }

.fp-affil { font-size: 12.5px; color: var(--fg-faint); margin-bottom: 14px; }

/* The two flagged credentials, given room. */
.fp-certs-top { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 18px; }
.fp-cert-top {
  padding: 24px;
  border-color:var(--brand-2);
  background: var(--brand-tint);
  position: relative;
}
.fp-cert-badge {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color:var(--accent-text);
  margin-bottom: 9px;
}
.fp-cert-top h5 { font-size: 16.5px; line-height: 1.35; }
.fp-cert-top p { font-size: 13px; color: var(--fg-muted); margin: 9px 0 0; line-height: 1.6; }
.fp-cert-note { font-size: 12px; color: var(--fg-muted); margin: 7px 0 0; line-height: 1.55; }

.fp-skills { display: flex; flex-wrap: wrap; gap: 9px; }
.fp-skill {
  font-size: 13px;
  font-weight: 600;
  padding: 8px 15px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--fg-muted);
  transition: transform 180ms ease, border-color 180ms ease, color 180ms ease;
}
.fp-skill:hover {
  border-color:var(--brand-2);
  color:var(--accent-text);
  transform: translateY(-2px);
}

.fp-cta {
  text-align: center;
  padding: 52px 26px;
  border-radius: var(--r-xl);
  background: var(--brand-tint);
  border: 1px solid var(--border);
}
.fp-cta h2 { font-size: clamp(22px, 3vw, 32px); margin-bottom: 12px; }
.fp-cta p {
  color: var(--fg-muted);
  max-width: 56ch;
  margin: 0 auto 22px;
  font-size: 15px;
}

/* -------------------------------- 3D tilt --------------------------------
   Pointer-only; scrolly.js and founder.js both refuse to bind this on touch. The sheen
   follows --mx/--my, written by JS once per frame. */
html.fp-tilt-on [data-tilt] {
  transition: transform 260ms cubic-bezier(.22, .61, .36, 1), box-shadow 260ms ease;
  transform-style: preserve-3d;
}
html.fp-tilt-on [data-tilt].is-tilting {
  transition: transform 60ms linear, box-shadow 260ms ease;
  box-shadow: var(--shadow-2);
  z-index: 2;
}
html.fp-tilt-on [data-tilt]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease;
  background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%),
              rgba(255, 255, 255, .16), transparent 55%);
}
html.fp-tilt-on [data-tilt].is-tilting::after { opacity: 1; }

/* --------------------------------- mobile ---------------------------------
   Layout only. No colours here, so mobile keeps inheriting every palette fix. */
/* ===================== scroll-linked timeline spine ===================== */
/* The lit portion, drawn down the CENTRE over the track. Scaled by founder-motion.js from
   scroll position, so stopping halfway leaves it halfway. */
.fp-spine { position: absolute; left: 50%; margin-left: -1px; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(180deg, var(--brand-2), var(--accent-bright));
  transform-origin: top; transform: scaleY(0); }
/* The travelling head: a glow riding the end of the drawn line. */
.fp-spine::after { content: ""; position: absolute; left: -5px; bottom: -6px;
  width: 12px; height: 12px; border-radius: 50%; background:var(--accent-solid);
  box-shadow: 0 0 18px 5px var(--accent-bright); }

/* ===================== publications =====================
   STACKED IS THE DEFAULT. The horizontal reel is opt-in below, and only for devices with
   a real pointer.

   WHY NOT A WIDTH QUERY. Chrome's "Desktop site" toggle makes an Android phone report a
   ~1024px viewport, so every max-width query sees a laptop and the reel came back on a
   phone that had explicitly been told to stack. Width describes the window; it says
   nothing about what is holding the device. `hover: hover` and `pointer: fine` describe
   the INPUT, and a touchscreen reports coarse in desktop mode too — so this is the only
   signal that survives the toggle.

   Defaulting to stacked also means any browser that reports neither gets the readable
   layout rather than a pinned rail it cannot drive. */
.fp-reel { height: auto; position: relative; }
.fp-reel-pin { position: static; height: auto; overflow: visible; display: block; }
.fp-reel-rail { display: block; }
.fp-reel-rail .fp-pub { width: 100%; margin-bottom: 12px; border-radius: var(--r-md); }
.fp-reel-rail .fp-pub:last-child { margin-bottom: 0; }
/* Both describe an interaction that does not exist in the stacked layout. */
.fp-reel-bar, .fp-reel-hint { display: none; }

/* ---- the reel proper: pointer devices only ---- */
@media (hover: hover) and (pointer: fine) and (min-width: 901px) {
  .fp-reel { height: var(--fp-reel-h, 200vh); }
  .fp-reel-pin { position: sticky; top: 96px; height: 76vh; overflow: hidden;
    display: flex; flex-direction: column; justify-content: center; }
  .fp-reel-rail { display: flex; gap: 18px; will-change: transform; }
  .fp-reel-rail .fp-pub { flex: 0 0 340px; width: auto; margin-bottom: 0;
    border-radius: var(--r-lg); }
  .fp-reel-bar { display: block; height: 3px; background: var(--border);
    border-radius: 2px; margin-top: 20px; }
  .fp-reel-bar span { display: block; height: 100%; border-radius: 2px;
    background:var(--accent-solid); transform-origin: left; transform: scaleX(0); }
  .fp-reel-hint { display: block; font-size: 11.5px; color: var(--fg-faint);
    margin-top: 9px; text-transform: uppercase; letter-spacing: .07em; }
}

/* ===================== staggered entrance ===================== */
.fp-pubs > *, .fp-certs > *, .fp-certs-top > *, .fp-skills > * {
  opacity: 0; transform: translateY(16px);
  transition: opacity 560ms ease var(--fp-stagger, 0ms),
              transform 560ms cubic-bezier(.22,.61,.36,1) var(--fp-stagger, 0ms); }
.fp-stagger-in > * { opacity: 1; transform: none; }

.is-magnetic { transition: transform 90ms linear !important; }

/* ------------------------------- tablets -------------------------------
   Alternating survives here: a 768px tablet gives each card ~300px, which is enough to
   read. Only the year column shrinks, since it was sized for a desktop measure. */
@media (max-width: 900px) {
  .fp-item { grid-template-columns: 1fr 40px 1fr; margin-bottom: 26px; }
  .fp-item-year { font-size: clamp(20px, 4vw, 30px); padding: 0 12px; }
  .fp-item-card { padding: 14px 15px; }
  .fp-item-card h4 { font-size: 15px; }
  .fp-item.is-right .fp-item-card { transform: translate(16px, 18px); }
  .fp-item.is-left  .fp-item-card { transform: translate(-16px, 18px); }
  .fp-item.is-lit .fp-item-card, .fp-item.is-seen .fp-item-card { transform: none; }

  .fp-review-grid { grid-template-columns: 1fr; }
  .fp-certs { grid-template-columns: repeat(2, 1fr); }
  .fp-certs-top { grid-template-columns: 1fr; }
}

/* -------------------------------- phones --------------------------------
   Alternating is KEPT — he asked for the same effect everywhere — but the year moves
   inside the card and the spine narrows, so each card gets roughly 46% of the width
   instead of the ~35% it would keep with a separate year column. Below this the cards
   would be too narrow to read, which is why the year is folded in rather than the
   alternation being dropped. */
@media (max-width: 620px) {
  .fp-item { grid-template-columns: 1fr 26px 1fr; margin-bottom: 20px; }
  .fp-item-card { padding: 11px 12px; border-radius: var(--r-md); }
  .fp-item-card h4 { font-size: 13.5px; line-height: 1.35; }
  .fp-when { font-size: 10px; }
  .fp-org { font-size: 12px; }
  .fp-type { font-size: 11px; }
  .fp-item-card p { font-size: 11.5px; margin-top: 6px; }
  /* The year column would eat half the row at this width; it becomes a chip on the card
     so the alternation is preserved without starving the text. */
  .fp-item-year { display: none; }
  .fp-item-card::before {
    content: attr(data-year);
    display: block;
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
    margin-bottom: 5px;
    opacity: .55;
  }
  .fp-item.is-lit .fp-item-card::before { opacity: 1; }
  .fp-item.is-right .fp-item-card { transform: translate(9px, 14px); }
  .fp-item.is-left  .fp-item-card { transform: translate(-9px, 14px); }
  .fp-item.is-lit .fp-item-card, .fp-item.is-seen .fp-item-card { transform: none; }
  .fp-dot { width: 10px; height: 10px; }

  .fp-certs { grid-template-columns: 1fr; }
  .fp-statband { grid-template-columns: repeat(2, 1fr); gap: 22px 14px; }
  .fp-photo { width: 130px; height: 130px; }
  .fp-initials { font-size: 40px; }
  .fp-post { display: block; white-space: normal; }

  /* The reel stays pinned and scroll-driven here too — position:sticky is supported on
     iOS and Android, and touch-action:pan-y keeps vertical scrolling native, so the page
     scrolls the reel sideways exactly as it does on desktop. */
  /* Publications are stacked by default now — see the reel block above. Nothing to
     undo here. */
}
}

@media (prefers-reduced-motion: reduce) {
  .fp-pubs > *, .fp-certs > *, .fp-certs-top > *, .fp-skills > * {
    opacity: 1 !important; transform: none !important; transition: none !important; }
  .fp-spine { transform: scaleY(1) !important; }
}

/* ---------------------------------------------------------------------------
   SIGN-OFF — the signature that closes the page.

   THE IMAGE HAS A WHITE BACKGROUND, not transparency, and the ink is near-black.
   Dropped in as-is it is a white rectangle on the dark theme and invisible if the white
   is keyed out. Blend modes solve both without editing the file:

     light theme : multiply  — white multiplies away, dark ink stays dark
     dark theme  : invert then screen — invert makes the ink pale and the paper black,
                   screen then drops the black, leaving pale ink on the dark ground

   Scoped with :not([data-theme="dark"]) / [data-theme="dark"] because the boot snippet
   sets no data-theme for light and sets "dark" for both dark and neon.
   --------------------------------------------------------------------------- */
.fp-signoff{margin-top:44px;padding-top:30px;border-top:0;
  display:flex;flex-direction:column;align-items:flex-start;gap:2px}
.fp-signoff img{display:block;width:220px;height:auto;margin-bottom:6px;
  /* Revealed left to right, the way it was written. */
  clip-path:inset(0 100% 0 0);
  transition:clip-path 1150ms cubic-bezier(.16,1,.3,1) 120ms}
.fp-signoff.is-signed img{clip-path:inset(0 0 0 0)}

:root:not([data-theme="dark"]) .fp-signoff img{mix-blend-mode:multiply}
:root[data-theme="dark"] .fp-signoff img{filter:invert(1);mix-blend-mode:screen}

.fp-signoff-name{font-family:var(--font-display);font-size:19px;color:var(--fg)}
.fp-signoff-role{font-family:var(--font-mono);font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--fg-faint)}

@media (prefers-reduced-motion:reduce){
  /* No writing animation, but the signature must still be visible — a clip-path left at
     100% would hide it entirely for anyone who asked for less motion. */
  .fp-signoff img{clip-path:inset(0 0 0 0);transition:none}
}

/* ============================================================
   FOUNDER HERO — the name as the stage
   ------------------------------------------------------------
   Replaces the constellation background. That was atmosphere behind a page; this is the page.

   HOW IT WORKS. The name is set very large and sits at the back of the stacking order; the
   portrait sits in front of it, so the head occludes part of the type. Nothing is masked or
   cut out in software — the overlap IS the effect, and it costs one z-index.

   The first half of the name is drawn as an outline and the second solid. A word this size
   set entirely solid reads as shouting; entirely outlined it reads as unfinished. The split
   is what makes it sit still.

   ON THE PORTRAIT. It is layered in front of live type, so a photo with its own background
   paints a rectangle across the letters. The rule below keeps a soft edge either way, but the
   effect only fully lands with a cut-out PNG.
   ============================================================ */
.fp-stage{
  position:relative;
  /* No bottom padding: the portrait is flush with the floor of the section. Any padding here
     shows as white space under the figure and makes it look pasted on. */
  /* Tight at the top: every pixel spent above the name is a pixel the figure does not get,
     and the whole composition has to land inside one screen. */
  padding:clamp(12px,2.5vw,34px) 0 0;
  /* The portrait animates up from below its resting place; clipping vertically would cut
     the start of that move. Horizontal clipping is still wanted, for the oversized name. */
  overflow-x:hidden;
  overflow-y:visible;
}

/* The hero name only. Anton is a condensed display face, which is what lets a
   twenty-character name run edge to edge instead of being fitted down to body size — the
   same reason poster typography is condensed. Deliberately NOT --font-display (Fraunces,
   a serif, which read like a book title at this scale) and NOT --font-body. */
:root{ --font-hero:'Anton','Arial Narrow',Impact,var(--font-body); }

/* ---------- the oversized name ---------- */
.fp-stage-name{
  position:relative;
  z-index:0;
  display:block;
  text-align:center;
  /* Break out of .wrap so the name can run to the edges of the screen. */
  margin-left:calc(50% - 50vw);
  margin-right:calc(50% - 50vw);
  font-family:var(--font-hero);
  /* Anton ships one weight; the fallbacks want the bold. */
  font-weight:400;
  letter-spacing:-.005em;
  line-height:.84;
  /* Fills the width at any viewport without a media query per size. */
  /* A starting size only. The real size is measured and set by fitStageName() in
     founder.js, because "as large as fits on one line" is a fact about the rendered glyphs
     and the container, and CSS cannot ask that question. */
  font-size:clamp(30px,11vw,180px);
  text-transform:uppercase;
  white-space:nowrap;
  /* The portrait covers the lower part of the letters, the way the reference does it. Stated
     in the name's OWN em, not in px or vw: the size is set at runtime by fitStageName(), so a
     fixed pixel overlap would be most of a small name and a sliver of a large one. The block
     is 0.84em tall (the line-height), so -0.66em hides about four fifths of it — enough to
     read as one layered image, while the head is narrower than the word so the outer letters
     stay legible. */
  margin-bottom:-.66em;
  user-select:none;
}
.fp-n-outline{
  color:var(--accent-text);
  -webkit-text-stroke:0;
  /* Firefox and older Safari ignore -webkit-text-stroke; they get the solid tone instead,
     which is a different look rather than an invisible word. */
}
.fp-n-solid{ color:var(--accent-text); }

/* ---------- the three-column stage ---------- */
.fp-stage-grid{
  position:relative;
  z-index:2;
  display:grid;
  /* The middle track is a spacer: the figure is absolutely positioned over it, and this is
     what holds the two text columns out to either side of it. It has to track the figure's
     width, which grew when the empty band was trimmed off the top of the cut-out. */
  /* NOT the figure width. The figure is absolutely positioned over this track, so the track
     only has to hold the columns clear of the SILHOUETTE, and the silhouette is 45% of the
     frame at head height, widening to the full frame only across the shoulders at the very
     bottom. Matching the track to the whole box squeezed the columns to 182px and stacked
     the buttons; this leaves the box free to overlap its own transparent margins. */
  grid-template-columns:minmax(0,1fr) var(--fp-figure-w) minmax(0,1fr);
  align-items:end;
  /* No grid gap: the columns must abut the figure exactly, so the breathing room is padding
     on the columns themselves, which does not eat into the track arithmetic. */
  gap:0;
  /* THE HERO IS ONE SCREEN. This height is what the portrait is then stretched to fill, so
     there is no arithmetic that can leave a gap under the figure — the figure IS this tall.
     svh, not vh: on a phone vh counts the area behind the browser chrome, which is exactly
     the band that showed as dead space below the photo. */
  min-height:var(--fp-hero-h);
}
/* Both columns sit on the floor beside the figure, filling the band that used to be empty. */
/* THE COLUMN REACHES ACROSS THE FIGURE'S TRACK, AND THE SHAPE HOLDS IT OFF THE BODY.
   Measured before this change, every line stopped between 184px and 419px short of the
   silhouette while the buttons on the last row sat 29px off it. That spread is the whole
   problem: the column ended at the figure's BOX, and on a cut-out the box is mostly empty
   air — so no single column width can work. Narrow enough for the buttons to clear the
   shoulder is far too narrow for the lines above it, which is why the eye read a rectangle.

   So the column now spans its own track AND the figure's, and .fp-flow — a float the exact
   size of the figure, carrying the portrait as its shape-outside — is what keeps the text
   off the body. Each line wraps at the outline at its own height: the designation runs on
   past the ear, and the buttons still stop at the shoulder, without either being told to. */
.fp-stage-left{
  grid-column:1 / span 2;
  align-self:stretch;
  /* flow-root, so the float is contained by the column rather than escaping it. */
  display:flow-root;
  padding-bottom:clamp(4px,1vw,14px);
  /* The gap to the body is shape-margin on the float now, not padding here: padding would
     hold the text off the whole rectangle again, which is the thing being removed.

     THIS PADDING IS THE ONLY WAY TO ALIGN THE SHAPE. A float's shape is mapped over its
     MARGIN box, and a float's margin box always starts at the container's content top no
     matter what its own margins are — so margins cannot move it. Padding moves the content
     top, and therefore the shape. founder.js measures where the figure actually starts. */
  padding-top:var(--fp-flow-top, 0px);
}
.fp-flow{
  float:right;
  width:var(--fp-figure-w);
  /* THE COLUMN'S OWN HEIGHT, MEASURED — deliberately NOT the figure's.
     The figure is the grid plus a bleed below the floor, so a float that tall would make the
     column taller than the grid, which makes the grid taller, which makes the figure taller
     again: the layout ran away to 946px on the first attempt. Matching the column is the
     stable fixed point. It costs a few per cent of vertical squash in the shape — the bleed
     the figure has and this box does not — which works out under 10px sideways across the
     text, comfortably inside shape-margin.

     NO NEGATIVE MARGINS HERE. The shape image is mapped over the float's MARGIN box, so the
     negative margin that an earlier version used to drag the float upward was silently
     shrinking the box the outline was drawn into. The copy is pushed down instead. */
  height:var(--fp-flow-h, var(--fp-hero-h));
  /* THE BODY'S LEFT EDGE, AS A PERCENTAGE POLYGON — NOT url(the portrait).
     Pointing shape-outside at the PNG is the obvious way to do this and it does not work:
     Chrome sizes an image shape at the image's NATURAL size anchored to the top-left of the
     reference box, rather than stretching it to the float. Measured on the page, the outline
     it produced sat up to 30% of the float's width away from the real body — text ran 211px
     into the shoulder in one place and stopped 350px short in another. A polygon is resolved
     in percentages of the float, which maps exactly at every viewport; that was confirmed
     against a control polygon before this was written.

     Sixteen bands, each taking the LEFTMOST edge within the band so the outline is always the
     conservative side of the body. Regenerate with tools/shape-polygon.js if the portrait is
     ever replaced — the numbers are this asset's silhouette and nothing else's. */
  shape-outside:polygon(100% 0%, 100% 6.2%, 100% 12.5%, 39% 12.5%, 39% 18.7%,
    33.7% 18.7%, 33.7% 25%, 33.1% 25%, 33.1% 31.2%, 33.7% 31.2%, 33.7% 37.5%, 35.7% 37.5%,
    35.7% 43.7%, 34.7% 43.7%, 34.7% 50%, 35.8% 50%, 35.8% 56.2%, 38.8% 56.2%, 38.8% 62.5%,
    40.1% 62.5%, 40.1% 68.7%, 34.5% 68.7%, 34.5% 75%, 20.6% 75%, 20.6% 81.2%, 7.3% 81.2%,
    7.3% 87.5%, 4.1% 87.5%, 4.1% 93.7%, 0.8% 93.7%, 0.8% 100%, 100% 100%, 100% 0%);
  shape-margin:clamp(12px,1.6vw,26px);
  pointer-events:none;
}
/* Pushed down to sit on the hero floor. A plain block, NOT a flex or grid container: only an
   ordinary block's line boxes wrap around a float, and wrapping around the float is the whole
   point. founder.js measures --fp-push. */
.fp-stage-copy{ margin-top:var(--fp-push, 0px); }
/* .fp-links is centred for the ordinary centred hero. In the stage the column is a left
   rail, so a wrapped second button was centring itself under the first and reading as an
   indent rather than a stack. */
.fp-stage-left .fp-links{ justify-content:flex-start; }
/* INLINE-FLEX, SO THE BUTTONS OBEY THE SILHOUETTE RATHER THAN THE BOX.
   A block-level flex container establishes its own formatting context, and such a box avoids
   a float's whole RECTANGLE — the shape does not apply to it. Beside a cut-out that means the
   buttons are held off the empty air beside the shoulder as though it were solid, and once
   the frame was restored to its full width there was no longer room for both on one line.
   Inline-level puts them in a line box, and line boxes are exactly what the shape governs. */
.fp-stage-copy .fp-links{ display:inline-flex; vertical-align:top; }
.fp-stage-right{
  grid-column:3;
  display:flex;
  justify-content:flex-end;
  padding-bottom:clamp(4px,1vw,14px);
  padding-left:clamp(10px,1.6vw,24px);
}
.fp-stage-right .fp-tags{ justify-content:flex-end; }

/* The real heading stays for screen readers and search, out of sight. */
.fp-sr-name{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* ---------- the portrait ---------- */
.fp-stage-photo{
  position:absolute;
  left:50%;
  /* Both edges pinned, so the figure spans the full hero: its top reaches the name and its
     bottom IS the section floor. Anchoring only the bottom left the leftover height as the
     black band under the photo. */
  top:0;
  /* Bled past the grid floor by the exact gap to the next block, so the figure crops at the
     edge of the composition the way a poster does rather than floating above a strip of
     ground. The text columns stay on the grid floor; only the figure runs on. */
  bottom:calc(var(--fp-gap-to-next) * -1);
  transform:translateX(-50%);
  z-index:2;
  display:flex;
  align-items:flex-end;
  justify-content:center;
  pointer-events:none;           /* it sits over the columns; it must not eat their clicks */
}
/* ONE NUMBER, USED TWICE. The band of ground between the figure's feet and whatever comes
   next is the stat band's top margin — and it was the black strip under the shoulders. The
   figure is now bled downward by exactly that amount, so the two cancel and the figure runs
   to where the next block starts. Declaring it once means they cannot drift apart. */
.fp-stage{
  --fp-gap-to-next:clamp(26px,4vw,48px);
  /* THE HERO IS ONE SCREEN, and every other number here is derived from this one.
     svh, not vh: vh counts the area behind mobile browser chrome, which is the band that
     showed as dead space under the figure. */
  --fp-hero-h:clamp(360px, calc(100svh - 250px), 700px);
  /* THE CUT-OUT'S OWN RATIO — assets/founder-2026.png, 1000 x 1000. Everything the figure
     needs is derived from this one number: its rendered width, and the width of the grid
     track that holds the text columns clear of it. Written out twice they would disagree the
     first time the asset was swapped, and the columns would land on the shoulders. */
  --fp-figure-ratio:calc(1000 / 1000);
  /* The figure is as tall as the hero plus the bleed, so its width follows from that ratio.
     Deriving it rather than hand-tuning a number is what keeps the text columns exactly
     beside the figure at every viewport instead of only at the one I happened to measure —
     set by hand, the columns overlapped the shoulders by 53px everywhere else. */
  --fp-figure-w:min(calc((var(--fp-hero-h) + var(--fp-gap-to-next)) * var(--fp-figure-ratio)), 64vw, 780px);
}
/* THE FIGURE GIVES WAY BEFORE THE COLUMNS DO.
   At 64vw the figure takes so much of a narrow page that the side rails fall to 128px, which
   is where the role chips wrap four lines deep and the hero reads as broken rather than as a
   composition. 52vw with a 640px ceiling holds the rails between 185px and 233px from 901px
   upward — measured — and the chips settle to two lines at worst.

   THERE IS A STEP AT 1150px AND IT IS DELIBERATE. The figure goes 597 to 696 as this block
   releases, and the rails 233 to 184. Smoothing it out means raising the cap to about 61vw,
   which drags the rails back to 142px at 950px and reintroduces exactly the problem this
   exists to solve. Below 1150 the rails matter more than the figure's size; above it there is
   room for both. The step is only visible to someone dragging a desktop window across that
   width, which is a far smaller cost than a cramped hero on every tablet. */
@media (max-width:1149px){
  .fp-stage{
    --fp-figure-w:min(calc((var(--fp-hero-h) + var(--fp-gap-to-next)) * var(--fp-figure-ratio)), 52vw, 640px);
  }
}
.fp-stage .fp-statband{ margin-top:var(--fp-gap-to-next); }
.fp-stage .fp-photo{
  /* SIZED BY HEIGHT, NOT WIDTH. The figure is as tall as the hero, and its width follows from
     the source ratio. Sizing by width meant the height was whatever it happened to be, and
     any shortfall showed as black under the shoulders. */
  height:100%;
  width:auto;
  /* The same expression the middle track uses, so the figure and the space reserved for it
     cannot disagree. */
  max-width:var(--fp-figure-w);
  aspect-ratio:var(--fp-figure-ratio);
  border-radius:0;
  border:0;
  background:transparent;
  overflow:visible;
  /* NO DROP SHADOW. The cut-out reaches the left, right and bottom edges of its own frame,
     so a drop-shadow renders as three straight dark edges rather than following a silhouette
     — invisible on black, and the "passport photo border" seen in the light theme. */
  filter:none;
}
.fp-stage .fp-photo img{
  width:100%;height:100%;
  /* cover, with the ratio above matching the file exactly: no crop in practice, and no
     letterbox band if the asset is ever swapped for one a few pixels off. */
  object-fit:cover;object-position:top center;display:block;
}
/* No photo yet: the initials ring stands in, sized to the same slot. */
.fp-stage .fp-photo.is-fallback{
  aspect-ratio:1;width:clamp(150px,18vw,220px);border-radius:50%;
  border:3px solid var(--accent-bright);background:var(--surface-1);
  align-items:center;justify-content:center;filter:none;
}

/* ---------- responsive ---------- */
/* THE COMPOSITION IS THE POINT, SO IT STAYS DOWN TO 900px.
   It was briefly stacked below 1100px to escape a 128px side rail, and that was the wrong
   trade: the rail was starved by the FIGURE being allowed 64vw, not by the layout being
   impossible at that width. Narrowing the figure below 1150px gives the rails 200px and
   more (see --fp-figure-w in the block further down) and the hero still reads as the
   composition it was designed as, which is what a tablet and a large phone should get.
   Only genuinely narrow screens stack, where three columns really cannot work. */
@media (max-width:900px){
  .fp-stage-grid{
    grid-template-columns:1fr;
    justify-items:center;
    text-align:center;
    min-height:0;
  }
  /* Stacked, the figure returns to the flow: absolute positioning only makes sense while
     there are columns for it to stand between. */
  .fp-stage-photo{
    position:static; transform:none; justify-content:center; pointer-events:auto;
  }
  .fp-stage-left, .fp-stage-right{ grid-column:auto; }
  /* Stacked, the figure is above the text rather than beside it, so there is no body for the
     text to flow around and the float would only indent it against nothing. */
  .fp-stage-left{ align-self:auto; }
  .fp-stage-copy{ margin-top:0; }
  .fp-flow{ display:none; }
  .fp-stage-right{ justify-content:center; }
  .fp-stage-right .fp-tags{ justify-content:center; }
  .fp-stage-photo{ order:-1; }
  .fp-stage-name{ margin-bottom:-.2em; }
  /* Stacked there is no hero height to fill, so the figure is sized by width again.
     SHOWN FROM THE TOP, NOT WHOLE. Measured on the shipping cut-out, the shoulders run to
     0.8% of the left edge and 0.0% of the right — they were already cropped square in the
     photograph before it ever reached this site. Desktop never shows that: the figure is bled
     past the section floor and clipped there, so the eye never reaches the widest part.
     Stacked, the whole square is on screen and both shoulders end flush against nothing,
     which is exactly the "cut at the borders" this layout exists to avoid.
     A box four fifths as tall as it is wide, filled by cover from the top, simply stops above
     that: what shows is the head and upper chest, where the body has a quarter of the frame
     clear on each side. Nothing is squashed — only less of it is shown. */
  .fp-stage .fp-photo{
    width:clamp(200px,62vw,320px); height:auto; max-width:none;
    aspect-ratio:1.25;
  }
  /* THE ONE PLACE THE CUT-OUT CAN STILL READ AS A RECTANGLE. The source photograph ends at
     the chest, so its bottom row is a straight edge across the full width of the shirt. On
     desktop that never shows: the figure is bled past the section floor and clipped there.
     Stacked, the figure sits in the flow and the straight edge lands in open black, which is
     the giveaway this layout exists to avoid. Fading the last fifth lets the shirt fall away
     instead of stopping.

     currentColor, not a literal: a mask reads the ALPHA channel and ignores the hue entirely,
     so naming a colour here would be stating something that has no effect — and this file
     allows no hardcoded colour inside a media query. */
  .fp-stage .fp-photo img{
    /* CENTRED ON THE FACE, NOT ON THE FRAME. The head sits at 55–56% of the source's width
       across every band of the torso — the subject is standing slightly to one side of his own
       shoulders, which is what a photograph of a person normally looks like. In a wide desktop
       composition that reads as natural; in a small box on a phone it reads as a picture hung
       crooked. Shifting by the measured difference puts the face on the middle of the box.
       object-position cannot do this: the box and the image are the same width here, so there
       is no horizontal slack for it to bite on. The vacated strip is transparent and the page
       behind it is the same ground, so nothing shows. */
    transform:translateX(-4.5%);
    -webkit-mask-image:linear-gradient(to bottom, currentColor 80%, transparent 100%);
    mask-image:linear-gradient(to bottom, currentColor 80%, transparent 100%);
  }
}
@media (max-width:560px){
  /* The name still fits on one line here — fitStageName() shrinks it to whatever the screen
     allows. Only the stroke thins, because 1.5px on 40px type is heavy. */
  .fp-n-outline{ -webkit-text-stroke-width:1px; }
}
@media (prefers-reduced-motion:reduce){
  .fp-stage .fp-photo{ transition:none; }
}
