/* =====================================================================
   GLOW — ERC Consolidator Grant project site stylesheet
   Dark theme, system sans-serif, single column with card components.
   ===================================================================== */

:root {
	/* Surfaces */
	--bg:            #0a0c11;
	--bg-raised:     #12151d;
	--bg-card:       #151926;
	--border:        #2a3040;
	--border-strong: #3b4358;

	/* Text */
	--text:      #e8eaf0;
	--text-soft: #c2c8d6;
	--text-mute: #9198a8;

	/* Accents: warm amber primary (the GLOW logo's colour), cyan secondary.
	   The accent means "link" and nothing else -- see docs/SITE_CONVENTIONS.md
	   section 7. Changing the project's colour is these four lines. */
	--accent:         #f2a45c;
	--accent-strong:  #ffc389;
	--accent-dim:     #c07a34;
	/* The underline under a body link. Derived from --accent and kept beside it
	   because it has already drifted once: it sat at the pre-amber purple for
	   several commits after the palette changed, putting a violet rule under
	   every amber link. */
	--link-line:      rgba(242, 164, 92, 0.45);
	/* Tinted fills that must track the accent: the badge ground, the New pulse's
	   halo. Both were still violet after the palette change. */
	--accent-wash:    rgba(242, 164, 92, 0.14);
	--accent-halo:    rgba(242, 164, 92, 0.50);
	/* Visited links stay in the accent's family, duller and browner. It has to
	   differ from --accent enough to be readable as "already seen" without
	   becoming a second accent colour competing for attention. */
	--accent-visited: #d3a37f;
	--cyan:           #6fd8ee;

	--radius: 10px;
	--measure: 1000px;

	--font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
	        Arial, "Noto Sans", sans-serif;
}

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
}

body {
	margin: 0;
	overflow-x: hidden;
	background: var(--bg);
	color: var(--text);
	font-family: var(--font);
	font-size: 17.5px;
	line-height: 1.65;
	text-align: left;          /* never justify */
	overflow-wrap: break-word;
	font-synthesis-weight: none;
}

/* Anything wider than the column shrinks rather than overflowing */
img, iframe, video, svg { max-width: 100%; }
img, video { height: auto; }

.wrap {
	max-width: var(--measure);
	margin: 0 auto;
	padding: 0 1.25rem;
}

/* ---- Links ---------------------------------------------------------- */

/* Amber means "link" and nothing else on this site, which is why emphasis below
   carries no colour at all. Links come in two families:

     * A link in running prose -- amber, underlined, hover to cyan. The
       underline is not decoration: it is the affordance that survives for a
       reader who cannot separate amber from the body text, and colour is never
       allowed to be the only signal here.
     * A link that is its own affordance -- a nav item, a button, a card, a
       topic chip, the skip link. Its container already says it is clickable, so
       it drops the underline and usually the amber too. Each such component
       sets `text-decoration: none` in its own rule rather than being collected
       into a list here: the declaration belongs with the padding and colour
       that make the component a button in the first place.

   .card-title is the one hybrid -- undecorated at rest, underlined on hover,
   because a title sitting alone on its line reads as a heading until you reach
   for it. */
a {
	color: var(--accent);
	text-decoration: underline;
	text-decoration-thickness: 1px;
	text-underline-offset: 0.18em;
	text-decoration-color: var(--link-line);
	transition: color 0.12s ease, text-decoration-color 0.12s ease;
}

a:visited { color: var(--accent-visited); }

a:hover,
a:focus-visible {
	color: var(--cyan);
	text-decoration-color: currentColor;
}

/* Visible keyboard focus everywhere */
:focus-visible {
	outline: 2px solid var(--cyan);
	outline-offset: 3px;
	border-radius: 3px;
}

/* Skip link for keyboard and screen-reader users */
.skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	background: var(--accent);
	color: #12101c;
	padding: 0.6em 1em;
	z-index: 100;
	font-weight: 600;
	text-decoration: none;
}

.skip-link:focus {
	left: 0;
}

/* ---- Header & navigation -------------------------------------------- */

