/* James T. Rosenbaum, M.D. — site styles
   Zero-build static CSS. No external fonts or frameworks. */

:root {
  --paper:      #fbf9f6;
  --paper-alt:  #f2eee8;
  --ink:        #1b2a33;
  --ink-soft:   #40525e;
  --muted:      #7b8790;
  --rule:       #ded7cd;
  --accent:     #2f6d8c;
  --accent-dark:#1f5471;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --wrap: 1080px;
  --measure: 68ch;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Reserve the scrollbar track whether or not the page scrolls. Without this,
     a short page has no scrollbar and a long one does, so every centred
     container shifts about 15px sideways as you move between pages. */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--accent-dark); text-decoration: none; border-bottom: 1px solid rgba(47,109,140,.35); }
a:hover { border-bottom-color: var(--accent); }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }

/* .narrow caps the reading measure WITHOUT re-centring.
   It used to be a 760px box centred in the viewport, so its left edge landed
   160px right of the 1080px .wrap used on other pages. Headlines and body text
   jumped sideways page to page, and on Expertise, Book and Contact the
   headline sat 160px right of the content directly beneath it.
   The box now matches .wrap and the 760px cap moves to the children, so every
   left edge on the site lines up while prose still wraps at a readable
   measure. */
.wrap.narrow { max-width: var(--wrap); }
.wrap.narrow > * { max-width: 760px; }

/* ---------- Header / nav ---------- */

.site-head {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 20;
}
.site-head .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-top: 18px;
  padding-bottom: 18px;
  flex-wrap: wrap;
}
.brand { border: 0; display: block; }
.brand .name {
  font-size: 1.22rem;
  letter-spacing: .01em;
  color: var(--ink);
  display: block;
  font-weight: 600;
}
.brand .role {
  font-family: var(--sans);
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  display: block;
  margin-top: 3px;
}

.site-nav ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 22px;
  margin: 0;
  padding: 0;
}
.site-nav a {
  font-family: var(--sans);
  font-size: .78rem;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border: 0;
  padding: 4px 0;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--accent-dark); border-bottom-color: var(--rule); }
.site-nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

/* ---------- Hero ---------- */

.hero { padding: 72px 0 64px; border-bottom: 1px solid var(--rule); }
.hero-grid {
  display: grid;
  grid-template-columns: 1.25fr .85fr;
  gap: 56px;
  align-items: center;
}
.hero h1 {
  font-size: clamp(2.1rem, 4.6vw, 3.25rem);
  line-height: 1.12;
  margin: 0 0 18px;
  font-weight: 500;
  letter-spacing: -.01em;
}
.hero .lede {
  font-size: 1.16rem;
  color: var(--ink-soft);
  max-width: 46ch;
  margin: 0 0 28px;
}
/* .kicker retired 2026-08-09 with the eyebrow it styled. index.html was its
   only user, and the line it carried ("Physician-Scientist") duplicated the site
   header sitting 91px above it on the same left edge. Kept out rather than left
   dangling; if an eyebrow is ever wanted again it should say something the
   header and the headline do not. */
.portrait {
  border-radius: 3px;
  box-shadow: 0 18px 44px rgba(27,42,51,.18);
}

/* ---------- Sections ---------- */

section { padding: 64px 0; }
section + section { border-top: 1px solid var(--rule); }
.tint { background: var(--paper-alt); }

h2 {
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: 500;
  line-height: 1.2;
  margin: 0 0 8px;
  letter-spacing: -.005em;
}
h3 { font-size: 1.18rem; font-weight: 600; margin: 0 0 6px; line-height: 1.3; }
h4 { font-family: var(--sans); font-size: .8rem; letter-spacing: .14em; text-transform: uppercase;
     color: var(--muted); margin: 36px 0 12px; font-weight: 600; }

p { max-width: var(--measure); }
.section-intro { color: var(--ink-soft); max-width: 62ch; margin: 0 0 36px; }

.page-head { padding: 60px 0 40px; border-bottom: 1px solid var(--rule); }

/* .flush retired 2026-08-09. It dropped the rule under a page heading and cut
   the gap to what followed, to stop a tall callout being pushed below the fold.
   Essays was its only user, which meant Essays was
   the one page without the divider under its heading -- the reason its header
   read as belonging to a different site. If a page ever needs the tighter
   pairing again, reinstate it deliberately and for more than one page. */

