/* Rivnay Group — Laboratory for Organic & Hybrid Bioelectronics
   One stylesheet, no build step. Tokens first, then components in page order. */

:root {
  --purple: #4E2A84;        /* Northwestern purple */
  --purple-dark: #3B1F63;
  --purple-tint: #EFEAF5;
  --rule: #C8C8C8;          /* active nav underline, neutral grey */
  --ink: #1C1826;
  --muted: #625C70;
  --ground: #F7F6F3;
  --paper: #FFFFFF;
  --line: #E3DFEA;
  --sans: "Figtree", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --serif: var(--sans);   /* headings share the body family; swap back to a serif here if wanted */
  --wrap: 1120px;
  --radius: 12px;
  --fs-xs: 0.8rem;      /* captions, tags, fine print */
  --fs-sm: 0.875rem;    /* dates, roles, table heads, small links */
  --fs-md: 0.95rem;     /* secondary body text */
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
:focus-visible { outline: 2px solid var(--purple); outline-offset: 3px; }
/* a link or button that is only a glyph: one square hit target, used by the share row and the
   contact links on a team card */
.ico {
  display: grid; place-items: center; width: 34px; height: 34px; padding: 0;
  color: var(--muted); background: none; border: 0; border-radius: 8px; cursor: pointer;
  font: inherit; transition: color 0.2s, background-color 0.2s;
}
.ico:hover { color: var(--purple); background: var(--purple-tint); }
.ico svg { width: 17px; height: 17px; display: block; }
.ico svg[fill="currentColor"] { width: 15px; height: 15px; }   /* the solid brand marks carry more ink than the outlined ones */
::selection { background: var(--purple-tint); color: var(--ink); }
/* keyboard users reach this before the seven header stops; it stays off-screen until focused */
.skip {
  position: absolute; left: 1.5rem; top: 0.5rem; z-index: 100; transform: translateY(-250%);
  background: var(--paper); color: var(--purple); font-size: var(--fs-sm); font-weight: 500;
  text-decoration: none; padding: 0.6rem 1rem; border: 1px solid var(--line);
  border-radius: var(--radius); transition: transform 0.15s;
}
.skip:focus { transform: none; }
main:focus { outline: none; }   /* the skip target is focused programmatically, not by tabbing */
[id] { scroll-margin-top: 5.5rem; }
/* smooth anchor scrolling is switched on by site.js after load, so a #hash on arrival jumps instead of animating */
@media (prefers-reduced-motion: no-preference) { html.smooth { scroll-behavior: smooth; } }
/* A page turn between pages, where the browser supports it and nothing anywhere else. The default
   is a cross-fade, which ghosts one page of body text over another; these two animations do not
   overlap, so the old page is gone before the new one arrives. The header is named so it holds
   still through the whole thing rather than fading with the rest. */
@view-transition { navigation: auto; }
.site-header { view-transition-name: site-header; }
/* the header swaps outright: cross-fading it shows the old and new nav underlines at once */
::view-transition-old(site-header) { animation: none; opacity: 0; }
::view-transition-new(site-header) { animation: none; }
::view-transition-old(root) { animation: vt-out 90ms cubic-bezier(0.4, 0, 1, 1) both; }
::view-transition-new(root) { animation: vt-in 190ms cubic-bezier(0, 0, 0.2, 1) 90ms both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* scroll reveal: content eases in once, 16px rise; the hidden state exists only after site.js has marked what is already on screen, never in print or under reduced motion */
.reveal-ready .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1), transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); transition-delay: var(--reveal-delay, 0ms); }
.reveal-ready .reveal.is-visible { opacity: 1; transform: none; }
.reveal-ready .reveal.no-anim { transition: none; }
@media print, (prefers-reduced-motion: reduce) { .reveal-ready .reveal { opacity: 1; transform: none; transition: none; } }
body { font-family: var(--sans); font-size: 1.05rem; line-height: 1.6; color: var(--ink); background: var(--ground); }
img, video { max-width: 100%; display: block; height: auto; }
a { color: var(--purple); text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
a:hover { color: var(--purple-dark); }
/* Links that stay on this site carry a hairline chevron instead of an underline, so following the
   site inward reads differently from leaving it. Same chevron the team cards use. */
a.go { text-decoration: none; font-weight: 500; }
a.go::after, .back::before {
  content: ""; display: inline-block; width: 0.34em; height: 0.34em; vertical-align: 0.1em;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
a.go::after { margin-left: 0.15em; }
/* only the standalone links slide; a chevron that moves mid-sentence is a distraction */
.more a.go:hover::after { transform: translateX(2px) rotate(-45deg); }
/* the back link is the same mark, mirrored */
.back::before { margin-right: 0.3em; border: 0; border-left: 1.5px solid currentColor; border-top: 1.5px solid currentColor; }
.back:hover::before { transform: translateX(-2px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { a.go::after, .back::before { transition: none; } }
/* a link that closes a section: one distance and one size, wherever it appears */
.more { margin-top: 2rem; font-size: var(--fs-sm); }
h1, h2, h3, h4 { font-family: var(--sans); font-weight: 700; line-height: 1.15; color: var(--ink); letter-spacing: -0.015em; }
h1 { font-size: clamp(2.2rem, 4.5vw, 3.3rem); }
:root { --hero-h1: clamp(2.2rem, 4.5vw, 3.3rem); }
h2 { font-size: clamp(1.6rem, 3vw, 2.15rem); }
h3 { font-size: 1.25rem; }
strong { font-weight: 600; }
figcaption { font-size: var(--fs-xs); color: var(--muted); margin-top: 0.6rem; line-height: 1.45; }

/* ---------- layout primitives ---------- */
.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: 1.5rem; }
.prose { max-width: none; }   /* body copy fills its column; only a layout narrows it */
.prose p + p, .prose p + ul, .prose p + ol, .prose ul + p, .prose ol + p { margin-top: 1rem; }
.prose h2 { margin: 2.5rem 0 0.8rem; }
.prose h3 { margin: 1.8rem 0 0.5rem; }
.prose > :first-child { margin-top: 0; }
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose li + li { margin-top: 0.3rem; }
.lead { font-size: 1.2rem; line-height: 1.55; }
.muted { color: var(--muted); }
.section { padding-block: 3.75rem; }
.section:last-child { padding-bottom: 0; }   /* the footer already carries 4rem above it */
/* two sections on the same background do not need a full 2x padding between them; a change of
   background (the white band) still gets the full break */
.section:not(.band) + .section:not(.band) { padding-top: 1rem; }
.section > .wrap > h2 { margin-bottom: 1.5rem; }
/* a deck sits closer to its heading than a block of content does, on a section as on a page head */
.section > .wrap > h2:has(+ .intro) { margin-bottom: 0.8rem; }
.band { background: var(--paper); border-block: 1px solid var(--line); }
.section-head { margin-bottom: 1.5rem; }
.section-head p { color: var(--muted); margin-top: 0.8rem; }
.page-head { padding: 3rem 0 1.75rem; }
.page-head h1 { max-width: 22ch; }
.page-head .lead { margin-top: 0.8rem; color: var(--muted); }
.intro { margin-top: 0.8rem; }          /* full-width lead text: no max-width, matching the Research intro */
.intro p + p { margin-top: 1rem; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.site-header .bar {
  max-width: var(--wrap); margin-inline: auto; padding: 0.9rem 1.5rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.brand { display: block; line-height: 0; }
.brand img { height: 30px; width: auto; }
.nav-toggle { display: none; }
.nav-toggle-label { display: none; cursor: pointer; padding: 0.5rem; }
.nav-toggle-label span, .nav-toggle-label span::before, .nav-toggle-label span::after {
  display: block; width: 22px; height: 2px; background: var(--ink); position: relative; content: "";
}
.nav-toggle-label span::before { position: absolute; top: -7px; }
.nav-toggle-label span::after { position: absolute; top: 7px; }
.site-nav ul { list-style: none; display: flex; gap: 1.6rem; align-items: center; }
.site-nav a {
  text-decoration: none; color: var(--ink); font-weight: 500; font-size: var(--fs-md);
  padding: 0.35rem 0; border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--purple); }
.site-nav a[aria-current="page"] { color: var(--purple); border-bottom-color: var(--rule); }
@media (max-width: 860px) {
  .nav-toggle { display: block; position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
  .nav-toggle:focus-visible + .nav-toggle-label { outline: 2px solid var(--purple); outline-offset: 2px; }
  .nav-toggle-label { display: grid; place-items: center; min-width: 44px; min-height: 44px; padding: 0; margin: -0.4rem -0.7rem -0.4rem 0; }
  .site-nav {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--paper); border-bottom: 1px solid var(--line); display: none;
  }
  .nav-toggle:checked ~ .site-nav { display: block; }
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 0; padding: 0.4rem 0; }
  .site-nav a { display: block; padding: 0.75rem 1.5rem; border-bottom: none; }
  .site-nav a[aria-current="page"] { background: var(--purple-tint); }
}

/* ---------- home hero: full-bleed GIWAXS loop ---------- */
.hero {
  position: relative; min-height: 63vh; display: flex; align-items: flex-end;
  background: #0B0910; color: #fff; overflow: hidden;
}
/* The clip (1030x1064) has a dead detector band at 48.3-51.7% of its height and a black bar from 87.3% down.
   The banner shows only the 52.3-86.9% window: the video is scaled so that window fills the banner height
   (min-height = 100% / 0.347) and shifted so the 86.9% line sits on the banner's bottom edge (13.1% of the
   video height hangs below). --focus-x picks which column of the frame is centred when the sides are cropped. */
.hero .hero-bg {
  --focus-x: 50%;
  position: absolute; left: 50%; bottom: 0;
  /* tall enough that the 34.7% window fills the banner, and wide enough to span it; ratio kept exactly */
  height: max(calc(100% / 0.347), calc(100vw / 0.968));
  width: auto; max-width: none; aspect-ratio: 1030 / 1064; object-fit: cover;
  transform: translate(calc(-1 * var(--focus-x)), 13.1%);
}
/* still photos that take turns with the clip; site.js fades one in at a time */
.hero .hero-slide { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 1.6s ease; }
.hero .hero-slide.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .hero .hero-slide { transition: none; } }
.hero::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(11, 9, 16, 0.10) 0%, rgba(11, 9, 16, 0.30) 45%, rgba(11, 9, 16, 0.84) 100%);
}
.hero .wrap { position: relative; z-index: 1; width: 100%; padding-block: 4.5rem 3.5rem; display: grid; grid-template-columns: auto 1fr; align-items: center; }
/* the mark is exactly as tall as the three-line headline: 3 lines x 1.15 line-height x the h1 size */
.hero .mark { height: calc(var(--hero-h1) * 3.45); width: auto; }
.hero .text { align-self: stretch; display: flex; align-items: center; margin-left: 2rem; padding-left: 2rem; border-left: 1px solid rgba(255, 255, 255, 0.55); }
.hero h1 { font-size: var(--hero-h1); }
.hero h1 { color: #fff; max-width: 16ch; }
@media (max-width: 900px) {
  /* narrow screens: pin the frame's right edge to the banner's right edge so the bright arcs stay in view
     and the video always covers the full width whatever the window shape */
  .hero .hero-bg { left: auto; right: 0; transform: translate(0, 13.1%); }
}
@media (max-width: 800px) {
  .hero { min-height: 56vh; flex-direction: column; justify-content: flex-end; }
  /* keep the lockup on phones: size the headline so its widest line ("Organic & Hybrid" = 7.76 x font size)
     plus the mark (2.84 x font size) and the rule gaps fit the viewport */
  :root { --hero-h1: min(2.2rem, calc((100vw - 78px) / 10.9)); }
  .hero .wrap { padding-block: 3rem 2rem; }
  .hero .text { margin-left: 0.9rem; padding-left: 0.9rem; }
}

/* ---------- cards ---------- */
.intro + .card-grid { margin-top: 2rem; }
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.25rem; }
/* four cards: never 3+1. auto-fit gives 2 columns from 528px (2x230+20+48) and 4 from 1028px (4x230+3x20+48); hold 2x2 in between, with room for a classic scrollbar */
@media (min-width: 529px) and (max-width: 1043px) { .card-grid { grid-template-columns: 1fr 1fr; } }
.card {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem;
  display: flex; flex-direction: column; gap: 0.5rem; text-decoration: none; color: inherit;
  transition: border-color 0.15s; overflow: hidden;
}
a.card:hover { border-color: var(--purple); color: inherit; }
.card:has(img) { padding: 0 0 1.5rem; }
.card img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; margin-bottom: 0.75rem; }
.card:has(img) h3, .card:has(img) p { margin-inline: 1.25rem; }
.card h3 { font-size: 1.2rem; }
.card p { color: var(--muted); font-size: var(--fs-md); }

