/* ==========================================================================
   Xinhui Ye — personal site
   Design system implemented from design.md (editorial color-blocking,
   grotesque + serif pairing, full-pill controls, flat / no-shadow).
   ========================================================================== */

/* ---- Design tokens --------------------------------------------------- */
:root {
  /* Colors */
  --surface: #fffef9;
  --surface-container-lowest: #ffffff;
  --surface-container: #ffefd6;          /* peach */
  --surface-container-high: #f2f1ed;      /* warm grey (media / dividers) */
  --background: #fffef9;
  --on-background: #2e2e38;
  --on-surface: #2e2e38;
  --on-surface-body: #45454d;
  --on-surface-variant: #76767a;
  --outline: #76767a;
  --outline-variant: #f2f1ed;
  --inverse-surface: #333462;             /* deep indigo */
  --inverse-on-surface: #ffefd6;          /* cream-on-indigo */
  --primary: #333462;
  --on-primary: #ffefd6;
  --primary-container: #3e3f7f;           /* lighter indigo, for use on indigo */
  --on-primary-container: #ffefd6;
  --secondary: #ff8a65;                   /* brand coral-orange — name / signal, used sparingly. Single source of truth: change here only. */
  --tertiary: #9ee2d3;                    /* mint — one pop per screen (menu pill) */
  --on-tertiary: #333462;
  --input-surface: #e2e2e2;

  /* Type families. Single grotesque typeface (Archivo) is used across the
     whole site by design — body, headings, quotes, and labels all share it
     for one consistent voice. --font-serif is kept as an alias so the many
     reading/quote rules that reference it resolve to the same family; vary
     weight/size for hierarchy instead of switching faces. */
  --font-grotesque: "Archivo", "Neue Haas Grotesk", Arial, sans-serif;
  --font-serif: "Archivo", "Neue Haas Grotesk", Arial, sans-serif;

  /* Radii */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;
  --radius-full: 9999px;

  /* Spacing (8px base) */
  --space-xs: 8px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 40px;
  --space-xl: 64px;
  --space-2xl: 96px;

  /* Layout */
  --page-max: 1280px;
  --page-pad: clamp(16px, 3vw, 40px);
}

/* ---- Reset ----------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--background);
  color: var(--on-surface-body);
  font-family: var(--font-serif);
  font-size: 17px;
  line-height: 28px;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; }
/* Deter casual image/video saving (drag-to-desktop, long-press menu) */
img, video {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
}
/* Watermark is now baked into the image files themselves (see
   tools/watermark.py) so it survives downloads and screenshots — no CSS
   overlay needed. */

a { color: inherit; }

/* ---- Typography ------------------------------------------------------ */
.display-xl,
.display-lg,
.headline-lg {
  font-family: var(--font-grotesque);
  color: var(--on-surface);
  margin: 0;
  letter-spacing: -0.02em;
}

.display-xl {
  font-weight: 800;
  font-size: clamp(56px, 9vw, 96px);
  line-height: 1.05;
}

.display-lg {
  font-weight: 800;
  font-size: clamp(34px, 5.5vw, 56px);
  line-height: 1.07;
}

.headline-lg {
  font-weight: 700;
  font-size: clamp(26px, 3.5vw, 40px);
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.title-serif {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 22px;
  line-height: 30px;
  color: var(--on-surface);
  margin: 0;
}

.quote-lg {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 30px;
  line-height: 40px;
  color: var(--on-surface);
}

/* Small uppercase grotesque eyebrow / section label */
.label-caps {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface);
}
/* Larger, navy section eyebrows on the About page */
.section-eyebrow {
  font-size: 20px;
  line-height: 26px;
  color: var(--inverse-surface);   /* deep navy indigo */
}

.body-lg { font-size: 17px; line-height: 28px; }
.body-md { font-size: 16px; line-height: 26px; }

.meta {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
  text-transform: uppercase;
}

/* Coral wordmark — reserved brand signal */
.wordmark {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--secondary);
}

/* ---- Layout helpers -------------------------------------------------- */
.band {                       /* full-bleed color band */
  width: 100%;
  padding: var(--space-2xl) 0;
}
.band--cream   { background: var(--surface); }
.band--white   { background: var(--surface-container-lowest); }
.band--peach   { background: var(--surface-container); }
.band--indigo  { background: var(--inverse-surface); color: var(--inverse-on-surface); }

.container {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

/* Two-part rhythm: eyebrow label in the far-left column, content to its right */
.section-split {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--space-lg);
  align-items: start;
}
@media (max-width: 760px) {
  .section-split { grid-template-columns: 1fr; gap: var(--space-md); }
}

/* Labelled accordion sections stack: header on top, thin navy rule beneath it,
   then the list full-width below. */
.section-split:has(.accordion) {
  display: block;
}
.section-split:has(.accordion) .section-eyebrow {
  display: block;
  padding-bottom: var(--space-sm);
  margin-bottom: var(--space-md);
  border-bottom: 2px solid var(--primary);   /* navy line beneath the header */
}

.stack-sm > * + * { margin-top: var(--space-sm); }
.stack-md > * + * { margin-top: var(--space-md); }

/* ---- Pills / buttons ------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 28px;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  font-family: var(--font-grotesque);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  background: none;
  transition: opacity .15s ease, background-color .15s ease, color .15s ease;
}
.btn:hover { opacity: .88; }

.btn--primary { background: var(--primary); color: var(--on-primary); }
.btn--primary-container { background: var(--primary-container); color: var(--on-primary-container); }
.btn--outline { background: transparent; color: var(--on-surface); border-color: var(--outline); padding: 0 24px; }
.btn--outline-on-dark { background: transparent; color: var(--inverse-on-surface); border-color: var(--inverse-on-surface); padding: 0 24px; }

/* small "READ" / "READ MORE" pills used on cards */
.btn--sm { height: 36px; padding: 0 20px; font-size: 11px; }

/* ---- Navigation ------------------------------------------------------ */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: 20px var(--page-pad);
  display: flex;
  justify-content: center;
  pointer-events: none;                 /* let band behind catch nothing extra */
}

/* Name at the top-left, level with the centered bar but outside it.
   Coral brand color; fades away as soon as the page scrolls. */
.site-wordmark {
  position: absolute;
  left: var(--page-pad);
  top: 50%;
  transform: translateY(-50%);
  pointer-events: auto;
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: clamp(15px, 1.4vw, 18px);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--secondary);
  white-space: nowrap;
  transition: opacity .25s ease;
}
.site-nav.is-scrolled .site-wordmark {
  opacity: 0;
  pointer-events: none;
}
.nav-pill {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: clamp(24px, 4vw, 56px);
  background: var(--background);
  border-radius: var(--radius-full);
  padding: 16px clamp(28px, 4vw, 48px);
  box-shadow: none;
  transition: box-shadow .25s ease;
}
/* Flat while pinned at the top; lift with a shadow once the page scrolls. */
.site-nav.is-scrolled .nav-pill {
  box-shadow: 0 18px 40px -12px rgba(46, 46, 56, 0.22);
}

