/* =============================================================================
 * styles.css — Cam An Nguyen Thanh · Senior Fullstack Engineer
 * A single light editorial résumé design system.
 *
 * Concept: a well-typeset printed CV brought online — confident, typographic,
 * calm, generous whitespace. Characterful Fraunces serif for the name and
 * section headings; clean Inter body copy; ONE warm sienna accent used only on
 * links, hairline rules, list markers, and small marks. Left-aligned single
 * column on a ~68ch measure. No terminal, no dark mode, no gradients, no glass,
 * no card grids, no drop shadows beyond 1px hairline rules.
 *
 * Structure
 *   01  Design tokens (custom properties)
 *   02  Reset & base elements
 *   03  Typography primitives
 *   04  Accessibility helpers (skip link, focus, sr-only)
 *   05  Layout primitives (measure, sections, section heads)
 *   06  Site nav
 *   07  Hero
 *   08  Link rows (chips + résumé button)
 *   09  Profile prose
 *   10  Highlights / metrics strip
 *   11  Experience (umbrella + engagements + standalone roles)
 *   12  Research & open source
 *   13  Education
 *   14  Publications
 *   15  Contact
 *   16  Footer
 *   17  noscript fallback
 *   18  Scroll-reveal (GSAP) hooks + reduced motion
 *   19  Responsive (>=480, >=640, >=768, >=1024)
 *   20  Print (@media print) — ATS-friendly paper résumé
 * ========================================================================== */

/* =============================================================================
 * 01 · DESIGN TOKENS
 * ========================================================================== */
:root {
  /* ---- Palette: single warm light theme (no dark mode) ---------------- */
  --bg: #FAF8F4;          /* page background, warm near-white            */
  --surface: #F2EEE7;     /* band / inset surface (highlights strip)     */
  --ink: #1A1714;         /* primary text — 14.8:1 on --bg               */
  --ink-strong: #0F0D0B;  /* name, strongest headings                    */
  --muted: #6B635A;       /* metadata, dates, cited-by — 5.0:1 AA        */
  --muted-soft: #8A8076;  /* non-essential / large text only — 3.4:1     */
  --accent: #9A3412;      /* warm sienna — links, rules, marks — 6.4:1   */
  --accent-ink: #7C2A0E;  /* darker accent for hover / pressed states    */
  --rule: #E2DBD0;        /* hairline rules, soft dividers               */
  --rule-strong: #CFC6B8; /* heading underlines, firmer dividers         */
  --focus: #1D4ED8;       /* focus ring — deliberately distinct from accent */

  /* ---- Type families ------------------------------------------------- */
  --font-serif: "Fraunces", "Iowan Old Style", "Palatino Linotype", Georgia, "Times New Roman", serif;
  --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ---- Modular scale (1.250 — major third) --------------------------- */
  --step--1: 0.80rem;   /* metadata, captions, cited-by, section index   */
  --step-0: 1rem;       /* body                                          */
  --step-1: 1.25rem;    /* lead / role titles                            */
  --step-2: 1.563rem;   /* section headings (serif)                      */
  --step-3: 1.953rem;   /* hero subtitle / tagline (serif)               */
  --step-4: clamp(2.6rem, 6vw, 3.75rem); /* fluid name                   */

  /* ---- Line heights -------------------------------------------------- */
  --lh-body: 1.6;
  --lh-bullets: 1.55;
  --lh-heading: 1.12;
  --lh-name: 1.0;

  /* ---- Measure ------------------------------------------------------- */
  --measure: 68ch;        /* comfortable prose measure (66–75ch target)  */
  --measure-bullets: 66ch;

  /* ---- Spacing scale (deliberate, doubling-ish rhythm) --------------- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-sm: 1rem;
  --space-md: 1.5rem;
  --space-lg: 2.25rem;
  --space-xl: 3.5rem;
  --space-2xl: 5rem;
  --space-3xl: 7rem;

  /* ---- Page horizontal padding (fluid gutter) ------------------------ */
  --gutter: clamp(1.25rem, 5vw, 3rem);

  /* ---- Shared page container width: nav + ALL content center to this,
         so the menu and the content column share identical L/R edges. ---- */
  --page-max: calc(var(--measure) + 12ch);

  /* ---- Misc ---------------------------------------------------------- */
  --hairline: 1px;
  --radius: 4px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Base font-size: 16px mobile, 17px (106.25%) from 768px up. */
:root { font-size: 100%; }
@media (min-width: 768px) {
  :root { font-size: 106.25%; }
}

/* =============================================================================
 * 02 · RESET & BASE
 * ========================================================================== */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* offset for the sticky nav when jumping to in-page anchors (clears the
     taller two-row mobile nav as well as the single-row desktop nav) */
  scroll-padding-top: 6.5rem;
  -webkit-tap-highlight-color: transparent;
}