/* ---------- news list ---------- */
.news-list { list-style: none; border-top: 1px solid var(--line); }
.news-list li { display: grid; grid-template-columns: 7.5rem 1fr 104px; gap: 1rem 1.25rem; padding: 1rem 0; border-bottom: 1px solid var(--line); align-items: start; }
.news-list .when { display: flex; flex-direction: column; align-items: flex-start; gap: 0.4rem; padding-top: 0.2rem; }
.news-list time { font-size: var(--fs-sm); color: var(--muted); }
.news-list .when .kind { margin-left: 0; }
.news-list .title { display: inline; font-size: 1.02rem; font-weight: 600; line-height: 1.4; }
.news-list .title a { color: var(--ink); text-decoration: none; }
.news-list .title a:hover { color: var(--purple); }
.news-list .excerpt { color: var(--muted); font-size: var(--fs-md); margin-top: 0.3rem; }
.news-list .excerpt, .post-body { overflow-wrap: anywhere; }
.news-list .thumb img { width: 104px; height: 78px; object-fit: cover; border-radius: 6px; border: 1px solid var(--line); }
.kind {
  display: inline-block; font-size: var(--fs-xs); font-weight: 500; text-transform: capitalize;
  color: var(--purple); background: var(--purple-tint); padding: 0.1rem 0.5rem; border-radius: 4px;
  margin-left: 0.5rem; vertical-align: 0.1em;
}
@media (max-width: 600px) {
  .news-list li { grid-template-columns: 1fr; gap: 0.2rem; }
  .news-list .when { flex-direction: row; align-items: center; gap: 0.55rem; padding-top: 0; }
  .news-list .thumb { display: none; }
}

