/* ==========================================================================
   Jethro Warnett, personal academic site
   Single stylesheet, no build step, no external requests.

   Contents
     1.  Design tokens (colour, type, spacing, motion)
     2.  Reset and base elements
     3.  Layout primitives
     4.  Typography helpers
     5.  Components (buttons, chips, cards, details)
     6.  Header and navigation
     7.  Hero
     8.  Section-specific styles
     9.  Footer
     10. Responsive, motion, print
   ========================================================================== */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

:root {
  /* --- Type stacks -------------------------------------------------------
     Self-hosted webfonts are deliberately avoided: system faces render
     instantly, cost zero bytes, and cause no layout shift. */
  --font-display: "Iowan Old Style", "Palatino Linotype", "Book Antiqua",
    Palatino, ui-serif, "New York", Georgia, "Times New Roman", serif;
  --font-sans: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
    "Roboto Mono", Menlo, Consolas, monospace;

  /* --- Fluid type scale --------------------------------------------------
     clamp() keeps every size legible from 320px phones to ultrawide desks. */
  --step--1: clamp(0.8125rem, 0.79rem + 0.11vw, 0.875rem);
  --step-0: clamp(0.9375rem, 0.92rem + 0.12vw, 1rem);
  --step-1: clamp(1.0625rem, 1.03rem + 0.2vw, 1.1875rem);
  --step-2: clamp(1.25rem, 1.17rem + 0.36vw, 1.5rem);
  --step-3: clamp(1.5rem, 1.36rem + 0.65vw, 2rem);
  --step-4: clamp(2rem, 1.65rem + 1.6vw, 3.25rem);

  /* --- Spacing ----------------------------------------------------------- */
  --space-2xs: 0.375rem;
  --space-xs: 0.625rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2rem;
  --space-xl: clamp(2.25rem, 1.85rem + 1.5vw, 3.125rem);

  /* --- Structure ---------------------------------------------------------- */
  --container: 68rem;
  --measure: 70ch;
  --radius-s: 6px;
  --radius-m: 10px;
  --header-h: 3.5rem;

  /* --- Motion ------------------------------------------------------------- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast: 120ms;
  --dur: 220ms;
}

/* --- Light theme (default) ------------------------------------------------ */
:root,
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #fbfaf8;          /* warm paper, softer than pure white */
  --bg-elev: #ffffff;     /* raised cards */
  --bg-sunken: #f3f1ec;   /* wells and subtle fills */
  --border: #e2ded5;
  --border-strong: #cdc7bb;

  --text: #14181e;
  --text-muted: #545c68;
  --text-subtle: #616974;  /* small mono labels must still clear AA */

  --accent: #1a4e7a;
  --accent-hover: #123a5c;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(26, 78, 122, 0.08);
  --accent-line: rgba(26, 78, 122, 0.24);

  /* Award badge. The text tone is darkened well past the fill so it clears
     4.5:1 against it. */
  --gold: #7d5a0b;
  --gold-soft: rgba(214, 162, 38, 0.18);
  --gold-line: rgba(160, 118, 20, 0.45);

  --particle: rgba(26, 78, 122, 0.85);

  --shadow-s: 0 1px 2px rgba(20, 24, 30, 0.05);
  --header-bg: rgba(251, 250, 248, 0.85);
}

/* --- Dark theme ----------------------------------------------------------- */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0e1116;
  --bg-elev: #151a21;
  --bg-sunken: #0a0d11;
  --border: #242c37;
  --border-strong: #37414f;

  --text: #e9ecf1;
  --text-muted: #a6afbb;
  --text-subtle: #828c99;

  --accent: #7bb3e8;
  --accent-hover: #a4ccf4;
  --accent-contrast: #08121c;
  --accent-soft: rgba(123, 179, 232, 0.12);
  --accent-line: rgba(123, 179, 232, 0.3);

  --gold: #e8c268;
  --gold-soft: rgba(232, 194, 104, 0.13);
  --gold-line: rgba(232, 194, 104, 0.34);

  --particle: rgba(158, 201, 243, 0.92);

  --shadow-s: 0 1px 2px rgba(0, 0, 0, 0.4);
  --header-bg: rgba(14, 17, 22, 0.85);
}