/* Publications page header only. Same defect as the essays header: a 320px
   square of art centred against 151px of copy, so the picture floated 84px
   above the h1 and the header inflated to 421px. The statistics and the
   Scholar button move up into this grid, which gives the left column the
   height to balance the art. */
.page-head-grid.pubs-head { align-items: start; }
.page-head-grid.pubs-head .stats { margin-top: 30px; }
.page-head-grid.pubs-head .btn-row { margin-top: 28px; }

/* Above 860px the header is two columns, so the stats sit in a ~640px column
   rather than the full 1032px wrap; pinning three tracks keeps the three
   figures on one line there. Below 860px the header stacks and the stats get
   the full width back, where the shared auto-fit rule stacks them properly --
   forcing three columns into a 327px phone width squeezed "46,000+" into about
   95px. */
@media (min-width: 861px) {
  .page-head-grid.pubs-head .stats { grid-template-columns: repeat(3, 1fr); gap: 22px; }
}

/* Essays header. Reinstated 2026-08-09 after the callout was unboxed into a
   note: with the note in the left column the copy is tall enough to pair with
   art, which the 122px version never was. align-items: start because the base
   grid centres, and centring a 320px square against shorter copy floats the
   picture above the headline. The art is capped nearer the copy's height so the
   two columns finish together. */
.page-head-grid.essays-head { align-items: start; }
.page-head-grid.essays-head .page-art { max-width: 250px; }
.page-head h1 {
  font-size: clamp(2rem, 4vw, 2.9rem);
  font-weight: 500;
  margin: 0 0 12px;
  line-height: 1.15;
}
.page-head p { color: var(--ink-soft); font-size: 1.08rem; margin: 0; }
/* .page-head p + p retired 2026-08-09, hours after it was added. It spaced the
   book page's three header paragraphs; the story then moved below the rule into
   body type, so every page head is back to a single subtitle line. */

/* ---------- Cards ---------- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 28px;
  margin-top: 8px;
}
/* The 8px above assumes a .section-intro sits between the heading and the
   cards, contributing its own 36px. Where cards follow a heading directly --
   "Inquiries" on the contact page -- that 8px was the whole gap and the cards
   crowded the heading. */
h2 + .cards { margin-top: 36px; }
.card {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 26px 26px 28px;
}
.card p { font-size: .97rem; color: var(--ink-soft); margin: 0; max-width: none; }
.card h3 { margin-bottom: 10px; }

/* ---------- Stats ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 32px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.stats li { border-left: 2px solid var(--accent); padding-left: 18px; }
.stats .num { display: block; font-size: 2.2rem; line-height: 1.05; color: var(--ink); }
.stats .lab {
  display: block;
  font-family: var(--sans);
  font-size: .78rem;
  color: var(--muted);
  margin-top: 6px;
  line-height: 1.45;
}

/* ---------- Lists ---------- */

.plain { list-style: none; margin: 0; padding: 0; max-width: var(--measure); }
.plain li { padding: 9px 0; border-bottom: 1px solid var(--rule); font-size: .98rem; }
.plain li:last-child { border-bottom: 0; }
.plain .yr {
  font-family: var(--sans);
  font-size: .78rem;
  color: var(--muted);
  margin-left: 8px;
  white-space: nowrap;
}

.cols-2 { columns: 2; column-gap: 48px; max-width: none; }
.cols-2 li { break-inside: avoid; }

/* ---------- Citations ---------- */

.cite-list { list-style: none; margin: 0; padding: 0; }
.cite-list li {
  padding: 16px 0 16px 0;
  border-bottom: 1px solid var(--rule);
  font-size: .97rem;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 78ch;
}
.cite-list li:last-child { border-bottom: 0; }
.cite-list .title { color: var(--ink); }
.tag {
  display: inline-block;
  font-family: var(--sans);
  font-size: .62rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #fff;
  background: var(--accent);
  border-radius: 2px;
  padding: 2px 7px;
  margin-right: 9px;
  vertical-align: 2px;
}
/* .ico-lock retired 2026-08-09, one day after it shipped. Jim reviewed the
   page and did not realize the essay titles were hyperlinks at all -- and he is
   the site's own demographic -- so a small mark decoding a link nobody noticed
   was the wrong tool. Access is now disclosed by the .cite-btn labels below,
   which name the destination instead of flagging the restriction. */

