/* GENERATED by tools/build_site.py from tokens.css + tools/site.css.
   Do not edit this file: edit those two and rebuild. */
/* ============================================================================
   tokens.css — design tokens for yueyang-zhong.com
   ----------------------------------------------------------------------------
   Written 2026-09-20. Companion to DESIGN.md. Consumed by Task 7.

   GATE B IS DECIDED: the chosen direction is SAMPLE PATH. The Broadsheet and
   Plate theme blocks have been deleted. They remain recoverable from
   _design\mockup-1.html and _design\mockup-3.html, which inline the
   three-theme version of this file and are kept on disk as the record.

   HOW THIS FILE IS ORGANISED

   Part 1  Invariants. Spacing, motion, focus, layout and z-index. Structural
           rather than stylistic, and unchanged from the three-direction
           version of this file.

   Part 2  The Sample Path theme: font stacks, type scale, colour palette,
           density and border rules, scoped to [data-design="samplepath"].
           Set it on the root element:  <html lang="en" data-design="samplepath">

   No @import, no @font-face, no CDN, no webfont. Every font stack below
   resolves against fonts already on the reader's machine, so this file has
   zero network dependencies and the site ships zero font bytes. See
   DESIGN.md §3.4 for why Sample Path in particular does not need one.

   Every colour pair is measured, not asserted. Run
   `venv\Scripts\python.exe _design\contrast.py` after changing any colour;
   it exits non-zero if a pair drops below its WCAG 2.1 AA threshold.
   ========================================================================= */

/* ============================================================================
   PART 1 — INVARIANTS
   ========================================================================= */