/* Follow the OS when the visitor has never chosen a theme. The inline script
   in <head> stamps data-theme before first paint, so this is the no-JS path. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --bg: #0e1116;
    --bg-elev: #151a21;
    --bg-sunken: #0a0d11;
    --border: #242c37;
    --border-strong: #37414f;
    --text: #e9ecf1;
    --text-muted: #a6afbb;
    --text-subtle: #828c99;
    --accent: #7bb3e8;
    --accent-hover: #a4ccf4;
    --accent-contrast: #08121c;
    --accent-soft: rgba(123, 179, 232, 0.12);
    --accent-line: rgba(123, 179, 232, 0.3);
    --gold: #e8c268;
    --gold-soft: rgba(232, 194, 104, 0.13);
    --gold-line: rgba(232, 194, 104, 0.34);
    --particle: rgba(158, 201, 243, 0.92);
    --shadow-s: 0 1px 2px rgba(0, 0, 0, 0.4);
    --header-bg: rgba(14, 17, 22, 0.85);
  }
}

/* ==========================================================================
   2. Reset and base
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Offset anchor targets so the sticky header never covers a heading. */
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  min-height: 100svh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

/* Colour-only transition, applied by JS solely while the theme flips. */
html.theme-anim,
html.theme-anim *,
html.theme-anim *::before,
html.theme-anim *::after {
  transition: background-color 280ms var(--ease),
              border-color 280ms var(--ease),
              color 280ms var(--ease),
              fill 280ms var(--ease) !important;
}

img,
svg,
picture { display: block; max-width: 100%; }

img { height: auto; }

a {
  color: var(--accent);
  text-decoration-color: var(--accent-line);
  text-underline-offset: 0.16em;
  text-decoration-thickness: 1px;
  transition: color var(--dur-fast) var(--ease),
              text-decoration-color var(--dur-fast) var(--ease);
}

a:hover { color: var(--accent-hover); text-decoration-color: currentColor; }

ul, ol { padding: 0; list-style: none; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p { text-wrap: pretty; }

hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin-block: var(--space-s);
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

::selection { background: var(--accent-soft); color: var(--text); }

.muted { color: var(--text-subtle); }

/* Text for screen readers only. Used to tell otherwise identical links and
   summaries ("PDF", "Abstract") apart when they are read out of context. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The skip link targets <main>, which carries tabindex="-1" purely to receive
   that focus. It is not a control, so it does not need a focus ring. */
#main:focus { outline: none; }

/* Skip link, visible only once focused. */
.skip-link {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -120%);
  z-index: 200;
  padding: 0.6rem 1.1rem;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: 0 0 var(--radius-s) var(--radius-s);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  transition: transform var(--dur) var(--ease);
}

.skip-link:focus,
.skip-link:focus-visible { transform: translate(-50%, 0); color: var(--accent-contrast); }

/* ==========================================================================
   3. Layout primitives
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 2.25rem);
}

.section {
  padding-block: var(--space-xl);
  position: relative;
}

/* Hairline rule between adjacent flat sections, inset to the container. */
.section + .section::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: clamp(1.15rem, 4vw, 2.25rem);
  max-width: var(--container);
  margin-inline: auto;
  height: 1px;
  background: var(--border);
}

.section--sunken { background: var(--bg-sunken); }
.section--sunken + .section::before,
.section--sunken::before { display: none; }

/* Pulls a specific join closer than the default section rhythm. Used on the
   About/Research pair, where the full gap on both sides read as a hole. */
.section--tight-bottom { padding-block-end: calc(var(--space-xl) * 0.55); }
.section--tight-top { padding-block-start: calc(var(--space-xl) * 0.7); }

.section > .container > h2 {
  font-size: var(--step-3);
  margin-bottom: var(--space-s);
}

.section-note {
  color: var(--text-muted);
  max-width: var(--measure);
  font-size: var(--step--1);
  margin-bottom: var(--space-m);
}

/* ==========================================================================
   4. Typography helpers
   ========================================================================== */

/* Small letterspaced mono label, the site's recurring "metadata" voice. */
.label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: var(--space-s);
}

.section > .container > .label { margin-top: var(--space-l); }
.section > .container > .label:first-of-type { margin-top: 0; }

/* Group heading inside a section, one level down from the h2. Larger than the
   mono .label because these two ("Published and accepted", "Preprints") have to
   outrank the paper titles sitting under them. */
.pub-group {
  font-size: var(--step-2);
  color: var(--text);
  padding-bottom: 0.45rem;
  margin-bottom: var(--space-s);
  border-bottom: 1px solid var(--border-strong);
  scroll-margin-top: calc(var(--header-h) + 1rem);
}