/* Per-citation action button. The label IS the access disclosure: "Read the
   essay" for free full text, "View abstract" where the journal charges, "Read
   on Doximity" for the physician-login piece. Two visual weights carry the
   same split a second way -- accent outline for the full-text pieces, .alt in
   rule-grey/muted for the gated ones -- so the eye can sort the list before
   reading a word. Block on its own line under the citation: a raggedly inline
   button after variable-length citations gave no scannable column, and this
   audience skews older, so the target is deliberately not subtle. Small caps
   sans matches the .go tile links and the h4s, so it reads native. */
.cite-btn {
  display: block;
  width: fit-content;
  margin-top: 9px;
  font-family: var(--sans);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: 6px 13px;
  border: 1px solid var(--accent);
  border-radius: 3px;
  color: var(--accent-dark);
  background: transparent;
}
.cite-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.cite-btn.alt { border-color: var(--rule); color: var(--muted); }
.cite-btn.alt:hover { background: transparent; border-color: var(--muted); color: var(--ink-soft); }

/* The freely readable pieces and the gated ones are two groups inside one list.
   Without a seam the dates read 2016, 2013, 2012, 2010, 2026 and the list looks
   mis-sorted rather than grouped, so the first gated entry opens a gap. Space
   rather than a heading: the button labels already say what the second group
   is, and a second run of headings under "Published" would out-shout the h4
   above it. */
.cite-list li.access-break { margin-top: 30px; }

/* .legend retired 2026-08-09. It was a tinted box with an accent left border,
   used on essays.html alone, and once Jim's journals sentence went into it the
   box stood 252px tall -- heavier than the h1 it sat under. The words were never
   the problem; the frame was. They now sit in the page head as .note. */

/* Secondary line in a page head: the subtitle says what the page is, this says
   how to use it. Smaller and greyer than the subtitle deliberately, so the two
   do not read as equals. */
.page-head .note {
  font-size: .93rem;
  line-height: 1.6;
  color: var(--muted);
  margin-top: 18px;
}

/* Two-stop contents line. Set as quiet type rather than buttons: with only two
   destinations, anything heavier competes with the section headings it points
   at. */
.jump-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 26px;
  font-family: var(--sans);
  font-size: .74rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  margin: 0 0 40px;
}
.jump-nav a { color: var(--accent-dark); border-bottom-color: rgba(47,109,140,.3); }

/* The site header is sticky and about 90px tall, so an anchor jump parks its
   target underneath it -- measured at exactly 0px from the viewport top, fully
   covered. This clears it plus a little air. Applies to every linkable target,
   not just the new jump nav: "Named lectureships" on the home page has pointed
   at about.html#lectureships since July with the same defect. */
h1[id], h2[id], h3[id], h4[id], section[id] { scroll-margin-top: 108px; }

/* The essays groups are h2 now, not the small-caps h4 they were: they are the
   page's real divisions, and at h4 they read as labels inside one list rather
   than as two halves of the page. A list running into the next heading needs
   the air an h4's 36px margin used to give it. */
.cite-list + h2 { margin-top: 60px; }
h2 + .cite-list { margin-top: 22px; }

/* ---------- Book ---------- */

.book-grid {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 52px;
  align-items: start;
  /* Every other section heading on the site is followed by a .section-intro,
     whose 36px bottom margin does the separating. This heading has no intro
     line, so .with-ico's 8px was the only gap and the content crowded it. */
  margin-top: 36px;
}
.book-cover { box-shadow: 0 16px 38px rgba(27,42,51,.2); border-radius: 2px; }
/* Columns are capped rather than 1fr so the covers stay a consistent size
   whatever the count. With 1fr, dropping the duplicated English cover from
   three tiles to two stretched the remaining pair to roughly 500px each. */
.editions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 260px));
  justify-content: center;
  gap: 28px;
  margin-top: 8px;
}
.editions figure { margin: 0; }
.editions figcaption {
  font-family: var(--sans);
  font-size: .78rem;
  color: var(--muted);
  margin-top: 10px;
  text-align: center;
}
.editions img { box-shadow: 0 10px 26px rgba(27,42,51,.16); border-radius: 2px; }
/* ---------- Editions carousel ----------
   A scroll-snap strip, not a scripted slider. Without JavaScript it stays a
   horizontal scroller that still shows every edition; the script only adds the
   arrows and dots. The covers have different proportions (0.68 for the Spanish
   and audio editions, 0.80 for the Japanese), so the image row is a fixed
   height with the covers sitting on a common bottom edge. That keeps the strip
   from changing height as you move between slides, and keeps every edition
   label on the same line. */