.nav-links { display: flex; align-items: center; gap: clamp(28px, 4vw, 56px); }
.nav-links a {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--font-grotesque);
  font-weight: 500;
  font-size: clamp(15px, 1.4vw, 18px);
  text-decoration: none;
  color: var(--on-surface);
  white-space: nowrap;
}
/* Reserve each link's bold width up front so the bar never reflows on hover.
   The ghost copy (data-text, set in script.js) is laid out but invisible. */
.nav-links a::after {
  content: attr(data-text);
  height: 0;
  visibility: hidden;
  overflow: hidden;
  font-weight: 800;
  pointer-events: none;
}
/* Page you're on: bold black. */
.nav-links a[aria-current="page"] {
  font-weight: 800;
  color: var(--on-surface);
}
/* Link you're hovering: mint accent (matches the back-to-top hover). */
.nav-links a:hover {
  font-weight: 800;
  color: var(--tertiary);
}

/* Mint MENU pill (mobile) — the single mint accent, design.md */
.menu-toggle {
  display: none;
  pointer-events: auto;
  height: 40px;
  padding: 0 20px;
  border: none;
  border-radius: var(--radius-full);
  background: var(--tertiary);
  color: var(--on-tertiary);
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

/* Full-screen indigo overlay menu (mobile) */
.nav-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--inverse-surface);
  color: var(--inverse-on-surface);
  display: none;
  flex-direction: column;
  justify-content: center;
  padding: var(--page-pad);
}
.nav-overlay.is-open { display: flex; }
.nav-overlay a {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: clamp(34px, 9vw, 44px);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-decoration: none;
  color: var(--inverse-on-surface);
}
.nav-overlay .menu-close {
  align-self: flex-end;
  margin-bottom: var(--space-xl);
}

@media (max-width: 720px) {
  .nav-pill .nav-links { display: none; }
  .menu-toggle { display: inline-flex; align-items: center; }
  .nav-pill { gap: 16px; }
}

/* ---- Hero ------------------------------------------------------------ */
.hero { padding-top: var(--space-lg); }
.hero .wordmark { display: block; margin-bottom: var(--space-sm); }

/* Hero theme: navy title on cream (default). The About hero flips to a full
   navy color-block with a cream title via .hero--navy. */
.hero .display-xl { color: var(--inverse-surface); }
.hero--navy { background: var(--inverse-surface); color: var(--inverse-on-surface); }
.hero--navy .display-xl { color: var(--inverse-on-surface); }

.hero-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  align-items: start;
  margin-top: var(--space-lg);
}
@media (max-width: 860px) {
  .hero-split { grid-template-columns: 1fr; }
}

/* Two-column hero: title + intro on the left, portrait rising to title level. */
.hero-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xl);
  align-items: start;
}
.hero-layout .stack-md { margin-top: var(--space-xl); }
.hero-layout .body-lg { margin-top: 0; }
/* Hero intro runs a touch larger so the text column finishes near the
   bottom of the portrait. */
.hero-layout .stack-md .body-lg { font-size: 18px; line-height: 29px; }
.hero-layout .stack-md .body-md { font-size: 17px; line-height: 28px; }
@media (max-width: 860px) {
  .hero-layout { grid-template-columns: 1fr; }
  .hero-portrait { order: -1; max-width: 360px; }
}

/* ---- Media (sharp-cornered, flat) ------------------------------------ */
.media,
.media-placeholder {
  background: var(--surface-container-high);
  border-radius: 0;             /* sharp corners — design.md */
  width: 100%;
}
.media-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--on-surface-variant);
  font-family: var(--font-grotesque);
  font-size: 14px;
  padding: var(--space-md);
  min-height: 200px;
}
.media img { border-radius: 0; }

.caption {
  margin-top: var(--space-xs);
  color: var(--on-surface-variant);
  font-size: 14px;
  line-height: 20px;
}

/* ---- RECENT --------------------------------------------------------- */
.recent-item + .recent-item { margin-top: var(--space-sm); }
.recent-item p { margin: 0 0 var(--space-sm); color: var(--on-surface-body); }

/* ---- Highlights carousel (About) ------------------------------------- */
/* Allow the carousel to shrink inside a grid/flex column so overflow-x
   scrolling engages instead of stretching the page. */
[data-carousel] { min-width: 0; }
.h-carousel {
  display: flex;
  gap: var(--space-sm);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-sm);
  scrollbar-width: none;
}
.h-carousel::-webkit-scrollbar { display: none; }
.h-card {
  /* Exactly two cards per view: each is half the track minus half the gap. */
  flex: 0 0 calc((100% - var(--space-sm)) / 2);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
/* Big cover on top, title/meta/Read stacked beneath. */
.h-card .media,
.h-card .media img { width: 100%; display: block; }
.h-card .media img { aspect-ratio: 4 / 3; object-fit: cover; }
.h-card .media-placeholder { min-height: 200px; aspect-ratio: 4 / 3; }
.h-card .title-serif { font-family: var(--font-grotesque); font-weight: 700; }

.carousel-controls { display: flex; gap: var(--space-xs); margin-top: var(--space-md); }
.round-btn {
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  border: 1px solid var(--outline);
  background: transparent;
  color: var(--on-surface);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 18px;
}
.round-btn:hover { background: var(--surface-container-high); }

/* dot indicators */
.dots { display: flex; gap: 6px; justify-content: center; margin-top: var(--space-sm); }
.dots span { width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--outline-variant); }
.dots span.is-active { background: var(--on-surface-variant); }

/* ---- Accordion (Education / Work) ------------------------------------ */
.accordion { border-top: none; }
.accordion-row + .accordion-row { border-top: 1px solid rgba(46, 46, 56, 0.15); }   /* faint grey line between entries */
.accordion-head {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-serif);
  font-size: 17px;
  color: var(--on-surface);
}
/* Circular toggle icon (à la Jess Hill "reporting archive") */
.accordion-head .sign {
  flex: none;
  position: relative;
  display: inline-block;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  background: var(--primary);
  transition: background 0.2s ease;
}
.accordion-head:hover .sign { background: var(--primary-container); }
/* Draw +/- as two centered bars so they are always optically centred,
   independent of font glyph metrics. */
.accordion-head .sign::before,
.accordion-head .sign::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--on-primary);
  border-radius: 1px;
}
.accordion-head .sign::before { width: 14px; height: 2px; transform: translate(-50%, -50%); }  /* horizontal bar */
.accordion-head .sign::after  { width: 2px; height: 14px; transform: translate(-50%, -50%); }  /* vertical bar (plus) */
.accordion-row.is-open .sign::after { display: none; }   /* open → minus */
.accordion-body {
  display: none;
  padding: 0 0 var(--space-md) var(--space-lg);
  color: var(--on-surface-body);
}
.accordion-row.is-open .accordion-body { display: block; }
.accordion-body p + p { margin-top: var(--space-sm); }
.accordion-tags {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 15px;
  line-height: 24px;
  font-weight: 500;
  color: var(--primary);
}

/* ---- Contact form (underline inputs — design.md) --------------------- */
.contact-heading { text-align: center; margin-bottom: var(--space-xl); }
.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xl);
  align-items: stretch;
}
.contact-grid--solo { grid-template-columns: minmax(0, 560px); justify-content: center; }

