:root {
  --ink: #efeee9;
  --bg: #0d0d0c;
  --gutter: clamp(24px, 4vw, 40px);
  /* Intrinsic size of assets/portrait.webp — drives the portrait box so the
     image is never stretched, whatever the viewport aspect. */
  --portrait-w: 972;
  --portrait-h: 1080;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-inout: cubic-bezier(0.76, 0, 0.24, 1);

  /* Hero footer metrics. The rule is positioned from these rather than from a
     standalone vh value: the footer's height depends on the viewport WIDTH
     (its font-size is in vw), so any height-based guess for the rule collides
     with the text on short windows. Update --footer-lines if the number of
     lines in the taller footer column changes. */
  --footer-fs: clamp(11px, 1.15vw, 14px);
  --footer-leading: 1.65;
  --footer-lines: 3;
  --footer-pad-b: clamp(20px, 3vw, 32px);
  --rule-gap: clamp(16px, 2.4vh, 28px);
  --footer-block: calc(var(--footer-pad-b) + var(--footer-fs) * var(--footer-leading) * var(--footer-lines));
}

html, body { margin: 0; padding: 0; background: var(--bg); }
body {
  color: var(--ink);
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

:where(a, button):focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

/* ── Hero ─────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  height: 100vh;
  height: 100dvh;
  min-height: 560px;
  width: 100%;
  overflow: hidden;
  background: var(--bg);
}

.scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8,8,8,.55) 0%, rgba(8,8,8,0) 38%, rgba(8,8,8,.7) 100%);
  pointer-events: none;
}

/* Sits in front of the portrait so the footer stays legible where the
   subject reaches the bottom edge on narrow viewports. */
.base-scrim {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: clamp(120px, 22vh, 260px);
  z-index: 22;
  background: linear-gradient(180deg, rgba(13,13,12,0) 0%, rgba(13,13,12,.72) 100%);
  pointer-events: none;
}

.marquee-wrap {
  position: absolute; left: 0; right: 0; top: 14vh;
  z-index: 10;
  overflow: hidden;
  animation: jsFadeUp .9s var(--ease-out) 500ms both;
}
.marquee {
  display: flex;
  width: max-content;
  white-space: nowrap;
  font-size: min(26vh, 19vw);
  line-height: .82;
  letter-spacing: -.02em;
  animation: jsMarquee 30s linear infinite;
}
.marquee span { padding-right: 6vw; }

.portrait-layer {
  position: absolute; inset: 0;
  z-index: 20;
  display: flex; align-items: flex-end; justify-content: center;
  pointer-events: none;
}
.portrait {
  position: relative;
  /* Height leads; width follows the image's own aspect ratio. The 128vw term
     caps the derived width on narrow screens — a little wider than the
     viewport, so the shoulders bleed off the edges rather than leaving the
     portrait stranded. The 1080px cap is deliberate: portrait.webp is exactly
     972×1080, so at full size it renders 1:1 on a standard display and the 2x
     srcset variant covers retina. Keep --portrait-w/h in step with the real
     file — the box takes its shape from them, and a mismatch letterboxes the
     art and shrinks the subject. */
  height: min(88vh, 128vw, 1080px);
  height: min(88dvh, 128vw, 1080px);
  aspect-ratio: var(--portrait-w) / var(--portrait-h);
  animation: jsRiseIn 1.4s var(--ease-out) 300ms both;
}
.portrait img {
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: bottom center;
}

.rule {
  position: absolute;
  left: var(--gutter); right: var(--gutter);
  bottom: calc(var(--footer-block) + var(--rule-gap));
  z-index: 10;
  height: 2px;
  background: var(--ink);
  transform-origin: left;
  animation: jsLine 1.1s var(--ease-inout) 1200ms both;
}

/* ── Header ───────────────────────────────────────────────────────── */
/* Fixed for the whole scroll. Transparent over the hero, then .solid once
   the page moves under it — see the sentinel observer in site.js. */
header {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 30;
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: clamp(20px, 3vw, 32px) var(--gutter);
  border-bottom: 1px solid transparent;
  transition: background-color 400ms ease, border-color 400ms ease;
}
header.solid {
  background: rgba(13, 13, 12, .82);
  backdrop-filter: blur(10px);
  border-bottom-color: rgba(239, 238, 233, .12);
}
/* Current section, set by the scroll spy. */
.cluster nav a[aria-current="true"] { opacity: 1; box-shadow: 0 1px 0 var(--ink); }
.brand {
  font-size: 18px;
  letter-spacing: .06em;
  animation: jsFadeUp .9s var(--ease-out) 800ms both;
}
.cluster { display: none; align-items: flex-start; gap: clamp(48px, 7vw, 96px); }
.cluster nav { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.cluster nav a { transition: opacity 300ms; }
.cluster nav a:hover { opacity: .6; }
.year { font-size: 14px; animation: jsFadeUp .9s var(--ease-out) 900ms both; }

.d1 { animation: jsFadeUp .9s var(--ease-out) 1000ms both; }
.d2 { animation: jsFadeUp .9s var(--ease-out) 1080ms both; }
.d3 { animation: jsFadeUp .9s var(--ease-out) 1160ms both; }
.d4 { animation: jsFadeUp .9s var(--ease-out) 1150ms both; }
.d5 { animation: jsFadeUp .9s var(--ease-out) 1230ms both; }
.d6 { animation: jsFadeUp .9s var(--ease-out) 1310ms both; }

.burger {
  position: relative;
  z-index: 50;
  width: 40px; height: 40px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px;
  background: none; border: 0; padding: 0;
  cursor: pointer;
  animation: jsFadeUp .9s var(--ease-out) 900ms both;
}
.burger span {
  width: 24px; height: 2px;
  background: var(--ink);
  display: block;
  transition: transform 500ms var(--ease-inout), opacity 300ms ease;
}
body.nav-open .burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body.nav-open .burger span:nth-child(2) { opacity: 0; }
body.nav-open .burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ── Hero footer ──────────────────────────────────────────────────── */
/* Scoped to .hero: the page's own <footer class="site-foot"> is in normal
   flow and must not pick up this absolute positioning. */
.hero > footer {
  position: absolute; left: 0; right: 0; bottom: 0;
  z-index: 25;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px;
  padding: 0 var(--gutter) var(--footer-pad-b);
  font-size: var(--footer-fs);
  line-height: var(--footer-leading);
}
.f-left { animation: jsFadeUp .9s var(--ease-out) 1400ms both; }
.f-right { text-align: right; animation: jsFadeUp .9s var(--ease-out) 1550ms both; }

/* ── Mobile menu ──────────────────────────────────────────────────── */
.backdrop {
  position: fixed; inset: 0;
  z-index: 40;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 500ms ease;
}
.backdrop[hidden] { display: none; }
body.nav-open .backdrop { opacity: 1; pointer-events: auto; }

.panel {
  position: fixed; top: 0; right: 0; bottom: 0;
  z-index: 40;
  width: 80%; max-width: 384px;
  background: #141414;
  padding: 40px 32px;
  box-sizing: border-box;
  overflow: hidden;
  transform: translateX(100%);
  transition: transform 600ms var(--ease-inout);
}
body.nav-open .panel { transform: translateX(0); }

.close {
  position: absolute; right: 24px; top: 24px;
  z-index: 50;
  width: 26px; height: 26px;
  background: none; border: 0; padding: 0;
  cursor: pointer;
  opacity: 0;
  transform: rotate(90deg);
  transition: opacity 400ms ease, transform 400ms ease;
}
.close i { position: absolute; left: 2px; top: 12px; width: 22px; height: 1.5px; background: var(--ink); display: block; }
.close i:first-child { transform: rotate(45deg); }
.close i:last-child { transform: rotate(-45deg); }
body.nav-open .close { opacity: 1; transform: rotate(0); transition-delay: 300ms; }

.label {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(239,238,233,.5);
}
.panel nav.stack { display: flex; flex-direction: column; gap: 6px; margin-top: 18px; }
.panel nav.stack a { font-size: 34px; letter-spacing: -.01em; }
.panel nav.row { display: flex; flex-wrap: wrap; gap: 18px; margin-top: 14px; font-size: 14px; }
.panel .label.second { margin-top: 44px; }

.rv {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 500ms ease, transform 600ms var(--ease-out);
}
body.nav-open .rv { opacity: 1; transform: translateY(0); }
body.nav-open .rv1 { transition-delay: 250ms; }
body.nav-open .rv2 { transition-delay: 300ms; }
body.nav-open .rv3 { transition-delay: 380ms; }
body.nav-open .rv4 { transition-delay: 460ms; }
body.nav-open .rv5 { transition-delay: 500ms; }
body.nav-open .rv6 { transition-delay: 550ms; }
body.nav-open .rv7 { transition-delay: 610ms; }
body.nav-open .rv8 { transition-delay: 670ms; }

@media (min-width: 640px) {
  .cluster { display: flex; }
  .burger, .backdrop, .panel { display: none; }
}

/* On wide viewports the rule passes behind the subject and reads as one line
   interrupted by him. Below 640px he spans the full width and swallows it
   whole, so bring the rule forward instead of letting it disappear. */
@media (max-width: 639px) {
  .rule { z-index: 26; }
}

/* Short landscape viewports: pull the marquee up so it clears the head. The
   rule needs no override here — it now tracks the footer's own height. */
@media (max-height: 560px) {
  :root { --rule-gap: clamp(10px, 2vh, 16px); }
  .marquee-wrap { top: 8vh; }
}

/* ── Motion ───────────────────────────────────────────────────────── */
@keyframes jsRiseIn {
  from { opacity: 0; transform: translateY(4vh) scale(1.03); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes jsFadeUp {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes jsLine {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes jsMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-delay: 0s !important;
    transition-duration: .01ms !important;
  }
  /* Collapsing the duration of an infinite loop would spin it, not stop it. */
  .marquee { animation: none !important; }
}

/* ══ Single-page sections ═════════════════════════════════════════════
   The hero is followed by Work / About / Contact as bands in normal flow.
   They reuse the .section, .role, .ticks and .links components. */

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

.band {
  padding: clamp(64px, 12vh, 140px) 0 clamp(48px, 9vh, 110px);
  border-top: 1px solid rgba(239, 238, 233, .16);
  /* Clear the fixed header when an anchor lands here. */
  scroll-margin-top: clamp(66px, 9vh, 96px);
}
.band:first-of-type { border-top: 0; }

.band-head { padding-bottom: clamp(24px, 5vh, 56px); }
.band-head .eyebrow { margin-bottom: clamp(20px, 4vh, 40px); }
.band-head h2 {
  margin: 0;
  font-size: clamp(44px, 8.5vw, 116px);
  line-height: .9;
  letter-spacing: -.03em;
  font-weight: 400;
}
.band-head .lede {
  margin: clamp(24px, 4vh, 40px) 0 0;
  max-width: 58ch;
  font-size: clamp(16px, 1.5vw, 21px);
  line-height: 1.55;
  color: rgba(239, 238, 233, .74);
}

/* ── Site footer ──────────────────────────────────────────────────── */
.site-foot {
  border-top: 1px solid rgba(239, 238, 233, .16);
  padding: clamp(28px, 4vh, 44px) 0 clamp(36px, 5vh, 56px);
  font-size: clamp(12px, 1.1vw, 14px);
  color: rgba(239, 238, 233, .55);
}
.foot-row {
  display: flex; flex-wrap: wrap; gap: 18px 40px;
  align-items: baseline; justify-content: space-between;
}
.site-foot nav { display: flex; flex-wrap: wrap; gap: 18px; }
.site-foot a { transition: opacity 300ms; }
.site-foot a:hover { opacity: 1; color: var(--ink); }

/* ── Print letterhead ─────────────────────────────────────────────── */
/* Hidden on screen; on paper it replaces the hero, which does not print. */
.print-head { display: none; }

/* ══ Interior pages ═══════════════════════════════════════════════════
   Same palette, type and rules as the hero, laid out as a document:
   a static header, a label/content two-column grid, a closing footer. */

body.subpage { background: var(--bg); }

body.subpage header {
  position: sticky;
  top: 0;
  background: rgba(13, 13, 12, .82);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(239, 238, 233, .12);
}
body.subpage .cluster nav a[aria-current="page"] { opacity: 1; box-shadow: 0 1px 0 var(--ink); }
body.subpage .panel nav a[aria-current="page"] { opacity: .45; }

.skip-link {
  position: absolute; left: -9999px; top: 0;
  z-index: 60;
  padding: 12px 18px;
  background: var(--ink); color: var(--bg);
  font-size: 13px;
}
.skip-link:focus { left: 0; }

main { display: block; }
.wrap { max-width: 1360px; margin: 0 auto; padding: 0 var(--gutter); }

/* ── Page masthead ────────────────────────────────────────────────── */
.masthead { padding: clamp(48px, 9vh, 112px) 0 clamp(32px, 5vh, 56px); }
.masthead .eyebrow { margin-bottom: clamp(20px, 4vh, 40px); }
.masthead h1 {
  margin: 0;
  font-size: clamp(52px, 11vw, 148px);
  line-height: .88;
  letter-spacing: -.03em;
  font-weight: 400;
}
.masthead .lede {
  margin: clamp(24px, 4vh, 40px) 0 0;
  max-width: 58ch;
  font-size: clamp(16px, 1.5vw, 21px);
  line-height: 1.55;
  color: rgba(239, 238, 233, .74);
}

.eyebrow {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(239, 238, 233, .5);
}

/* ── Label / content sections ─────────────────────────────────────── */
.section {
  display: grid;
  gap: clamp(16px, 3vh, 28px);
  padding: clamp(36px, 6vh, 72px) 0;
  border-top: 1px solid rgba(239, 238, 233, .16);
}
.section > .eyebrow { padding-top: .35em; }
@media (min-width: 900px) {
  .section {
    grid-template-columns: 200px minmax(0, 1fr);
    gap: clamp(40px, 6vw, 96px);
  }
}

.prose { max-width: 68ch; font-size: clamp(15px, 1.25vw, 18px); line-height: 1.7; color: rgba(239, 238, 233, .82); }
.prose p { margin: 0 0 1.1em; }
.prose p:last-child { margin-bottom: 0; }

/* ── Lists ────────────────────────────────────────────────────────── */
.ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; max-width: 72ch; }
.ticks li {
  position: relative;
  padding-left: 22px;
  font-size: clamp(14px, 1.15vw, 17px);
  line-height: 1.6;
  color: rgba(239, 238, 233, .82);
}
.ticks li::before {
  content: "";
  position: absolute; left: 0; top: .72em;
  width: 10px; height: 1px;
  background: rgba(239, 238, 233, .45);
}
@media (min-width: 700px) {
  .ticks.two-col { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(32px, 4vw, 64px); }
}

/* ── Roles (Work) ─────────────────────────────────────────────────── */
.roles { display: grid; gap: clamp(36px, 6vh, 64px); }
.role { display: grid; gap: 14px; }
.role h3 {
  margin: 0;
  font-size: clamp(22px, 2.8vw, 36px);
  line-height: 1.1;
  letter-spacing: -.015em;
  font-weight: 400;
}
.role .meta {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px;
  font-size: clamp(13px, 1.1vw, 15px);
  color: rgba(239, 238, 233, .58);
}
.role .meta .org { color: rgba(239, 238, 233, .85); }
.role .meta .dot { color: rgba(239, 238, 233, .3); }

/* ── Entries (Education, certifications) ──────────────────────────── */
.entries { display: grid; gap: clamp(24px, 4vh, 40px); }
.entry h3 {
  margin: 0 0 6px;
  font-size: clamp(18px, 1.9vw, 24px);
  line-height: 1.25;
  letter-spacing: -.01em;
  font-weight: 400;
}
.entry .meta { font-size: clamp(13px, 1.1vw, 15px); color: rgba(239, 238, 233, .58); margin-bottom: 10px; }

/* ── Big link list (Contact) ──────────────────────────────────────── */
.links { display: grid; margin: 0; }
.links a {
  display: flex; align-items: baseline; justify-content: space-between; gap: 20px;
  padding: clamp(18px, 3vh, 30px) 0;
  border-bottom: 1px solid rgba(239, 238, 233, .16);
  transition: opacity 300ms, padding-left 400ms var(--ease-out);
}
.links a:first-child { border-top: 1px solid rgba(239, 238, 233, .16); }
.links a:hover { opacity: .62; padding-left: 10px; }
.links .name { font-size: clamp(24px, 3.4vw, 44px); letter-spacing: -.02em; }
.links .value { font-size: clamp(12px, 1.1vw, 15px); color: rgba(239, 238, 233, .55); text-align: right; }

/* ── Buttons ──────────────────────────────────────────────────────── */
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; }
.btn-row.spaced { margin-top: clamp(24px, 4vh, 40px); }
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 13px 22px;
  border: 1px solid rgba(239, 238, 233, .35);
  border-radius: 999px;
  font-size: 14px;
  /* Reset UA button chrome so <button class="btn"> matches <a class="btn">. */
  background: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
  transition: background-color 300ms, color 300ms, border-color 300ms;
}
.btn:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* ── Page footer ──────────────────────────────────────────────────── */
.page-foot {
  border-top: 1px solid rgba(239, 238, 233, .16);
  margin-top: clamp(32px, 6vh, 72px);
  padding: clamp(28px, 4vh, 44px) 0 clamp(36px, 5vh, 56px);
  display: flex; flex-wrap: wrap; gap: 20px 40px;
  align-items: baseline; justify-content: space-between;
  font-size: clamp(12px, 1.1vw, 14px);
  color: rgba(239, 238, 233, .55);
}
.page-foot nav { display: flex; flex-wrap: wrap; gap: 18px; }
.page-foot a { transition: opacity 300ms; }
.page-foot a:hover { opacity: .6; }

/* Interior pages scroll, so the fixed-position mobile menu needs to sit
   above the sticky header rather than the hero's absolute one. */
body.subpage .panel, body.subpage .backdrop { z-index: 45; }

/* The hero's staggered entrance suits a landing page; on interior pages it
   just delays the chrome on every navigation. */
body.subpage :is(.brand, .year, .burger, .d1, .d2, .d3, .d4, .d5, .d6) { animation: none; }

/* ── Compact CV card (read-cv.html) ───────────────────────────────────
   A summary artifact, not a second copy of Work and About: titles and
   dates only, with pointers to the pages that hold the detail. */
.cv-contact {
  margin: clamp(18px, 3vh, 28px) 0 0;
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  font-size: clamp(13px, 1.15vw, 15px);
  color: rgba(239, 238, 233, .62);
}
.cv-contact a { box-shadow: 0 1px 0 rgba(239, 238, 233, .28); transition: box-shadow 300ms, color 300ms; }
.cv-contact a:hover { color: var(--ink); box-shadow: 0 1px 0 var(--ink); }

/* ── Print ────────────────────────────────────────────────────────────
   The page doubles as the CV. On paper the hero, navigation and contact
   band drop away; the .print-head letterhead takes their place and the
   Work and About bands become the document body. */
@media print {
  @page { margin: 14mm 13mm; }
  html, body { background: #fff !important; color: #111 !important; }

  header, .hero, .backdrop, .panel, .skip-link, .btn-row,
  #contact, .site-foot nav { display: none !important; }

  .print-head { display: block; margin: 0 0 14pt; }
  .print-head h1 { margin: 0; font-size: 24pt; line-height: 1.05; font-weight: 400; letter-spacing: -.01em; }
  .print-role { margin: 5pt 0 8pt; font-size: 11pt; color: #333 !important; }
  .cv-contact { gap: 4pt 14pt; font-size: 9.5pt; color: #222 !important; }
  .cv-contact a { box-shadow: none; }

  .wrap { max-width: none; padding: 0; }
  .band { padding: 0; border-top: 0; }
  .band-head { padding: 0 0 4pt; }
  .band-head h2, .band-head .lede, .eyebrow .num { display: none !important; }
  .band-head .eyebrow {
    font-size: 11.5pt; letter-spacing: .1em; color: #111 !important;
    border-bottom: 1px solid #bbb; padding: 10pt 0 4pt;
  }

  .section {
    grid-template-columns: 96pt minmax(0, 1fr);
    gap: 14pt;
    border-top: 1px solid #e0e0e0;
    padding: 9pt 0;
    break-inside: avoid;
  }
  /* The band head draws its own underline; suppress the adjacent section's. */
  .band-head + .section { border-top: 0; padding-top: 2pt; }
  .section > .eyebrow { color: #666 !important; font-size: 7.5pt; padding-top: .3em; }
  .roles, .entries { gap: 10pt; }
  .role, .entry { break-inside: avoid; }
  .role h3, .entry h3 { font-size: 11pt; }
  .prose, .ticks li, .role .meta, .entry .meta { color: #222 !important; font-size: 9.5pt; }
  /* These carry pale on-screen colours that vanish on paper. */
  .role .meta .org { color: #111 !important; }
  .role .meta .dot { color: #999 !important; }
  .ticks li::before { background: #999; }

  .site-foot { border-top: 1px solid #ccc; color: #666 !important; padding: 8pt 0 0; font-size: 8.5pt; }
}