.site-header {
	border-bottom: 1px solid var(--border);
	background: linear-gradient(180deg, #0d1017, var(--bg));
	position: sticky;
	top: 0;
	z-index: 40;
	backdrop-filter: blur(6px);
}

.site-header .wrap {
	display: flex;
	flex-wrap: wrap;
	/* Centred rather than baseline-aligned: the brand carries an image beside
	   its text, and an image has no baseline to share with the nav links. */
	align-items: center;
	gap: 0.4rem 1.5rem;
	padding-top: 0.7rem;
	padding-bottom: 0.7rem;
}

.brand {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 650;
	font-size: 1.05rem;
	letter-spacing: -0.01em;
	color: var(--text);
	text-decoration: none;
	white-space: nowrap;
}

.brand-mark {
	width: 1.6rem;
	height: 1.6rem;
	display: block;
	flex: 0 0 auto;
}

.brand:visited { color: var(--text); }
.brand:hover    { color: var(--cyan); }

.site-nav {
	display: flex;
	flex-wrap: wrap;
	gap: 0.15rem 0.35rem;
	margin-left: auto;
}

.site-nav a {
	display: inline-block;
	padding: 0.28em 0.62em;
	border-radius: 6px;
	font-size: 0.93rem;
	color: var(--text-soft);
	text-decoration: none;
	white-space: nowrap;
}

.site-nav a:hover {
	color: var(--text);
	background: var(--bg-card);
}

/* Active page: color plus an underline bar, so it is not color-only */
.site-nav a[aria-current="page"] {
	color: var(--accent-strong);
	background: var(--bg-card);
	box-shadow: inset 0 -2px 0 var(--accent);
}

/* ---- Typography ------------------------------------------------------ */

h1, h2, h3, h4, h5 {
	margin-left: 0;                /* no heading indentation */
	line-height: 1.2;
	letter-spacing: -0.015em;
	font-weight: 640;
	overflow-wrap: break-word;
}

h1 { font-size: 2.15rem; margin: 0 0 0.6rem; }
h2 { font-size: 1.55rem; margin: 2.6rem 0 0.9rem; }
h3 { font-size: 1.2rem;  margin: 2rem 0 0.6rem; }
h4 { font-size: 1.02rem; margin: 1.6rem 0 0.4rem; color: var(--text-soft); }

h2 + h3, h3 + h4 { margin-top: 1rem; }

p { margin: 0 0 1.05em; }

ul, ol { margin: 0 0 1.05em; padding-left: 1.3em; }
li { margin-bottom: 0.35em; }

/* ---- Emphasis -------------------------------------------------------- */

/* Emphasis carries no colour. Amber is the link and nothing else, so reaching
   for a hue to stress a word would make it look clickable -- weight and slope
   are all that is left, and they are enough.

   <strong> is the one claim in a section that has to survive skimming.
   <em> is contrastive stress: the word that changes the sentence's meaning if
   you move it ("the *range* of masses", "*statistics*, not individual events").
   Never both on the same span, and never on more than a clause.

   Both lift to full brightness so they still register inside dimmed text --
   .lede, figure captions, the descriptions in .roles. That is a return to the
   body colour, not a new colour. Inside a link they inherit the link's amber
   instead, or a bolded link would go grey. */
strong { font-weight: 640; color: var(--text); }
em { font-style: italic; color: var(--text); }
a strong, a em { color: inherit; }

hr {
	border: none;
	border-top: 1px solid var(--border);
	margin: 2.5rem 0;
}

small, .small { font-size: 0.86rem; color: var(--text-mute); }

code, kbd, samp {
	font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
	font-size: 0.9em;
	background: var(--bg-card);
	border: 1px solid var(--border);
	border-radius: 4px;
	padding: 0.08em 0.35em;
}

pre {
	background: var(--bg-card);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 0.9rem 1rem;
	overflow-x: auto;
}

pre code { background: none; border: none; padding: 0; }

/* Page intro paragraph under an h1 */
.lede {
	font-size: 1.12rem;
	color: var(--text-soft);
	max-width: 68ch;
}

/* A sentence naming what is further down the page, for pages short enough that
   a sticky section bar would cost more room than it saves. */
.page-toc {
	color: var(--text-mute);
	max-width: 68ch;
	margin: 1.2rem 0 1.8rem;
}

.page-head {
	padding: 2.4rem 0 0.4rem;
	border-bottom: 1px solid var(--border);
	margin-bottom: 1.6rem;
}

.eyebrow {
	text-transform: uppercase;
	letter-spacing: 0.11em;
	font-size: 0.76rem;
	font-weight: 650;
	color: var(--accent);
	margin: 0 0 0.5rem;
}

main { padding-bottom: 3rem; }

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

.btn-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin: 1.4rem 0;
}

.btn {
	display: inline-block;
	padding: 0.5em 1.05em;
	border-radius: 8px;
	border: 1px solid var(--border-strong);
	background: var(--bg-card);
	color: var(--text);
	font-size: 0.95rem;
	font-weight: 550;
	text-decoration: none;
}

.btn:visited { color: var(--text); }

.btn:hover, .btn:focus-visible {
	border-color: var(--accent);
	color: var(--accent-strong);
	background: #1b1f30;
}

.btn-primary {
	background: var(--accent);
	border-color: var(--accent);
	color: #14101f;
}

.btn-primary:visited { color: #14101f; }

.btn-primary:hover, .btn-primary:focus-visible {
	background: var(--accent-strong);
	border-color: var(--accent-strong);
	color: #14101f;
}

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

.card {
	background: var(--bg-card);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 1.15rem 1.25rem;
}

.card > :last-child { margin-bottom: 0; }

.card h3, .card h4 { margin-top: 0; }

.card-title {
	margin: 0 0 0.35rem;
	font-size: 1.1rem;
	font-weight: 640;
}

.card-title a { text-decoration: none; }
.card-title a:hover { text-decoration: underline; }

.card-meta {
	font-size: 0.83rem;
	color: var(--text-mute);
	margin: 0 0 0.6rem;
}

.card-link {
	border-color: var(--border);
	display: block;
	text-decoration: none;
	color: inherit;
}

.card-link:visited { color: inherit; }

.card-link:hover, .card-link:focus-visible {
	border-color: var(--accent-dim);
	background: #1a1e2d;
}

/* Grids of cards */
.grid {
	display: grid;
	gap: 1rem;
	margin: 1.3rem 0 1.8rem;
}

/* min() guards the track floor: without it, auto-fit forces a 340px column even
   when the container is narrower, and the page scrolls sideways on small phones. */
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }

/* A card that spans the whole grid, so an odd count still balances */
.card-wide { grid-column: 1 / -1; }

.card-split { display: grid; grid-template-columns: 1fr minmax(240px, 340px); gap: 1.4rem 1.8rem; align-items: center; }
.card-split > .card-col > :last-child { margin-bottom: 0; }
.card-split > .card-side { margin: 0; }
.card-split > .card-side img { width: 100%; height: auto; }

@media (max-width: 720px) {
	.card-split { display: block; }
	.card-split > .card-side { margin: 1.2rem 0 0; }
}
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(275px, 100%), 1fr)); }

/* Compact label/value rows: keeps the software cards from turning into a
   stack of headed sections for one line of text each. */
.code-facts {
	display: grid;
	grid-template-columns: max-content 1fr;
	gap: 0.3rem 1rem;
	margin: 1.2rem 0 1.4rem;
	font-size: 0.92rem;
}

.code-facts dt {
	color: var(--text-mute);
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.09em;
	font-weight: 650;
	padding-top: 0.25em;
}

.code-facts dd { margin: 0; }

@media (max-width: 620px) {
	.code-facts { grid-template-columns: 1fr; gap: 0.1rem; }
	.code-facts dt { padding-top: 0.7em; }
	.code-facts dd { margin-bottom: 0.2rem; }
}

/* ---- How many papers used the code ----------------------------------- */

/* A page listing other people's work should say how much of it there is
   before showing any, which is what hi_class does with its `.pub-headline`.
   The number carries the weight and the sentence stays quiet beside it. */
.count-headline {
	margin: 1.2rem 0 0.4rem;
	display: flex;
	align-items: baseline;
	gap: 0.55rem;
	flex-wrap: wrap;
}

.count-headline b {
	font-size: 2.4rem;
	line-height: 1;
	font-weight: 700;
	color: var(--accent);
}