/* Left note column: image flexes to fill the leftover height so the
   column bottom aligns with the form on the right. */
.contact-note {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.contact-note__label { margin-top: 0; }
.contact-note__text { margin: 0 0 var(--space-xl); }
/* Left column: bare photo (no frame/background). It fills the row height set
   by the right column and keeps its own aspect ratio, so it shows larger and
   its top/bottom line up with the note + form on the right. */
.contact-photo {
  display: flex;
  align-items: stretch;
  justify-content: flex-start;
}
.contact-photo img {
  height: 100%;
  width: auto;
  max-width: 100%;
  display: block;
}

@media (max-width: 860px) {
  .contact-grid { grid-template-columns: 1fr; gap: var(--space-lg); align-items: start; }
  .contact-photo img { height: auto; width: auto; max-width: 100%; }
}

.form-group + .form-group { margin-top: var(--space-md); }
.form-label {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 14px;
  color: var(--on-surface);
  display: block;
  margin-bottom: var(--space-sm);
}
.field-row { display: flex; gap: var(--space-md); }
.field-row > * { flex: 1; }

.field {
  width: 100%;
  height: 44px;
  padding: 8px 0;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--outline);
  font-family: var(--font-serif);
  font-size: 16px;
  color: var(--on-surface);
}
textarea.field { height: auto; min-height: 140px; resize: vertical; padding-top: 8px; }
.field::placeholder { color: var(--on-surface-variant); }
.field:focus { outline: none; border-bottom-color: var(--primary); border-bottom-width: 2px; }

.form-actions { margin-top: var(--space-lg); display: flex; justify-content: center; }
.contact-success {
  border: 1px solid var(--outline-variant);
  padding: var(--space-lg);
  text-align: center;
}
.contact-success .title-serif { margin: 0 0 var(--space-sm); color: var(--inverse-surface); }
.contact-success .body-lg { margin: 0; }

/* ---- Footer ---------------------------------------------------------- */
.site-footer { background: var(--inverse-surface); color: var(--inverse-on-surface); padding: var(--space-xl) 0; text-align: center; }
.back-to-top {
  display: inline-flex;
  flex-direction: row-reverse;     /* label, then the chevron in its own dot */
  align-items: center;
  gap: 12px;
  background: var(--primary-container);
  color: var(--inverse-on-surface);
  border: none;
  border-radius: var(--radius-full);   /* full pill */
  padding: 14px 16px 14px 28px;
  font-family: var(--font-grotesque);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .2s ease, color .2s ease;
}
/* The chevron sits in a small round dot, like the reference button. */
.back-to-top::before {
  content: "\2191";                 /* up arrow */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-full);
  background: var(--surface-container-high);
  color: var(--primary);
  font-size: 16px;
  line-height: 1;
  transition: background-color .2s ease, color .2s ease;
}
/* The markup keeps a raw chevron glyph before the label; the ::before dot
   replaces it, so hide the inline glyph node. */
.back-to-top {
  font-size: 0;                     /* collapse the stray chevron text node */
}
.back-to-top span {
  font-size: 12px;                  /* restore the label */
}
/* Touch / hover: flip to the mint accent used across the nav. */
.back-to-top:hover,
.back-to-top:focus-visible {
  background: var(--tertiary);
  color: var(--on-tertiary);
  outline: none;
}
.back-to-top:hover::before,
.back-to-top:focus-visible::before {
  background: var(--surface);
  color: var(--on-tertiary);
}
.footer-social {
  display: flex;
  gap: var(--space-lg);
  justify-content: center;
  margin: var(--space-lg) 0 var(--space-sm);
  flex-wrap: wrap;
}
.footer-social a { color: var(--inverse-on-surface); text-decoration: underline; font-size: 15px; }
.footer-copy { font-size: 13px; color: var(--inverse-on-surface); opacity: .85; }
.footer-copy strong { font-family: var(--font-grotesque); }

/* ---- Work page: role cards ------------------------------------------- */
.role-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg) var(--space-lg);
}
@media (max-width: 760px) { .role-grid { grid-template-columns: 1fr; } }
.role-card { display: flex; flex-direction: column; gap: var(--space-sm); }
.role-card .media-placeholder {
  aspect-ratio: 16 / 11;
  align-items: flex-start;
  justify-content: flex-start;
  padding: var(--space-md);
}
.role-card .headline-lg { font-size: clamp(20px, 2.2vw, 26px); }

/* ---- Work page: certificates ----------------------------------------- */
.cert-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-md);
}
@media (max-width: 900px) { .cert-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .cert-grid { grid-template-columns: 1fr; } }
.cert-hint-note {
  font-size: 14px;
  color: var(--on-surface-variant);
  margin: calc(-1 * var(--space-md)) 0 var(--space-lg);
}
.cert-card .media-placeholder { aspect-ratio: 4 / 3; min-height: 120px; }

/* ---- Certificate flip cards ----------------------------------------- */
.cert-flip {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  position: relative;
  aspect-ratio: 1 / 1.414;          /* A4 portrait — fits the TU/e certs */
  background: transparent;
  cursor: pointer;
  perspective: 1400px;
}
.cert-flip--static { cursor: default; }
.cert-flip__inner {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transition: transform 0.7s cubic-bezier(0.4, 0.1, 0.2, 1);
}
.cert-flip.is-flipped .cert-flip__inner { transform: rotateY(180deg); }
.cert-flip__face {
  position: absolute;
  inset: 0;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  overflow: hidden;
  border: 1px solid var(--outline-variant);
  background: var(--surface-container-lowest);
}
.cert-flip__face img { width: 100%; height: 100%; object-fit: contain; display: block; }
.cert-flip__back { transform: rotateY(180deg); }
.cert-flip__hint {
  position: absolute;
  right: 8px;
  bottom: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  font-family: var(--font-grotesque);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--on-surface);
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--outline-variant);
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.cert-flip:hover .cert-flip__hint,
.cert-flip:focus-visible .cert-flip__hint { opacity: 1; }
.cert-flip.is-flipped .cert-flip__hint { opacity: 0; }
.cert-flip:focus-visible { outline: 2px solid var(--on-surface); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .cert-flip__inner { transition: none; }
}

.cert-card .cert-name { font-family: var(--font-grotesque); font-weight: 700; font-size: 14px; color: var(--on-surface); margin: var(--space-xs) 0 2px; }
.cert-card .cert-desc { font-size: 14px; color: var(--on-surface-body); }
.cert-skills {
  grid-column: span 2;
  border: 1px solid var(--outline);
  padding: var(--space-md);
  color: var(--on-surface-body);
}

/* ---- Craft page: masonry --------------------------------------------- */
.masonry { columns: 3; column-gap: var(--space-md); }
@media (max-width: 900px) { .masonry { columns: 2; } }
@media (max-width: 560px) { .masonry { columns: 1; } }
.masonry .media-placeholder {
  break-inside: avoid;
  margin-bottom: var(--space-md);
  min-height: 0;
}