:root {
  /* --- Spacing scale ------------------------------------------------------
     A single 8px-rooted scale with two sub-8 steps for inline gaps. Named by
     t-shirt size so a component never invents a number. Anything not on this
     scale is a bug, with one exception: optical alignment of the motif, which
     is documented inline where it occurs. */
  --space-3xs: 0.25rem; /*  4px */
  --space-2xs: 0.5rem; /*  8px */
  --space-xs: 0.75rem; /* 12px */
  --space-s: 1rem; /* 16px */
  --space-m: 1.5rem; /* 24px */
  --space-l: 2rem; /* 32px */
  --space-xl: 3rem; /* 48px */
  --space-2xl: 4rem; /* 64px */
  --space-3xl: 6rem; /* 96px */
  --space-4xl: 8rem; /* 128px */

  /* --- Layout -------------------------------------------------------------
     --measure is the reading measure for prose. The theme overrides it; the
     value here is the fallback. --gutter grows with the viewport so the
     390px case is not starved and the 1440px case is not cramped. */
  --measure: 66ch;
  --measure-tight: 58ch;
  --page-max: 72rem;
  --gutter: clamp(1.25rem, 4vw, 3rem);

  /* Clears the sticky masthead when an anchor is targeted. IA.md §2.3.1 makes
     this mandatory: without it the browser scrolls a paper entry underneath
     the header and the anchor arrival lands on the wrong paper. */
  --anchor-offset: 5.5rem;

  /* --- Motion -------------------------------------------------------------
     Four durations and one signature. Anything longer than --motion-slow on
     an interaction is a delay the reader can feel and did not ask for. */
  --motion-instant: 90ms;
  --motion-fast: 160ms;
  --motion-base: 240ms;
  --motion-slow: 420ms;
  --motion-signature: 2600ms; /* the hero motif draw, once, on load */
  --motion-signature-delay: 250ms;

  --ease-out: cubic-bezier(0.22, 0.7, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-step: steps(1, end);

  /* --- Focus --------------------------------------------------------------
     One focus treatment across the whole site. Colour comes from the theme;
     geometry does not, because an inconsistent focus ring is a usability bug
     rather than a style choice. */
  --focus-width: 3px;
  --focus-offset: 2px;

  /* --- Elevation ----------------------------------------------------------
     Deliberately minimal. This site separates things with rules and space,
     not shadows; a drop shadow on an academic paper list reads as a product
     dashboard. --elev-1 exists only for chrome that floats over content
     (the mobile jump bar), and nothing else may use it. */
  --elev-0: none;
  --elev-1: 0 1px 2px rgb(0 0 0 / 0.06), 0 4px 12px rgb(0 0 0 / 0.05);

  /* --- Stacking ----------------------------------------------------------- */
  --z-base: 0;
  --z-sticky: 10;
  --z-masthead: 20;
  --z-skiplink: 100;
}

/* Reduced motion is honoured at the token level, so a component that forgets
   to write its own media query still gets the right behaviour. The signature
   motif goes to zero duration, which means it renders in its final, complete
   state — not that it disappears. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-instant: 1ms;
    --motion-fast: 1ms;
    --motion-base: 1ms;
    --motion-slow: 1ms;
    --motion-signature: 1ms;
    --motion-signature-delay: 0ms;
  }
}

/* ============================================================================
   PART 2 — THEME: SAMPLE PATH

   Type role names, in the order they appear in an entry:

     --font-display / --font-body / --font-meta   font stacks
     --t-display  page H1
     --t-h2       direction heading (I. Behavioral and strategic queueing)
     --t-h3       paper title
     --t-body     overview, takeaway
     --t-small    author line, abstract
     --t-meta     third line (status), credit bullets, link row
     --t-micro    letterspaced labels and counts

   Each --t-* role has a matching --lh-* line height and --ls-* letter spacing
   where it differs from normal.
   ========================================================================= */

[data-design="samplepath"] {
  /* Fonts — the platform UI sans, plus the platform monospace for anything
     that is a value rather than a word. ui-monospace and the Cascadia/Consolas
     fallbacks all carry tabular figures, which is the point: the third line,
     years, page ranges and counts line up in a column. */
  --font-display: system-ui, -apple-system, "Segoe UI Variable Display",
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-body: var(--font-display);
  --font-meta: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
    "Segoe UI Mono", Consolas, "Liberation Mono", Menlo, monospace;

  /* Type scale — 1.2 (minor third) off a 15px base. This direction buys its
     legibility from alignment and whitespace discipline rather than size. */
  --t-display: clamp(1.875rem, 1.4rem + 2vw, 2.875rem); /* 30 → 46 */
  --t-h2: clamp(1.1875rem, 1.05rem + 0.6vw, 1.4375rem); /* 19 → 23 */
  --t-h3: 1.0625rem; /* 17 */
  --t-body: 0.9375rem; /* 15 */
  --t-small: 0.875rem; /* 14 */
  --t-meta: 0.8125rem; /* 13 */
  --t-micro: 0.6875rem; /* 11 */

  --lh-display: 1.05;
  --lh-h2: 1.25;
  --lh-h3: 1.35;
  --lh-body: 1.6;
  --lh-small: 1.5;
  --lh-meta: 1.35;

  --ls-display: -0.025em;
  --ls-h2: -0.01em;
  --ls-micro: 0.08em;

  --weight-body: 400;
  --weight-medium: 550;
  --weight-bold: 680;

  /* Colour — cool neutrals, one teal signal, one dark band.
     Ratios measured in _design/contrast.json. */
  --c-bg: #f6f7f8;
  --c-bg-sunk: #eef0f2;
  --c-card: #ffffff;
  --c-ink: #14181c; /* 17.84:1 on --c-card */
  --c-ink-muted: #565e66; /*  6.59:1 on --c-card */
  --c-ink-faint: #565e66;
  --c-rule: #dce0e4; /*  1.33:1 — decorative only */
  --c-rule-strong: #838c95; /*  3.18:1 — control boundaries */
  --c-accent: #0a5f5a; /*  7.51:1 on --c-card */
  --c-accent-hover: #073f3c; /* 11.76:1 */
  --c-award-ink: #6b4a00; /*  7.13:1 on --c-card — award bullet marker */
  --c-target-tint: #e8f4f3; /* 15.86:1 for --c-ink */
  --c-focus: var(--c-accent);

  /* The 3px status edge on an entry. Gate B moved status into the third line
     as words, so these two are now a redundant second channel, not the only
     one. "In progress" deliberately has no colour of its own: it takes
     --c-rule, so the edge is present but says nothing louder than the text. */
  --c-state-published: #0a5f5a; /*  7.51:1 on --c-card */
  --c-state-review: #8a4b00; /*  6.80:1 on --c-card */

  /* Dark hero band. LBS navy (PMS 282 C, RGB 0 26 71) since 2026-09-22 by the
     author's ruling, replacing the near-black #0d1117; the three inks on it
     were re-measured by _design/contrast.py and all stay above AA. */
  --c-band: #001a47;
  --c-band-ink: #e8ebee; /* 14.17:1 on --c-band */
  --c-band-muted: #a3acb6; /*  7.37:1 */
  --c-band-accent: #4fd1c5; /*  9.09:1 — the motif stroke */

  /* Density and shape.
     --measure was 62ch until 2026-09-22 (review round 4): the author found every
     prose block "split into too many unnecessary lines" and asked for boxes
     wider by half. 93ch = 1.5 x 62ch; it still sits inside every column it is
     used in, so on narrow viewports the column, not the measure, governs. */
  --measure: 93ch;
  --entry-gap: 0; /* rows meet on a shared hairline */
  --entry-pad: var(--space-s);
  --section-gap: var(--space-2xl);
  --rail-width: 15rem;
  --radius: 4px;
  --radius-chip: 3px;
  --border-hair: 1px;
  --border-emphasis: 2px;
  --target-rule: 3px;
}
/* ============================================================================
   site.css — components for yueyang-zhong.com
   ----------------------------------------------------------------------------
   Concatenated after tokens.css by tools/build_site.py, which writes the single
   served stylesheet. Edit this file or tokens.css, never site/styles.css.

   Two halves:

   PART A  The reset, the page chrome (masthead, hero band, footer) and the
           research page. Ported verbatim from RESET_CSS and SAMPLEPATH_CSS in
           _design/build_mockups.py, which produced the render the author signed
           off. Do not restyle anything here; DESIGN.md is the contract and
           _design/mockup-2.html is the reference.

   PART B  The four pages that had no mockup: home, teaching, service, bio.
           These add no new colours, no new type sizes and no new spacing
           values. Every rule below composes tokens.css and reuses the research
           page's own conventions: monospace for anything that is a value
           rather than a word, hairlines instead of shadows, and one measure for
           prose.

   PART C  Print.

   No @import. No webfont: the design is system stacks by decision, so the site
   ships zero font bytes. No url() of any kind, so the stylesheet requests
   nothing.
   ========================================================================= */

/* ============================================================================
   PART A1 — reset
   ========================================================================= */

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0}
img,svg{display:block;max-width:100%}
a{color:inherit}
h1,h2,h3,p,ul,ol,dl,figure{margin:0}
ul{padding:0;list-style:none}
:focus-visible{outline:var(--focus-width) solid var(--c-focus);
 outline-offset:var(--focus-offset)}
