:root {
  --accent-strong: #5c2a6d;
  --accent-soft: #b48fc4;
  --accent-line: rgba(92, 42, 109, 0.15);    /* card boundary */
  --accent-shadow: rgba(92, 42, 109, 0.05);
  --accent-shadow-lift: rgba(92, 42, 109, 0.12);
  --on-accent: #fff;                         /* text on a filled purple */

  --accent-wash: rgba(247, 208, 70, 0.45);   /* the highlighter */
  --marker: rgba(247, 208, 70, 0.85);        /* the rail thumb */
  --marker-strong: #f0c62e;
  --on-wash: #000;                           /* text sitting in it */

  --ink: #000;
  --ink-soft: rgba(0, 0, 0, 0.7);            /* author lines */
  --body-grey: #4c4c4c;                      /* news copy, venues */
  --muted: #737373;                          /* sidebar meta, inactive nav */
  --rule: #e4e4e4;
  /* Swapped: the page is plain white and the warmth sits in the bar, so
     the tint appears only in a 60px strip at the top rather than across
     the whole screen. */
  --bg: #ffffff;
  --bar: #fffefc;
  --card-bg: #fff;
  --thumb-bg: #fbfaf7;

  --sans: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --serif: "Cormorant Garamond", Georgia, serif;
  --ui: "Instrument Sans", var(--sans);
  /* Wider than the original, because the sidebar now holds its column for
     the whole page rather than only alongside the bio. */
  --page: 1120px;
  --hang: 96px;   /* news category slot; must clear the longest word */
}

* { box-sizing: border-box; }

/* Selecting text is marking it, so it gets the highlighter too. */
::selection {
  background: var(--accent-wash);
  color: var(--on-wash);
}

html { scroll-behavior: smooth; scroll-padding-top: 90px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* Hovering marks a link the way a highlighter would: the text goes to ink
   and the background goes yellow. One behaviour for every link on the page. */
a {
  color: var(--accent-strong);
  text-decoration: none;
  transition: color 0.15s, background-color 0.15s;
}

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 28px;
}

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* --- top navigation ------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: rgba(255, 254, 252, 0.96);
  backdrop-filter: saturate(180%) blur(8px);
  border-bottom: 1px solid var(--rule);
}

/* The rule along the top of the nav doubles as a reading-progress track:
   the soft hue is the track, the strong hue fills it as you scroll. */
.nav::before,
.nav::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
}

.nav::before {
  right: 0;
  background: var(--accent-soft);
}

.nav::after {
  width: 100%;
  background: var(--accent-strong);
  transform: scaleX(var(--progress, 0));
  transform-origin: left;
}

.nav .wrap {
  display: flex;
  align-items: center;
  gap: 26px;
  height: 58px;
}

.nav a {
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: var(--muted);
  transition: color 0.2s, background-color 0.2s;
}

.nav a.active {
  color: var(--accent-strong);
  font-weight: 700;
}

.nav .home { display: inline-flex; align-items: center; color: var(--accent-strong); margin-right: 4px; }
.nav .home svg { width: 20px; height: 20px; }

/* --- about ----------------------------------------------------- */

/* The sidebar is a sibling of every section rather than of the bio alone,
   so it can stay in view while news and works scroll past. align-items:
   start is what leaves the column room to stick in. */
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 36px;
  align-items: start;
  padding: 58px 0 12px;
}

.card {
  position: sticky;
  top: 82px;
}

.bio { padding-bottom: 8px; }

/* Narrower than the column, and left-aligned within it. */
.card img.portrait {
  width: 164px;
  height: 164px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  margin-bottom: 22px;
}

.card h1 {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 30px;
  line-height: 1.1;
  color: var(--accent-strong);
  margin: 0 0 10px;
}

.card .role,
.card .affil { font-style: italic; font-size: 15px; margin: 0; color: var(--muted); }
.card .email {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0 18px;
  font-size: 15px;
  color: var(--muted);
}