/* ---- Detail page (single highlight / work) --------------------------- */
.detail-blocks { display: flex; flex-direction: column; gap: var(--space-lg); }
.detail-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  align-items: start;
}
/* Variant with a wider media column and equal-height columns (INTERACT page).
   Kept in the stylesheet — not as an inline style — so the mobile rule below
   can still collapse it to a single column. */
.detail-split--media-lg { grid-template-columns: 1fr 1.35fr; align-items: stretch; }
@media (max-width: 760px) {
  .detail-split,
  .detail-split--media-lg { grid-template-columns: 1fr; }
}
/* Detail-page images always show in full, never cropped */
.detail-blocks .media { background: transparent; }
.detail-blocks .media img {
  width: 100%;
  height: auto !important;
  object-fit: contain !important;
  display: block;
}
.detail-text {
  border: 1px solid var(--outline-variant);
  padding: var(--space-md);
  color: var(--on-surface-body);
}
.detail-full .media-placeholder { aspect-ratio: 21 / 9; }
.detail-split .media-placeholder { min-height: 240px; }

/* five co-exploration pattern charts */
.pattern-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-sm); }
@media (max-width: 760px) { .pattern-grid { grid-template-columns: repeat(2, 1fr); } }

/* Five co-exploration pattern blocks. Built on .photo-block but calmer:
   text-forward columns (image in the narrower track), a smaller rounded image
   that is capped so it never towers, a wider gap, and roomier reading copy. */
.pattern-block {
  grid-template-columns: 1fr 1fr;
  gap: clamp(36px, 5vw, 80px);
  align-items: center;
}
.pattern-block .media { display: flex; justify-content: center; }
.pattern-block .media img {
  max-width: 340px;
  width: 100%;
}
/* Square corners: override .photo-block/.defense-playful .media img rounding
   (equal specificity but declared later) so the heatmap frame isn't clipped. */
.pattern-block.photo-block .media img { border-radius: 0; }
.pattern-block .block-desc { font-size: 17px; line-height: 28px; max-width: 500px; }
@media (max-width: 760px) {
  .pattern-block,
  .pattern-block.photo-block--reverse { grid-template-columns: 1fr; }
  .pattern-block .media img { max-width: 100%; }
}

/* Closing pull-quote — big navy quote mark leading an italic serif line */
.pull-quote {
  margin: 32px 0;
  text-align: left;
}
.pull-quote__mark {
  display: block;
  width: clamp(48px, 5vw, 72px);
  height: auto;
  fill: var(--primary);
  transform: rotate(180deg);
  margin-bottom: var(--space-xs);
}
.pull-quote blockquote {
  margin: 0;
  max-width: none;
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(24px, 2.6vw, 34px);
  line-height: 1.4;
  color: var(--primary);
}

/* Full-width standalone figure (e.g. thriving vs struggling chart) */
.detail-figure {
  margin: var(--space-xl) 0 var(--space-md);
}
.detail-figure img {
  width: 100%;
  display: block;
  border-radius: var(--radius-lg);
}

/* DTC guideline table (INTERACT detail) — the design knowledge laid out
   across the three collaborative spaces. Scrolls horizontally on narrow
   viewports so the page body never does. */
.dtc-scroll { overflow-x: auto; }
.dtc-table {
  width: 100%;
  min-width: 320px;
  border-collapse: collapse;
  font-family: var(--font-grotesque);
  font-size: 11.5px;
  line-height: 1.2;
  color: var(--primary);
}
.dtc-table thead th {
  text-align: left;
  font-weight: 700;
  padding: 0 10px 5px 0;
  border-bottom: 2px solid var(--primary);
  white-space: nowrap;
}
.dtc-table td {
  text-align: left;
  vertical-align: top;
  padding: 2.5px 10px 2.5px 0;
  border-bottom: 1px solid rgba(20, 24, 54, 0.10);
}
.dtc-table tr.dtc-sec > td { border-top: 2px solid rgba(20, 24, 54, 0.28); }
.dtc-table .dtc-space { font-weight: 700; white-space: nowrap; }
.dtc-table .dtc-theme { font-weight: 600; white-space: nowrap; }

/* Inline links inside detail copy (e.g. publication titles) */
.detail-text a:not(.btn) {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.detail-text a:not(.btn):hover { text-decoration: none; }

/* Editor / press quote on detail pages */
.detail-quote {
  border-left: 3px solid var(--primary);
  padding: var(--space-xs) 0 var(--space-xs) var(--space-md);
  margin: var(--space-sm) 0 0;
  color: var(--on-surface-body);
}
.detail-quote p {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-serif);
  font-style: italic;
  line-height: 1.5;
}
.detail-quote cite {
  display: block;
  font-style: normal;
  font-family: var(--font-grotesque);
  font-size: 14px;
  color: var(--on-surface-variant);
}

/* ---- Testimonial / quote carousel (detail pages) -------------------- */
.testimonials .label-caps { display: block; margin-bottom: var(--space-lg); }
.t-carousel {
  display: flex;
  gap: var(--space-xl);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-sm);
  scrollbar-width: none;
}
.t-carousel::-webkit-scrollbar { display: none; }
.testimonial {
  flex: 0 0 clamp(280px, 44%, 560px);
  scroll-snap-align: start;
  min-width: 0;
  margin: 0;
  border-left: 6px solid var(--surface-container);
  padding: var(--space-xs) 0 var(--space-xs) var(--space-lg);
}
.testimonial blockquote {
  margin: 0;
  font-family: var(--font-serif);
  font-weight: 400;
  font-style: italic;
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.5;
  color: var(--on-surface);
}
.testimonial blockquote p { margin: 0 0 var(--space-sm); }
.testimonial blockquote p:last-child { margin-bottom: 0; }
.testimonial blockquote a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.t-cite { display: block; margin-top: var(--space-lg); font-style: normal; }
.t-name,
.t-org {
  display: block;
  font-family: var(--font-grotesque);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.t-name { font-weight: 700; color: var(--on-surface); }
.t-org { margin-top: 4px; font-weight: 500; color: var(--on-surface-variant); }
a.t-org { text-decoration: underline; text-underline-offset: 2px; }
a.t-org:hover { color: var(--on-surface); }
@media (max-width: 760px) {
  .testimonial { flex-basis: 86%; padding-left: var(--space-md); }
}

/* ---- Defense Day — playful editorial layout -------------------------- */
/* Warm cream backdrop, big black headings beside rounded photos that
   alternate sides — the marketing-page rhythm from the reference. */
/* Moderate air between top-level blocks on this page */
.defense-playful .detail-blocks { gap: clamp(40px, 5vw, 72px); }

/* Reading copy sits straight on the page — no boxed border here, navy ink */
.defense-playful .detail-text { border: none; padding: 0; color: var(--primary); }
.defense-playful .detail-text p { color: var(--primary); }
/* Section header sitting above navy prose reads navy too, not near-black */
.defense-playful .detail-text .title-serif { color: var(--primary); }

/* Soft-rounded corners on every photo */
.defense-playful .media img { border-radius: var(--radius-lg); }

/* Coloured feature band: the framework → party photos grouped on warm
   cream, sitting tighter together than the rest of the page. */
.photo-feature {
  background: #f3efe3;
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4vw, 56px);
  display: flex;
  flex-direction: column;
  gap: clamp(24px, 3vw, 40px);
}
/* Photos sitting inside the cream band get soft-rounded corners so they read
   as one calm group (pattern heatmaps opt back out via their own rule). */