body {
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: var(--lh-body);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
  min-height: 100%;
  overflow-x: hidden;
}

img,
svg { display: block; max-width: 100%; }

ul,
ol { list-style: none; padding: 0; }

a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  transition: color 0.18s var(--ease), text-decoration-color 0.18s var(--ease);
}
a:hover { color: var(--accent-ink); text-decoration-color: currentColor; }

strong,
b { font-weight: 600; }

::selection {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  color: var(--ink-strong);
}

/* =============================================================================
 * 03 · TYPOGRAPHY PRIMITIVES
 * ========================================================================== */
h1, h2, h3, h4 {
  font-family: var(--font-serif);
  font-optical-sizing: auto;
  color: var(--ink-strong);
  line-height: var(--lh-heading);
  font-weight: 600;
  text-wrap: balance;
}

p { text-wrap: pretty; }

/* =============================================================================
 * 04 · ACCESSIBILITY HELPERS
 * ========================================================================== */

/* Skip link — first focusable element, hidden until focused. */
a.skip-link {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 200;
  transform: translateY(calc(-100% - 1rem));
  background: var(--ink-strong);
  color: var(--bg);
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  padding: 0.6rem 1rem;
  border-radius: var(--radius);
  transition: transform 0.18s var(--ease);
}
a.skip-link:focus-visible,
a.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Visually-hidden utility (kept for any sr-only spans). */
.visually-hidden,
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Global focus-visible ring — never bare outline:none. */
:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}
/* Drop focus ring for pointer interactions that aren't keyboard. */
:where(a, button):focus:not(:focus-visible) { outline: none; }

/* =============================================================================
 * 05 · LAYOUT PRIMITIVES
 * ========================================================================== */

/* Main landmark — the editorial column. */
main#main { display: block; }

/* Every content section sits on the measured column with vertical rhythm. */
section.section {
  padding-block: var(--space-xl);
  padding-inline: var(--gutter);
}

/* Center each (non-band) section box on the shared page container so its
   left/right edges line up with the nav. Inner blocks keep the readable
   measure and stay left-aligned within that centered box. */
section.section:not(.section--band) {
  max-width: var(--page-max);
  margin-inline: auto;
}

/* The inner content of a section is constrained to the measure and
   left-aligned. Section heads + prose + lists share this column. */
section.section > * {
  max-width: var(--measure);
}

/* Subtle hairline between consecutive sections (band sections carry their own
   surface borders, so skip the duplicate top rule there). */
section.section + section.section:not(.section--band) {
  border-top: var(--hairline) solid var(--rule);
}

/* ---- Section head ---------------------------------------------------- */
header.section-head {
  margin-bottom: var(--space-lg);
}

/* Small accent index, e.g. "01" */
header.section-head > p.section-index {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-2xs);
  display: flex;
  align-items: center;
  gap: 0.7em;
}
/* Decorative hairline tick trailing the index number. */
header.section-head > p.section-index::after {
  content: "";
  flex: 0 0 2.5rem;
  height: var(--hairline);
  background: var(--accent);
  opacity: 0.55;
}

/* Serif section heading with a hairline beneath spanning the measure. */
h2.section-title {
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--ink-strong);
  padding-bottom: var(--space-xs);
  border-bottom: var(--hairline) solid var(--rule-strong);
}

/* Subsection heading (open-source libraries). */
h3.subsection-title {
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.005em;
  margin-top: var(--space-xl);
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-2xs);
  border-bottom: var(--hairline) solid var(--rule);
}

/* =============================================================================
 * 06 · SITE NAV
 * ========================================================================== */