.carousel-track {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Scroll anchoring was parking the strip on slide two: the covers finish
     decoding after the script has reset the position, and the browser then
     "helpfully" preserved what it thought the user was looking at. */
  overflow-anchor: none;
  /* Deliberately NOT scroll-behavior: smooth, and the script deliberately does
     not use scrollTo({behavior:'smooth'}). Either one, combined with
     scroll-snap-type: mandatory, silently cancels the scroll on this engine:
     the dots advanced correctly while the strip never moved at all. Tested both
     ways -- a plain scrollLeft assignment with the default behaviour is the
     only form that reliably moves. Slides change instantly, which suits the
     restraint of the rest of the site. */
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.carousel-slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  margin: 0;
  display: grid;
  grid-template-rows: 444px auto;
  justify-items: center;
  align-items: end;
}
.carousel-slide img {
  max-height: 444px;
  width: auto;
  max-width: 100%;
  box-shadow: 0 16px 38px rgba(27,42,51,.2);
  border-radius: 2px;
}
.edition-name {
  font-family: var(--sans);
  font-size: .8rem;
  color: var(--muted);
  text-align: center;
  margin: 14px 0 0;
}

.carousel-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 16px;
}
.carousel-arrow {
  font-family: var(--sans);
  font-size: 1.4rem;
  line-height: 1;
  width: 32px;
  height: 32px;
  padding: 0;
  cursor: pointer;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 50%;
}
.carousel-arrow:hover:not(:disabled) { color: var(--accent-dark); border-color: var(--accent); }
.carousel-arrow:disabled { opacity: .3; cursor: default; }
.carousel-dots { display: flex; gap: 8px; }
.carousel-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  cursor: pointer;
  border: 0;
  border-radius: 50%;
  background: var(--rule);
}
.carousel-dot[aria-current="true"] { background: var(--accent); }

.rating {
  font-family: var(--sans);
  font-size: .9rem;
  color: var(--ink-soft);
  margin: 28px 0 0;
}

/* ---------- Figures / family ---------- */

/* minmax(0, 1fr), not the implicit auto column. An auto column is sized from
   its items' max-content, so the figures carrying an explicit --row-w (up to
   1000px) stretched the column past the wrap and overflowed the page between
   900px and about 1080px. Worse, max-width: 100% on those figures then
   resolved against the inflated column and could not pull them back. */
.figure-stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 46px; }