.photo-feature .media img { border-radius: var(--radius-lg); }
/* Match the "Research Methods and Analysis" block: navy title, grey body —
   overriding the .defense-playful rule that otherwise paints detail-text body
   copy navy. */
.photo-feature--navy-titles .detail-text .title-serif { color: var(--primary); }
.photo-feature--navy-titles .detail-text p { color: var(--on-surface-body); }

/* Intro copy inside the band reads as body grey, matching the pattern
   captions beside it rather than the navy detail-text elsewhere on the page. */
.photo-feature .detail-text p { color: var(--on-surface-body); }

/* ---- Paper pile (INTERACT: two papers stacked as pickable cards) -----
   Two cream cards piled on top of each other. One is open and fully
   readable; the other collapses to a rotated tab peeking behind it. Hover
   the peeking tab to lift it, click to bring that paper forward. The pile
   behaviour is added by JS (.is-piled); with no JS both cards render as
   plain, fully-readable stacked blocks, and on mobile it flattens back to
   the same simple stack. */
.paper-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  position: relative;
}
.paper-card {
  background: #f3efe3;
  border-radius: var(--radius-lg);
  box-shadow: 0 10px 30px rgba(46, 46, 56, 0.10);
  scroll-margin-top: 96px;
  transition: transform .4s cubic-bezier(.2, .7, .2, 1),
              box-shadow .4s ease;
}
/* Distinct paper stock per talk so the pile clearly reads as two papers:
   the original cream for the full paper, deep navy for the workshop paper. */
.paper-card[data-paper="dtc"]   { background: #f3efe3; }
.paper-card[data-paper="pheno"] { background: var(--inverse-surface); }

/* Navy card runs on cream ink; the coral eyebrow keeps its pop. */
.paper-card[data-paper="pheno"] .paper-tab,
.paper-card[data-paper="pheno"] .paper-tab__title,
.paper-card[data-paper="pheno"] .detail-text .title-serif {
  color: var(--inverse-on-surface);
}
.paper-card[data-paper="pheno"] .paper-tab__hint { color: rgba(255, 239, 214, 0.72); }
.paper-card[data-paper="pheno"] .detail-text p { color: rgba(255, 239, 214, 0.90); }
.paper-card[data-paper="pheno"] .detail-text a:not(.btn) {
  color: var(--inverse-on-surface);
  text-decoration-color: rgba(255, 239, 214, 0.5);
}
.paper-card[data-paper="pheno"] .btn--outline {
  color: var(--inverse-on-surface);
  border-color: var(--inverse-on-surface);
}
.paper-card .media img { border-radius: var(--radius-lg); }
.paper-card .detail-text .title-serif { color: var(--primary); }
.paper-card .detail-text p { color: var(--on-surface-body); }

/* Tab / header — the always-visible handle for each paper */
.paper-tab {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-grotesque);
  color: var(--primary);
  padding: clamp(20px, 3vw, 32px) clamp(28px, 4vw, 56px) 0;
}
.paper-tab__eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--secondary);
}
.paper-tab__title {
  font-size: clamp(19px, 2.4vw, 26px);
  font-weight: 700;
  line-height: 1.25;
}
.paper-tab__hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  font-size: 13px;
  font-weight: 600;
  color: var(--on-surface-variant);
  opacity: 0;
  transition: opacity .3s ease;
}
.paper-tab__hint::after { content: "\2191"; font-size: 15px; }

.paper-body {
  display: flex;
  flex-direction: column;
  gap: clamp(24px, 3vw, 40px);
  padding: clamp(20px, 3vw, 32px) clamp(28px, 4vw, 56px) clamp(28px, 4vw, 56px);
}

/* --- Piled behaviour (JS on): two tilted sheets -------------------------
   Like two sheets of paper on a desk: the open, readable paper lies on top,
   barely tilted; the second paper sits behind it tilted the other way, its
   header peeking above the top edge while its lower half disappears behind the
   front sheet — so it reads as a whole sheet tucked under, never a stub. The
   back sheet is a fixed, moderate height so its tilt can't swing past the page
   even though the front paper can be very long. */
.paper-stack.is-piled {
  gap: 0;
  padding-top: 92px;              /* room for the sheet behind to peek above */
}

/* Front sheet: the open paper, on top, barely tilted (about its centre so a
   long card's far corner barely moves). */
.paper-stack.is-piled .paper-card.is-active {
  position: relative;
  z-index: 2;
  transform: rotate(-0.6deg);
  transform-origin: 50% 50%;
  box-shadow: 0 18px 44px rgba(46, 46, 56, 0.20);
}
.paper-stack.is-piled .paper-card.is-active .paper-tab { cursor: default; }

/* Back sheet: sits behind the front, tilted the other way. Fixed height with
   its lower part hidden behind the front card. */
.paper-stack.is-piled .paper-card.is-peek {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 400px;
  z-index: 1;
  cursor: pointer;
  overflow: hidden;
  transform: rotate(3deg) translateX(-6px);
  transform-origin: 50% 50%;
  box-shadow: 0 12px 32px rgba(46, 46, 56, 0.16);
  transition: transform .4s cubic-bezier(.2, .7, .2, 1), box-shadow .4s ease;
}
.paper-stack.is-piled .paper-card.is-peek .paper-body { display: none; }
.paper-stack.is-piled .paper-card.is-peek .paper-tab { padding-top: 22px; }
.paper-stack.is-piled .paper-card.is-peek .paper-tab__hint { opacity: 1; }
.paper-stack.is-piled .paper-card.is-peek:hover,
.paper-stack.is-piled .paper-card.is-peek:focus-within {
  transform: rotate(3deg) translateX(-6px) translateY(-6px);
  box-shadow: 0 18px 40px rgba(46, 46, 56, 0.22);
}

/* Mobile / no-pile: both papers fully expanded in a plain stack */
@media (max-width: 760px) {
  .paper-stack.is-piled { gap: var(--space-lg); padding-top: 0; }
  /* The selectors need .is-active/.is-peek so they outweigh the desktop
     tilt rules — a plain .paper-card reset loses on specificity and the
     rotated cards overflow the phone screen. */
  .paper-stack.is-piled .paper-card,
  .paper-stack.is-piled .paper-card.is-active,
  .paper-stack.is-piled .paper-card.is-peek,
  .paper-stack.is-piled .paper-card.is-peek:hover,
  .paper-stack.is-piled .paper-card.is-peek:focus-within { transform: none; }
  .paper-stack.is-piled .paper-card.is-peek {
    position: static;
    inset: auto;
    height: auto;
    overflow: visible;
  }
  .paper-stack.is-piled .paper-card.is-peek .paper-body { display: flex; }
  .paper-tab__hint { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .paper-card { transition: none; }
}

/* ---- Stacking deck ---------------------------------------------------
   The data-collection chunks as a pinned deck: the stage sticks under the
   nav so only one card fills the view at a time, and scrolling slides the
   next card up to stack on top of the pile. Per-page JS drives the
   transforms (adding .is-deck); with no JS the cards just flow as normal
   stacked blocks, so the content is always readable. */
.deck-card {
  background: #f3efe3;
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4vw, 56px);
  box-shadow: 0 -8px 30px rgba(46, 46, 56, 0.10);
  margin-bottom: 28px;
}
.deck-card .media img { border-radius: var(--radius-lg); }
.deck-card .title-serif { color: var(--primary); }
.deck-card .block-desc,
.deck-card .deck-col p { color: var(--on-surface-body); }