nav#site-nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: saturate(120%) blur(8px);
  backdrop-filter: saturate(120%) blur(8px);
  border-bottom: var(--hairline) solid var(--rule);
}
/* Graceful fallback for browsers without backdrop-filter — opaque bar. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  nav#site-nav { background: var(--bg); }
}

nav#site-nav > .nav-inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-block: 0.7rem;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.3rem var(--space-sm);
  min-height: 3.25rem;
}

.nav-name {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
  text-decoration: none;
  white-space: nowrap;
}
.nav-name:hover { color: var(--ink-strong); }

ul.nav-links {
  /* Mobile-first: a horizontally-scrollable strip on its own row so section
     links stay reachable on phones/tablets. Becomes an inline row >=860px. */
  display: flex;
  order: 3;
  flex-basis: 100%;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 1.85rem);
  overflow-x: auto;
  flex-wrap: nowrap;
  margin-top: 0.1rem;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
ul.nav-links::-webkit-scrollbar { display: none; }
ul.nav-links > li { flex: none; }
ul.nav-links > li > a.nav-link {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  padding: 0.4rem 0.1rem;
  position: relative;
  transition: color 0.18s var(--ease);
}
ul.nav-links > li > a.nav-link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0.05rem;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s var(--ease);
}
ul.nav-links > li > a.nav-link:hover,
ul.nav-links > li > a.nav-link:focus-visible { color: var(--ink-strong); }
ul.nav-links > li > a.nav-link:hover::after { transform: scaleX(1); }

/* Slim résumé button living in the nav. */
a.nav-resume {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.5rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  border: var(--hairline) solid var(--rule-strong);
  border-radius: var(--radius);
  background: transparent;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}
a.nav-resume:hover,
a.nav-resume:focus-visible {
  color: var(--accent-ink);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}

/* =============================================================================
 * 07 · HERO
 * ========================================================================== */
header#top.hero {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-top: clamp(var(--space-2xl), 9vw, var(--space-3xl));
  padding-bottom: var(--space-2xl);
}

header#top.hero > .hero-inner {
  max-width: var(--measure);
}

/* The single H1 — name, set large in Fraunces. */
h1#name.hero-name {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--step-4);
  line-height: var(--lh-name);
  letter-spacing: -0.02em;
  color: var(--ink-strong);
  margin-bottom: var(--space-md);
}

/* Title + subtitle line. */
p.hero-title {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--step-3);
  line-height: 1.18;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: var(--space-sm);
}
.hero-title__role { color: var(--ink-strong); }
.hero-title__sub {
  color: var(--muted);
  font-weight: 500;
}
/* Subtle separating mark between role and subtitle. */
.hero-title__role + .hero-title__sub::before {
  content: " · ";
  color: var(--accent);
  font-weight: 600;
}

/* One-line tagline. */
p.hero-tagline {
  font-family: var(--font-sans);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink);
  max-width: 60ch;
  margin-bottom: var(--space-md);
}

/* Location + availability metadata. */
p.hero-meta {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.01em;
  margin-bottom: var(--space-lg);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.85rem;
}
.hero-meta__loc { font-weight: 500; }
.hero-meta__loc + .hero-meta__avail::before {
  content: "";
  display: inline-block;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 0.85rem;
  vertical-align: 0.18em;
}

/* =============================================================================
 * 08 · LINK ROWS (chips + résumé button) — hero + contact
 * ========================================================================== */
div.link-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 0.75rem;
}

/* Profile link chip. */
a.link-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  min-height: 44px;
  padding: 0.5rem 0.95rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  background: transparent;
  border: var(--hairline) solid var(--rule-strong);
  border-radius: 999px;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease), background-color 0.18s var(--ease);
}
/* Small accent leading mark inside each chip. */
a.link-chip::before {
  content: "";
  flex: 0 0 auto;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  transition: transform 0.18s var(--ease);
}
.link-chip__label { line-height: 1; }
a.link-chip:hover,
a.link-chip:focus-visible {
  color: var(--ink-strong);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
a.link-chip:hover::before { transform: scale(1.25); }

/* Download résumé button — accent outline to anchor the row. */
a.btn-resume {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;
  padding: 0.55rem 1.25rem;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  color: var(--accent);
  text-decoration: none;
  background: transparent;
  border: 1.5px solid var(--accent);
  border-radius: 999px;
  transition: color 0.18s var(--ease), background-color 0.18s var(--ease), border-color 0.18s var(--ease);
}
a.btn-resume::before {
  content: "↓";
  font-size: 0.95em;
  line-height: 1;
  transition: transform 0.18s var(--ease);
}
a.btn-resume:hover,
a.btn-resume:focus-visible {
  color: var(--bg);
  background: var(--accent);
  border-color: var(--accent);
}
a.btn-resume:hover::before { transform: translateY(2px); }

/* Hero link row spacing. */
div.link-row#hero-links { margin-top: var(--space-xs); }

/* =============================================================================
 * 09 · PROFILE PROSE
 * ========================================================================== */
div#profile-body.prose { max-width: var(--measure); }
div#profile-body.prose > p {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink);
}
div#profile-body.prose > p + p { margin-top: var(--space-sm); }
/* Lead paragraph gets a touch more presence. */
div#profile-body.prose > p:first-child {
  font-size: 1.08rem;
  color: var(--ink-strong);
}