.skip{position:absolute;left:-9999px;top:0;z-index:var(--z-skiplink);
 padding:.75rem 1rem;background:var(--c-bg);color:var(--c-ink)}
.skip:focus{left:0}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);
 white-space:nowrap}
article{scroll-margin-top:var(--anchor-offset)}
section{scroll-margin-top:var(--anchor-offset)}
summary{cursor:pointer}
summary::-webkit-details-marker{display:none}

/* ============================================================================
   PART A2 — page chrome and the research page (Sample Path, as signed off)
   ========================================================================= */

body{background:var(--c-bg);color:var(--c-ink);font-family:var(--font-body);
 font-size:var(--t-body);line-height:var(--lh-body);
 -webkit-font-smoothing:antialiased}
.wrap{max-width:var(--page-max);margin:0 auto;padding:0 var(--gutter)}

.masthead{position:sticky;top:0;z-index:var(--z-masthead);
 background:color-mix(in srgb,var(--c-bg) 88%,transparent);
 backdrop-filter:saturate(140%) blur(8px);
 border-bottom:var(--border-hair) solid var(--c-rule)}
.masthead .wrap{display:flex;flex-wrap:wrap;gap:var(--space-2xs) var(--space-m);
 align-items:center;justify-content:space-between;min-height:3.25rem}
.wordmark{font-weight:var(--weight-bold);font-size:var(--t-small);
 letter-spacing:-.01em;text-decoration:none}
.masthead nav{display:flex;flex-wrap:wrap;gap:var(--space-xs) var(--space-s)}
.masthead nav a{font-size:var(--t-meta);text-decoration:none;
 color:var(--c-ink-muted);padding:.3rem 0;
 border-bottom:var(--border-emphasis) solid transparent}
.masthead nav a:hover{color:var(--c-ink)}
.masthead nav a[aria-current]{color:var(--c-ink);
 border-bottom-color:var(--c-accent)}

/* The sticky masthead over a dark band: at the top of the page the blurred
   light background would sit on top of the band. It is only sticky once it has
   left, so the band supplies its own top padding and the masthead is
   transparent until it detaches -- which is what `position:sticky` already
   does. Nothing to add; noted so it is not "fixed" later. */

/* Signature motif: the queue-length sample path, drawn once with a CSS
   stroke-dash animation. No JS. The path is complete in the markup, so with
   animation suppressed the figure is simply already finished. */
.band{background:var(--c-band);color:var(--c-band-ink);position:relative;
 overflow:hidden;padding:var(--space-2xl) 0 0}
.band h1{font-size:var(--t-display);line-height:var(--lh-display);
 letter-spacing:var(--ls-display);font-weight:var(--weight-bold)}
.band .kicker{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;
 color:var(--c-band-accent);margin-bottom:var(--space-xs)}
/* The label above the statement ("Research interest"), set like the kicker but
   in the muted band colour so the accent kicker stays the single signal. */
.band .olabel{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;
 color:var(--c-band-muted);margin-top:var(--space-m)}
.band .overview{max-width:var(--measure);margin-top:var(--space-2xs);
 color:var(--c-band-muted);font-size:var(--t-body)}
/* Review round 1 deleted the hero totals line and the motif caption. The
   caption used to supply the band's bottom padding, so the motif carries it
   now; without this the sample path runs into the edge of the dark band. */
.motif{margin-top:var(--space-l);line-height:0;
 padding-bottom:var(--space-l)}
.motif svg{width:100%;height:auto}
.motif .grid{stroke:rgb(255 255 255 / .1);stroke-width:1}
.motif .path{fill:none;stroke:var(--c-band-accent);stroke-width:2;
 stroke-linejoin:round;stroke-dasharray:2048;stroke-dashoffset:2048;
 animation:draw var(--motion-signature) var(--ease-out)
  var(--motion-signature-delay) forwards}