.count-headline span {
	color: var(--text-soft);
	font-size: 1rem;
}

/* ---- The papers to cite ---------------------------------------------- */

/* Not a reading list. These are the references a paper using the code has to
   carry, so they are set apart from ordinary links: accent rule, tinted
   ground, title over a meta line. The shape is hi_class's `.refs--cite`, where
   the same distinction is drawn between "further reading" and "put this in
   your bibliography", and the weight is the whole point of the component. */
.cite-list {
	list-style: none;
	margin: 1.3rem 0 0;
	padding: 0;
	display: grid;
	gap: 0.8rem;
}

.cite-list > li {
	border-left: 3px solid var(--accent);
	background: var(--accent-wash);
	border-radius: 0 var(--radius) var(--radius) 0;
	padding: 0.7rem 1rem;
}

.cite-title {
	display: block;
	font-weight: 640;
	line-height: 1.35;
	font-size: 1rem;
}

.cite-meta {
	display: block;
	margin-top: 0.25rem;
	font-size: 0.87rem;
	color: var(--text-mute);
	line-height: 1.5;
}

/* The journal reference is the part a reader copies, so it is the one thing in
   the meta line that carries the accent. */
.cite-where { color: var(--accent-dim); }

/* ---- Tags ------------------------------------------------------------ */

.tags {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem;
	list-style: none;
	padding: 0;
	margin: 0.6rem 0 0;
}

.tag {
	font-size: 0.75rem;
	letter-spacing: 0.03em;
	padding: 0.14em 0.55em;
	border: 1px solid var(--border-strong);
	border-radius: 999px;
	color: var(--text-mute);
	margin: 0;
}

.tag-accent {
	border-color: var(--accent-dim);
	color: var(--accent);
}

/* ---- Hero (home page) ------------------------------------------------ */

.hero {
	display: grid;
	grid-template-columns: 1fr minmax(200px, 300px);
	gap: 2rem;
	align-items: start;
	padding: 2.8rem 0 2rem;
}

.hero h1 { font-size: 2.5rem; }

.hero-title {
	font-size: 1.18rem;
	font-weight: 550;
	color: var(--accent);
	margin: 0 0 1rem;
}

/* The diffracted wordmark above the hero. Full width, no border: the render
   fades to black at its edges, so a frame would only draw a rectangle around
   nothing. The still is a real frame, as everywhere else on this site. */
.hero-banner { margin: 1.6rem 0 0; }

/* Cropped, not scaled. At full width the 2:1 render is ~470px tall and pushes
   the heading off the fold; the letters sit in the middle band, so trimming
   top and bottom costs nothing and the fringes still bleed off both edges. */
.hero-banner video {
	display: block;
	width: 100%;
	max-height: 300px;
	object-fit: cover;
	border-radius: var(--radius);
	background: #05070c;
}

@media (max-width: 720px) {
	.hero-banner video { max-height: 190px; }
}

/* The two hero clips occupy one box: the reveal sizes it, the breathing loop
   lies on top of it. Stacked rather than swapped in place so the second is
   already decoded when it is needed -- replacing the src of one element shows
   a gap while the new file is fetched, and this hero is the first thing on the
   page. Both end and begin on the same frame, so the crossover is invisible. */
.hero-stack { position: relative; }

.hero-breathe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
}

/* Swapped by assets/js/hero-loop.js when the reveal ends. No transition: the
   two frames are identical, so a fade would only make the change visible. */
.hero-stack.is-looping .hero-breathe { opacity: 1; }
.hero-stack.is-looping > video:first-child { opacity: 0; }

.hero-banner figcaption {
	margin-top: 0.7rem;
	font-size: 0.86rem;
	line-height: 1.6;
	color: var(--text-mute);
	max-width: 68ch;
}

/* The project lock-up, not a portrait: it keeps its own 2.04:1 aspect ratio and
   is never cropped. The emblem rules for the logo forbid distorting it, and a
   wordmark clipped mid-letter reads as a broken image. */
.hero-logo {
	width: 100%;
	max-width: 300px;
	height: auto;
	display: block;
	margin-top: 0.6rem;
}

/* Beneath the lock-up and clearly second to it: two thirds the width, centred
   under it, and set off by a rule rather than sitting flush, so it reads as a
   funder credit and not as a second mark.

   The rule is on the wrapper, not on the image, so it spans the column rather
   than stopping at the emblem's edges -- a rule exactly as wide as the thing
   under it underlines rather than separates. The emblem's own proportions are
   fixed by the EU rules, so width is capped and height follows. */
.hero-eu {
	margin-top: 1.1rem;
	padding-top: 1.1rem;
	border-top: 1px solid var(--border);
}

.hero-eu img {
	width: 66%;
	max-width: 200px;
	height: auto;
	display: block;
	margin: 0 auto;
}

/* ---- Roles strip ----------------------------------------------------- */

.roles {
	list-style: none;
	padding: 0;
	margin: 0;
	display: grid;
	gap: 0.75rem;
	grid-template-columns: repeat(auto-fit, minmax(min(215px, 100%), 1fr));
}

/* A description list, not a bullet list: every one of these is a label and the
   thing it labels -- "Work packages" / WP2-A, "Host institution" / IFT. It was
   <li><strong>label</strong><span>value</span></li> until the emphasis rule
   above needed <strong> to mean emphasis and nothing else. The <div> wrappers
   are what the grid lays out; a <dl> is allowed to group its pairs that way. */
.roles > div {
	margin: 0;
	padding: 0.75rem 0.9rem;
	border-left: 3px solid var(--accent-dim);
	background: var(--bg-raised);
	border-radius: 0 8px 8px 0;
	font-size: 0.95rem;
	line-height: 1.45;
}

.roles dt { font-weight: 640; }

.roles dd { margin: 0; color: var(--text-mute); font-size: 0.87rem; }

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

figure { margin: 1.6rem 0; max-width: 100%; }

figure img, figure video { display: block; width: 100%; border-radius: 8px; }

figcaption {
	margin-top: 0.5rem;
	font-size: 0.86rem;
	line-height: 1.5;
	color: var(--text-mute);
}