/* =============================================================================
 * 10 · HIGHLIGHTS / METRICS STRIP ("By the Numbers")
 * ========================================================================== */

/* Band section sits on --surface and bleeds full-width while keeping the
   inner content on the measure. */
section#highlights.section.section--band {
  background: var(--surface);
  border-top: var(--hairline) solid var(--rule);
  border-bottom: var(--hairline) solid var(--rule);
  padding-inline: 0; /* full-bleed band; children own the gutter (below) */
}
/* Band inner blocks center on the same page container + gutter as every other
   section, so the band content aligns with the nav and the rest of the page. */
section#highlights.section.section--band > * {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

ul#metrics-list.metrics {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md) var(--space-xl);
}

li.metric {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding-left: var(--space-sm);
  border-left: 2px solid var(--rule-strong);
}

.metric__value {
  font-family: var(--font-serif);
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: clamp(1.6rem, 3.2vw, 2.05rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--ink-strong);
  white-space: nowrap;
}
.metric__unit {
  font-size: 0.55em;
  font-weight: 500;
  color: var(--accent);
  margin-left: 0.15em;
  letter-spacing: 0;
}
.metric__label {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0.005em;
}
.metric__detail {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--muted);
  max-width: 38ch;
}

/* =============================================================================
 * 11 · EXPERIENCE
 * ========================================================================== */
div#experience-list.roles {
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}

/* A single role block (umbrella + standalone share .role). */
article.role { max-width: var(--measure); }

/* Umbrella role reads as one continuous track; give its head a touch more
   weight so the engagements clearly nest beneath it. */
article.role.role--umbrella > header.role-head h3.role-title { font-size: 1.4rem; }
article.role.role--umbrella { max-width: var(--measure); }

/* Role head — title + meta. */
header.role-head { margin-bottom: var(--space-xs); }

h3.role-title {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--step-1);
  line-height: 1.2;
  letter-spacing: -0.005em;
  color: var(--ink-strong);
}

p.role-meta {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.01em;
  margin-top: var(--space-3xs);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.6rem;
}
.role-meta__company { font-weight: 600; color: var(--ink); }
.role-meta__period {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.role-meta__company + .role-meta__period::before {
  content: "·";
  color: var(--muted-soft);
  margin-right: 0.6rem;
}

/* Umbrella role summary lead. */
p.role-summary {
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--ink);
  margin-top: var(--space-sm);
  max-width: var(--measure);
}

/* Standalone-role project subtitle (small serif). */
p.role-project {
  font-family: var(--font-serif);
  font-weight: 500;
  font-style: italic;
  font-size: 1.02rem;
  color: var(--accent-ink);
  margin-top: var(--space-2xs);
}

/* ---- Engagements (nested sub-blocks, NO own dates) ------------------- */
div.engagements {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  margin-top: var(--space-lg);
  /* indented rail to read as "inside" the umbrella role */
  padding-left: var(--space-md);
  border-left: var(--hairline) solid var(--rule);
}

article.engagement { position: relative; }
/* Accent node marking each engagement on the rail. */
article.engagement::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--space-md) - 0.5px);
  top: 0.5em;
  transform: translateX(-50%);
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--bg);
  border: 1.5px solid var(--accent);
}

h4.engagement-title {
  font-family: var(--font-serif);
  font-weight: 600;
  font-size: 1.075rem;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--ink-strong);
}

p.engagement-note {
  font-family: var(--font-sans);
  font-style: italic;
  font-size: var(--step--1);
  color: var(--muted);
  margin-top: var(--space-3xs);
}