figure.plate { margin: 0; }
figure.plate img { border: 1px solid var(--rule); border-radius: 3px; background: #fff; }
figure.plate figcaption {
  font-family: var(--sans);
  font-size: .88rem;
  color: var(--ink-soft);
  /* Sits close under the photograph. At 12px the caption read as detached from
     the image it belongs to. */
  margin-top: 8px;
  /* Was 62ch, which is the textbook readable measure but wrapped these captions
     at roughly half the width of the photograph above them, leaving a ragged
     gap on the right. 82ch is the top of the comfortable range and tracks the
     image far better. Captions here run one to three sentences, so the longer
     line is not the reading burden it would be in body copy. */
  /* Never wider than the plate it sits under, so it cannot stretch the figure
     past the photograph and re-break the alignment. */
  max-width: min(82ch, 100%);
  line-height: 1.5;
}

/* Group photographs that dominate at full wrap width. The cap sits on the
   figure, not the image, so the caption shrinks and centres with the photo
   instead of stranding itself under the left edge. */
figure.plate.plate-medium { max-width: min(100%, 860px); margin-left: auto; margin-right: auto; }

/* ---------- Icons ----------
   Inline line-art SVG. Stroke inherits currentColor via .ico's own color rule,
   so an icon can be recolored by setting `color` on it or a parent. */

.ico {
  display: block;
  width: 30px;
  height: 30px;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  margin: 0 0 16px;
  flex: none;
}
.ico-dot { fill: var(--accent); stroke: none; }

/* icon sitting inline before a heading */
.with-ico { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; }
.with-ico .ico { margin: 0; width: 26px; height: 26px; }
.with-ico h2, .with-ico h3 { margin: 0; }

/* larger, quieter icon for page headers */
/* .page-head .ico retired 2026-08-09. Only About, Expertise and Contact carried
   a title icon -- the pages with no other imagery -- so their headline sat 52px
   lower than the four illustrated pages, and the h1 jumped down the screen as
   you moved between them. Removing the three is the fix that keeps the left
   edge: an icon beside the h1 would have indented every page title ~48px from
   its own body text, undoing the single-left-edge work, and hanging it in the
   margin breaks below 1080px where the wrap has only 24px of padding.
   Icons still mark section headings everywhere via .with-ico; the idiom is
   intact, it just no longer appears at page-title level on some pages and not
   others. */

/* small icon inside a definition list term */
.dt-ico {
  width: 17px; height: 17px; display: inline-block; vertical-align: -3px;
  margin-right: 8px; fill: none; stroke: var(--accent); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}

/* cards get a lighter-weight icon so six of them don't shout */
.card .ico { width: 26px; height: 26px; margin-bottom: 14px; }
.tile .ico { width: 26px; height: 26px; margin-bottom: 14px; }

/* ---------- Tile / card artwork ----------
   Full-color editorial illustrations that replace the line-icons on the
   home page. Square 1:1, centered above a left-aligned heading. */

.tile-art {
  display: block;
  width: 75%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  margin: 0 auto 20px;
}
.tiles .tile .tile-art { max-width: 150px; }
.cards .card .tile-art { max-width: 195px; }

@media (max-width: 700px) {
  .tile-art { width: min(60%, 180px); }
}

/* Subpage editorial illustrations — same artwork reused as supporting
   imagery. page-art heads a page; section-art sits above a section title. */

.page-art {
  display: block;
  width: min(100%, 420px);
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  margin: 0 auto 32px;
}

.section-art {
  display: block;
  width: min(100%, 240px);
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  margin: 8px auto 24px;
}

@media (max-width: 700px) {
  .page-art { width: min(76vw, 340px); }
  .section-art { width: min(58vw, 220px); }
}

/* Editorial two-column rows: copy beside artwork instead of stacked.
   .page-head-grid pairs a page heading with its illustration;
   .illustrated-row pairs a section with its illustration
   (.art-left flips the columns). Below 860px both stack, art first. */

.page-head-grid,
.illustrated-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 280px);
  gap: clamp(36px, 6vw, 72px);
  align-items: start;
}

.page-head-grid {
  grid-template-columns: minmax(0, 1fr) minmax(260px, 320px);
  align-items: center;
}

.page-head-grid .page-head-copy,
.illustrated-row .illustrated-copy {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
}

.page-head-grid .page-art {
  grid-column: 2;
  grid-row: 1;
  width: 100%;
  max-width: 320px;
  margin: 0;
  justify-self: end;
}

.illustrated-row .section-art {
  grid-column: 2;
  grid-row: 1;
  width: 100%;
  max-width: 280px;
  margin: 0;
  justify-self: end;
}

.illustrated-row.art-left {
  grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
}

.illustrated-row.art-left .section-art {
  grid-column: 1;
  justify-self: start;
}

.illustrated-row.art-left .illustrated-copy {
  grid-column: 2;
}

/* Contact inquiry cards: smaller, uniform artwork */
.contact-art {
  display: block;
  width: min(60%, 160px);
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  margin: 0 auto 20px;
}

@media (max-width: 860px) {
  .page-head-grid,
  .illustrated-row,
  .illustrated-row.art-left {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .page-head-grid .page-art,
  .illustrated-row .section-art,
  .illustrated-row.art-left .section-art {
    grid-column: 1;
    grid-row: 1;
    justify-self: center;
    margin: 0 auto;
  }

  .page-head-grid .page-art {
    width: min(76vw, 300px);
  }

  .illustrated-row .section-art,
  .illustrated-row.art-left .section-art {
    width: min(58vw, 220px);
  }

  .page-head-grid .page-head-copy,
  .illustrated-row .illustrated-copy,
  .illustrated-row.art-left .illustrated-copy {
    grid-column: 1;
    grid-row: 2;
  }
}

/* ---------- Two-up figures ---------- */

.plate-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 32px;
  align-items: start;
}
.plate-pair figure { margin: 0; }