.card .email .mail {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  fill: none;
  stroke: var(--accent-strong);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.socials { display: flex; align-items: center; gap: 10px; }

.socials a {
  display: inline-flex;
  color: var(--accent-strong);
  transition: transform 0.2s, background-color 0.2s;
}

.socials svg { width: 20px; height: 20px; fill: currentColor; }
.socials .sep { color: var(--rule); font-size: 15px; }

/* No measure cap: the bio shares the content column with news and works, and
   a narrower paragraph read as a misalignment rather than as good typography. */
.bio p { margin: 0 0 18px; }
.bio ul { list-style: none; padding: 0; margin: 0; }

.bio li {
  position: relative;
  padding-left: 30px;
  margin-bottom: 18px;
}

.bio li::before {
  content: "\2192";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent-strong);
  font-size: 17px;
}

/* --- section headings ------------------------------------------ */

.section { padding: 34px 0; }

.section > h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 400;
  font-size: 26px;
  letter-spacing: 0.02em;
  color: var(--accent-strong);
  margin: 0 0 20px;
}

.section > h2 svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}

/* --- news ------------------------------------------------------ */

.news {
  max-height: 250px;
  overflow-y: scroll;
  padding-right: 52px;   /* clears the rail */
  /* The native bar is replaced by .rail, built in script. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* Fade the bottom edge so a clipped line reads as "more below" rather than
     as the end of the list. Dropped once you have scrolled to the end. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 36px), transparent);
}

.news.at-end {
  -webkit-mask-image: none;
  mask-image: none;
}

.news::-webkit-scrollbar { display: none; }

/* --- the news rail ---------------------------------------------- */

/* The scroller is a spine rather than a bar: a thin rule that fills purple
   behind you as you read, with a single marker at the head. */

.news-scroll { position: relative; }

.rail {
  position: absolute;
  top: 12px;
  bottom: 30px;      /* clear of the fade at the foot of the list */
  right: 8px;
  width: 30px;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}

.rail-line,
.rail .fill {
  position: absolute;
  left: 13px;
  width: 4px;
  border-radius: 2px;
}

.rail-line {
  top: 0;
  bottom: 0;
  background: var(--accent-line);
}

.rail .fill {
  top: 0;
  background: var(--accent-strong);
  opacity: 0.5;
}

/* Big enough to read as a handle, ringed in the page colour so it sits on
   top of the rule rather than in it. */
.rail .thumb {
  position: absolute;
  left: 2px;
  width: 26px;
  height: 26px;
  margin-top: -13px;
  border-radius: 50%;
  background: var(--marker-strong);
  box-shadow: 0 0 0 4px var(--bg), 0 1px 3px var(--accent-shadow-lift);
  cursor: grab;
  transition: transform 0.15s ease-out;
}

/* Three grip lines, so it reads as something to take hold of. */
.rail .thumb::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 9px;
  height: 7px;
  margin: -3.5px 0 0 -4.5px;
  background:
    linear-gradient(var(--accent-strong), var(--accent-strong)) 0 0 / 100% 1px no-repeat,
    linear-gradient(var(--accent-strong), var(--accent-strong)) 0 3px / 100% 1px no-repeat,
    linear-gradient(var(--accent-strong), var(--accent-strong)) 0 6px / 100% 1px no-repeat;
  opacity: 0.55;
}

.rail .thumb.grabbing { transform: scale(1.12); cursor: grabbing; }

/* Date gets its own column so the entries line up; the category is a chip
   rather than bracketed text. */
.news p {
  display: grid;
  grid-template-columns: 58px 1fr;
  gap: 0 16px;
  align-items: baseline;
  margin: 0 0 16px;
  color: var(--body-grey);
  line-height: 1.5;
}

.news .date {
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: var(--muted);
  white-space: nowrap;
}

/* Hanging indent: the category holds a fixed slot on the first line and the
   sentence hangs past it, so categories line up into a column inside the
   running text without needing a grid column of their own. */
.news .item {
  display: block;
  padding-left: var(--hang);
  text-indent: calc(var(--hang) * -1);
}

.news .kind {
  display: inline-block;
  /* Exactly --hang, so text following the category starts at the same x as
     the wrapped lines hanging beneath it. Anything less and the first line
     sits proud of the rest by the difference. */
  width: var(--hang);
  text-indent: 0;
  font-family: var(--sans);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent-strong);
  white-space: nowrap;
}

.news .venue { font-style: italic; }

/* Only ever shown on phones — see the narrow-screen block at the foot. */
.news-more { display: none; }

/* The award is a distinction rather than a category, so it is the one label
   that gets marked with the highlighter. */
