/* ============================================================
   site.css
   The whole visual system for https://ssouyris.com/.

   The palette is the deck theme's, repeated rather than imported: these
   pages render through Quarto's html format, which never compiles
   theme.scss. Keep these seven values in step with
   Slide-deck-template/_extensions/slide-deck/theme.scss lines 36-49 if
   the palette ever moves.

   Paths here are relative to THIS FILE, which lands at
   _output/assets/site.css, so assets/fonts/x.woff2 is fonts/x.woff2.
   ============================================================ */

:root {
  --deckblue: #1D4158;
  --rpired:   #D6001C;
  --ink:      #23373B;
  --paper:    #FAFAFA;
  --gray:     #B9CCD8;
  --tint:     #EFF2F3;
  --muted:    #5B7386;

  --gutter: 52px;

  /* The sticky bar's height, overwritten at runtime by the script in
     _includes/foot.html with the value it measures. 61px is the
     desktop height and is what applies if that never runs. Declared
     here so the fallback lives in one place rather than inside the
     `var()` call that uses it. */
  --barclear: 61px;

  /* THE ONE NUMBER THAT SETS THE PAGE'S WIDTH, and it is derived rather
     than chosen. The nav bar, every section hairline and the footer all
     take it, so the content column is `--measure` minus two gutters:

         1044 - 2 x 52 = 940

     940 is the hero's width, which is itself forced (see the long note
     above `.hero`: the bio at a readable 64ch plus a photograph that
     stays portrait-shaped comes to about 940, and stretching the pair
     wider opens a hole in the middle).

     It was 1180 until 13 September 2026. The hero was capped at 940
     while everything else kept the full 1076px content column, so the
     photograph's right edge stopped 136px short of the nav's last item
     and of every hairline below it [measured at 1440 and 1600]. Two
     right edges on one page, 136px apart, is the kind of thing that
     reads as "the page is too wide" without the cause being obvious.
     Sebastián saw it and asked for the menu to come left to the photo.

     Change this and the whole site moves together. Do not reintroduce a
     width on any single block: that is what broke the alignment. */
  --measure: 1044px;
}

/* ------------------------------------------------------------
   The two faces, from the same woff2 files the decks load.

   Two unicode ranges each. The latin file covers ASCII and the common
   accents; latin-ext covers the rest, and this site needs it: Cortés,
   Ordoñez, Durán, Sauré, Ramírez and Alarcón all appear in the
   publication list, and without the ext file the browser falls back
   mid-word and the accented letter changes typeface.
   ------------------------------------------------------------ */