/* ---- Bullets (roles / engagements / research) ----------------------- */
ul.bullets {
  margin-top: var(--space-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-width: var(--measure-bullets);
}
li.bullet {
  position: relative;
  padding-left: 1.4rem;
  font-size: var(--step-0);
  line-height: var(--lh-bullets);
  color: var(--ink);
}
/* Accent list-marker glyph. */
li.bullet::before {
  content: "—";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent);
  font-weight: 600;
}

/* ---- Tech tag list (inline; NOT a skills section) ------------------- */
ul.tech {
  margin-top: var(--space-sm);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 0.5rem;
  max-width: var(--measure);
}
.tech__label {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted); /* AA at small size (5.1:1); --muted-soft fails 4.5:1 */
  margin-right: 0.25rem;
  align-self: center;
}
li.tech__item {
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1;
  color: var(--muted);
  padding: 0.35rem 0.6rem;
  background: var(--surface);
  border: var(--hairline) solid var(--rule);
  border-radius: var(--radius);
  white-space: nowrap;
}

/* =============================================================================
 * 12 · RESEARCH & OPEN SOURCE
 * ========================================================================== */

/* Research featured block reuses .role styling. */
article.role.role--research { max-width: var(--measure); }

/* Open-source libraries list. */
ul#opensource-list.oss {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-width: var(--measure);
}
li.oss__item {
  display: block;
  font-size: var(--step-0);
  line-height: var(--lh-bullets);
  padding-left: 1.4rem;
  position: relative;
  color: var(--ink);
}
li.oss__item::before {
  content: "›";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent);
  font-weight: 600;
}
.oss__name {
  font-family: var(--font-sans);
  font-weight: 600;
  color: var(--accent);
}
.oss__name::after {
  content: " — ";
  color: var(--muted-soft);
  font-weight: 400;
}
.oss__desc { color: var(--ink); }

/* =============================================================================
 * 13 · EDUCATION
 * ========================================================================== */
ul#education-list.education {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  max-width: var(--measure);
}
li.edu {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: var(--space-md);
  row-gap: 0.15rem;
  padding-bottom: var(--space-md);
  border-bottom: var(--hairline) solid var(--rule);
}
li.edu:last-child { border-bottom: 0; padding-bottom: 0; }

.edu__degree {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--step-1);
  line-height: 1.2;
  color: var(--ink-strong);
  grid-column: 1;
}
.edu__school {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  color: var(--muted);
  grid-column: 1;
}
.edu__period {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  grid-column: 2;
  grid-row: 1;
  white-space: nowrap;
  text-align: right;
}

/* =============================================================================
 * 14 · PUBLICATIONS
 * ========================================================================== */
p#publications-stats.pub-stats {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: 0.005em;
  margin-bottom: var(--space-lg);
  padding: 0.7rem 0;
  border-top: var(--hairline) solid var(--rule);
  border-bottom: var(--hairline) solid var(--rule);
  max-width: var(--measure);
}

ol#publications-list.pubs {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  max-width: var(--measure);
  counter-reset: pub;
}
li.pub {
  position: relative;
  padding-left: 2.4rem;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink);
  counter-increment: pub;
}
li.pub::before {
  content: counter(pub, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0.1em;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  letter-spacing: 0.04em;
}
.pub__cite { color: var(--ink); }
.pub__cited {
  display: inline;
  color: var(--muted);
  font-style: italic;
  white-space: nowrap;
}

p#publications-note.pub-note {
  font-size: var(--step--1);
  color: var(--muted);
  margin-top: var(--space-lg);
  max-width: var(--measure);
  font-style: italic;
}

/* =============================================================================
 * 15 · CONTACT
 * ========================================================================== */
p.contact-lead {
  font-family: var(--font-sans);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink);
  margin-bottom: var(--space-lg);
  max-width: 56ch;
}
div.link-row#contact-links { margin-top: var(--space-sm); }

/* =============================================================================
 * 16 · FOOTER
 * ========================================================================== */
footer#site-footer.footer {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-block: var(--space-xl);
  padding-inline: var(--gutter);
  border-top: var(--hairline) solid var(--rule-strong);
  margin-top: var(--space-lg);
}
footer#site-footer.footer > * { max-width: var(--measure); }

p.footer-id {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: 1.02rem;
  color: var(--ink-strong);
  letter-spacing: -0.005em;
  margin-bottom: var(--space-2xs);
}