.work .award {
  display: inline-block;
  padding: 1px 7px 2px;
  border-radius: 2px;
  background: var(--accent-wash);
  color: var(--on-wash);
  font-family: var(--sans);
  font-size: 10.5px;
  font-style: normal;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* News links read as body text with a dashed rule, not as links.

   On hover the highlighter is drawn rather than switched on: the wash is a
   gradient anchored to the bottom of the text with no height, which grows up
   to cover it. box-decoration-break: clone gives every wrapped line its own
   band, so a link spanning three lines is stroked three times over. */
.news a {
  color: var(--ink);
  text-decoration: underline dashed var(--accent-soft);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 4px;

  background-image: linear-gradient(var(--accent-wash), var(--accent-wash));
  background-repeat: no-repeat;
  background-position: 0 100%;
  /* Both ends are percentages: interpolating a length against a percentage
     is not reliably animatable. */
  background-size: 100% 0%;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;

  transition: background-size 0.28s ease-out, color 0.28s ease-out;
}

/* --- works ----------------------------------------------------- */

.work {
  display: grid;
  grid-template-columns: 170px 1fr;
  gap: 30px;
  align-items: center;
  padding: 24px 28px;
  margin-bottom: 16px;
  background: var(--card-bg);
  border: 1px solid var(--accent-line);
  border-radius: 8px;
  box-shadow: 0 1px 2px var(--accent-shadow);
  transition: box-shadow 0.25s, border-color 0.25s, transform 0.25s;
}

/* The publications page lists entries without thumbnails. */
.work.no-thumb { grid-template-columns: 1fr; }

/* Thumbnails range from wide banners to squares. A fixed-ratio mount with
   the figure contained inside keeps the column regular without cropping
   anything out of a research figure. */
.work .thumb {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: contain;
  background: var(--thumb-bg);
  border-radius: 4px;
  padding: 8px;
}

.work h4 {
  font-size: 16px;
  font-weight: 400;
  margin: 0 0 8px;
  line-height: 1.4;
}

.work .authors { margin: 0 0 4px; font-size: 15px; color: var(--ink-soft); }

.work .authors .me {
  text-decoration: underline;
  text-decoration-color: var(--accent-soft);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.work .venue {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 12px;
  font-size: 16px;
  font-style: italic;
  color: var(--body-grey);
}

.work .links { display: flex; flex-wrap: wrap; gap: 10px; }

.work .links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 90px;
  height: 30px;
  padding: 0 12px;
  background: transparent;
  border: 1px solid var(--accent-soft);
  border-radius: 8px;
  font-family: var(--ui);
  font-size: 14px;
  font-weight: 500;
  /* Label and icon are the strong purple; the icons inherit it through
     stroke: currentColor. */
  color: var(--accent-strong);
  transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}

/* Sizes are 14px of drawing plus the 0.7 units of stroke padding baked into
   each symbol's viewBox, so nothing clips at the edges. */
.work .links svg {
  flex-shrink: 0;
  width: 15.4px;
  height: 15.4px;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.work .links svg.icon-code { height: 9.4px; }

/* --- footer ---------------------------------------------------- */

.foot {
  border-top: 1px solid var(--rule);
  margin-top: 40px;
  padding: 24px 0 48px;
  font-size: 16px;
  font-weight: 300;
  color: var(--ink);
}

/* --- scroll reveal ---------------------------------------------- */

/* The class is applied by script, so with JS off nothing is ever hidden.
   Once an element has finished arriving the classes are stripped, which
   keeps this transform from overriding the card's hover lift. */
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.55s ease, transform 0.55s ease;
  }

  .reveal.in {
    opacity: 1;
    transform: none;
  }
}

/* --- motion ----------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* --- keyboard focus -------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--accent-strong);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Off-screen until the first Tab press, then drops into the top-left. */
.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  z-index: 20;
  padding: 9px 16px;
  background: var(--accent-strong);
  color: var(--on-accent);
  border-radius: 0 0 8px 8px;
  font-size: 14px;
  font-weight: 700;
  transition: top 0.15s;
}

.skip-link:focus {
  top: 0;
  background: var(--accent-strong);
  color: var(--on-accent);
}

/* --- print ------------------------------------------------------ */