.pub-list + .pub-group { margin-top: var(--space-l); }

/* ==========================================================================
   5. Components
   ========================================================================== */

/* --- Buttons -------------------------------------------------------------- */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-bd: var(--border-strong);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--btn-bd);
  border-radius: var(--radius-s);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.btn:hover { color: var(--btn-fg); }
.btn svg { width: 1em; height: 1em; flex: none; }

.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-contrast);
  --btn-bd: var(--accent);
}

.btn--primary:hover { --btn-bg: var(--accent-hover); --btn-bd: var(--accent-hover); }
.btn:not(.btn--primary):hover { --btn-bd: var(--accent); --btn-fg: var(--accent); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;
}

/* --- Link chips (DOI, arXiv, PDF) ----------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-s);
  background: var(--bg-elev);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}

.chip:hover {
  color: var(--accent);
  border-color: var(--accent-line);
  background: var(--accent-soft);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.6rem;
}

/* --- Card ----------------------------------------------------------------- */
.card {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-m);
  padding: var(--space-m);
}

/* ==========================================================================
   6. Header and navigation
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: var(--header-bg);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease);
}

/* .is-stuck is toggled by JS once the page scrolls away from the top. */
.site-header.is-stuck { border-bottom-color: var(--border); }

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
}

.brand {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}

.nav { display: flex; align-items: center; gap: 0.1rem; }

.nav a {
  position: relative;
  padding: 0.35rem 0.6rem;
  border-radius: var(--radius-s);
  font-size: 0.875rem;
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}

.nav a:hover { color: var(--text); }
.nav a[aria-current="true"] { color: var(--text); }

.nav a[aria-current="true"]::after {
  content: "";
  position: absolute;
  left: 0.6rem;
  right: 0.6rem;
  bottom: 0.05rem;
  height: 1.5px;
  border-radius: 2px;
  background: var(--accent);
}

.header-tools { display: flex; align-items: center; gap: 0.35rem; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius-s);
  border: 1px solid var(--border);
  background: var(--bg-elev);
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}

.icon-btn:hover { color: var(--accent); border-color: var(--accent-line); }
.icon-btn svg { width: 1rem; height: 1rem; }

/* Sun/moon swap inside the theme toggle. */
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme]) .theme-toggle .icon-moon { display: block; }
}

.nav-toggle { display: none; }

.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-menu { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: block; }
.nav-toggle[aria-expanded="true"] { color: var(--accent); border-color: var(--accent-line); }

/* --- Mobile navigation ---------------------------------------------------- */
@media (max-width: 56rem) {
  .nav-toggle { display: grid; }

  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0 clamp(1.15rem, 4vw, 2.25rem) var(--space-s);
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-0.5rem);
    transition: opacity var(--dur) var(--ease),
                transform var(--dur) var(--ease),
                visibility 0s linear var(--dur);
    max-height: calc(100svh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .nav.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition-delay: 0s;
  }

  .nav a {
    padding: 0.75rem 0.15rem;
    font-size: 1rem;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
  }

  .nav a[aria-current="true"]::after { display: none; }
  .nav a[aria-current="true"] { color: var(--accent); }
}

/* ==========================================================================
   7. Hero
   ========================================================================== */

.hero {
  padding-block: clamp(1.5rem, 3.5vw, 2.5rem) var(--space-l);
  position: relative;
  isolation: isolate;
}

/* Particle canvas. Sits behind the hero content, sized by JS to its own box.
   The mask softens the lower edge so the field ends without a visible line. */
.hero__particles {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 700ms var(--ease);
  -webkit-mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
}

/* Added by JS on the first painted frame, so the field fades in rather than
   appearing mid-drift. */
.hero__particles.is-running { opacity: 1; }

.hero > .container { position: relative; z-index: 1; }

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3.25rem);
  align-items: center;
}

.hero__affil {
  font-size: var(--step--1);
  color: var(--text-muted);
}

.hero__name {
  font-size: var(--step-4);
  line-height: 1.04;
  letter-spacing: -0.025em;
  margin-block: 0.35rem var(--space-s);
}

.hero__tagline {
  font-size: var(--step-1);
  color: var(--text-muted);
  max-width: 46ch;
  margin-bottom: var(--space-s);
}

.hero__supervisors {
  font-size: var(--step--1);
  color: var(--text-subtle);
  margin-bottom: var(--space-m);
  max-width: 52ch;
}

/* --- Portrait ------------------------------------------------------------- */
.portrait { margin: 0; min-width: 0; }