a.footer-top {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  letter-spacing: 0.02em;
  margin-bottom: var(--space-sm);
}
a.footer-top::before { content: "↑ "; }
a.footer-top:hover,
a.footer-top:focus-visible {
  color: var(--accent-ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

p.footer-colophon {
  font-family: var(--font-sans);
  font-size: var(--step--1);
  color: var(--muted);
}

/* =============================================================================
 * 17 · NOSCRIPT FALLBACK
 * Plain, semantic, readable even without the design system.
 * ========================================================================== */
section#noscript-cv {
  padding-inline: var(--gutter);
  padding-block: var(--space-xl);
  max-width: var(--measure);
  margin-inline: auto;
}
section#noscript-cv h1 {
  font-size: var(--step-4);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-sm);
}
section#noscript-cv h2 {
  font-size: var(--step-2);
  margin-top: var(--space-xl);
  margin-bottom: var(--space-sm);
  padding-bottom: var(--space-2xs);
  border-bottom: var(--hairline) solid var(--rule-strong);
}
section#noscript-cv h3 {
  font-size: var(--step-1);
  margin-top: var(--space-md);
  margin-bottom: var(--space-2xs);
}
section#noscript-cv h4 {
  font-size: 1.05rem;
  margin-top: var(--space-sm);
  margin-bottom: var(--space-3xs);
}
section#noscript-cv p { margin-bottom: var(--space-2xs); }
section#noscript-cv ul,
section#noscript-cv ol {
  list-style: disc;
  padding-left: 1.4rem;
  margin-bottom: var(--space-sm);
}
section#noscript-cv ol { list-style: decimal; }
section#noscript-cv li { margin-bottom: var(--space-3xs); line-height: var(--lh-bullets); }

/* =============================================================================
 * 18 · SCROLL-REVEAL HOOKS + REDUCED MOTION
 * Default = fully visible (no JS). The hidden start-state is opted into ONLY
 * when main.js confirms IntersectionObserver is available and adds .reveal-on;
 * IO then adds .is-visible per element to fade it in. A JS safety timeout also
 * force-adds .is-visible, so content can never stay hidden.
 * ========================================================================== */

/* Default and pre-activation: fully visible. */
html.js [data-reveal],
html.js [data-reveal-item] {
  opacity: 1;
  transform: none;
}

/* Opt into the hidden start-state only when IO is active + motion allowed. */
@media (prefers-reduced-motion: no-preference) {
  html.js.reveal-on [data-reveal],
  html.js.reveal-on [data-reveal-item] {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
    will-change: opacity, transform;
  }
  html.js.reveal-on [data-reveal].is-visible,
  html.js.reveal-on [data-reveal-item].is-visible {
    opacity: 1;
    transform: none;
  }
}

/* Reduced motion: kill smooth scroll, transitions, and any reveal transforms. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal],
  [data-reveal-item] {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* =============================================================================
 * 19 · RESPONSIVE
 * Mobile-first. Enhancements layered at 480 / 640 / 768 / 1024.
 * ========================================================================== */

/* ---- >= 480px : metrics two-up earlier on roomy phones --------------- */
@media (min-width: 480px) {
  ul#metrics-list.metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---- >= 640px : tighten hero meta ----------------------------------- */
@media (min-width: 640px) {
  p.hero-meta { font-size: 0.85rem; }
}

/* ---- >= 860px : reveal nav links -----------------------------------
   Raised from 640px: below this width nav-inner overflows the viewport and
   the Résumé button + Contact link clip off-screen (hidden by overflow-x). */
@media (min-width: 860px) {
  nav#site-nav > .nav-inner { flex-wrap: nowrap; }
  ul.nav-links {
    order: 0;
    flex-basis: auto;
    overflow: visible;
    margin-top: 0;
  }
}

/* ---- >= 768px : richer rhythm, three-up metrics --------------------- */
@media (min-width: 768px) {
  section.section { padding-block: var(--space-2xl); }

  ul#metrics-list.metrics {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-lg) var(--space-xl);
  }

  li.edu { grid-template-columns: 1fr auto; }
  h2.section-title { letter-spacing: -0.01em; }
}

/* ---- >= 1024px : widen the shared page container (nav + all content) -- */
@media (min-width: 1024px) {
  :root { --page-max: 72rem; }
  header#top.hero { padding-top: var(--space-3xl); }
}