/* ---------- recent publications (home) ---------- */
.recent-pubs { list-style: none; border-top: 1px solid var(--line); }
.recent-pubs li { display: grid; grid-template-columns: 4.5rem 1fr; gap: 0.25rem 1.25rem; padding: 0.9rem 0; border-bottom: 1px solid var(--line); align-items: start; }
.recent-pubs .year { font-size: var(--fs-sm); color: var(--muted); padding-top: 0.2rem; }
.recent-pubs .title { font-weight: 600; color: var(--ink); text-decoration: none; line-height: 1.4; }
.recent-pubs a.title:hover { color: var(--purple); }
.recent-pubs .venue { font-size: var(--fs-md); color: var(--muted); margin-top: 0.15rem; }
@media (max-width: 600px) { .recent-pubs li { grid-template-columns: 1fr; gap: 0.1rem; } }

/* ---------- team ---------- */
/* The group photo gets a screen to itself. Two proximity snap points, the photo and the roster, mean a small
   scroll settles back onto the photo and a firmer one carries past it. Native snapping: the scroll itself is
   never intercepted, so the wheel, trackpad, scrollbar and keyboard all behave normally. */
html.snap { scroll-snap-type: y proximity; }
.team-stage {
  min-height: calc(100vh - var(--header-h, 61px));
  min-height: calc(100svh - var(--header-h, 61px));   /* phones: exclude the collapsible browser chrome */
  display: flex; flex-direction: column; justify-content: center;
  scroll-snap-align: start; scroll-margin-top: var(--header-h, 61px);
  padding-bottom: 2rem;
}
.team-stage .page-head { padding-top: 0; }
.team { margin: 0; }
.team img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
.roster { scroll-snap-align: start; scroll-margin-top: var(--header-h, 61px); padding-top: 1rem; }
@media (prefers-reduced-motion: reduce) { html.snap { scroll-snap-type: none; } }
.group { margin-top: 3rem; }
.group:first-of-type { margin-top: 0; }
.group h2 { margin-bottom: 1.5rem; }
.pi {
  display: grid; grid-template-columns: 200px 1fr; gap: 2.25rem; align-items: start;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.75rem;
}
.pi img { width: 200px; aspect-ratio: 1; border-radius: var(--radius); object-fit: cover; border: 1px solid var(--line); }
.pi h3 { font-size: clamp(1.35rem, 2.6vw, 1.7rem); }
.pi .role { color: var(--muted); margin-top: 0.15rem; }
.pi .links { margin-top: 0.4rem; font-size: var(--fs-md); }
.pi .prose { margin-top: 1rem; font-size: 0.98rem; }
@media (max-width: 700px) { .pi { grid-template-columns: 1fr; padding: 1.25rem; } .pi img { width: 140px; } }