/* ---- Pinned window (desktop) ------------------------------------------
   The deck reads as a single fixed window. As you scroll into it, the first
   card pins just under the nav; scrolling on slides the next card straight up
   over it (every card pins at the same spot, so each fully covers the last —
   no peeking ledges). Each card snaps as it lands and can't be flung past, so
   there's a beat to read; once the last card lands, the page scrolls on. Pure
   CSS sticky drives it — no JS, no nested scrollbar to trap the wheel. */
@media (min-width: 760px) {
  .deck-stage { display: flex; flex-direction: column; gap: 0; }

  .deck-card {
    position: sticky;
    top: 88px;
    /* Fill the window below the nav so a pinned card fully covers the one
       behind it — no strip of the next card peeking at the bottom edge. */
    height: calc(100vh - 88px);
    margin-bottom: 0;
    overflow: hidden;
    /* No outer drop shadow here: every card stays pinned at top:88px, so an
       upward shadow from each one would pile into the same strip above the
       edge and darken with every card. Instead the shadow is drawn *inside*
       the top of the card (::before below), where the next card's opaque body
       covers it — so only the current top card's shadow ever shows. */
    box-shadow: none;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    scroll-margin-top: 88px;
  }

  /* Soft top shadow contained within the card. Because it sits inside the
     card body (top edge, fading down), the next card slides up and covers
     it — so stacked cards never accumulate overlapping shadows. */
  .deck-card::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 40px;
    z-index: 1;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      rgba(46, 46, 56, 0.05),
      rgba(46, 46, 56, 0)
    );
  }

  /* The caption keeps its natural height and is never clipped; the image fills
     whatever space is left above it. The image is positioned absolutely inside
     its box so its intrinsic size can't push the caption out and overlap it. */
  .deck-card .deck-grid { flex: 1 1 auto; min-height: 0; }
  .deck-card .deck-grid .deck-col { flex: 0 0 auto; }
  .deck-card .deck-grid .media {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-lg);
  }
  /* override .detail-blocks .media img { height:auto } so the image is sized to
     its box and object-fit:contain scales the WHOLE picture down to fit —
     nothing cropped, just smaller within the window */
  .deck-card .deck-grid .media img {
    position: absolute;
    inset: 0;
    width: 100% !important;
    height: 100% !important;
    object-fit: contain !important;
  }
}

.deck-count {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  margin: 0 0 var(--space-sm);
}
/* Cards 2–4: image runs full width of the card, copy sits underneath it */
.deck-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.deck-grid .media img { width: 100%; display: block; }

/* Image + bold-headline pairing, vertically centred, alternating sides */
.photo-block {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}
.photo-block--reverse .media { order: 2; }
.photo-block .media img { border-radius: var(--radius-lg); }

/* Thesis-book block: top-align the copy with the (tall) cover image instead
   of vertically centring it, sit the two columns closer together, and use
   full reading-size copy (matching the intro) in navy. Only this block sits
   directly under .detail-blocks; the rest live inside .photo-feature. */
.defense-playful .detail-blocks > .photo-block {
  align-items: start;
  gap: clamp(24px, 2.5vw, 40px);
  border: 1px solid var(--outline);
  border-radius: var(--radius-lg);
  padding: clamp(24px, 3vw, 48px);
}
.defense-playful .detail-blocks > .photo-block .block-title { color: var(--primary); }
.defense-playful .detail-blocks > .photo-block .block-desc {
  font-size: 17px;
  line-height: 28px;
  max-width: none;
  color: var(--primary);           /* page-level intro copy reads in navy ink */
}

/* Compact variant: cap the photo so it doesn't tower over its caption */
.photo-block--compact .media { display: flex; justify-content: center; }
.photo-block--compact .media img { max-width: 300px; width: 100%; }

.block-title {
  font-family: var(--font-grotesque);
  font-weight: 800;
  font-size: clamp(22px, 2.6vw, 36px);
  line-height: 0.98;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: var(--on-surface);
  margin: 0 0 var(--space-sm);
}
.block-desc {
  font-family: var(--font-grotesque);
  font-weight: 400;
  font-size: 14px;
  line-height: 22px;
  color: var(--on-surface-body);
  margin: 0;
  max-width: 44ch;
}
.block-copy .utility-row { margin-top: var(--space-md); }

@media (max-width: 760px) {
  .photo-block { grid-template-columns: 1fr; gap: var(--space-md); }
  .photo-block--reverse .media { order: 0; }
}

/* Plain (borderless) intro photo-blocks + width variants. The half block
   is the default 1fr 1fr; the wide block gives the image two-thirds. */
.defense-playful .detail-blocks > .photo-block--plain { border: none; padding: 0; }
.photo-block--wide { grid-template-columns: 2fr 1fr; }
@media (max-width: 760px) { .photo-block--wide { grid-template-columns: 1fr; } }

/* ---- Reveal card: framework / mapped example -------------------------
   A before/after image wiper. Two aligned framework images are stacked; a
   draggable handle wipes between them (clip-path on the top image). The
   copy beside it swaps between the two texts when the wipe passes the
   midpoint. The Framework|Example labels are clickable and reflect state. */
.reveal-card {
  --reveal: 100%;                 /* how much of the framework image shows */
  border: 1px solid var(--outline);
  border-radius: var(--radius-lg);
  padding: clamp(20px, 2.4vw, 36px);
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 2vw, 28px);
}
/* Left column stacks the toggle above the image so the "|" separator lines
   up with the wipe handle's centre. */
.reveal-left {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 2vw, 28px);
}
/* Plain text toggle: "Framework | Example" — active navy bold, other grey.
   A 3-column grid (1fr | 1fr) keeps the separator dead-centre over the image. */
.reveal-toggle {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  column-gap: 14px;
  width: 100%;
}
.reveal-toggle-sep { color: var(--outline); font-weight: 300; }
.reveal-toggle button {
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  font-family: var(--font-grotesque);
  font-size: 16px;
  letter-spacing: 0.01em;
  font-weight: 400;
  color: #8a8a90;
  transition: color 0.25s ease, font-weight 0.25s ease;
}
.reveal-toggle button[data-face="front"] { justify-self: end; }
.reveal-toggle button[data-face="back"] { justify-self: start; }
.reveal-toggle button:hover { color: var(--primary); }
.reveal-toggle button[aria-selected="true"] { color: var(--primary); font-weight: 700; }