@keyframes draw{to{stroke-dashoffset:0}}
@media (prefers-reduced-motion:reduce){
  .motif .path{animation:none;stroke-dashoffset:0}
}

/* Two-column: sticky direction rail, then the list.
   min-width:0 on both tracks is load-bearing, not defensive. A grid item's
   default min-width is auto, so the rail's nowrap direction links would set a
   min-content width of ~1000px and push the whole page into horizontal
   scroll at 390px. With min-width:0 the rail scrolls internally instead. */
.layout{display:grid;gap:var(--space-xl);padding:var(--space-xl) 0 var(--space-2xl);
 grid-template-columns:minmax(0,1fr)}
.layout>*{min-width:0}
@media (min-width:64rem){
  .layout{grid-template-columns:var(--rail-width) minmax(0,1fr);
   gap:var(--space-2xl)}
  .rail{position:sticky;top:5rem;align-self:start}
}
.rail h2{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;
 color:var(--c-ink-muted);margin-bottom:var(--space-xs)}
.jumpbar ul{display:flex;gap:var(--space-2xs);overflow-x:auto;
 padding-bottom:var(--space-2xs);scrollbar-width:thin}
@media (min-width:64rem){
  .jumpbar ul{flex-direction:column;overflow:visible}
}
.jumpbar a{display:flex;gap:.55em;align-items:baseline;text-decoration:none;
 font-size:var(--t-meta);padding:.4rem .6rem;border-radius:var(--radius);
 border:var(--border-hair) solid var(--c-rule);background:var(--c-card);
 white-space:nowrap;transition:border-color var(--motion-fast) var(--ease-out)}
@media (min-width:64rem){.jumpbar a{white-space:normal}}
.jumpbar a:hover{border-color:var(--c-rule-strong)}
.jb-num{font-family:var(--font-meta);color:var(--c-accent);
 font-weight:var(--weight-bold)}
.jb-name{flex:1}
/* No paper count in the rail since 2026-09-22 (author ruling). */
.legend{margin-top:var(--space-m);font-size:var(--t-micro);
 color:var(--c-ink-muted);line-height:1.5}

/* The Methodology / Application pairs and the Topics list that open the
   column (2026-09-22). Same vocabulary as the entries: mono for labels,
   hairline rules, one measure. */
.rintro{margin-bottom:var(--section-gap)}
.facets{display:grid;gap:var(--space-xs)}
.facets>div{display:grid;gap:var(--space-3xs) var(--space-m);
 padding:var(--space-xs) 0;border-top:var(--border-hair) solid var(--c-rule)}
.facets>div:last-child{border-bottom:var(--border-hair) solid var(--c-rule)}
@media (min-width:40rem){.facets>div{grid-template-columns:8.5rem minmax(0,1fr)}}
.facets dt{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;color:var(--c-ink-muted);
 padding-top:.25em}
.facets dd{margin:0}
.topicshead{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;color:var(--c-ink-muted);
 margin-top:var(--space-l)}
/* The (I)-(IV) numerals are set by hand, so the <ol> must not add its own
   marker on top; the column is sized for the widest numeral, "(III)". */
.topics{margin-top:var(--space-xs);display:grid;gap:var(--space-xs);
 list-style:none;padding:0}
.topics li{display:grid;grid-template-columns:3.1rem 1fr;column-gap:.35em;
 align-items:baseline}
.tnum{font-family:var(--font-meta);font-size:var(--t-meta);color:var(--c-accent);
 font-weight:var(--weight-bold)}
.tname{font-weight:var(--weight-bold);text-decoration:none;
 text-underline-offset:.15em}
.tname:hover{color:var(--c-accent);text-decoration:underline}

.direction{margin-bottom:var(--section-gap)}
.direction>header{margin-bottom:var(--space-s)}
.direction h2{font-size:var(--t-h2);line-height:var(--lh-h2);
 letter-spacing:var(--ls-h2);font-weight:var(--weight-bold);
 display:flex;gap:.5em;align-items:baseline}
.dnum{font-family:var(--font-meta);font-size:var(--t-meta);
 color:var(--c-accent);font-weight:var(--weight-bold);
 border:var(--border-hair) solid var(--c-accent);border-radius:var(--radius-chip);
 padding:.1em .45em;letter-spacing:.04em}
/* No .orienting line under a direction heading since 2026-09-22. */

.list{background:var(--c-card);border:var(--border-hair) solid var(--c-rule);
 border-radius:var(--radius);overflow:hidden}
.entry{padding:var(--entry-pad);position:relative;
 border-top:var(--border-hair) solid var(--c-rule)}
.entry:first-child{border-top:0}
.entry::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
 background:var(--state-colour)}
.entry[data-state="published"]{--state-colour:var(--c-state-published)}
.entry[data-state="review"]{--state-colour:var(--c-state-review)}
.entry[data-state="progress"]{--state-colour:var(--c-rule)}
.entry h3{font-size:var(--t-h3);line-height:var(--lh-h3);
 font-weight:var(--weight-medium);padding-left:var(--space-xs)}