.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(162px, 1fr)); gap: 2.5rem 2rem; }  /* min matches the headshot; the wide column gap keeps a long name clear of its neighbour */
.person { min-width: 0; }
.person summary { list-style: none; cursor: pointer; display: block; }
.person summary::-webkit-details-marker { display: none; }
.person img { width: 162px; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius); border: 1px solid var(--line); background: var(--paper); }  /* fixed, so every section matches */
.person .name { display: block; font-family: var(--sans); font-weight: 600; font-size: 1.06rem; line-height: 1.25; margin-top: 0.8rem; margin-right: -1.5rem; }  /* the chevron hangs into the gutter, so names wrap on their own width */
.person .tail { white-space: nowrap; }
.person .name { text-wrap: balance; }   /* if a name must wrap, split it evenly rather than leaving one word alone */
.person .role { display: block; color: var(--muted); font-size: var(--fs-sm); margin-top: 0.15rem; }
/* the name is the control: a hairline chevron sits beside it and turns over when the card opens */
details.person .chev {
  display: inline-block; width: 0.42rem; height: 0.42rem; margin-left: 0.5rem;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: translateY(-0.16rem) rotate(45deg);
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.2s;
}
details.person[open] .chev { transform: translateY(0.06rem) rotate(-135deg); }
.person summary { color: inherit; transition: color 0.2s; }
.person summary:hover .name, .person summary:focus-visible .name { color: var(--purple); }
.person summary:hover .chev { border-color: var(--purple); }
.person summary img { transition: border-color 0.2s; }
.person summary:hover img { border-color: var(--purple); }
@media (prefers-reduced-motion: reduce) { details.person .chev, .person summary img { transition: none; } }
.person[open] {
  grid-column: 1 / -1; display: grid; grid-template-columns: 162px 1fr; gap: 1rem 2rem; align-items: start;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem;
}
.person .bio { font-size: var(--fs-md); }
.person .bio .role { margin: 0 0 0.6rem; }
.person .bio p + p { margin-top: 0.8rem; }
.person .links { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; font-size: var(--fs-sm); margin-top: 0.9rem; }
/* the glyphs sit inside a 34px square, so pull the row left when it starts with one and the
   icon lines up with the text above it rather than sitting 8px inside */