.portrait picture,
.portrait img {
  border-radius: var(--radius-m);
  overflow: hidden;
}

.portrait img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: 62% 38%;
  border: 1px solid var(--border);
}

.portrait figcaption {
  margin-top: 0.45rem;
  font-size: 0.72rem;
  color: var(--text-subtle);
}

/* ==========================================================================
   8. Sections
   ========================================================================== */

/* --- About ---------------------------------------------------------------- */
.about__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3rem);
  align-items: start;
}

.prose h2 { font-size: var(--step-3); margin-bottom: var(--space-s); }
.prose > * + * { margin-top: 0.85rem; }
.prose p { max-width: var(--measure); color: var(--text-muted); }
.prose strong { color: var(--text); font-weight: 600; }

.facts { display: grid; gap: 0.55rem; }

.facts > div {
  display: grid;
  grid-template-columns: 5.75rem minmax(0, 1fr);
  gap: 0.75rem;
  font-size: var(--step--1);
  align-items: baseline;
}

.facts dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.facts dd { margin: 0; color: var(--text); }

.facts__links { display: flex; flex-wrap: wrap; gap: 0.15rem 0.85rem; }

/* --- Research ------------------------------------------------------------- */
.research__intro {
  max-width: var(--measure);
  margin-bottom: var(--space-m);
}

.research__intro p { color: var(--text-muted); }
.research__intro p + p { margin-top: 0.85rem; }

/* Wide screens: run the two intro paragraphs side by side. This uses the width
   the section actually has without letting any single line get long enough to
   be hard to track back. */
@media (min-width: 56rem) {
  .research__intro {
    max-width: none;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(1.75rem, 4vw, 3rem);
    align-items: start;
  }

  .research__intro p + p { margin-top: 0; }
}

.interests {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: var(--space-s) clamp(1.5rem, 3vw, 2.5rem);
}

.interests > div { border-top: 1px solid var(--border); padding-top: 0.7rem; }
.interests h4 { font-size: var(--step-0); margin-bottom: 0.2rem; }
.interests p { font-size: var(--step--1); color: var(--text-muted); }

/* --- Publications and preprints ------------------------------------------- */
.pub-list { display: grid; }

.pub {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: var(--space-s);
  padding-block: var(--space-s);
  border-top: 1px solid var(--border);
}

.pub:first-child { border-top: 0; padding-top: 0; }

.pub__year {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-subtle);
  padding-top: 0.2rem;
}

.pub__title {
  font-size: var(--step-1);
  line-height: 1.3;
  margin-bottom: 0.25rem;
}

.pub__title a { color: var(--text); text-decoration: none; }
.pub__title a:hover { color: var(--accent); }

.pub__authors { font-size: var(--step--1); color: var(--text-muted); }
.pub__authors .me { color: var(--text); font-weight: 600; }

.pub__venue {
  font-size: var(--step--1);
  color: var(--text-subtle);
}

.pub__venue em { color: var(--text-muted); font-style: italic; }

/* Award flag. One per paper at most, so it can afford to be the only gold on
   the page. */
.award {
  display: inline-flex;
  align-items: center;
  margin-top: 0.45rem;
  padding: 0.18rem 0.55rem;
  border: 1px solid var(--gold-line);
  border-radius: var(--radius-s);
  background: var(--gold-soft);
  color: var(--gold);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Collapsible abstract. <details> keeps this working with JS disabled. */
.abstract { margin-top: 0.5rem; max-width: var(--measure); }

.abstract > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-subtle);
  list-style: none;
  transition: color var(--dur-fast) var(--ease);
}

.abstract > summary::-webkit-details-marker { display: none; }
.abstract > summary:hover { color: var(--accent); }

.abstract > summary svg {
  width: 0.8rem;
  height: 0.8rem;
  transition: transform var(--dur) var(--ease);
}

.abstract[open] > summary svg { transform: rotate(180deg); }

.abstract__body {
  margin-top: 0.4rem;
  padding-left: 0.85rem;
  border-left: 2px solid var(--border);
  font-size: var(--step--1);
  color: var(--text-muted);
}

/* --- CV ------------------------------------------------------------------- */
.cv__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  margin-bottom: var(--space-l);
}

.cv__head .section-note { margin-bottom: 0; max-width: 44ch; }

.cv__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: var(--space-l) clamp(1.75rem, 4vw, 3rem);
  align-items: start;
}

.cv-list { display: grid; gap: 0.4rem; }