.entry>*:not(h3){padding-left:var(--space-xs)}
.authors{font-size:var(--t-small);color:var(--c-ink-muted);
 margin-top:var(--space-3xs)}
.au-self{font-weight:var(--weight-bold);color:var(--c-ink)}
.au-student{text-decoration:underline;text-decoration-style:dotted;
 text-underline-offset:.18em}
/* The third line. One slot, one treatment, whatever the state: a published
   paper's citation and a working paper's "Work in progress" are the same font,
   size and colour, because they answer the same question. */
.statusline{font-family:var(--font-meta);font-size:var(--t-meta);
 font-variant-numeric:tabular-nums;color:var(--c-ink-muted);
 margin-top:var(--space-3xs)}

/* Awards, coverage and funding as visible bullets. They used to sit below a
   one-line summary; review round 1 deleted that slot, so they now follow the
   status line directly. The award marker is the one place this list uses a
   second colour, so the strongest credential is findable without a pill. */
.credits{margin-top:var(--space-2xs);max-width:var(--measure);
 display:grid;gap:var(--space-3xs)}
.credit{position:relative;padding-left:1.1em;font-size:var(--t-meta);
 line-height:1.55;color:var(--c-ink)}
.credit::before{content:"";position:absolute;left:0;top:.55em;
 width:.34em;height:.34em;background:var(--c-ink-muted)}
.credit-award{color:var(--c-ink)}
.credit-award::before{background:var(--c-award-ink)}
.credit-k{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:.06em;text-transform:uppercase;color:var(--c-ink-muted);
 margin-right:.55em}
.credit-award .credit-k{color:var(--c-award-ink)}
.credits a{color:var(--c-accent);text-underline-offset:.15em}

.links{margin-top:var(--space-2xs);display:flex;flex-wrap:wrap;
 gap:var(--space-2xs) var(--space-xs);font-family:var(--font-meta);
 font-size:var(--t-meta)}
.links a{color:var(--c-accent);text-underline-offset:.15em}
.links a:hover,.credits a:hover,.pagetail a:hover{color:var(--c-accent-hover)}

details{margin-top:var(--space-2xs)}
summary{display:inline-flex;align-items:center;gap:.5em;
 font-family:var(--font-meta);font-size:var(--t-meta);color:var(--c-accent);
 list-style:none;border:var(--border-hair) solid var(--c-rule-strong);
 border-radius:var(--radius-chip);padding:.2em .6em;
 transition:background var(--motion-fast) var(--ease-out)}
summary::before{content:"";width:.45em;height:.45em;border-right:2px solid
 currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg)
 translate(-1px,-1px);transition:transform var(--motion-fast) var(--ease-out)}
details[open] summary::before{transform:rotate(-135deg) translate(-2px,-2px)}
summary:hover{background:var(--c-bg-sunk)}
/* No max-width since review round 7: the summary box spans the entry, like
   the title and byline above it, instead of stopping at 93 small characters. */
.t2-body{margin-top:var(--space-2xs);padding:var(--space-s);
 background:var(--c-bg);border-left:var(--border-emphasis) solid var(--c-rule)}
/* For 16 of 22 entries this is now the only prose on the page, not
   supplementary detail, because the one-line summary above it is gone. */
.precis{font-size:var(--t-small);line-height:1.62}

/* Justified prose (review round 7, 2026-09-22). The author asked for these
   six blocks, and only these, to be set flush left AND right: the research
   statement, the Topics texts, every paper summary, the home hooks, the
   course descriptions and the bio lede. Headings, bylines, status lines,
   lists of links and the mono metadata stay ragged. hyphens:auto is what
   makes justification safe at 93ch: without it a long word forces a river
   of white across the line. <html lang="en"> is what lets it work. */
.band .overview,.topics .ttext,.precis,.picks .hook,.course p:last-child,
.herogrid>.lede{text-align:justify;hyphens:auto;-webkit-hyphens:auto}

.entry:target{background:var(--c-target-tint)}
.entry:target::before{background:var(--c-accent);width:4px}

/* No .pagetail since 2026-09-22: the Bio cross-link and the CV lines at the
   foot of four pages were removed by the author. */

/* ============================================================================
   PART B — home, teaching, service, bio

   B1. Page skeleton. Every page opens with the same dark band as /research/,
   so the five destinations read as one site. The motif appears in one place
   only, per DESIGN.md section 8, so on these four pages the band carries the
   heading and nothing else and therefore needs its own bottom padding.
   ========================================================================= */

.band.plain{padding-bottom:var(--space-2xl)}
.band .lede{max-width:var(--measure);margin-top:var(--space-m);
 color:var(--c-band-ink);font-size:var(--t-body)}
.band .lede a{color:var(--c-band-accent);text-underline-offset:.15em}
/* The bio lede beside the portrait: "slightly narrower" than the full measure
   (review round 5), so 82ch against the 93ch used everywhere else. */
.herogrid>.lede{max-width:82ch}