figure.float-right,
figure.float-left {
	margin-top: 0.3rem;
	margin-bottom: 1rem;
}

.float-right { float: right; margin-left: 1.6rem; }
.float-left  { float: left;  margin-right: 1.6rem; }

.center { display: block; margin-left: auto; margin-right: auto; }

/* Clear floats at the end of a section so they never bleed into the next */
.section::after,
.clearfix::after { content: ""; display: block; clear: both; }

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

.video {
	position: relative;
	width: 100%;
	max-width: 760px;
	margin: 1.4rem 0;
	aspect-ratio: 16 / 9;
	background: var(--bg-card);
	border-radius: 8px;
	overflow: hidden;
}

.video iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

.video.embed-4x3 { aspect-ratio: 4 / 3; }

/* Topic-page animation. The renders are not all the same shape -- 3:1 for the
   sweep families, 16:10 for the birefringence one -- so the ratio comes from
   each video's own width/height attributes in _data/topics.yml rather than
   from a rule here. The attributes are what reserves the space before the
   video loads, which is what keeps the caption from jumping. */
.topic-figure {
	margin: 1.6rem 0 2rem;
}

/* Video or still: a plot and an animation are the same kind of thing here, and
   giving them different frames would imply a distinction that is not there. */
.topic-figure video,
.topic-figure img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--radius);
	border: 1px solid var(--border);
	background: var(--bg-card);
}

/* A play badge for the figures that wait to be asked (figure-ondemand.html).
   Native `controls` is the affordance and stays; the browsers' own bar sits at
   the bottom edge over a full-width still, where it is easy to miss, and a
   reader can be left thinking a figure is a static plot. Owner, 2026-08-31.

   Shown while `.video-frame` carries `data-idle`, which
   assets/js/video-play-badge.js sets whenever the video is not running,
   including when it ends: on these figures the last frame is the result, so
   the badge returning over it reads as a replay cue. The obvious version of
   this needs no script at all, `:has(video:paused)`, and works in Firefox and
   Safari, but Chrome 151 does not support `:paused` (checked in the browser,
   not assumed). Without JavaScript no badge appears, which is the behaviour
   this replaces, so the fallback is the status quo, not a broken control.

   Everything outside the disc is `pointer-events: none` so clicks fall through
   to the video and its control bar. The disc itself takes pointer events, but
   only while idle, and the script turns that click into a play: Chrome does
   not start these from a click on the video body, only from its own control
   bar, so a badge that merely pointed at the real control would look like a
   button and do nothing. The fade is neutralised by the reduced-motion block
   at the end of this file. */
.video-frame {
	position: relative;
	display: block;
}

.video-frame::before,
.video-frame::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.18s ease;
}

.video-frame::before {
	width: 72px;
	height: 72px;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	background: rgba(10, 12, 18, 0.55);
	border: 1px solid rgba(255, 255, 255, 0.55);
}

/* The triangle is nudged right of centre so its visual mass, not its bounding
   box, sits in the middle of the disc. */
.video-frame::after {
	width: 0;
	height: 0;
	transform: translate(-35%, -50%);
	border-left: 21px solid rgba(255, 255, 255, 0.94);
	border-top: 13px solid transparent;
	border-bottom: 13px solid transparent;
}

.video-frame[data-idle]::before,
.video-frame[data-idle]::after {
	opacity: 1;
}

.video-frame[data-idle]::before {
	pointer-events: auto;
	cursor: pointer;
}

.video-frame[data-idle]:hover::before {
	background: rgba(10, 12, 18, 0.72);
	border-color: rgba(255, 255, 255, 0.85);
}

.topic-figure figcaption {
	margin-top: 0.7rem;
	font-size: 0.88rem;
	line-height: 1.6;
	color: var(--text-mute);
	max-width: 68ch;
}

.audio-embed {
	width: 100%;
	height: 200px;
	border: 0;
	margin-bottom: 1rem;
	border-radius: 8px;
}

/* ---- Publication lists ----------------------------------------------- */

.pubs {
	list-style: none;
	padding: 0;
	margin: 0.8rem 0 1.2rem;
	border-top: 1px solid var(--border);
}

.pubs li {
	margin: 0;
	padding: 0.7rem 0;
	border-bottom: 1px solid var(--border);
	font-size: 0.95rem;
	line-height: 1.5;
}

.pubs .authors { color: var(--text-mute); display: block; font-size: 0.88rem; }

.pubs .journal { color: var(--text-mute); }

/* Where an entry carries its authors and reference in one line rather than in
   a separate .authors span, break it below the title so the titles still scan
   down the left edge. */
.pubs-stacked .journal { display: block; font-size: 0.9rem; }

/* Institutional or press coverage of a paper, distinct from its citation */
.pubs .feature {
	display: block;
	font-size: 0.85rem;
	color: var(--text-mute);
	margin-top: 0.1rem;
}

.badge {
	display: inline-block;
	font-size: 0.72rem;
	letter-spacing: 0.03em;
	padding: 0.1em 0.5em;
	border-radius: 4px;
	background: var(--accent-wash);
	color: var(--accent);
	border: 1px solid var(--accent-dim);
	white-space: nowrap;
}

/* ---- News ------------------------------------------------------------
   Dated one-liners on the home page. The date sits in its own column so the
   list scans as a timeline rather than as prose. */

.news {
	list-style: none;
	padding: 0;
	margin: 1.1rem 0 0;
	border-top: 1px solid var(--border);
}

.news li {
	display: flex;
	gap: 0.85rem;
	padding: 0.8rem 0;
	border-bottom: 1px solid var(--border);
}

/* Narrow enough to read as a margin note rather than a column of its own.
   Tabular figures keep the years stacked even at an abbreviated width. */
.news-date {
	flex: 0 0 4.2rem;
	color: var(--text-mute);
	font-size: 0.86rem;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
	padding-top: 0.18rem;
}

.news-body { margin: 0; }

.news-links {
	color: var(--text-mute);
	font-size: 0.92rem;
}

/* Link to the archive, under the list. Sized with .news-links rather than with
   body text: it is an exit from the section, not another item in it. Only
   rendered when the archive holds more than the home page shows. */
.news-more {
	margin: 0.9rem 0 0;
	font-size: 0.92rem;
}