/* Pair whose two photographs have opposing aspect ratios (one landscape, one
   portrait): cap the height so they read as one shelf rather than one image
   towering over the other, and centre each within its column. */
.plate-pair.shelf img { max-height: 340px; width: auto; }

/* The pair sits as one centred group rather than one photo per grid column.
   As equal columns the two photos ended up ~228px apart, which read as two
   stray images rather than a pair. Flex lets each figure keep its own width
   and the group centre together.
   The zeroed margins matter: .plate-fit centres a figure with auto margins,
   but auto margins on a flex item absorb all the free space and would push
   the two apart again, overriding justify-content. */
.plate-pair.shelf {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 32px;
}
.plate-pair.shelf > figure.plate { margin-left: 0; margin-right: 0; }

/* Row of related images inside one plate (photo + book covers, etc.).
   Heights are capped so mixed aspect ratios sit as one shelf. */
.plate-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  align-items: center;
  justify-content: flex-start;
}
.plate-row img {
  width: auto;
  height: auto;
  max-height: 330px;
  max-width: 100%;
}
/* Four-image shelf: the default 330px cap overflows the wrap at desktop width
   and drops the fourth image onto a line of its own. A slightly lower cap keeps
   all four on one line; narrow viewports still wrap as usual. */
.plate-row.four-up img { max-height: 292px; }

/* Centred image rows with a left-justified caption.
   -------------------------------------------------
   The caption is a plain block starting at the figure's left edge, so centring
   the images inside a full-width figure strands the caption to their left. The
   fix is to shrink the FIGURE to the width of its image row and centre the
   figure: the row then fills it, and the caption starts under the leftmost
   photograph.
   That requires the row's intrinsic width to be resolvable, which max-height
   does not give -- a height-capped image reports its natural width to intrinsic
   sizing, not its rendered width, so the figure came out ~70px too wide. A
   definite height plus the aspect ratio from the width/height attributes makes
   the used width definite, and fit-content lands exactly on the row.
   Desktop only. Below this breakpoint the row wraps and a fixed height would
   fight max-width:100% and distort the images. */
@media (min-width: 900px) {
  /* Width comes from --row-w, set per figure in the HTML, NOT from fit-content.
     fit-content overshoots the real row width here -- measured 951px against an
     882px row for Rose, 788px against 736px for Ed -- which pushes the centred
     images right of the caption again. Rick's and Sandra's rows happen to land
     exact, so the bug is invisible on two of the four. Pinning the width makes
     all four exact.
     --row-w = sum of the rendered image widths + 18px per gap. Rendered width
     is (row height / natural height) x natural width. Re-measure if an image in
     one of these rows is swapped or a row height below changes. */
  figure.plate:has(.plate-row) {
    width: var(--row-w, fit-content);
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
  }
  /* The row stays left-aligned INSIDE the centred figure. Centring it here
     instead would re-introduce the offset whenever --row-w is a pixel or two
     wider than the true row, and --row-w carries a deliberate cushion so the
     row can never wrap. Left-aligned, the cushion is invisible slack on the
     right and the caption stays exactly under the first image. */
  .plate-row { justify-content: flex-start; }
  /* Heights are chosen to sit at or below each row's shortest natural image,
     so nothing is upscaled: Rick's carpal-tunnel cover is 286px tall, and the
     Sandra pair has to clear a 4.3:1 banner beside a 1.9:1 slide. */
  .plate-row img { height: 330px; max-height: none; }
  .plate-row.four-up img { height: 286px; }
  .plate-row.pair-wide img { height: 158px; }
}
figure.plate .plate-narrow { max-width: min(100%, 430px); }

/* Single-image plates that centre while keeping the caption left-justified
   under the image. Same mechanism as the rows below: pin the figure to the
   image's rendered width and centre the figure, so the caption -- a plain
   block starting at the figure's left edge -- lands under the image's left
   edge instead of the container's. Applies at every width; these figures hold
   one image and cannot wrap, and max-width:100% takes over on narrow screens. */
figure.plate.plate-fit {
  width: var(--row-w);
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
}

/* grid items must be allowed to shrink below their content's intrinsic width */
.figure-stack > figure { min-width: 0; }

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  font-family: var(--sans);
  font-size: .82rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 13px 26px;
  border: 1px solid var(--accent);
  border-radius: 2px;
  color: var(--accent-dark);
  background: transparent;
}
.btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-dark); }
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 26px; }