main{display:block}
.page{padding:var(--space-xl) 0 var(--space-3xl);max-width:var(--page-max);
 margin:0 auto}
.section{margin-top:var(--section-gap)}
.section:first-child{margin-top:0}
.section>h2,.band-section>h2{font-size:var(--t-h2);line-height:var(--lh-h2);
 letter-spacing:var(--ls-h2);font-weight:var(--weight-bold);
 padding-bottom:var(--space-2xs);
 border-bottom:var(--border-emphasis) solid var(--c-ink)}
.section>h2+*{margin-top:var(--space-m)}
.prose{max-width:var(--measure)}
.prose p+p{margin-top:var(--space-s)}
.prose a{color:var(--c-accent);text-underline-offset:.15em}
.prose a:hover{color:var(--c-accent-hover)}

/* B2. The year-and-line row. One component for positions, education, awards,
   industry experience and every service section: the left column is a value,
   so it is monospace and tabular like the research page's third line, and the
   rows meet on a shared hairline exactly as the paper entries do. Below 34rem
   the year moves above its line rather than squeezing the text into a third of
   the viewport. */
.rows{border-top:var(--border-hair) solid var(--c-rule)}
.rows>li{border-bottom:var(--border-hair) solid var(--c-rule);
 padding:var(--space-xs) 0}
.rk{display:block;font-family:var(--font-meta);font-size:var(--t-meta);
 font-variant-numeric:tabular-nums;color:var(--c-ink-muted);
 margin-bottom:var(--space-3xs)}
@media (min-width:34rem){
  .rows>li{display:grid;grid-template-columns:8.5rem minmax(0,1fr);
   gap:var(--space-s)}
  .rk{margin-bottom:0;padding-top:.15em}
}
/* No max-width on the value column (review round 4): a position or degree
   should sit on one line where the row has room for it. */
.rv>p:first-child{font-size:var(--t-body)}
.sub{font-size:var(--t-small);color:var(--c-ink-muted);
 margin-top:var(--space-3xs)}
.sub-k{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:.06em;text-transform:uppercase;color:var(--c-ink-muted);
 margin-right:.55em}
.sub a{color:var(--c-accent);text-underline-offset:.15em}
.rv .links{margin-top:var(--space-2xs)}
/* An undated list -- the journals, the conference venues -- keeps the same
   hairline rows without an empty first column. */
.rows.undated>li{display:block}

/* B3. Two-column lists where each line is short. The journals are 13 one-word
   to four-word names; a single column wastes two thirds of a desktop row. */
.cols{columns:2;column-gap:var(--space-2xl)}
@media (max-width:40rem){.cols{columns:1}}
.cols>li{break-inside:avoid}
.subhead{font-family:var(--font-meta);font-size:var(--t-micro);
 letter-spacing:var(--ls-micro);text-transform:uppercase;
 color:var(--c-ink-muted);margin-bottom:var(--space-2xs)}
/* The guest-AE subhead follows the two-column journal list directly; without
   this it sits flush against the last journal's hairline. */
.cols+.group{margin-top:var(--space-m)}
.group+.group{margin-top:var(--space-l)}

/* B4. Home hero. Text first in the DOM so the name is the first thing read on
   a phone and the first thing a screen reader reaches; the portrait floats to
   the right only once there is room for it. */
/* minmax(0,1fr) at EVERY width, not just the two-column one: an `auto` grid
   column sizes itself to a max-content child, so .herotext's max-width:100%
   would resolve against a column that had already grown past the phone, and
   the hero ran off the right edge at 390px (caught in review round 6). */
.herogrid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-l);
 align-items:start}
@media (min-width:52rem){
  .herogrid{grid-template-columns:minmax(0,1fr) 17rem;gap:var(--space-2xl);
   align-items:start}
  /* Home only (the portrait follows the text <div>; on /bio/ it follows the
     lede <p>). The author asked for the top of the picture to line up with
     the top of her name, so the offset is the h1's own top margin plus the
     gap between the line box and the cap height at display size. */
  .herogrid>div+.portrait{margin-top:calc(var(--space-3xs) + .17 * var(--t-display))}
}
.band .herogrid h1{margin-top:var(--space-3xs)}
.band .affil{margin-top:var(--space-2xs);font-size:var(--t-body);
 color:var(--c-band-ink)}
/* "Exactly as wide as the affiliation line" (review round 5). A block sized to
   its max-content is as wide as its widest unbreakable line; a child set to
   width:0 contributes nothing to that measurement and then min-width:100%
   stretches it back to the block. So the statement (and the button row and
   the contact lines under it) take the affiliation's width, and nothing is
   hard-coded. max-width:100% keeps the block inside its column on phones,
   where the affiliation itself wraps. Same trick as .fitkicker on /teaching/. */
.herotext{width:max-content;max-width:100%}
.herotext>:not(h1):not(.affil){width:0;min-width:100%}
.band .identity{margin-top:var(--space-m);color:var(--c-band-muted)}
.fitkicker{width:max-content;max-width:100%}
.fitkicker>.lede{width:0;min-width:100%}