.cv-list li {
  display: grid;
  grid-template-columns: 4.75rem minmax(0, 1fr);
  gap: 0.75rem;
  font-size: var(--step--1);
  align-items: baseline;
}

.cv-list .when {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-subtle);
}

.cv-list .what { color: var(--text-muted); }
.cv-list .what b { color: var(--text); font-weight: 600; }

/* --- Availability --------------------------------------------------------- */
.availability {
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-m);
  background: var(--bg-elev);
  padding: clamp(1.25rem, 3vw, 2rem);
}

.availability h2 { font-size: var(--step-2); margin-bottom: 0.5rem; }
.availability p { color: var(--text-muted); max-width: 58ch; }

.availability__intro p + p { margin-top: 0.85rem; }

/* Wide screens: the two paragraphs run side by side, the same treatment the
   Research intro gets. The card then fills its width without the single
   capped paragraph leaving a hole on the right. The gap matches the spec
   sheet below, so the two columns sit on the same lines. */
@media (min-width: 56rem) {
  .availability__intro {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-l);
    align-items: start;
  }

  .availability__intro p { max-width: none; }
  .availability__intro p + p { margin-top: 0; }
}

/* Mock spec sheet, the joke delivered straight-faced. */
.spec {
  margin-block: var(--space-s);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-s) var(--space-l);
  padding-block: var(--space-s);
  border-block: 1px dashed var(--border-strong);
}

.spec > div { font-size: var(--step--1); }

.spec dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: 0.1rem;
}

.spec dd { margin: 0; color: var(--text); }

/* ==========================================================================
   9. Footer
   ========================================================================== */

.site-footer {
  border-top: 1px solid var(--border);
  padding-block: var(--space-m);
  background: var(--bg-sunken);
}

.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-m);
  align-items: center;
  justify-content: space-between;
}

.site-footer p { font-size: var(--step--1); color: var(--text-subtle); }
.site-footer a { color: var(--text-muted); }
.site-footer a:hover { color: var(--accent); }

.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
  font-size: var(--step--1);
}

.to-top { display: inline-flex; align-items: center; gap: 0.3rem; }
.to-top svg { width: 0.85em; height: 0.85em; }

/* ==========================================================================
   10. Responsive, motion, print
   ========================================================================== */

@media (max-width: 56rem) {
  /* One column, but centred rather than pinned left, so neither the bio nor the
     facts card leaves a gutter on one side only. The paragraphs themselves stay
     left-aligned; centred body copy at this length is not readable. */
  .about__grid {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: var(--space-m);
  }

  .about__grid > * { width: 100%; max-width: 40rem; }

  /* The column is already at the measure, so the inner cap would only add a
     ragged right edge. */
  .prose p { max-width: none; }
}

@media (max-width: 48rem) {
  :root { --header-h: 3.25rem; }

  .hero__grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-m); }

  /* Photo first on small screens: the face is the fastest introduction. It is
     capped so it does not become a billboard, and centred so the cap does not
     leave dead space on the right. */
  .portrait {
    order: -1;
    width: 100%;
    max-width: 26rem;
    margin-inline: auto;
  }

  .pub { grid-template-columns: minmax(0, 1fr); gap: 0.2rem; }
  .pub__year { padding-top: 0; }
  .facts > div { grid-template-columns: minmax(0, 1fr); gap: 0.05rem; }
  .cv-list li { grid-template-columns: minmax(0, 1fr); gap: 0.05rem; }
}

/* Short landscape viewports (phone on its side): the hero is still two columns
   here, so cap the portrait to stop it driving the fold down. */
@media (min-width: 48.0625rem) and (max-height: 30rem) and (orientation: landscape) {
  .hero { padding-block: var(--space-s) var(--space-m); }
  .portrait img { aspect-ratio: 3 / 2; }
}

/* Ultrawide: widen the reading column slightly rather than stretching it. */
@media (min-width: 100rem) {
  :root { --container: 74rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* JS also refuses to start the field; this covers the case where the setting
     is turned on after the page has loaded. */
  .hero__particles { display: none; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .site-header,
  .hero__particles,
  .availability,
  .to-top,
  .btn { display: none !important; }

  body { background: #fff; color: #000; font-size: 10.5pt; }
  .section { padding-block: 0.75rem; page-break-inside: avoid; }
  .abstract[open] .abstract__body { display: block; }
  a::after { content: " (" attr(href) ")"; font-size: 8.5pt; color: #444; }
}