/* Only items inside the freshness window carry this, and the template stops
   emitting it once they age out — so the motion always means something. It
   cycles between the two accents rather than the old site's yellow and pink,
   which would read as a different website. Users who ask for reduced motion
   get a still flag: see the prefers-reduced-motion block at the end. */
.news-flag {
	display: inline-block;
	font-size: 0.68rem;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	font-weight: 600;
	padding: 0.15em 0.55em;
	border-radius: 4px;
	margin-right: 0.55rem;
	vertical-align: 0.1em;
	color: var(--bg);
	background: var(--accent);
	animation: news-pulse 4.5s ease-in-out infinite;
}

@keyframes news-pulse {
	0%, 100% {
		background: var(--accent);
		box-shadow: 0 0 0 0 var(--accent-halo);
	}
	50% {
		background: var(--cyan);
		box-shadow: 0 0 0.6rem 0.12rem rgba(111, 216, 238, 0.4);
	}
}

@media (max-width: 620px) {
	.news li { flex-direction: column; gap: 0.25rem; }
	.news-date { flex: none; padding-top: 0; }
}

/* Talk / link lists with an arrow marker */
.arrow-list { list-style: none; padding: 0; }

.arrow-list li {
	position: relative;
	padding-left: 1.4em;
	font-size: 0.95rem;
}

.arrow-list li::before {
	content: "↪";
	position: absolute;
	left: 0;
	color: var(--accent-dim);
}

/* ---- Person cards ---------------------------------------------------- */

/* A photograph or, for anyone without one, their initials. Both are the same
   circle at the same size, so a group where only some people have supplied a
   photograph still reads as one row of cards rather than as a list with holes
   in it. Initials carry the accent because they are type, not an image; a
   grey monogram on a grey ground reads as a missing asset. */
.person {
	display: flex;
	gap: 0.85rem;
	align-items: flex-start;
}

.person-avatar {
	width: 72px;
	height: 72px;
	border-radius: 50%;
	flex: 0 0 auto;
	border: 1px solid var(--border-strong);
}

img.person-avatar {
	object-fit: cover;
	/* The box is reserved by width/height, so the card does not reflow when a
	   lazily-loaded photograph arrives. */
	background: var(--bg-raised);
}

.person-initials {
	display: grid;
	place-items: center;
	background: var(--bg-raised);
	color: var(--accent);
	font-weight: 620;
	font-size: 1.05rem;
	letter-spacing: 0.03em;
}

/* Without this the flex item refuses to shrink and a long name pushes the
   card wider than its grid column. */
.person-body { min-width: 0; }

.person-body > :last-child { margin-bottom: 0; }

@media (max-width: 480px) {
	.person-avatar { width: 56px; height: 56px; }
	.person-initials { font-size: 0.9rem; }
}

/* ---- Filterable indexes (publications, talks) ------------------------ */

/* The toolbar carries `hidden` in the markup and assets/js/search.js removes
   it. Without JavaScript there is no input, and the list below is complete. */
.filter {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.6rem 1rem;
	margin: 0 0 1.4rem;
}

.filter[hidden] { display: none; }

.filter input[type="search"] {
	flex: 1 1 18rem;
	min-width: 0;
	background: var(--bg-raised);
	border: 1px solid var(--border);
	border-radius: 6px;
	color: var(--text);
	font: inherit;
	font-size: 0.92rem;
	padding: 0.5em 0.75em;
}

.filter input[type="search"]::placeholder { color: var(--text-mute); }

.filter input[type="search"]:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 1px;
	border-color: var(--border-strong);
}

.filter-count {
	margin: 0;
	font-size: 0.85rem;
	color: var(--text-mute);
	white-space: nowrap;
}

.filter-count b { color: var(--text); font-weight: 600; }

.filter-empty {
	color: var(--text-mute);
	font-style: italic;
}

/* A talk is a title followed by everything else, dimmed and running on from
   it. Compact rather than one line at any cost: the owner asked for a single
   line on 2026-08-28 and then, seeing what it cost, said a second line is fine
   when it is needed. So nothing is truncated here.

   The date is not rendered at all. It orders the list and nothing more. */
.talk-list .talk-row {
	padding: 0.35rem 0;
	font-size: 0.93rem;
	line-height: 1.45;
}

.talk-title { color: var(--text); }

.talk-where {
	font-size: 0.83rem;
	color: var(--text-mute);
}

.talk-where::before { content: "· "; }

@media (max-width: 720px) {
	.talk-list .talk-row { padding: 0.5rem 0; }
	.talk-where { display: block; }
	.talk-where::before { content: none; }
}

.pub-year { margin: 0 0 1.6rem; }

.pub-year-head {
	font-size: 0.82rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--text-mute);
	font-weight: 600;
	margin: 0 0 0.6rem;
	padding-bottom: 0.35rem;
	border-bottom: 1px solid var(--border);
}

.pub-list { list-style: none; padding: 0; margin: 0; }

.pub-entry {
	padding: 0.55rem 0;
	font-size: 0.95rem;
	border-bottom: 1px solid var(--border);
}

.pub-entry:last-child { border-bottom: 0; }

.pub-entry-title { color: var(--text); }

/* The second line of an entry: everything that is not the title. Dimmed so a
   list of thirty scans by title, and still searchable, since the filter reads
   the entry's whole textContent. */
.pub-entry-meta,
.talk-meta {
	display: block;
	font-size: 0.83rem;
	color: var(--text-mute);
	margin-top: 0.15rem;
}

/* ---- Section framing ------------------------------------------------- */

.section { margin: 2.8rem 0; }

/* The dominant research program gets a subtle left rail. It runs per section
   rather than unbroken down the whole block, so the five read as five things
   sharing a rail and not as one continuous stretch of page. */
.program { margin-left: 0; }

.program > .section {
	border-left: 2px solid var(--accent-dim);
	padding-left: 1.6rem;
}

/* Secondary material sits in a quieter, denser block */
.secondary {
	background: var(--bg-raised);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	padding: 1.4rem 1.5rem;
}

.secondary > h3:first-child { margin-top: 0; font-size: 1.3rem; }
.secondary h4 { font-size: 1.02rem; color: var(--text-soft); }
.secondary > :last-child { margin-bottom: 0; }