.person .links:has(> .ico:first-child) { margin-left: -0.5rem; }
.person.no-bio .links { margin-top: 0.4rem; }
@media (max-width: 600px) {
  .people-grid { grid-template-columns: 1fr; gap: 0; border-top: 1px solid var(--line); }
  .person { border-bottom: 1px solid var(--line); padding: 0.85rem 0; }
  .person summary, .person.no-bio { display: grid; grid-template-columns: 64px 1fr; gap: 1rem; align-items: center; }
  .person img { width: 64px; }
  .person .name { margin-top: 0; font-size: 1rem; }
  .person[open] { grid-template-columns: 1fr; gap: 0.75rem; padding: 0.85rem 0; background: transparent; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; }
  .person[open] summary { grid-template-columns: 64px 1fr; }
  .person.no-bio .links { grid-column: 2; margin-top: 0; }
}
.alumni table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: var(--fs-md); margin-top: 0.6rem; }
.alumni col.name { width: 38%; }
.alumni col.year { width: 6.5rem; }
.alumni th, .alumni td { text-align: left; padding: 0.5rem 0.8rem 0.5rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.alumni th { font-size: var(--fs-sm); font-weight: 600; color: var(--muted); }
.alumni td.year { color: var(--muted); }
@media (max-width: 600px) { .alumni col.name { width: 36%; } .alumni col.year { width: 3.6rem; } }

/* ---------- publications ---------- */
.pub-year { display: grid; grid-template-columns: 6rem 1fr; gap: 1.5rem; padding-top: 2.5rem; }
.pub-year:first-of-type { padding-top: 0; }
.pub-year h2 { font-size: 1.5rem; align-self: start; }
.pub-list { list-style: none; }
.pub { padding: 1rem 0; border-bottom: 1px solid var(--line); font-size: var(--fs-md); }
.pub:first-child { padding-top: 0; }
.pub .title { font-weight: 600; display: block; color: var(--ink); text-decoration: none; font-size: 1.02rem; line-height: 1.4; }
a.title:hover { color: var(--purple); }
.pub .authors { color: var(--muted); margin-top: 0.25rem; }
.pub .authors strong { color: var(--ink); }
.pub .venue { margin-top: 0.15rem; }
.pub .doi { font-size: var(--fs-xs); color: var(--muted); margin-top: 0.2rem; }
@media (max-width: 640px) {
  .pub-year { grid-template-columns: 1fr; gap: 0.5rem; }
  details.collapsed .pub-list { margin-left: 0; }
}

/* year timeline: a fixed rail in the right gutter on wide screens, a sticky strip under the header elsewhere */
.years { position: sticky; top: var(--header-h, 61px); z-index: 40; display: flex; gap: 1.1rem; overflow-x: auto; scrollbar-width: none; background: var(--ground); border-bottom: 1px solid var(--line); padding: 0.6rem 1.5rem; margin: 0 -1.5rem 1.25rem; scroll-padding-inline: 1.5rem; }
.years::-webkit-scrollbar { display: none; }
.years a { flex: none; font-size: var(--fs-sm); color: var(--muted); text-decoration: none; line-height: 1; padding: 0.15rem 0 0.35rem; border-bottom: 2px solid transparent; transition: color 0.35s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.35s cubic-bezier(0.4, 0, 0.2, 1); }
.years a:hover { color: var(--ink); }
.years a.is-active { color: var(--purple); border-color: var(--purple); }
.years-marker { display: none; }
.pub-year[id] { scroll-margin-top: calc(var(--header-h, 61px) + 3.4rem); }
@media (min-width: 1260px) {
  .years { position: fixed; top: 50%; right: max(1rem, calc((100vw - var(--wrap)) / 2 - 4.25rem)); transform: translateY(-50%); flex-direction: column; gap: 0.55rem; overflow: visible; background: transparent; border: 0; padding: 0.2rem 0 0.2rem 1rem; margin: 0; transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1), transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); }
  .years::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background: var(--rule); }
  .years a { font-size: var(--fs-xs); padding: 0.1rem 0; border: 0; }
  .years a.is-active { border: 0; }
  .pub-year h2 { position: sticky; top: 5rem; }
  .js .years-marker { display: block; position: absolute; left: -2.5px; top: 0; width: 6px; height: 6px; border-radius: 50%; background: var(--purple); transform: translateY(-50%); transition: top 0.35s cubic-bezier(0.4, 0, 0.2, 1); }
  .js .years:not(.is-shown):not(:focus-within) { opacity: 0; transform: translate(0.5rem, -50%); pointer-events: none; }
  .pub-year[id] { scroll-margin-top: 5.5rem; }
}
@media (prefers-reduced-motion: reduce) { .years, .years a, .years-marker { transition: none !important; } }
details.collapsed { margin-top: 3rem; border-top: 1px solid var(--line); padding-top: 1.5rem; }
details.collapsed summary { cursor: pointer; font-family: var(--sans); font-size: 1.4rem; font-weight: 700; color: var(--purple); }
details.collapsed .pub-list { margin-top: 1rem; margin-left: 7.5rem; }   /* line up with the year sections above */