/* ---------- Tiles (home nav) ---------- */

/* Stated column counts, NOT auto-fit. auto-fit packs in as many 240px columns
   as fit, which for four tiles meant three across with the fourth stranded
   underneath everywhere from about 812px to 1073px -- a band that covers most
   small laptops and landscape tablets. Four items read correctly as 4, as 2x2,
   or as 1x4; 3+1 is the one arrangement that looks like a bug. Naming the
   counts per breakpoint makes that state unreachable rather than unlikely.
   Anyone adding or removing a tile has to revisit these numbers: at three or
   five items the counts below no longer divide evenly, which is exactly the
   problem this rule exists to prevent. */
.tiles { display: grid; grid-template-columns: 1fr; gap: 22px; }
/* 560px: below this a pair of tiles would be about 245px each, at which point
   the heading and the one-line description start breaking badly. */
@media (min-width: 560px) { .tiles { grid-template-columns: repeat(2, 1fr); } }
/* 1040px: four tiles about 228px each. Measured, not guessed -- at 1000px the
   "Areas of expertise" link wrapped and left its arrow alone on a second line,
   and the wrap cleared at 1002px. Sitting 2px off that edge would be false
   precision: these labels are set in the system sans and the tiles in the
   Iowan/Palatino stack, both of which resolve differently on macOS, Windows
   and Linux, so the true threshold moves per machine. 1040 buys ~26px of tile
   width over the measured edge, which should absorb that spread.
   ("Selected publications" wraps at every width, including the 242px tile the
   1080px wrap allows. That predates this rule and is untouched here.) */
@media (min-width: 1040px) { .tiles { grid-template-columns: repeat(4, 1fr); } }
.tile {
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 28px 24px;
  background: #fff;
  display: block;
  color: var(--ink);
}
.tile:hover { border-color: var(--accent); background: #fff; }
.tile h3 { margin-bottom: 8px; }
.tile p { font-size: .93rem; color: var(--ink-soft); margin: 0; max-width: none; }
.tile .go {
  font-family: var(--sans);
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
  display: block;
  margin-top: 14px;
}

/* ---------- Pull quote ---------- */

blockquote.pull {
  margin: 0;
  padding: 0 0 0 26px;
  border-left: 3px solid var(--accent);
  font-size: 1.25rem;
  line-height: 1.5;
  color: var(--ink);
  max-width: 56ch;
}
blockquote.pull cite {
  display: block;
  font-family: var(--sans);
  font-style: normal;
  font-size: .8rem;
  letter-spacing: .06em;
  color: var(--muted);
  margin-top: 14px;
}

/* ---------- Contact ---------- */

.contact-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 40px; }
.contact-grid dt {
  font-family: var(--sans);
  font-size: .74rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.contact-grid dd { margin: 0 0 22px; }

/* ---------- Footer ---------- */

.site-foot {
  border-top: 1px solid var(--rule);
  padding: 44px 0 56px;
  font-family: var(--sans);
  font-size: .84rem;
  color: var(--muted);
  background: var(--paper-alt);
}
.site-foot .wrap { display: flex; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.site-foot a { color: var(--ink-soft); border: 0; }
.site-foot a:hover { color: var(--accent-dark); }
.site-foot ul { list-style: none; display: flex; gap: 20px; margin: 0; padding: 0; flex-wrap: wrap; }

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .hero-grid { grid-template-columns: 1fr; gap: 36px; }
  .hero-grid .portrait-wrap { order: -1; max-width: 300px; }
  .book-grid { grid-template-columns: 1fr; gap: 32px; }
  .book-grid .book-cover { max-width: 260px; }
  .cols-2 { columns: 1; }
  section { padding: 48px 0; }
  .hero { padding: 48px 0 44px; }
}

@media (max-width: 620px) {
  body { font-size: 17px; }
  /* wrapped header eats ~150px of a small viewport while scrolling — unstick it */
  .site-head { position: static; }
  .site-head .wrap { padding-top: 14px; padding-bottom: 14px; }
  .site-nav ul { gap: 2px 16px; }
  .site-nav a { font-size: .72rem; letter-spacing: .07em; }
}

@media print {
  .site-head, .site-foot, .btn-row { display: none; }
  body { background: #fff; font-size: 11pt; }
}