/* The portrait is a 5:4 landscape frame shown in a SQUARE slot (review round 6,
   2026-09-22: "larger and square like on my current website"; it was a 4:5
   upright slot at 13rem before). object-fit trims a tenth off each side and
   keeps the full height, so nothing of the head or shoulders is lost; a hard
   crop baked into the file would be one irreversible guess about framing. */
.portrait{display:block;width:9.5rem;border-radius:var(--radius);
 overflow:hidden;background:var(--c-bg-sunk)}
.portrait img{width:100%;height:auto;aspect-ratio:1/1;object-fit:cover;
 object-position:48% 50%}
@media (min-width:52rem){.portrait{width:17rem}}
.biophoto{width:9.5rem;margin-bottom:var(--space-m)}
@media (min-width:40rem){.biophoto{width:15rem}}

/* B5. Button row. Four identical outlined buttons since 2026-09-22 (author
   ruling): no dominant action, no filled button anywhere on the site. */
.actions{display:flex;flex-wrap:wrap;gap:var(--space-2xs) var(--space-s);
 margin-top:var(--space-m)}
.actions a{display:inline-flex;align-items:center;gap:.4em;
 font-size:var(--t-meta);font-family:var(--font-meta);text-decoration:none;
 padding:.5em .9em;border-radius:var(--radius);
 border:var(--border-hair) solid var(--c-band-muted);
 color:var(--c-band-ink);
 transition:border-color var(--motion-fast) var(--ease-out),
  background var(--motion-fast) var(--ease-out)}
.actions a:hover{border-color:var(--c-band-accent)}

/* B6. Office and email under the buttons, each led by a 16px inline icon in
   currentColor. Replaces the credential strip (2026-09-22). */
.contactlines{display:grid;gap:var(--space-2xs);
 margin-top:var(--space-l);padding-top:var(--space-s);
 border-top:var(--border-hair) solid rgb(255 255 255 / .14);
 font-family:var(--font-meta);font-size:var(--t-meta);
 color:var(--c-band-muted)}
.contactlines li{display:flex;gap:.6em;align-items:flex-start}
.contactlines .ico{flex:none;width:1rem;height:1rem;margin-top:.15em;
 fill:currentColor;color:var(--c-band-accent)}
.contactlines a{color:var(--c-band-ink);text-underline-offset:.15em}
.contactlines a:hover{color:var(--c-band-accent)}

/* B7. Selected work on the home page. Deliberately not the research entry
   component: three papers with one sentence each, no status edge, no
   disclosure. Same card surface and hairlines so it still looks like the same
   site. */
.picks{background:var(--c-card);border:var(--border-hair) solid var(--c-rule);
 border-radius:var(--radius);overflow:hidden}
.picks>li{padding:var(--space-s);
 border-top:var(--border-hair) solid var(--c-rule)}
.picks>li:first-child{border-top:0}
.picks h3{font-size:var(--t-h3);line-height:var(--lh-h3);
 font-weight:var(--weight-medium)}
.picks h3 a{text-decoration:none;text-underline-offset:.15em}
.picks h3 a:hover{color:var(--c-accent);text-decoration:underline}
.picks .statusline{margin-top:var(--space-3xs)}
/* No max-width on the hook (review round 7): at the small size, 93ch came out
   narrower than the title above it, which is what the author saw. The card
   bounds it now, so the hook is exactly as wide as the title. */
.picks .hook{margin-top:var(--space-2xs);font-size:var(--t-small)}
.morelink{margin-top:var(--space-s);font-family:var(--font-meta);
 font-size:var(--t-meta)}
.morelink a{color:var(--c-accent);text-underline-offset:.15em}
.morelink a:hover{color:var(--c-accent-hover)}

/* B8. Currently. A dated list that is expected to go stale, so it says when it
   was last true. */
.nowlist{display:grid;gap:var(--space-2xs);max-width:var(--measure)}
.nowlist li{position:relative;padding-left:1.1em;font-size:var(--t-small)}
.nowlist li::before{content:"";position:absolute;left:0;top:.6em;
 width:.34em;height:.34em;background:var(--c-accent)}
.nowlist a{color:var(--c-accent);text-underline-offset:.15em}
.asof{margin-top:var(--space-s);font-family:var(--font-meta);
 font-size:var(--t-micro);color:var(--c-ink-muted)}

/* B9. Call to action. The one sunk panel on the site, because it is an offer
   rather than a record. */
.cta{margin-top:var(--section-gap);padding:var(--space-l);
 background:var(--c-bg-sunk);border-left:var(--target-rule) solid var(--c-accent);
 border-radius:var(--radius)}
.cta h2{font-size:var(--t-h2);line-height:var(--lh-h2);
 letter-spacing:var(--ls-h2);font-weight:var(--weight-bold)}
.cta p{margin-top:var(--space-2xs);max-width:var(--measure)}
.cta a{color:var(--c-accent);text-underline-offset:.15em}
.cta a:hover{color:var(--c-accent-hover)}