/* ---------- research ---------- */
.theme { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: 3rem; align-items: start; padding-block: 3.75rem; border-top: 1px solid var(--line); }
.theme:first-of-type { border-top: 0; padding-top: 0.5rem; }
.theme + .section { border-top: 1px solid var(--line); }   /* Support gets the same rule the themes use */
.theme .tagline { color: var(--muted); font-size: 1.08rem; margin: 0.8rem 0 1.2rem; }
.theme .prose { margin-top: 1.2rem; }   /* collapses with the tagline, so the gap under the heading is the same either way */
.theme .prose h3 { font-size: 1.15rem; }
/* the paper list spans the whole theme, under both the prose and the figures, rather than being
   squeezed into the text column */
.theme-pubs { grid-column: 1 / -1; margin-top: 1rem; }
.theme-pubs h3 { font-size: var(--fs-sm); font-weight: 600; color: var(--muted); margin-bottom: 0.9rem; }
.theme-pubs ul { list-style: none; display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1.5rem; }
.theme-pubs li { font-size: var(--fs-md); border-top: 1px solid var(--line); padding-top: 0.7rem; }
.theme-pubs a { display: block; color: var(--ink); text-decoration: none; font-weight: 600; line-height: 1.4; }
.theme-pubs a:hover { color: var(--purple); }
.theme-pubs .venue { display: block; color: var(--muted); font-size: var(--fs-sm); margin-top: 0.25rem; }
.theme figure img, .theme figure video { border-radius: var(--radius); width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border: 1px solid var(--line); background: var(--paper); }
.theme figure.square img, .theme figure.square video { aspect-ratio: 1; }
.theme figure.natural img, .theme figure.natural video { aspect-ratio: auto; height: auto; }
.media-stack { display: grid; gap: 1.25rem; }
/* two figures side by side, so a pair of small images costs a fraction of the column height */
.figure-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.figure-pair figure img { aspect-ratio: 1; }
.media-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.media-pair img { aspect-ratio: 1; }
@media (max-width: 800px) { .theme { grid-template-columns: 1fr; gap: 1.5rem; } }
/* Each logo gets an equal cell and is fitted inside it, so an ornate round seal and a wide
   wordmark carry similar weight instead of one being seven times the width of the other.
   The two widest lockups take a double cell so their type stays readable. */