.reveal-grid {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: clamp(24px, 3vw, 48px);
  align-items: center;
}

/* The image wiper. The media box lets the handle overflow (so the grip stays
   whole at the edges); an inner frame does the rounding + clipping. */
.reveal-media {
  position: relative;
  aspect-ratio: 1 / 1;
  touch-action: pan-y;              /* allow vertical page scroll, we handle X */
  user-select: none;
}
.reveal-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.reveal-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  user-select: none;
}
/* Top image is clipped from the right by (100% - reveal). */
.reveal-img--framework { clip-path: inset(0 calc(100% - var(--reveal)) 0 0); }
.reveal-media.is-animating .reveal-img--framework { transition: clip-path 0.5s cubic-bezier(0.2, 0.75, 0.2, 1); }

.reveal-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--reveal);
  transform: translateX(-50%);
  width: 44px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: ew-resize;
  display: grid;
  place-items: center;
  touch-action: none;
}
.reveal-media.is-animating .reveal-handle { transition: left 0.5s cubic-bezier(0.2, 0.75, 0.2, 1); }
/* the vertical divider line */
.reveal-handle::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 2px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(46, 46, 56, 0.15);
}
/* the round grip with two arrows */
.reveal-handle-grip {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 2px 10px rgba(46, 46, 56, 0.3);
  display: grid;
  place-items: center;
  transition: transform 0.2s ease;
}
.reveal-handle:hover .reveal-handle-grip,
.reveal-handle:focus-visible .reveal-handle-grip { transform: scale(1.08); }
.reveal-handle:focus-visible { outline: none; }
.reveal-handle-grip::before {
  content: "\2039\00a0\203a";      /* ‹ › */
  color: #fff;
  font-size: 18px;
  line-height: 1;
  letter-spacing: 2px;
}

/* The swapping copy */
.reveal-copy { display: grid; }
.reveal-text {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease;
  pointer-events: none;
}
.reveal-card[data-face="front"] .reveal-text--front,
.reveal-card[data-face="back"]  .reveal-text--back {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.reveal-text .block-title { color: var(--primary); }
.reveal-text .block-desc { max-width: none; }

/* Tooltip that only appears when the handle is hovered/focused */
.reveal-tip {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  white-space: nowrap;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-grotesque);
  font-size: 12px;
  letter-spacing: 0.02em;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.reveal-tip::after {                 /* little pointer under the pill */
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--primary);
}
.reveal-handle:hover .reveal-tip,
.reveal-handle:focus-visible .reveal-tip {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

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

/* Full-width prose blocks (Research Objectives, Research Methods) sitting
   directly under .detail-blocks — span the page at full reading size in navy */
.defense-playful .detail-prose .block-desc {
  font-size: 17px;
  line-height: 28px;
  max-width: none;
}
/* Page-level prose reads in navy ink; the same block reused inside the cream
   .photo-feature band (e.g. thriving vs struggling) keeps its calmer grey body,
   so only prose that is a *direct* child of .detail-blocks turns navy. */
.defense-playful .detail-blocks > .detail-prose .block-desc { color: var(--primary); }
/* Image that the prose wraps around: floats to one side after the intro copy,
   so the title + first line run full width, then text surrounds the figure */
.detail-prose .prose-figure {
  float: right;
  width: 50%;
  object-fit: contain;
  margin: var(--space-xs) 0 var(--space-md) var(--space-lg);
  border-radius: var(--radius-lg);
}
@media (max-width: 760px) {
  .detail-prose .prose-figure {
    float: none;
    width: 100%;
    height: auto;
    margin: var(--space-md) 0;
  }
}

/* Named sub-sections inside a block-copy (e.g. Background / Methodology) */
.block-copy .title-serif { margin: 0 0 var(--space-xs); color: var(--primary); }
.block-copy .title-serif + .block-desc { margin-top: 0; }
.block-copy .block-desc + .title-serif { margin-top: var(--space-md); }
.block-copy .block-desc + .block-desc { margin-top: var(--space-md); }

/* Two figures stacked in one media cell (image 11 over image 12). The block
   stretches both columns to the same height, and the media cell pins its lower
   image to the bottom — so image 12's base lines up with the end of the copy. */
.defense-playful .detail-blocks > .photo-block--fill { align-items: stretch; }
.media--stack {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-md);
  height: 100%;
}
@media (max-width: 760px) { .media--stack { height: auto; } }

/* ---- Highlights list (Highlights page) ------------------------------- */
.hl-item {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  align-items: start;
}
.hl-item + .hl-item { margin-top: var(--space-2xl); }
@media (max-width: 760px) { .hl-item { grid-template-columns: 1fr; gap: var(--space-md); } }
.hl-item .media-placeholder { aspect-ratio: 4 / 3; }
.hl-item .headline-lg { font-size: clamp(22px, 2.6vw, 32px); margin-bottom: var(--space-xs); }

.utility-row { margin-top: var(--space-lg); }

/* ---- Craft page: masonry gallery + lightbox -------------------------- */
.gallery-group { margin-bottom: var(--space-lg); }
.gallery-group > h2 {
  font: 700 13px/1 var(--font-grotesque);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  margin: 0 0 var(--space-sm);
}

/* Masonry: columns of varied height, each photo keeps its true shape. */
.thumb-grid {
  columns: 4 220px;
  column-gap: var(--space-xs);
}
@media (max-width: 900px) { .thumb-grid { columns: 3 180px; } }
@media (max-width: 560px) { .thumb-grid { columns: 2 140px; } }
.thumb {
  padding: 0;
  border: 0;
  cursor: pointer;
  overflow: hidden;
  display: block;
  width: 100%;
  margin: 0 0 var(--space-xs);
  break-inside: avoid;            /* don't split a photo across columns */
  background: var(--surface-container-high);
  position: relative;
}
.thumb img,
.thumb video {
  width: 100%;
  height: auto;                   /* natural aspect ratio — no cropping */
  display: block;
  transition: transform .4s ease;
}
.thumb:hover img,
.thumb:hover video { transform: scale(1.04); }
/* crop a tall video down to a 3:4 portrait, trimming the top */
.thumb--crop { aspect-ratio: 3 / 4; }
.thumb--crop video {
  height: 100%;
  object-fit: cover;
  object-position: center bottom; /* keep the bottom, cut the top */
}
/* the thumbnail crop is for the grid only — show the whole frame full screen */
.thumb--crop video:fullscreen,
.thumb--crop video:-webkit-full-screen {
  object-fit: contain;
  object-position: center;
}
/* Video thumbnails autoplay; real controls overlay them. */
.thumb--video { cursor: default; }
.video-controls {
  position: absolute;
  bottom: var(--space-xs);
  right: var(--space-xs);
  display: flex;
  gap: 6px;
  opacity: 0;
  transition: opacity .25s ease;
}
.thumb--video:hover .video-controls,
.thumb--video:focus-within .video-controls { opacity: 1; }
.video-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(0,0,0,.5);
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
}
.video-btn:hover { background: rgba(0,0,0,.72); }
.video-btn svg { width: 18px; height: 18px; }
.video-btn[data-video-fs] svg { width: 16px; height: 16px; }
/* toggle shows the pause icon while playing, the play icon once paused */
.video-btn .icon-play { display: none; }
.video-btn.is-paused .icon-pause { display: none; }
.video-btn.is-paused .icon-play { display: block; }