/* ---- People ---------------------------------------------------------- */

/* Career-stage bands in the group list. Built from the same uppercase-accent
   vocabulary as .eyebrow so it reads as a structural label, not a heading
   competing with the h2 above it. */
.stage-heading {
	display: flex;
	align-items: center;
	gap: 0.8rem;
	margin: 2.2rem 0 1rem;
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.12em;
	font-weight: 650;
	/* Neutral, not accent: the accent is what marks a link, and these bands sit
	   directly above lists of names that are links. Caps, letter-spacing and
	   the trailing rule already say "heading" without borrowing that colour. */
	color: var(--text-soft);
	line-height: 1.3;
}

.stage-heading::after {
	content: "";
	flex: 1 1 auto;
	height: 1px;
	background: var(--border);
}


/* First band sits directly under the section heading */
h2 + .stage-heading { margin-top: 1.2rem; }


.person {
	display: flex;
	gap: 0.9rem;
	align-items: flex-start;
}

.person img {
	width: 62px;
	height: 62px;
	border-radius: 50%;
	object-fit: cover;
	flex: 0 0 auto;
	border: 1px solid var(--border-strong);
}

.person-avatar-fallback {
	width: 62px;
	height: 62px;
	border-radius: 50%;
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	background: var(--bg-raised);
	border: 1px solid var(--border-strong);
	color: var(--accent);
	font-weight: 620;
	font-size: 0.95rem;
	letter-spacing: 0.02em;
}

.person-body { min-width: 0; }

.person-name { font-weight: 620; margin: 0 0 0.1rem; }

.person-role { font-size: 0.84rem; color: var(--text-mute); margin: 0 0 0.35rem; }

.person-note { font-size: 0.9rem; margin: 0; color: var(--text-soft); }

.person-incoming { margin: 0 0 0.4rem; }

.people-list { list-style: none; padding: 0; }

.people-list li {
	padding: 0.5rem 0;
	border-bottom: 1px solid var(--border);
	font-size: 0.94rem;
}

.people-list .next::before { content: " → "; color: var(--accent-dim); }

/* Project and software logos float right so the card text wraps around them.
   Declared before the heading in source order, which is what float needs. */
.card-logo {
	float: right;
	display: block;
	max-height: 130px;
	max-width: 300px;
	width: auto;
	margin: 0.15rem 0 1rem 1.6rem;
}

/* A package page leads with the code's logo rather than mentioning it, so the
   130px card size is too timid there: on a page whose whole subject is one
   code, the wordmark is the masthead. Height-limited like the base rule, so
   the width follows from whatever aspect ratio the logo happens to have. */
.card-logo--page {
	max-height: 210px;
	max-width: 460px;
}

/* Cards must contain the float, or it bleeds into the next card. */
.card::after { content: ""; display: block; clear: both; }

/* Funding acknowledgement: a block of its own under the card body, so it never
   competes with the project's own logo floating opposite it. */
/* Funding acknowledgement: stacked directly under the project's own logo, at
   the same width. The card logo is height-limited (130px), so its rendered
   width follows from its aspect ratio -- 265px for the GLOW lock-up. Matching
   on width rather than height is what makes the two read as one block, since
   the two images are not the same shape. */
.funding-logo {
	float: right;
	clear: right;
	display: block;
	width: 265px;
	max-width: 100%;
	height: auto;
	margin: 0 0 1.2rem 1.6rem;
}

@media (max-width: 620px) {
	.card-logo {
		float: none;
		max-height: 76px;
		max-width: 210px;
		margin: 0 0 1rem;
	}

	.funding-logo {
		float: none;
		width: 155px; /* 76px tall at the GLOW logo's aspect ratio */
		margin: 0 0 1rem;
	}
}

/* A row of photos with mismatched aspect ratios: match on height, not width,
   so a square and a panorama sit together without one dwarfing the other. */
.photo-strip {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem 1.2rem;
	align-items: flex-start;
	margin: 2rem 0 0;
}

.photo-strip figure { margin: 0; }

.photo-strip img {
	height: 190px;
	width: auto;
	max-width: 100%;
	border-radius: 8px;
}

@media (max-width: 620px) {
	.photo-strip img { height: auto; width: 100%; }
}

/* ---- Logo strips ----------------------------------------------------- */

.logo-row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem 1.8rem;
	margin: 1.4rem 0;
}

.logo-row img { display: block; }

.logo-row-quiet img { opacity: 0.8; }

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

.site-footer {
	border-top: 1px solid var(--border);
	background: var(--bg-raised);
	padding: 2.2rem 0 2.6rem;
	font-size: 0.9rem;
	color: var(--text-mute);
	margin-top: 3rem;
}

.footer-grid {
	display: grid;
	gap: 1.5rem;
	grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
}

.site-footer h2 {
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.11em;
	color: var(--text-soft);
	margin: 0 0 0.6rem;
}

.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: 0.3rem; }

/* Mandatory EU funding acknowledgement. The emblem and the disclaimer are one
   block so they can never be separated by a reflow; the emblem keeps its own
   width rather than stretching, because the emblem rules forbid distorting it. */
.eu-funding {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: 1rem 1.4rem;
	margin-top: 2rem;
	padding-top: 1.4rem;
	border-top: 1px solid var(--border);
}

.eu-funding img {
	flex: 0 0 auto;
	width: 260px;
	max-width: 100%;
	height: auto;
}

.eu-funding p {
	flex: 1 1 320px;
	margin: 0;
	font-size: 0.78rem;
	line-height: 1.55;
	color: var(--text-mute);
}

.footer-note {
	margin-top: 2rem;
	padding-top: 1.2rem;
	border-top: 1px solid var(--border);
	font-size: 0.83rem;
}

/* ---- Mobile ---------------------------------------------------------- */