/* Each mark sits in a fixed-height band above its name, so the row of names lines up however
   uneven the logos are. Heights come from support.yml and equalise visual area, not height:
   a round seal has to be taller than a wordmark to read as the same size. */
/* Equal cells, every mark flush to the same left edge, each in a fixed-height band so rows line up.
   Mark heights come from support.yml and equalise visual area, not height: a round seal must be
   taller than a wordmark to read as the same size. */
h3:has(+ .logo-row) { margin-top: 2.5rem; }   /* Funding and Facilities sit the same distance from what precedes them */
.logo-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.75rem 1.5rem; margin-top: 1.5rem; }
.logo-row a { display: grid; align-items: center; justify-items: start; height: 108px; min-width: 0; }
.logo-row img { max-height: var(--h, 68px); max-width: 100%; width: auto; height: auto; }
@media (max-width: 560px) {
  .logo-row { gap: 1.25rem 1rem; }
  .logo-row a { height: 88px; }
  .logo-row img { max-height: calc(var(--h, 68px) * 0.82); }
}

/* ---------- post ---------- */
/* an article is one centred reading column: the wrap itself is narrowed, so every part shares the
   measure without per-element caps, and the column sits centred rather than stranded on the left */
article.wrap { max-width: calc(42rem + 3rem); }
.post-head { padding: 3rem 0 1.75rem; }
.post-head time { font-size: var(--fs-sm); color: var(--muted); }
.post-head h1 { margin-top: 0.5rem; font-size: clamp(1.8rem, 3.5vw, 2.6rem); }
.post-figure { margin: 1rem 0 2rem; }
.post-figure figcaption { margin-top: 0.6rem; }
.post-figure img { border-radius: var(--radius); border: 1px solid var(--line); width: 100%; height: auto; }
.post-body img { border-radius: var(--radius); margin: 1.75rem 0; border: 1px solid var(--line); width: 100%; height: auto; }
.post-body figure { margin: 1.75rem 0; }
.post-body figure img, .post-body .pair img { margin: 0; }
.post-body .pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin: 1.75rem 0 0; }
.post-body .figcap { font-size: var(--fs-xs); color: var(--muted); margin-top: 0.6rem; line-height: 1.45; }
.back { display: inline-block; margin-top: 2rem; font-size: var(--fs-sm); text-decoration: none; font-weight: 500; }
.post-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem 1.5rem; flex-wrap: wrap; }
.post-foot .back { margin-top: 2rem; }
.share { display: flex; align-items: center; gap: 0.35rem; margin-top: 2rem; }
.share-label { font-size: var(--fs-sm); color: var(--muted); margin-right: 0.4rem; }
.share .copy-link.is-copied { color: var(--purple); }
@media (prefers-reduced-motion: reduce) { .ico, .skip { transition: none; } }