@font-face {
  font-family: "Source Sans 3"; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,
    U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
    U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Source Sans 3"; font-style: normal; font-weight: 400;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-ext-400-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,
    U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: "Source Sans 3"; font-style: normal; font-weight: 600;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,
    U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
    U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Source Sans 3"; font-style: normal; font-weight: 600;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-ext-600-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,
    U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family: "Source Sans 3"; font-style: italic; font-weight: 400;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-400-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,
    U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
    U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family: "Source Sans 3"; font-style: italic; font-weight: 400;
  font-display: swap;
  src: url("fonts/source-sans-3-latin-ext-400-italic.woff2") format("woff2");
  unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,
    U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

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

/* Quarto builds its own <header id="title-block-header"> from the YAML
   `title:`, which would sit above the nav as a second banner. The title
   stays in the YAML because it is what fills <title> and the tab. */
header#title-block-header { display: none !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Source Sans 3", "Helvetica Neue", "Segoe UI", Helvetica,
               Arial, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
/* LINKS CARRY NO UNDERLINE AT REST, with one scoped exception below.
   The colour separates them from the body text; the underline arrives on
   hover, which is where a reader asks "is this clickable?". The CV page
   alone holds forty links inside tables, and underlining them all at rest
   turned the page into a field of rules. */
a { color: var(--deckblue); text-decoration: none; }
a:hover, a:focus {
  color: var(--rpired);
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

/* ------------------------------------------------------------
   THE EXCEPTION, AND IT IS A CONFORMANCE REQUIREMENT RATHER THAN A
   PREFERENCE. WCAG 1.4.1 Use of Color, Level A: colour may not be the
   only way a link is distinguished from the text around it. The
   threshold for identifying something by colour alone is 3:1 against
   its surroundings.

   Measured: --deckblue #1D4158 against --ink #23373B is 1.16:1. A
   reader with deuteranopia sees an unbroken run of text.

   NO BLUE FIXES THIS. The whole range was walked against both the body
   colour and the ground [measured]:

       link        vs body text    vs paper
       #1D4158       1.16            10.32   (current)
       #15406B       1.18            10.17
       #1257A0       1.72             6.95
       #0B5CA8       1.85             6.47   (already too bright for the page)

   Every value approaching 3:1 against the ink loses its contrast against
   the paper. A non-colour cue is the only conformant option, so the 37
   links that sit inside a sentence get one.

   SCOPE IS THE WHOLE DESIGN OF THIS RULE. A link that is the entire
   content of a chip, a nav item, a .doi line or a table cell is
   identified by position, the same way a menu item is, and stays bare.
   Only a link with text running either side of it is ambiguous. That is
   17 of the CV's links rather than all forty, which is what the original
   objection was about.

   The underline is set at 30% alpha and pushed 3px clear of the
   baseline, which is how the FT and the NYT set in-body links: present
   to the eye, quiet enough not to read as a rule. On hover it goes to
   full strength along with the colour change.
   ------------------------------------------------------------ */
.hero .role a,
main p a,
main .lede a,
table.rows td a,
.now .when a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(29, 65, 88, .30);
  text-underline-offset: 3px;
}
.hero .role a:hover, .hero .role a:focus,
main p a:hover, main p a:focus,
main .lede a:hover, main .lede a:focus,
table.rows td a:hover, table.rows td a:focus,
.now .when a:hover, .now .when a:focus {
  text-decoration-color: currentColor;
}

/* The DOI line is handled where it is defined, further down: it already
   uses a border-bottom rather than text-decoration, for a reason given
   there, so its rest state is set on that border instead. */
em { font-style: italic; }

/* The skip link is invisible until a keyboard lands on it. Every page
   opens with a nav of five items, and without this a screen-reader or
   keyboard user walks all five on every page before reaching content. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 20;
  background: var(--rpired); color: #fff; padding: 10px 16px;
  font-weight: 600; text-decoration: none;
}
.skip:focus { left: 0; color: #fff; }

/* ------------------------------------------------------------
   The bar every page carries
   ------------------------------------------------------------ */
.topbar {
  background: var(--deckblue);
  border-bottom: 4px solid var(--rpired);
  position: sticky; top: 0; z-index: 10;
}
.topbar .inner {
  max-width: var(--measure); margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap;
}
/* THE BRAND IS THE DOMAIN, IN LOWERCASE. It was uppercase for one
   round, matching the social card's top band, and Sebastián asked for
   lowercase instead.

   The tracking came down with the case. The card's 0.136em is right for
   capitals, which need air between them to stay legible; the same value
   on lowercase pulls the word apart and reads as a mistake. .02em is
   enough to keep the domain from looking cramped beside the 4px red
   rule and no more.

   The size goes back up to 1.06rem: lowercase occupies less width and
   reads smaller than capitals at the same value. It stays larger than
   the nav items on the right (.86rem) so the bar still has an anchor. */
.brand {
  color: #fff; text-decoration: none; font-weight: 600; font-size: 1.06rem;
  letter-spacing: .02em; padding: 15px 0;
}
.brand:hover, .brand:focus { color: #fff; opacity: .82; }
/* THE NEGATIVE MARGIN IS WHAT CENTRES THE CURRENT-PAGE MARKER, and it
   pairs with the padding on `:last-child` below. Read the two together.

   The last item used to carry `padding-right: 0` so that its box ended
   exactly on the content column's right edge. That kept the TEXT flush,
   which is right, but it left the item's padding box 15px wider on the
   left than on the right, and the marker is an inset shadow that spans
   that box: under CV the white rule ran from 1158 to 1190 while the
   letters ran from 1173 to 1190, overhanging 15px on one side and 0 on
   the other [measured].

   So the padding goes back to symmetric and the whole nav is pulled
   right by the same 15px instead. The text still ends on the content
   edge, the marker is now centred on it, and the 15px of box that hangs
   past the edge is navy on navy inside the gutter. */
.topbar nav { display: flex; flex-wrap: wrap; margin-right: -15px; }
.topbar nav a {
  color: #fff; text-decoration: none; font-size: .86rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 17px 15px; border-bottom: 3px solid transparent;
  margin-bottom: -3px;
}
/* Symmetric, like every other item. See the negative margin above. */
.topbar nav a:last-child { padding-right: 15px; }
.topbar nav a:hover, .topbar nav a:focus {
  color: #fff; background: rgba(255,255,255,.09);
}
/* THE CURRENT-PAGE MARKER, and it was invisible on every page from the
   first render until 13 September 2026.

   Each page marks its own item in a <style> block in its own body,
   because include-before-body serves one nav file to all five, and the
   declaration it wrote was `border-bottom-color: var(--rpired)`. The nav
   item's border-bottom is 3px with `margin-bottom: -3px`, so it lands at
   y 57-60. The bar's own rule is 4px of the SAME RED at y 57-61
   [measured]. The marker was painted inside the rule, red on red,
   1.00:1. Nothing was ever shown.

   index.qmd already knew this. Its comment says a border-bottom "would
   collide with the bar's own 4px red rule, so this is an inset shadow
   sitting just above it", and the brand marker has been a box-shadow
   since day one. The reasoning was never carried across to the nav.

   So the nav uses the brand's mechanism, with one change: WHITE rather
   than red. An inset shadow paints inside the link's own box, which is
   navy, and red on navy is 1.99:1 against the 3:1 a state indicator
   wants. White is 10.77:1 and matches the type it sits under. */
/* MATCHED BY THE ONE-LINE <style> EACH PAGE WRITES IN ITS OWN BODY, by
   id: `#nav-cv{box-shadow:inset 0 -3px 0 #fff}`. This rule holds the
   shape so the pages only restate the id.

   It used to read `[aria-current="page"], .is-current`, and NOTHING ON
   THE SITE SET EITHER [measured]: two selectors that matched zero
   elements on all five pages while the marker was really being drawn by
   the per-page blocks.

   aria-current IS set now, by the script in _includes/foot.html, which
   finds the marked link by reading this declaration back off the
   computed style. DELIBERATELY NOT A SELECTOR HERE: if
   `[aria-current="page"]` also drew the shadow, the script's test would
   be reading its own output. The attribute is for a screen reader, this
   declaration is for the eye, and they are kept one-directional. */
.topbar nav a.is-current {
  box-shadow: inset 0 -3px 0 #fff;
}

/* ------------------------------------------------------------
   Page furniture
   ------------------------------------------------------------ */
.band { max-width: var(--measure); margin: 0 auto; padding: 0 var(--gutter); }
main { display: block; }

.pagehead { padding: 40px 0 6px; }
.pagehead h1 {
  margin: 0; font-size: 2.05rem; font-weight: 600; color: var(--deckblue);
  letter-spacing: .01em;
}
.pagehead p.stand {
  margin: 12px 0 0; font-size: 1.06rem; color: var(--muted);
  max-width: 68ch; line-height: 1.6;
}

section.sec { padding: 38px 0 0; }
section.sec > h2 {
  margin: 0 0 16px; font-size: 1rem; font-weight: 600; color: var(--deckblue);
  text-transform: uppercase; letter-spacing: .09em;
  padding-bottom: 7px; border-bottom: 1px solid var(--gray);
}
section.sec h3 { font-size: 1.05rem; color: var(--deckblue); font-weight: 600;
  margin: 0; }
p { margin: 13px 0; }
p.lede { font-size: 1.08rem; max-width: 66ch; }

/* ------------------------------------------------------------
   Home: the hero

   THE PHOTOGRAPH SITS ON THE RIGHT AND ENDS WHERE THE PROSE ENDS.

   Two named areas in one column each, and a third row for the chips:

       text   photo
       chips  photo(no)

   The grid is two columns and two rows. `.hero-text` takes row 1 of
   column 1, `.chips` row 2 of column 1, and the image row 1 of column 2
   alone. Because a grid item stretches to its row by default, the image
   fills exactly the height of the text above the chips, which is what
   makes its bottom edge land on the last line of the bio rather than on
   the chips below it.

   `object-fit: cover` with `object-position` a little above centre is
   what lets a fixed box hold a portrait of any proportion without
   distorting it: the image is scaled to fill and the overflow is
   cropped from the bottom, where a portrait has least to lose.
   ------------------------------------------------------------ */
/* THE HERO SETS THE PAGE'S WIDTH, and carries no width of its own.

   The arithmetic runs one way. The bio is capped at a readable measure
   (509px at 64ch) and the photograph has to stay roughly portrait-shaped
   or `cover` starts cropping his shoulders: at 442px tall, its natural
   width is 297px, and it is drawn at 360. Text plus gap plus photo is
   about 940px, so 940 is what the content column should be, and
   `--measure` is set to 940 plus two gutters for exactly that reason.

   Stretching the pair wider does not work: at the old 1076px column it
   left 244px of dead air in the middle [measured], because widening
   either side is blocked (a 70-character measure is the limit of
   comfortable reading, and a photograph wide enough to close the
   distance is no longer a portrait).

   The first fix capped THE HERO at 940 and left the rest of the page at
   1076. That traded a hole in the middle for two right edges 136px
   apart, which is worse: the photograph stopped short of the nav and of
   every hairline under it. Setting the page to the hero instead makes
   one edge for everything. So: no max-width here. */
.hero {
  display: grid;
  grid-template-columns: 1fr var(--portrait-w, 360px);
  grid-template-rows: auto auto;
  column-gap: 40px;
  padding: 48px 0 8px;
}
.hero-text { grid-column: 1; grid-row: 1; }
/* The chips span BOTH columns. Held to column 1 they were confined to
   the text's 540px and the sixth one wrapped to a second line; across
   the full 940px the six sit on one row, and the run of pills anchors
   the whole hero block rather than just the prose half. */
.hero .chips { grid-column: 1 / -1; grid-row: 2; }
.hero img.portrait {
  grid-column: 2; grid-row: 1;
  align-self: stretch;
  width: 100%;
  /* `height: 0` WITH `min-height: 100%`, and the pair is load-bearing.
     A row sized `auto` takes the tallest item in it, and an image's
     intrinsic height counts: at 320px wide this portrait is 475px tall,
     which was taller than the prose and so the image was setting the
     row height and then filling it, landing 32px below the last line
     [measured]. `height: 0` drops its contribution to the row to
     nothing, so the row is sized by the text alone; `min-height: 100%`
     then stretches it back to exactly that height. */
  height: 0; min-height: 100%;
  object-fit: cover; object-position: 50% 22%;
  display: block;
  border: 1px solid var(--gray); background: var(--tint);
}
.hero h1 {
  margin: 0; font-size: 2.35rem; font-weight: 600; color: var(--deckblue);
  letter-spacing: .01em; line-height: 1.16;
}
.hero .role { margin: 10px 0 0; font-size: 1.06rem; color: var(--muted);
  line-height: 1.5; }
/* The rule stops just past the prose rather than running the width of
   the column. Both take their measure from one variable so they cannot
   drift: the bio is capped at it, the rule overhangs by two characters,
   which at this size is about 16px [measured: text box 512px, rule
   528px, column 712px]. */
.hero { --bio-measure: 64ch; }
.hero .rule {
  height: 2px; background: var(--rpired); margin: 20px 0;
  max-width: calc(var(--bio-measure) + 2ch);
}
.hero p.bio { margin: 0 0 14px; max-width: var(--bio-measure); }
.hero p.bio:last-child { margin-bottom: 0; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 22px 0 0; }
.chips a {
  border: 1px solid var(--gray); background: #fff; color: var(--deckblue);
  padding: 5px 13px; border-radius: 999px; font-size: .87rem;
  text-decoration: none; font-weight: 600;
}
/* BLUE IS THE DEFAULT PILL, not red. A filled pill is a strong shape
   and red made it an alert; the deck blue is the nav bar's own colour,
   so a filled pill reads as part of the site. Red survives as a line,
   in the nav underline and the hero rule, where it points rather than
   fills. */
.chips a:hover, .chips a:focus {
  background: var(--deckblue); border-color: var(--deckblue); color: #fff;
  text-decoration: none;
}
/* `.chips span` was here, for a chip that is a label rather than a link.
   No such chip exists on any page [measured], so the rule is gone. The
   `.jumps` eyebrow is a sibling of `.chips`, not a child of it. */

/* ------------------------------------------------------------
   THE SECTION INDEX, on the CV page only.

   That page is ten sections and about four screens with no way to reach
   the sixth one except by scrolling past five. Every other page here is
   one or two screens and needs nothing.

   It reuses the pill rather than inventing a second shape, and the row
   is LABELLED instead of arrowed. Ten chips each carrying a down-arrow
   is ten decorative characters in one line; the eyebrow says the same
   thing once, in the uppercase letterspaced treatment the section
   headings already use, so the row reads as a heading with its contents
   beside it.

   The labels are shortened from the headings they point at: "Academic"
   for "Academic appointments". The long forms put the row over three
   lines at 1180px, at which width an index is longer than the thing it
   indexes.
   ------------------------------------------------------------ */
.jumps {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 8px 14px;
  margin: 26px 0 0; padding-top: 14px;
  border-top: 1px solid var(--gray);
}
.jumps .eyebrow {
  font-size: .78rem; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .09em;
  margin-right: 4px;
}
.jumps .chips { margin: 0; }

/* THE BAR IS STICKY, so a heading jumped to lands underneath it without
   this. --barclear is the bar's measured height, published by the seven
   lines of script in _includes/foot.html; 61px is its desktop height and
   the fallback if that never runs. The 18px on top leaves the section's
   own hairline visible above the heading, which is how a reader sees
   they arrived at the top of a section rather than the middle of one.

   A HARDCODED VALUE WOULD BE WRONG AND WAS. The bar's height is not
   monotonic in viewport width: the nav items wrap against the brand, so
   across 320 to 700 pixels it runs 162, 120, 58, 120, 58, 61, flipping
   every four pixels in two ranges [measured]. A fixed 80px cleared the
   bar at 1280 and 768 and put the heading 2px UNDER it at 390. */
section.sec[id] { scroll-margin-top: calc(var(--barclear, 61px) + 18px); }

/* ------------------------------------------------------------
   THE HOME PAGE BLOCKS, and why there are no boxes.

   The first version drew each item as a white card with a thin grey
   border and a 3px coloured rule across the top, sitting on a tinted
   band. That is the default card of every presentation template, and
   Sebastian recognised it as such on sight. The chrome was also doing
   no work: a border around a block of text says "this is a block of
   text", which the whitespace already said.

   What replaces it is the type. Each item is a column under a single
   hairline, with a letterspaced eyebrow above the title carrying the
   funder and the year. Vertical hairlines separate the columns. That is
   the same rule-and-gutter language as the publication list and the CV
   tables, so the site reads as one system rather than as boxes here and
   rows there.
   ------------------------------------------------------------ */
/* Alternative B, chosen over the three-column version: full-width rows
   with the year in a fixed left column in tabular figures and the funder
   under it. Structurally identical to the CV tables, so the two pages
   use one language, and it scales past three entries without becoming a
   wall of narrow columns.

   The first row takes the dark rule and the rest a grey one, so the
   block reads as one table with a head rather than three separate
   things. */
.now { margin-top: 20px; }
.now > article {
  display: grid; grid-template-columns: 12em 1fr; gap: 30px;
  padding: 16px 0; border-top: 1px solid var(--gray);
}
.now > article:first-child { border-top: 1px solid var(--ink); }
.now .when {
  font-variant-numeric: tabular-nums; color: var(--deckblue);
  font-weight: 600; font-size: .95rem;
}
.now .when em {
  display: block; font-style: normal; font-weight: 400;
  color: var(--muted); font-size: .88rem; margin-top: 2px;
}
.now .when a { color: inherit; }
.now .when a:hover, .now .when a:focus { color: var(--rpired); }
.now h3 { margin: 0; font-size: 1.06rem; color: var(--deckblue);
  font-weight: 600; line-height: 1.32; }
/* 66ch, down from 72. 72 characters is past the ceiling where the eye
   starts losing its place on the line return. */
.now p { margin: 6px 0 0; font-size: .95rem; color: #3d5361; max-width: 66ch; }

/* THE SELECTED-PUBLICATIONS VARIANT. Same rows, one thing inverted: the
   gutter leads with the journal rather than the year, because a paper is
   placed by where it appeared and a project is placed in time. Read down
   the column and it says Operations Research, POM, INFORMS Journal on
   Applied Analytics, EJOR, which is the front page's argument set as a
   list.

   The journal is italic and takes the weight; the year drops to the
   muted second line the funder occupies in the block below. Journal
   names run long, so this column wraps to two and three lines by design,
   and the tightened line-height keeps a wrap from reading as two
   separate entries. */
.now-pubs .when em {
  display: block; font-style: italic; font-weight: 600;
  color: var(--deckblue); font-size: .95rem;
  line-height: 1.34; margin: 0 0 3px;
}
.now-pubs .when {
  font-weight: 400; color: var(--muted); font-size: .88rem;
}
/* A title, not a headline: it is set at the same size as the block below
   but has to survive being 130 characters long. */
.now-pubs h3 { line-height: 1.34; }
.now-pubs p { color: var(--muted); font-size: .93rem; }

/* The award marker follows a BLOCK here, where on the publications page
   it follows an inline span, so it lands on its own line rather than
   beside the title. That is the better arrangement for titles this long,
   and it means the 11px inline nudge is now a stray indent: a marker
   starting a line sits on the line's left edge. */
.now-pubs .mark { margin: 7px 0 0; }

/* The way out of a selected list has to be visible, or "selected" reads
   as "all". */
p.more { margin: 22px 0 0; font-size: .95rem; }

/* NOTHING FOLLOWS THE CURRENT BLOCK ON THE HOME PAGE. A `.ways` grid of
   four link blocks lived here and was removed with nothing in its place
   [Sebastián, Sept 2026]: it restated the top bar's four destinations one
   screen lower. Its rules are gone rather than orphaned; `git log -p
   assets/site.css` has them if the decision is revisited. */

/* ------------------------------------------------------------
   Publication lists
   ------------------------------------------------------------ */
ul.pubs { list-style: none; margin: 0; padding: 0; }
ul.pubs > li { padding: 14px 0; border-top: 1px solid var(--gray); }
ul.pubs > li:first-child { border-top: none; }
.pt { font-weight: 600; }
.pv { display: block; color: var(--muted); font-size: .94rem; margin-top: 2px; }
/* The identifier line under a citation: a full DOI, an SSRN address, a
   conference URL. Set as its own line rather than run into the
   reference, so a reader scanning for something to click finds it in
   the same place every time.

   `overflow-wrap: anywhere` is load-bearing on a phone. A DOI is one
   unbreakable token of up to about fifty characters, and at 390px it
   pushes the page wider than the viewport and turns on horizontal
   scrolling for the whole document [measured before the rule was added].

   Underlined with a border rather than text-decoration so the rule sits
   clear of the descenders in a string full of slashes and digits.

   THE BORDER IS NOW VISIBLE AT REST, at 35% alpha. It was transparent
   until the WCAG 1.4.1 pass, which made this the worst measured case on
   the site: a muted link sitting on muted text is 1.00:1, the identical
   colour, so nothing whatsoever distinguished it. The DOI is a block of
   its own, so position argues it is identifiable, and the cost of making
   that explicit is one value. */
.doi { display: block; margin-top: 4px; font-size: .87rem;
  overflow-wrap: anywhere; }
.doi a { color: var(--muted); text-decoration: none;
  border-bottom: 1px solid rgba(91, 115, 134, .35); }
.doi a:hover, .doi a:focus { color: var(--rpired); text-decoration: none;
  border-bottom-color: var(--rpired); }

/* The award marker. It was a filled red pill, which shouted louder than
   the paper it was attached to and read as a presentation callout. It is
   now set as type: small, letterspaced, in the muted grey, hanging off a
   thin rule. It reads as an annotation on the citation, which is what it
   is, and it survives being printed in black and white. */
/* THE AWARD MARKER.
   Three versions, and the reasoning is worth keeping because two of
   them were rejected.

   1. A filled RED pill. Rejected: it shouted louder than the paper it
      was attached to, and the shape reads as a presentation callout.
   2. Grey letterspaced small caps hanging off a thin rule. Quiet, but
      too quiet: an award was no more visible than a page number.
   3. What is here. A filled box in the deck blue with white type and a
      3px radius, asked for directly. It carries the weight of version
      one without the red, and the blue is the nav bar's own colour, so
      it reads as part of the site rather than as an alert.

   Four papers carry one. That is the whole reason a filled box works
   here and would not work on, say, the journal name: it marks the
   exception, and there are few enough exceptions for the page to keep
   its quiet.

   The link inside is white and takes an underline only on hover, so a
   linked marker and an unlinked one are identical at rest. */
.mark {
  display: inline-block; margin-left: 11px;
  padding: 3px 9px 2px;
  background: var(--deckblue); color: #fff;
  border-radius: 3px;
  font-size: .72rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; line-height: 1.5;
  vertical-align: 2px; white-space: nowrap;
}
.mark a { color: #fff; text-decoration: none;
  border-bottom: 1px solid transparent; }
.mark a:hover, .mark a:focus { color: #fff; text-decoration: none;
  border-bottom-color: rgba(255,255,255,.65); }
/* Lifted blue on hover rather than red, for the same reason the chips
   changed: a filled pill in red reads as a warning. */
.mark:has(a):hover, .mark:has(a):focus-within { background: #2B5F80; }

/* ------------------------------------------------------------
   Two-column rows: years, course codes, outlets
   ------------------------------------------------------------ */
/* QUARTO REWRITES THESE TABLES, and the two rules below undo the part
   that breaks them.

   A `{=html}` block is NOT passed through untouched when it contains a
   table: Pandoc parses it, Quarto's table post-processor reserialises
   it, and two things happen. It injects a <colgroup> of equal widths
   (33/33/33 on the teaching page, 50/50 on the five CV tables), which
   silently overrode every `td.crs`, `td.yr`, `td.out` and `td.term`
   width on this site from the first render. And it strips class
   attributes from elements inside cells, so a `<p class="blurb">`
   arrives as a bare `<p>`.

   Hence: neutralise the injected column widths, and target paragraphs
   inside cells structurally rather than by a class that will not
   survive. Measured before the fix: the course-code column rendered at
   33% of 1180px rather than the 8.4em asked for. */
table.rows { width: 100%; border-collapse: collapse; font-size: .97rem;
  table-layout: auto; }
table.rows col { width: auto !important; }
table.rows td { padding: 11px 0; border-top: 1px solid var(--gray);
  vertical-align: top; }
table.rows tr:first-child td { border-top: none; }
td.yr { width: 5.2em; color: var(--deckblue); font-weight: 600;
  font-variant-numeric: tabular-nums; }
td.crs { width: 8.4em; color: var(--deckblue); font-weight: 600;
  white-space: nowrap; }
td.out { width: 13em; color: var(--muted); }
td.term { width: 6em; color: var(--muted); text-align: right;
  white-space: nowrap; vertical-align: top; }

/* A course description is a paragraph, not a half-line, so it takes a
   reading measure of its own. Without the cap it runs the full width of
   the table cell, which at 1280px is over 110 characters and past the
   point where the eye reliably finds the next line. */
table.rows td p { margin: 5px 0 0; color: var(--muted); font-size: .94rem;
  max-width: 68ch; line-height: 1.55; }
td strong { color: var(--ink); font-weight: 600; }

/* ------------------------------------------------------------
   Footer
   ------------------------------------------------------------ */
footer.sitefoot {
  border-top: 1px solid var(--gray);
  margin-top: 54px;
}
footer.sitefoot .inner {
  max-width: var(--measure); margin: 0 auto;
  padding: 18px var(--gutter) 44px;
  font-size: .88rem; color: var(--muted);
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
footer.sitefoot a { color: inherit; text-decoration: none; }
footer.sitefoot a:hover, footer.sitefoot a:focus {
  color: var(--rpired); text-decoration: underline;
  text-underline-offset: 2px;
}

/* ------------------------------------------------------------
   Narrow
   ------------------------------------------------------------ */
@media (max-width: 900px) {
  .now > article { grid-template-columns: 8.5em 1fr; gap: 20px; }
  :root { --gutter: 24px; --portrait-w: 270px; }
  /* `max-width: none` used to be here, to release the hero's own 940px
     cap once the viewport was narrower than it. The cap is gone: the
     page's width is the hero's width now, so there is nothing to
     release. Only the gap tightens. */
  .hero { column-gap: 30px; }
  .hero h1 { font-size: 2rem; }
}
@media (max-width: 620px) {
  /* The year column has nowhere to sit at this width, so the row
     becomes two stacked blocks and the year leads. */
  .now > article { grid-template-columns: 1fr; gap: 6px; }
  /* The box keeps its shape and moves to its own line, rather than
     stretching the width of the column or forcing the title to wrap
     around it. */
  .mark { margin: 7px 0 0; white-space: normal; }
  .pt + .mark { display: table; }
  /* One column. The stretch has nothing to stretch against, so the
     photograph goes back to its own proportions at a fixed width and
     leads, with the name under it. */
  .hero { grid-template-columns: 1fr; row-gap: 0; }
  .hero-text { grid-row: 2; }
  .hero .chips { grid-row: 3; }
  .hero img.portrait {
    grid-column: 1; grid-row: 1;
    width: 190px; height: auto; min-height: 0; align-self: start;
    margin-bottom: 24px;
  }
  .topbar .inner { padding-bottom: 4px; }
  .brand { padding-bottom: 8px; }
  /* The same centring problem as the desktop bar, mirrored. The nav
     stacks under the brand here and has to start on the gutter, and the
     old way to get that was `padding-left: 0`, which left every item's
     box 14px wider on the right than on the left and the marker
     correspondingly off-centre [measured: 0 left, 14 right].

     Symmetric padding, and the nav is pulled out by that amount on both
     sides instead, so the first item's text still starts on the gutter
     and the marker is centred on whatever it marks.

     `:last-child` IS NAMED AGAIN HERE ON PURPOSE. The desktop rule
     `.topbar nav a:last-child` has higher specificity than
     `.topbar nav a`, and a media query adds none, so without repeating
     the selector the last item kept its 15px right padding against 14px
     everywhere else and the marker under CV came out 14/15 [measured]. */
  .topbar nav { margin-left: -14px; margin-right: -14px; }
  .topbar nav a,
  .topbar nav a:last-child { padding: 10px 14px; }
  td.out, td.crs, td.yr { display: block; width: auto; border: none;
    padding-bottom: 0; }
  table.rows td { padding: 9px 0; }
  td.term { text-align: left; }
}

/* ------------------------------------------------------------
   Print. A colleague printing the CV page should get the text, not a
   deckblue bar across the top of every sheet.
   ------------------------------------------------------------ */
@media print {
  .topbar, .skip, footer.sitefoot { display: none !important; }
  /* A filled box prints as a solid dark rectangle, and browsers drop
     background colours by default anyway, which would leave white type
     on white paper. Outline it and set the type in black instead. */
  .mark { background: none !important; color: #000 !important;
    border: 1px solid #000; }
  .mark a { color: #000 !important; }
  body { font-size: 11pt; background: #fff; }
  a { text-decoration: none; color: #000; }
  section.sec { break-inside: avoid; }
}