@media (max-width: 760px) {
	body { font-size: 17px; }

	.hero {
		grid-template-columns: 1fr;
		padding-top: 1.8rem;
	}

	.hero h1 { font-size: 2rem; }

	.hero-logo { max-width: 260px; }

	/* Portrait first would push the text down; keep text first, image after */
	.hero-media { order: 2; }

	/* Once the hero is one column the media block spans the full width, and a
	   centred emblem floats away from the lock-up above it, which is capped at
	   260px and sits left. Right-aligned it reads as attached to the block
	   rather than adrift in the middle of the page. */
	.hero-eu img { margin-left: auto; margin-right: 0; }

	h1 { font-size: 1.85rem; }
	h2 { font-size: 1.35rem; }

	/* Stack floated figures rather than crowding a narrow column */
	.float-left, .float-right {
		float: none;
		margin-left: 0;
		margin-right: 0;
		max-width: 100% !important;
		width: 100% !important;
	}

	.program { padding-left: 1rem; }

	.secondary { padding: 1.1rem; }

	.site-header { position: static; }

	.site-header .wrap { padding-bottom: 0.4rem; }

	.site-nav { margin-left: 0; width: 100%; }
}

/* ---- In-page section navigation -------------------------------------- */

/* Fallbacks: subnav.js measures both and overrides them. */
:root { --header-h: 54px; --topicnav-h: 0px; --subnav-h: 44px; }

.subnav {
	position: sticky;
	/* Below the topic bar where a page has one -- on science.html both are
	   present -- and directly below the header where it does not. */
	top: calc(var(--header-h) + var(--topicnav-h));
	z-index: 30;
	margin: 1.75rem 0;
	padding: 0.4rem 0;
	background: rgba(10, 12, 17, 0.94);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
	border-top: 1px solid var(--border);
	border-bottom: 1px solid var(--border);
}

.subnav ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.subnav a {
	display: block;
	padding: 0.25rem 0.7rem;
	border: 1px solid transparent;
	border-radius: 999px;
	font-size: 0.87rem;
	line-height: 1.5;
	color: var(--text-soft);
	text-decoration: none;
	white-space: nowrap;
}

.subnav a:hover {
	background: var(--bg-card);
	color: var(--text);
}

.subnav a[aria-current="true"] {
	background: var(--bg-card);
	border-color: var(--accent-dim);
	color: var(--accent-strong);
}

/* ---- Topic bar ------------------------------------------------------- */

/* The science submenu. Shares the pill vocabulary of .subnav so the two read as
   the same kind of control, but it is not sticky: only one bar should follow
   the reader down a page, and on topic pages this one replaces the section bar
   rather than stacking with it. */
.topicnav {
	position: sticky;
	top: var(--header-h);
	z-index: 35;
	margin: 1.6rem 0 2rem;
	padding: 0.5rem 0;
	/* Opaque enough to hide the content sliding under it. The blur matches the
	   header and the section bar so the three read as one stack. */
	background: rgba(10, 12, 17, 0.94);
	-webkit-backdrop-filter: blur(6px);
	backdrop-filter: blur(6px);
	border-top: 1px solid var(--border);
	border-bottom: 1px solid var(--border);
}

/* Anchor jumps on a page with a topic bar must clear it as well as the
   header. */
.topicnav ~ * [id],
.topicnav ~ [id] {
	scroll-margin-top: calc(var(--header-h) + var(--topicnav-h) + 0.75rem);
}