/* ---------- footer ---------- */
.site-footer { background: #E9E9E9; color: var(--ink); margin-top: 4rem; border-top: 1px solid #DCDCDC; }
.site-footer .wrap { padding-block: 3rem 1.75rem; display: grid; grid-template-columns: 1.2fr 1fr 1fr auto; gap: 2.5rem; align-items: start; }
.site-footer h2 { font-family: var(--sans); font-size: var(--fs-sm); font-weight: 600; color: var(--ink); margin-bottom: 0.4rem; letter-spacing: 0; }
.site-footer p + h2 { margin-top: 1.2rem; }
.site-footer a { color: var(--purple); }
.site-footer .brand img { height: 26px; }
.site-footer .brand + p { margin-top: 0.9rem; }
.site-footer p, .site-footer address { font-size: var(--fs-md); font-style: normal; line-height: 1.55; }
.site-footer .nu { display: block; line-height: 0; padding-top: 0.15rem; }
.site-footer .nu img { height: 24px; width: auto; }
.site-footer .fine { grid-column: 1 / -1; border-top: 1px solid #D4D4D4; padding-top: 1.1rem; font-size: var(--fs-xs); color: #555; }
@media (max-width: 860px) { .site-footer .wrap { grid-template-columns: 1fr 1fr; } .site-footer .nu { grid-column: 1 / -1; } }
@media (max-width: 560px) { .site-footer .wrap { grid-template-columns: 1fr; gap: 1.75rem; } }