/* B10. Courses: a short heading, then metadata, then her sentence. Same
   three-tier shape as a paper entry, without the edge. The PhD students,
   Methods and bio Research sections were removed on 2026-09-22, and their
   rules (.student, .methods, .topic) with them. */
.course{padding:var(--space-s) 0;
 border-bottom:var(--border-hair) solid var(--c-rule)}
.course:first-child{border-top:var(--border-hair) solid var(--c-rule)}
.course h3{font-size:var(--t-h3);line-height:var(--lh-h3);
 font-weight:var(--weight-medium)}
.course .meta{margin-top:var(--space-3xs);font-family:var(--font-meta);
 font-size:var(--t-meta);color:var(--c-ink-muted);
 font-variant-numeric:tabular-nums}
.course p:last-child{margin-top:var(--space-2xs);
 max-width:var(--measure);font-size:var(--t-small)}
/* The compact list for the Booth TA courses: title and meta on one row, no
   description, same hairlines. */
.courselist{border-top:var(--border-hair) solid var(--c-rule)}
.courselist li{display:flex;flex-wrap:wrap;gap:var(--space-3xs) var(--space-m);
 align-items:baseline;padding:var(--space-xs) 0;
 border-bottom:var(--border-hair) solid var(--c-rule)}
.courselist .ctitle{font-weight:var(--weight-medium)}
.courselist .meta{font-family:var(--font-meta);font-size:var(--t-meta);
 color:var(--c-ink-muted);font-variant-numeric:tabular-nums}

/* B12. Footer. Text, not a logo image: the affiliation is a fact, and a
   bitmap of a wordmark is a network request that says the same thing. */
.footer{background:var(--c-band);color:var(--c-band-ink);
 padding:var(--space-xl) 0}
/* One column since review round 6 (2026-09-22): the links column is gone. */
.footer .wrap{display:grid;gap:var(--space-m)}
.footer .fname{font-weight:var(--weight-bold)}
.footer .frole{margin-top:var(--space-3xs);font-size:var(--t-small);
 color:var(--c-band-muted);max-width:var(--measure)}
.footer .fmeta{margin-top:var(--space-2xs);font-family:var(--font-meta);
 font-size:var(--t-meta);color:var(--c-band-muted)}
.footer .fmeta a{color:var(--c-band-ink);text-underline-offset:.15em}
.footer a:hover{color:var(--c-band-accent)}

/* B13. 404. Short by design: it exists to get the reader to one of five
   places, not to apologise. */
.notfound{padding:var(--space-3xl) 0;max-width:var(--measure)}
.notfound h1{font-size:var(--t-display);line-height:var(--lh-display);
 letter-spacing:var(--ls-display);font-weight:var(--weight-bold);
 margin-bottom:var(--space-m)}
.notfound ul{margin-top:var(--space-m);display:grid;gap:var(--space-2xs)}
.notfound a{color:var(--c-accent)}

/* ============================================================================
   PART C — print

   A browser prints only an OPEN <details>, and after review round 1 the
   disclosure holds the only prose on the page for most entries, so a default
   print of /research/ would come out as a list of bare titles. Forcing it open
   is therefore a content requirement, not a nicety.

   Three mechanisms, because the UA hides closed disclosure content differently
   across engines: `display` on the children, `content-visibility` on the
   modern ::details-content pseudo, and the `open` attribute selector.
   ========================================================================= */

@media print{
  :root{--c-bg:#fff;--c-card:#fff;--c-bg-sunk:#fff}
  body{background:#fff;color:#000;font-size:10pt}
  details{display:block}
  details>*{display:block!important}
  details:not([open])>*:not(summary){display:block!important}
  details::details-content{content-visibility:visible!important;
   display:block!important;block-size:auto!important}
  summary{display:none}
  .t2-body{border-left:1pt solid #999;background:none;padding:0 0 0 8pt}
  /* Furniture a sheet of paper has no use for. */
  .motif,.jumpbar,.rail,.skip,.masthead,.actions,.cta,
  .morelink{display:none}
  .layout{display:block;padding:0}
  .band,.footer{background:none;color:#000;padding:0 0 12pt}
  .band .kicker,.band .olabel,.band .overview,.band .lede,.band .identity,
  .contactlines,.contactlines .ico,.footer .frole,.footer .fmeta{color:#000}
  .list,.picks{border:none}
  .entry,.picks>li{break-inside:avoid;page-break-inside:avoid}
  .entry::before{display:none}
  .direction{break-before:auto}
  .direction>header{break-after:avoid;page-break-after:avoid}
  /* A printed page cannot be clicked, so a bare URL after each link is the
     only way the reader gets the destination. Entry links only: the nav and
     the footer are gone. */
  .links a::after,.credits a::after{content:" (" attr(href) ")";
   font-size:8pt;color:#444;word-break:break-all}
  a{text-decoration:none;color:#000}
}