/* =============================================================================
 * 20 · PRINT — clean, ATS-friendly black-on-white paper résumé
 * Recruiters will Cmd+P this. One refined column, sensible page breaks, no
 * decoration, no nav/buttons, plain-text contact line at the top.
 * ========================================================================== */
@media print {
  /* ---- Force print palette tokens --------------------------------- */
  :root {
    --bg: #ffffff;
    --surface: #ffffff;
    --ink: #000000;
    --ink-strong: #000000;
    --muted: #333333;
    --muted-soft: #555555;
    --accent: #000000;
    --accent-ink: #000000;
    --rule: #bbbbbb;
    --rule-strong: #999999;
    --focus: #000000;
    --measure: none;
    font-size: 10.5pt;
  }

  @page { margin: 14mm 15mm; }

  html, body {
    background: #fff !important;
    color: #000 !important;
    line-height: 1.35;
    overflow: visible;
  }
  body {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    font-size: 10.5pt;
  }

  /* ---- Hide all interactive / decorative chrome -------------------- */
  nav#site-nav,
  a.skip-link,
  a.btn-resume,
  a.nav-resume,
  a.footer-top,
  ul.tech,
  header.section-head > p.section-index::after {
    display: none !important;
  }

  /* Hide profile/contact chip rows (URLs survive on .print-contact). */
  div.link-row { display: none !important; }

  /* Hide the whole on-screen Contact section on paper: its chip row is hidden
     and the heading would otherwise print a dead, near-empty section. The
     .print-contact line at the top already carries email + all profile URLs. */
  section#contact { display: none !important; }

  /* ---- Print-only contact line at the very top -------------------- */
  p.print-contact {
    display: block !important;
    font-family: var(--font-sans);
    font-size: 9.5pt;
    line-height: 1.4;
    color: #000;
    margin: 0 0 8pt 0;
    padding-bottom: 6pt;
    border-bottom: 0.75pt solid #000;
  }

  /* ---- Layout: single full-width column, no measure cap ----------- */
  main#main { display: block; }
  section.section,
  header#top.hero,
  footer#site-footer.footer {
    padding: 0 !important;
    margin: 0 0 6pt 0 !important;
    border: 0 !important;
    max-width: none !important;
  }

  /* Tighten the experience role gap on paper. */
  div#experience-list.roles { gap: 8pt !important; }
  div#research-list.roles { gap: 8pt !important; }
  section.section > *,
  header#top.hero > .hero-inner,
  footer#site-footer.footer > *,
  section#highlights.section.section--band > *,
  article.role,
  article.role.role--research,
  div#profile-body.prose,
  ul.bullets,
  ul#opensource-list.oss,
  ul#education-list.education,
  ol#publications-list.pubs,
  p.role-summary,
  p#publications-stats.pub-stats,
  p#publications-note.pub-note,
  p.contact-lead {
    max-width: none !important;
  }

  /* Drop the band background; flatten to paper. */
  section#highlights.section.section--band {
    background: #fff !important;
    border: 0 !important;
  }

  /* Hairline dividers between sections removed for paper. */
  section.section + section.section { border-top: 0 !important; }

  /* ---- Typography for paper --------------------------------------- */
  h1#name.hero-name {
    font-size: 22pt;
    margin-bottom: 2pt;
    letter-spacing: -0.01em;
  }
  p.hero-title {
    font-size: 12pt;
    color: #000 !important;
    margin-bottom: 2pt;
  }
  .hero-title__sub { color: #222 !important; }
  p.hero-tagline {
    font-size: 10.5pt;
    color: #000 !important;
    margin-bottom: 4pt;
  }
  p.hero-meta {
    font-size: 9pt;
    color: #222 !important;
    margin-bottom: 0;
  }

  header#top.hero {
    margin-bottom: 8pt !important;
    padding-bottom: 6pt !important;
  }

  header.section-head { margin-bottom: 6pt; }
  header.section-head > p.section-index {
    font-size: 8pt;
    color: #000 !important;
    margin-bottom: 1pt;
  }
  h2.section-title {
    font-size: 13pt;
    color: #000 !important;
    padding-bottom: 3pt;
    border-bottom: 0.75pt solid #000;
    margin-bottom: 6pt;
  }
  h3.subsection-title {
    font-size: 11.5pt;
    margin-top: 10pt;
    margin-bottom: 4pt;
    border-bottom: 0.5pt solid #999;
  }

  h3.role-title { font-size: 11.5pt; color: #000 !important; }
  p.role-meta { font-size: 9pt; color: #222 !important; }
  .role-meta__company { color: #000 !important; }
  p.role-project { color: #000 !important; font-size: 10pt; }
  p.role-summary { font-size: 10pt; margin-top: 3pt; }

  h4.engagement-title { font-size: 10.5pt; color: #000 !important; }
  p.engagement-note { color: #333 !important; font-size: 9pt; }

  div.engagements {
    border-left: 0.5pt solid #999 !important;
    padding-left: 8pt !important;
    margin-top: 6pt !important;
    gap: 8pt !important;
  }
  article.engagement::before { display: none !important; }

  ul.bullets { margin-top: 3pt; gap: 1.5pt; }
  li.bullet { padding-left: 12pt; color: #000 !important; }
  li.bullet::before { content: "•"; color: #000 !important; }

  /* ---- Metrics: tight inline value+label list --------------------- */
  ul#metrics-list.metrics {
    display: block !important;
    columns: 2;
    column-gap: 18pt;
  }
  li.metric {
    border-left: 0 !important;
    padding-left: 0 !important;
    margin-bottom: 3pt;
    break-inside: avoid;
  }
  .metric__value {
    font-size: 11pt;
    color: #000 !important;
    display: inline;
  }
  .metric__unit { color: #000 !important; }
  .metric__label {
    display: inline;
    font-size: 9.5pt;
    font-weight: 600;
  }
  .metric__value + .metric__label::before { content: " — "; }
  /* Drop detail to keep within 1–2 pages. */
  .metric__detail { display: none !important; }

  /* ---- Education -------------------------------------------------- */
  li.edu {
    padding-bottom: 4pt;
    border-bottom: 0.5pt solid #ccc;
  }
  .edu__degree { font-size: 11pt; color: #000 !important; }
  .edu__school { color: #222 !important; font-size: 9.5pt; }
  .edu__period { color: #222 !important; font-size: 9pt; }

  /* ---- Publications ----------------------------------------------- */
  p#publications-stats.pub-stats {
    font-size: 9.5pt;
    color: #000 !important;
    border-color: #999 !important;
    padding: 3pt 0;
    margin-bottom: 6pt;
  }
  li.pub { font-size: 9pt; padding-left: 16pt; }
  li.pub::before { color: #000 !important; }
  .pub__cited { color: #333 !important; }
  p#publications-note.pub-note { color: #333 !important; font-size: 8.5pt; }

  /* ---- Open source ------------------------------------------------ */
  li.oss__item { color: #000 !important; }
  li.oss__item::before { color: #000 !important; }
  .oss__name { color: #000 !important; }

  /* ---- Footer ----------------------------------------------------- */
  footer#site-footer.footer {
    border-top: 0.5pt solid #999 !important;
    padding-top: 6pt !important;
    margin-top: 8pt !important;
  }
  p.footer-id { font-size: 9.5pt; color: #000 !important; }
  p.footer-colophon { display: none !important; }

  /* ---- Links: black, no color underline --------------------------- */
  a {
    color: #000 !important;
    text-decoration: none !important;
  }

  /* ---- Page-break control ----------------------------------------- */
  /* Keep only atomic blocks together; let engagements/pubs split so content
     flows instead of ejecting whole blocks to the next page. */
  article.role,
  li.edu {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  /* Keep headings with the content that follows, but NOT engagement titles
     (h4) — forcing those creates orphan gaps. */
  header.section-head,
  h2.section-title,
  h3.role-title,
  h3.subsection-title {
    break-after: avoid;
    page-break-after: avoid;
  }
  /* Prefer soft widow/orphan control over hard break-avoids on flowing text. */
  p.role-summary,
  li.bullet,
  li.pub {
    widows: 2;
    orphans: 2;
  }
  section.section {
    break-inside: auto;
    page-break-inside: auto;
  }

  /* Reveal transforms must never hide print content. */
  [data-reveal],
  [data-reveal-item] {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* =============================================================================
 * print-contact: hidden on screen, shown only in @media print (above).
 * ========================================================================== */
p.print-contact { display: none; }