@media print {
  :root { --accent-strong: #000; --accent-soft: #999; }

  .nav, .theme-toggle, .skip-link { display: none; }

  body { font-size: 11pt; background: #fff; color: #000; }

  .wrap { max-width: none; padding: 0; }

  /* Nothing should stay hidden inside a scroll area on paper. */
  .news {
    max-height: none;
    overflow: visible;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .work {
    border: 1px solid #ccc;
    box-shadow: none;
    break-inside: avoid;
  }

  .section { padding: 12px 0; }

  /* Links lose their href on paper unless it is spelled out. */
  .bio a::after,
  .news a::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #555;
    word-break: break-all;
  }

  .work .links a::after {
    content: " " attr(href);
    font-size: 8pt;
    color: #555;
    word-break: break-all;
  }

  .work .links { display: block; }
  .work .links a { display: block; min-width: 0; height: auto; border: none; padding: 0; }
}

/* --- dark mode -------------------------------------------------- */

/* data-theme is stamped on <html> by an inline script in <head>, before
   first paint, from the saved choice or the system setting. Keying off the
   attribute rather than the media query lets the toggle override the system. */

:root[data-theme="dark"] {
  /* Same two roles, lifted for a dark ground: the strong purple carries
     text, the soft purple stays behind it on borders. The highlighter has
     to go nearly opaque here, because a translucent yellow over a dark
     ground reads as brown — so what sits in it flips to dark ink. */
  --accent-strong: #c49ad4;
  --accent-soft: #8a6b99;
  --accent-line: rgba(196, 154, 212, 0.18);
  --accent-shadow: rgba(0, 0, 0, 0.35);
  --accent-shadow-lift: rgba(0, 0, 0, 0.55);
  --on-accent: #14101a;

  --accent-wash: rgba(247, 208, 70, 0.82);
  --marker: rgba(247, 208, 70, 0.78);
  --marker-strong: #f7d046;
  --on-wash: #14101a;

  --ink: #ece8f0;
  --ink-soft: rgba(236, 232, 240, 0.72);
  --body-grey: #b6aec1;
  --muted: #8d8496;
  --rule: #2b2434;
  --bg: #14101a;
  --card-bg: #1b1622;
}

:root[data-theme="dark"] .nav { background: rgba(20, 16, 26, 0.94); }

/* The paper thumbnails are artwork on white, so keep their mount white on
   a dark ground rather than letting the figures glare. */
:root[data-theme="dark"] { --thumb-bg: #fff; }

:root[data-theme="dark"] .card img.portrait { filter: brightness(0.94); }

/* --- theme toggle ----------------------------------------------- */

.theme-toggle {
  margin-left: auto;         /* pins the control to the right of the bar */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--accent-line);
  border-radius: 8px;
  background: transparent;
  color: var(--accent-strong);
  cursor: pointer;
  transition: background-color 0.2s, border-color 0.2s;
}

.theme-toggle svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Show the destination, not the current state: a moon while light. */
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }

/* --- interaction states ------------------------------------------

   Hover is gated behind a real pointer. On a touchscreen :hover latches
   after a tap and stays until you tap something else, so a tapped news
   link would sit there highlighted for no reason. Touch gets :active,
   which fires on press and releases on lift.
   ------------------------------------------------------------------ */

/* The browser's own grey tap flash, replaced by the states below. */
a, button { -webkit-tap-highlight-color: transparent; }

@media (hover: hover) and (pointer: fine) {
  a:hover {
    background-color: var(--accent-wash);
    color: var(--on-wash);
  }

  .nav a:hover {
    color: var(--on-wash);
    background-color: var(--accent-wash);
  }

  .nav .home:hover { background-color: transparent; }

  .socials a:hover { transform: translateY(-2px); background-color: transparent; }

  .rail:hover .thumb { transform: scale(1.12); }

  /* Drawn on rather than switched on — see the .news a rule above. */
  .news a:hover {
    color: var(--on-wash);
    background-color: transparent;
    background-size: 100% 100%;
  }

  /* Cards lift toward the reader and firm up their boundary. */
  .work:hover {
    border-color: var(--accent-soft);
    box-shadow: 0 6px 20px var(--accent-shadow-lift);
    transform: translateY(-2px);
  }

  .work .links a:hover {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--on-accent);
  }

  .theme-toggle:hover {
    background: var(--accent-wash);
    border-color: var(--accent-soft);
  }
}

/* Press states. These run everywhere — on a mouse they read as a click,
   on a finger they are the only feedback there is. */