/* Lightbox overlay */
.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 16, 0.92);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.lightbox.is-open { display: flex; }
.lightbox-stage {
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-stage img,
.lightbox-stage video {
  max-width: 90vw;
  max-height: 86vh;
  object-fit: contain;
  display: block;
}
.lightbox-close,
.lightbox-prev,
.lightbox-next {
  position: absolute;
  background: rgba(255,255,255,.14);
  color: #fff;
  border: 0;
  width: 52px; height: 52px;
  font-size: 22px;
  cursor: pointer;
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
}
.lightbox-close { top: 20px; right: 20px; }
.lightbox-prev { left: 20px; top: 50%; transform: translateY(-50%); }
.lightbox-next { right: 20px; top: 50%; transform: translateY(-50%); }
.lightbox-prev:hover,
.lightbox-next:hover,
.lightbox-close:hover { background: rgba(255,255,255,.28); }

/* ---- Book details modal (Defense Day) -------------------------------- */
.book-modal {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 16, 0.92);
  display: none;
  z-index: 1000;
  overflow-y: auto;
  padding: 64px 20px;
}
.book-modal.is-open { display: block; }
.book-modal-panel {
  max-width: 980px;
  margin: 0 auto;
  background: var(--surface);
  padding: var(--space-lg);
}
.book-modal-grid {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: var(--space-lg);
  align-items: start;
}
.book-modal-cover { position: static; }
.book-modal-cover .media img { width: 100%; display: block; }
.book-chapters > .label-caps { display: block; margin-bottom: var(--space-md); }
.book-chapter + .book-chapter { margin-top: var(--space-md); }
.book-chapter .title-serif { margin-bottom: var(--space-xs); }
.book-chapter p:not(.title-serif) { color: var(--on-surface-body); margin: 0; }
.book-modal-close {
  position: fixed;
  top: 20px;
  right: 20px;
  background: rgba(255, 255, 255, .14);
  color: #fff;
  border: 0;
  width: 52px;
  height: 52px;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
  z-index: 1001;
}
.book-modal-close:hover { background: rgba(255, 255, 255, .28); }
@media (max-width: 760px) {
  .book-modal { padding: 56px 12px; }
  .book-modal-panel { padding: var(--space-md); }
  .book-modal-grid { grid-template-columns: 1fr; }
  .book-modal-cover { position: static; max-width: 260px; }
}

/* ---- Detail-page image gallery (masonry of real images) -------------- */
.masonry img,
.masonry video {
  display: block;
  width: 100%;
  height: auto;
  break-inside: avoid;
  margin-bottom: var(--space-md);
}

/* ---- Takeaway CV (About) --------------------------------------------- */
/* One typeface throughout (grotesque) — entries differ only by colour, size,
   weight and case. Compact scale, centred in a thin frame above the contact
   section. */
.cv-shell { max-width: 1010px; margin-inline: auto; }

/* Header row: eyebrow on the left, download pill on the right, navy rule
   beneath — same rhythm as the Education / Experiences section headers. */
.cv-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-md);
  flex-wrap: wrap;
  border-bottom: 2px solid var(--primary);
  padding-bottom: var(--space-sm);
  margin-bottom: var(--space-lg);
}
.cv-head .section-eyebrow { display: block; }
/* Thin bordered frame around the whole CV block */
.cv-frame { border: 1px solid var(--outline); padding: clamp(32px, 4.5vw, 56px); }
@media (max-width: 560px) { .cv-frame { padding: var(--space-md); } }
/* Download pill: navy at rest, mint on hover — same accent as back-to-top */
.btn--mint-hover:hover,
.btn--mint-hover:focus-visible {
  background: var(--tertiary);
  color: var(--on-tertiary);
  opacity: 1;
  outline: none;
}

/* Columns hug their content (flex items) and are spread with equal gaps, so the
   visual channel between columns reads identically regardless of line lengths. */
.cv-grid {
  display: flex;
  justify-content: flex-start;
  gap: 76px;                   /* fixed, identical channel between every column */
  align-items: flex-start;
}
@media (max-width: 860px) { .cv-grid { flex-direction: column; gap: var(--space-lg); } }
.cv-col { flex: 0 0 auto; min-width: 0; }
/* Explicit column widths → fixed flex boxes → space-between yields exactly
   equal gaps between columns. Widths are sized to each column's content. */
.cv-grid > .cv-col:nth-child(1) { width: 290px; }       /* sets the bio measure */
.cv-grid > .cv-col:nth-child(2) { width: max-content; }  /* hug content */
.cv-grid > .cv-col:nth-child(3) { width: max-content; }
@media (max-width: 860px) {
  .cv-grid > .cv-col:nth-child(1),
  .cv-grid > .cv-col:nth-child(2),
  .cv-grid > .cv-col:nth-child(3) { flex: 1 1 auto; width: auto; }
}
.cv-block + .cv-block { margin-top: var(--space-md); }

/* Everything below uses var(--font-grotesque) — single family by design. */
.cv-name {
  font-family: var(--font-grotesque);
  font-weight: 800;
  font-size: 26px;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--inverse-surface);
  margin: 0;
}
.cv-role {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--secondary);
  margin: 5px 0 var(--space-sm);
}
.cv-portrait { width: 100%; height: auto; max-width: 180px; display: block; margin-bottom: var(--space-sm); }
.cv-bio {
  font-family: var(--font-grotesque);
  font-weight: 400;
  font-size: 12.5px;
  line-height: 19px;
  color: var(--on-surface-body);
  margin: 0;                   /* column width (280px) sets the measure */
}
.cv-contact {
  font-family: var(--font-grotesque);
  font-weight: 600;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--primary);
  margin: 0 0 var(--space-md);
}
.cv-contact a { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }

.cv-sub {
  font-family: var(--font-grotesque);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--inverse-surface);
  margin: 0 0 8px;
}
.cv-entry { line-height: 18px; }   /* match the list rhythm; avoid inherited tall leading */
.cv-entry + .cv-entry { margin-top: 11px; }
.cv-entry .role {
  display: block;
  font-family: var(--font-grotesque);
  font-weight: 600;
  font-size: 12px;
  color: var(--on-surface);
  line-height: 18px;
}
.cv-entry .where {
  font-family: var(--font-grotesque);
  font-weight: 400;
  font-size: 12px;
  line-height: 18px;
  color: var(--on-surface-body);
}
.cv-entry .when {
  font-family: var(--font-grotesque);
  font-weight: 500;
  font-size: 12px;
  color: var(--on-surface-variant);
  white-space: nowrap;          /* keep date ranges like 2019–2025 on one line */
}
.cv-list { list-style: none; margin: 0; padding: 0; }
.cv-list li { font-family: var(--font-grotesque); font-weight: 400; font-size: 12px; line-height: 18px; color: var(--on-surface-body); }
.cv-list li .lvl { color: var(--on-surface-variant); white-space: nowrap; }