.topicnav ul {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.3rem 0.35rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.topicnav a {
	display: block;
	padding: 0.25rem 0.7rem;
	border: 1px solid transparent;
	border-radius: 999px;
	font-size: 0.87rem;
	line-height: 1.5;
	color: var(--text-soft);
	text-decoration: none;
	white-space: nowrap;
}

.topicnav a:visited { color: var(--text-soft); }

.topicnav a:hover, .topicnav a:focus-visible {
	background: var(--bg-card);
	color: var(--text);
}

.topicnav a[aria-current="page"] {
	background: var(--bg-card);
	border-color: var(--accent-dim);
	color: var(--accent-strong);
}

@media (max-width: 620px) {
	/* Six labels wrap to two or three lines here, and a sticky bar that tall
	   eats too much of a phone screen. sticky-bars.js reports 0 for it once it
	   is static, so the section bar closes the gap by itself. */
	.topicnav {
		position: static;
	}
}

/* Where a page's sections are not all at the same level, a rule keeps the
   subsections of one heading reading as a group. */
.subnav li.subnav-rule {
	margin-left: 0.55rem;
	padding-left: 0.65rem;
	border-left: 1px solid var(--border-strong);
}

/* Anchor jumps must clear both sticky bars. */
.subnav ~ * [id],
.subnav ~ [id] {
	scroll-margin-top: calc(var(--header-h) + var(--topicnav-h) + var(--subnav-h) + 0.75rem);
}

/* One scrollable line rather than three stacked rows on a phone. */
@media (max-width: 620px) {
	.subnav ul {
		flex-wrap: nowrap;
		overflow-x: auto;
		scrollbar-width: none;
	}

	.subnav ul::-webkit-scrollbar { display: none; }
}

@media print {
	.subnav { display: none; }
}

/* ---- Sky map: source, lens, observer --------------------------------- */

.skymap { margin: 2rem 0 1.5rem; }

.skymap-frame {
	position: relative;
	border: 1px solid var(--border);
	border-radius: var(--radius);
	overflow: hidden;
	background: #05070c;
}

.skymap img {
	display: block;
	width: 100%;
	height: auto;
}

.skymap-cards {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 0.7rem;
	position: absolute;
	left: 3.5%;
	right: 3.5%;
}

/* Both rows share the three-column track, which puts the bottom cards under
   the source, the lens and the observer respectively. */
.skymap-cards-top    { top: 1rem; }
.skymap-cards-bottom { bottom: 1rem; }

.skymap-card {
	display: block;
	padding: 0.65rem 0.75rem;
	border: 1px solid var(--tint, var(--border-strong));
	border-radius: 8px;
	background: rgba(10, 12, 17, 0.72);
	-webkit-backdrop-filter: blur(4px);
	backdrop-filter: blur(4px);
	color: var(--text);
	text-decoration: none;
	transition: background 0.15s ease, border-color 0.15s ease;
}

a.skymap-card:hover,
a.skymap-card:focus-visible {
	background: rgba(10, 12, 17, 0.88);
	border-color: var(--tint-strong, var(--accent));
}

.skymap-card .skymap-title {
	display: block;
	margin-bottom: 0.15rem;
	font-size: 0.95rem;
	color: var(--tint-strong, var(--accent-strong));
}

.skymap-card .skymap-blurb {
	display: block;
	font-size: 0.8rem;
	line-height: 1.4;
	color: var(--text-soft);
}

/* Keep a hyphenated term from splitting across lines in a narrow column. */
.skymap-card .skymap-title span { white-space: nowrap; }

.skymap-card.tint-source  { --tint: #3f7f68; --tint-strong: #8fe0c0; }
.skymap-card.tint-wave    { --tint: #2f6a7d; --tint-strong: #8ad6e8; }
.skymap-card.tint-methods { --tint: #4a4a5e; --tint-strong: #c2c8d6; }
.skymap-card.tint-lens    { --tint: #3a4a8c; --tint-strong: #9fb4ff; }
.skymap-card.tint-strong  { --tint: #8a5a2a; --tint-strong: #f0c08a; }
.skymap-card.tint-grav    { --tint: #7a3f66; --tint-strong: #f0a6cd; }

/* Three columns need the full measure. Below it, drop the cards out of the
   picture and let them sit underneath in two columns, then one. The bottom
   row's alignment with the source, the lens and the observer is lost there --
   the figcaption states the light-path argument in words for that case. */
@media (max-width: 940px) {
	.skymap-frame { border: 0; border-radius: 0; background: none; }

	.skymap img {
		border: 1px solid var(--border);
		border-radius: var(--radius);
	}

	/* auto-fit, not repeat(2, 1fr): each row is its own grid, so a fixed two
	   columns leaves three cards as 2 + 1 and puts a hole in both rows. */
	.skymap-cards {
		position: static;
		grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
		gap: 0.8rem;
		margin-top: 0.8rem;
	}

	.skymap-card { background: var(--bg-card); }
}

@media (max-width: 620px) {
	.skymap-cards { grid-template-columns: 1fr; }
}

/* ---- Matrix table ----------------------------------------------------- */

/* The only table on the site, and it is deliberately not a list of records:
   the columns are an expansion of the signal in frequency, the rows are what
   each order of that expansion does, and the reader is meant to run both ways
   across it. That is why the row labels get a filled left column of their own
   -- with five columns and no anchor, the eye loses the row halfway across.

   Adapted from Table 1 of the grant proposal, where it is set in LaTeX with a
   photograph per column. The photographs are not reproduced here: they are
   other people's, under licences that would each need an attribution line, and
   the analogy survives as four words. */

/* Five columns do not fit a phone, and shrinking the type until they do makes
   the table unreadable in the name of avoiding a scrollbar. It scrolls
   sideways instead. tabindex makes the scroll container focusable, which is
   what lets a keyboard user reach the far column at all. */
.table-scroll {
	overflow-x: auto;
	margin: 1.6rem 0;
	-webkit-overflow-scrolling: touch;
}

.matrix {
	border-collapse: collapse;
	width: 100%;
	min-width: 620px;
	font-size: 0.88rem;
	line-height: 1.4;
}

.matrix caption {
	caption-side: bottom;
	margin-top: 0.6rem;
	font-size: 0.86rem;
	line-height: 1.5;
	color: var(--text-mute);
	text-align: left;
}

.matrix th,
.matrix td {
	border: 1px solid var(--border);
	padding: 0.5rem 0.7rem;
	text-align: center;
	vertical-align: middle;
}

.matrix thead th {
	background: var(--bg-raised);
	color: var(--text-soft);
	font-weight: 640;
}

/* The band naming the two regimes. Caps and tracking carry the hierarchy here
   exactly as they do for section headings (see the header rules above); the
   accent does not, because on this site amber means link and nothing else. */
.matrix .colgroup-head {
	text-transform: uppercase;
	letter-spacing: 0.06em;
	font-size: 0.76rem;
}

/* The corner cell spans both header rows and names the table rather than a
   column, so it is left-aligned with the row labels it sits above. */
.matrix .matrix-corner {
	text-align: left;
	color: var(--text);
	line-height: 1.3;
}

/* Real superscripts in the column heads, not Unicode lookalikes: there is no
   precomposed minus-two that sits at the same weight as the rest of the row.
   Raised less than the browser default, which would push them into the band
   above, and tracked in tight so "f" and its exponent read as one symbol. */
.matrix thead sup {
	font-size: 0.72em;
	vertical-align: 0.42em;
	line-height: 0;
	margin-left: 0.02em;
}

.matrix tbody th {
	text-align: left;
	background: var(--bg-raised);
	color: var(--text-soft);
	font-weight: 640;
	white-space: nowrap;
}

.matrix tbody td { color: var(--text-soft); }

/* Dimmed text, so <strong> lifts back to the body colour rather than
   introducing one -- the same rule the lede and the captions follow. */
.matrix strong { color: var(--text); }

/* ---- Citations and references ---------------------------------------- */

/* The marker rides above the line without stretching it: `vertical-align`
   plus a zero line-height, rather than <sup>'s default, which opens a gap in
   every paragraph that carries one. */
.cite {
	font-size: 0.72rem;
	line-height: 0;
	vertical-align: super;
	white-space: nowrap;
	margin-left: 0.08em;
	color: var(--text-mute);
}

.cite a { text-decoration: none; }
.cite a:hover, .cite a:focus-visible { text-decoration: underline; }

.references {
	margin: 1.2rem 0 0;
	padding-left: 1.9rem;
	font-size: 0.9rem;
	line-height: 1.55;
}

.references li {
	margin-bottom: 0.7rem;
	color: var(--text-soft);
}

/* The anchor a citation jumps to. Clearing the sticky stack is the same
   problem the section bars have, and the same fix. */
.references li:target {
	background: var(--bg-card);
	box-shadow: 0 0 0 0.45rem var(--bg-card);
	border-radius: 3px;
}

.ref-authors { color: var(--text-mute); }
.ref-where { color: var(--text-mute); }
.ref-where::before { content: " · "; }

/* ---- Motion ---------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }

	*, *::before, *::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
	}
}

/* ---- Print ----------------------------------------------------------- */

@media print {
	body { background: #fff; color: #000; }
	.site-header, .site-footer, .btn-row { display: none; }
	a { color: #000; }
}