a:active { background-color: var(--accent-wash); color: var(--on-wash); }

.nav .home:active,
.socials a:active { background-color: transparent; transform: scale(0.92); }

/* The highlighter arrives at once under a finger; there is no travelling
   pointer for it to follow. */
.news a:active {
  color: var(--on-wash);
  background-color: transparent;
  background-size: 100% 100%;
  transition: none;
}

.work .links a:active {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--on-accent);
}

.theme-toggle:active {
  background: var(--accent-wash);
  border-color: var(--accent-soft);
}

/* --- small screens --------------------------------------------- */

@media (max-width: 760px) {
  .wrap { padding: 0 20px; }

  /* --- navigation ---------------------------------------------- */

  /* Sized to fit a 390px screen without sideways scroll, with the padding
     doing double duty as touch target. overflow-x stays as a safety valve
     for anything narrower. */
  .nav .wrap {
    gap: 0;
    height: 54px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .nav .wrap::-webkit-scrollbar { display: none; }

  .nav a {
    padding: 12px 9px;
    font-size: 13px;
    letter-spacing: 0.01em;
    white-space: nowrap;
  }

  .nav .home { margin-right: 0; padding: 12px 8px; }

  .theme-toggle {
    flex-shrink: 0;      /* the flex row was squeezing it to 19px */
    width: 40px;
    height: 40px;
    margin-left: 8px;
  }

  /* --- profile -------------------------------------------------- */

  .layout { grid-template-columns: 1fr; gap: 26px; padding-top: 30px; }
  .card { position: static; text-align: center; }
  .card img.portrait { margin-left: auto; margin-right: auto; }
  .card .email { justify-content: center; }

  /* Padding rather than gaps, so each icon is a 40px target. */
  .socials { justify-content: center; gap: 2px; }
  .socials a { padding: 10px; }
  .socials .sep { display: none; }

  /* --- sections ------------------------------------------------- */

  .section { padding: 26px 0; }
  .section > h2 { font-size: 23px; gap: 10px; }
  .section > h2 svg { width: 19px; height: 19px; }

  /* No nested scroller on a phone: the list either shows its first few
     entries or runs full length, switched by a button. The rail has
     nothing to do either way. */
  .news { max-height: none; overflow: visible; padding-right: 0; }
  .rail { display: none; }

  .news.folded > p:nth-child(n + 5) { display: none; }

  .news-more {
    display: block;
    width: 100%;
    margin-top: 2px;
    height: 44px;
    padding: 0 14px;
    background: transparent;
    border: 1px solid var(--accent-soft);
    border-radius: 8px;
    font-family: var(--ui);
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--accent-strong);
    cursor: pointer;
    transition: background-color 0.2s, color 0.2s, border-color 0.2s;
  }

  .news-more:active {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--on-accent);
  }

  /* Too narrow to hang a label and still leave a readable measure, so the
     whole entry collapses to one flowing paragraph — date, category, then
     the sentence — rather than giving the date a line of its own. */
  .news p { display: block; margin-bottom: 18px; }
  .news .date { margin-right: 9px; }
  .news .item { display: inline; padding-left: 0; text-indent: 0; }
  .news .kind { width: auto; margin-right: 8px; }

  /* --- works ---------------------------------------------------- */

  .work { grid-template-columns: 1fr; gap: 14px; padding: 18px; }
  .work .thumb { max-width: 220px; }

  /* Two to a row, so four buttons make a block rather than a ragged 3 + 1,
     and each one is tall enough to hit. */
  .work .links { gap: 8px; }
  .work .links a {
    flex: 1 1 calc(50% - 4px);
    min-width: calc(50% - 4px);
    height: 38px;
    padding: 0 12px;
  }

  .foot { padding: 20px 0 40px; }
}

/* Narrow phones: the nav has to fit without scrolling sideways, or
   PUBLICATIONS ends up off the edge where nobody finds it. */
@media (max-width: 380px) {
  .wrap { padding: 0 16px; }
  .nav a { padding: 12px 6px; font-size: 12px; letter-spacing: 0; }
  .theme-toggle { margin-left: 4px; }

  /* The house is redundant here — ABOUT goes to the same place, and on the
     publications page the other two links still lead home. */
  .nav .home { display: none; }

  .card img.portrait { width: 140px; height: 140px; }
}
