/* Plicara Labs — site styles
 *
 * The palette, the four schemes and the two type systems come from
 * assets/tokens.css, which is a copy of assets/tokens/tokens.css in the
 * foundation_lab repo. Do not edit the tokens here — change them there and
 * copy the file across, so the site and the brand guide cannot drift.
 *
 * Rules: https://github.com/plicara/foundation_lab/blob/main/docs/brand.md
 *
 * Scheme use on this site:
 *   twilight / cel   the lab and the models  — default, /models/
 *   notepad           tools                   — the #tools band, /tools/
 *   blueprint         benchmarks              — /benchmarks/
 */

/* --- faces --------------------------------------------------------------- */
/* Self-hosted. No external requests, by design. */

@font-face {
  font-family: "Fraunces";
  src: url("fonts/fraunces.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("fonts/newsreader.woff2") format("woff2-variations");
  font-weight: 200 800;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("fonts/archivo.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/jetbrains-mono-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

/* --- base ---------------------------------------------------------------- */

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

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

body,
[data-scheme] {
  /* --pl-face-body follows the scheme's typeset: Newsreader in the warm half,
     Archivo in the technical half. Using --pl-font-body here instead would
     pin every scheme to the serif. */
  font-family: var(--pl-face-body);
}

body {
  margin: 0;
  font-size: 18px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  width: min(100% - 2.5rem, 74rem);
  margin-inline: auto;
}

a { color: inherit; }

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

.skip-link {
  position: absolute;
  left: -9999px;
  background: var(--pl-accent);
  color: var(--pl-accent-on);
  padding: 0.6rem 1rem;
  font-family: var(--pl-font-mono);
  font-size: 0.8rem;
}
.skip-link:focus { left: 1rem; top: 1rem; z-index: 10; }

/* --- type roles ---------------------------------------------------------- */

/* CASE. One rule, both typesets, no exceptions:
 *
 *   headings are sentence case      — warm and technical alike
 *   uppercase belongs to mono only  — eyebrows, nav, buttons, tags, pills,
 *                                     column heads
 *
 * Uppercase is what marks a label as a label. Spending it on headings too
 * would leave the two roles looking the same, and it would wreck the package
 * names in the Code band — regexbench and labloop are identifiers, and
 * REGEXBENCH is a different string.
 *
 * Display roles are LOWERCASE (2026-08, brand.md § Typography): headlines,
 * the lab name, section titles. Professional with an air of levity, and a
 * property of the role rather than something to remember per heading. */
h1, h2, h3 {
  font-family: var(--pl-face-display);
  font-variation-settings: var(--pl-display-settings);
  font-weight: var(--pl-display-weight);
  letter-spacing: var(--pl-tracking-display);
  text-transform: lowercase;
  line-height: 1.04;
  text-wrap: balance;
  margin: 0;
}

/* The display face also sets row headers in data tables — model names, phase
   numbers. Same case rule applies. */
.data-table tbody th { text-transform: lowercase; }

/* Eyebrows are lowercase by decision (2026-08): "01. the mission", not
   "01. THE MISSION". The other mono labels (nav, buttons, tags, column heads)
   keep uppercase until the wider branding pass. */
.eyebrow {
  font-family: var(--pl-font-mono);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: var(--pl-tracking-label);
  text-transform: lowercase;
  color: var(--pl-text-muted);
  margin: 0;
}

.mono,
.tag,
.tags,
code {
  font-family: var(--pl-font-mono);
  font-variant-numeric: tabular-nums;
}

/* --- header -------------------------------------------------------------- */

.site-header {
  border-bottom: 1px solid var(--pl-rule);
  position: sticky;
  top: 0;
  background: var(--pl-bg);
  z-index: 5;
}
.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 1rem;
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  font-family: var(--pl-face-display);
  font-variation-settings: var(--pl-display-settings);
  font-weight: var(--pl-display-weight);
  font-size: 1.15rem;
  letter-spacing: var(--pl-tracking-display);
  /* The in-page header sets the name in the surface's display face beside
     the mark — lowercase either way (brand.md § Logo). */
  text-transform: lowercase;
}
.brand svg { display: block; color: var(--pl-accent); flex: none; }

/* The header .wrap wraps the nav onto its own line, but the nav has to wrap
   its own items too: five uppercase mono labels measure ~408px, which is
   wider than a 390px phone, and without this the page scrolls sideways. The
   row gap is tighter than the column gap so a wrapped second row sits close
   to the first rather than reading as a separate block. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.6rem;
  font-family: var(--pl-font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.site-nav a {
  text-decoration: none;
  color: var(--pl-text-muted);
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--pl-text); border-bottom-color: var(--pl-accent); }

/* --- hero ---------------------------------------------------------------- */

.hero { padding-block: clamp(3.5rem, 9vw, 7.5rem) clamp(2.5rem, 6vw, 5rem); }
.hero-inner { display: grid; gap: 1.4rem; }
.hero h1 {
  font-size: clamp(2.6rem, 6.4vw, 5.1rem);
  max-width: 18ch;
}

/* The mark and the wordmark together. In-page headers may set the name in the
   surface's display face beside the mark — the mark carries the identity, the
   letterforms follow the page's register. */
.hero-lockup { display: flex; align-items: center; gap: clamp(1.1rem, 2.6vw, 1.9rem); flex-wrap: wrap; }
.hero-mark {
  width: clamp(78px, 11vw, 132px);
  aspect-ratio: 1;
  flex: none;
  background: var(--pl-mark) center / contain no-repeat;
}
.lede {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  color: var(--pl-text-muted);
  max-width: 58ch;
  margin: 0;
}

.cta-row { display: flex; gap: 0.9rem; flex-wrap: wrap; margin-top: 0.6rem; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.72rem 1.25rem;
  text-decoration: none;
  font-family: var(--pl-font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  border: 1px solid transparent;
}
.btn-primary {
  background: var(--pl-orange);
  color: var(--pl-on-orange);
}
.btn-primary:hover { filter: brightness(1.07); }
.btn-ghost {
  border-color: var(--pl-rule);
  color: var(--pl-text);
}
.btn-ghost:hover { border-color: var(--pl-accent); }
.arrow { transition: transform 140ms ease-out; }
.btn:hover .arrow { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .arrow { transition: none; }
  .btn:hover .arrow { transform: none; }
}

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

.section { padding-block: clamp(2.75rem, 6vw, 5rem); }

.section-head {
  border-top: 2px solid var(--pl-text);
  padding-top: 0.9rem;
  margin-bottom: 2rem;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.section-head h2 { font-size: clamp(1.5rem, 2.8vw, 2.1rem); }
.section-head p { margin: 0; }

/* --- focus grid ---------------------------------------------------------- */

.focus-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.75rem;
}
.focus h3 { font-size: 1.2rem; margin-bottom: 0.4rem; }
.focus p { color: var(--pl-text-muted); margin: 0; font-size: 0.97rem; }

/* --- projects ------------------------------------------------------------ */

.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1.25rem;
}
.card {
  background: var(--pl-surface);
  border: 1px solid var(--pl-rule);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  min-width: 0;
}
.card-title { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.card-title h3 { font-size: 1.1rem; }
.card p { margin: 0; color: var(--pl-text-muted); font-size: 0.95rem; }

.tags { display: flex; gap: 0.45rem; flex-wrap: wrap; margin-top: auto; padding-top: 0.4rem; }
.tag {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--pl-rule);
  color: var(--pl-text-muted);
}
.tag[data-state="live"] { background: var(--pl-orange); color: var(--pl-on-orange); border-color: transparent; }

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

.site-footer {
  border-top: 1px solid var(--pl-rule);
  padding-block: 2rem 3rem;
  margin-top: 2rem;
}
.site-footer .wrap {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-family: var(--pl-font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--pl-text-muted);
}
.footer-links { display: flex; gap: 1.4rem; }
.site-footer a { text-decoration: none; }
.site-footer a:hover { color: var(--pl-accent); }

/* --- 404 ----------------------------------------------------------------- */

.notfound {
  min-height: 62vh;
  display: grid;
  align-content: center;
  gap: 1rem;
  padding-block: 4rem;
}
.notfound h1 { font-size: clamp(2.4rem, 6vw, 4rem); }

/* --- wide content -------------------------------------------------------- */

table { width: 100%; border-collapse: collapse; }
.table-wrap { overflow-x: auto; }

/* --- hero waves ---------------------------------------------------------- */
/* One wavelength, one gap, one amplitude law, and a constant phase step from
   line to line, so the set nests and no two lines can cross. That is what
   makes it read as tidy rather than as a tangle. Recedes by fading.
   Generated by assets/brand/waves.py. */

.waves {
  display: block;
  width: 100%;
  height: clamp(92px, 11vw, 168px);
  color: var(--pl-accent);
  opacity: 0.8;
  margin-top: clamp(1.25rem, 3vw, 2.25rem);
}
[data-scheme="cel"] .waves,
:root:not([data-scheme]) .waves { opacity: 0.62; }

/* --- scheme seams -------------------------------------------------------- */
/* Where two schemes meet, the ground changes along a squiggle: the incoming
   scheme's ground is filled to a wave edge, the edge is inked, and one echo
   rides above it. Generated by assets/brand/waves.py.

   The ground path takes its colour from `color` rather than `fill`, because
   the SVG sets fill="currentColor" as a presentation attribute: if this
   stylesheet is ever served stale against newer markup, the fill degrades to
   the inherited text colour instead of SVG's black default. */

.seam-wrap { line-height: 0; }  /* no descender gap under the inline svg */

.seam {
  display: block;
  width: 100%;
  height: clamp(76px, 10vw, 150px);
}

/* Entering the band, the tools' paper ground rises into the lab's sky, inked
   so the line never sits on its own colour.
   COUPLING: the seam sits OUTSIDE the band it transitions into, so it cannot
   inherit the band's scheme. It used to hard-code --pl-white and --pl-emerald (now --pl-teal)
   as literals that had to be retuned by hand whenever the band's scheme was
   repainted upstream — and the harbour repaint duly moved the technical
   accent to rust while these stayed teal. --pl-band-* are the upstream tokens
   for exactly this: they follow the band's light/dark switch, so the seam
   tracks it without a literal to forget. */
.seam-down { background: var(--pl-bg); }
.seam-down .wave-ground { color: var(--pl-band-bg); }
.seam-down .wave-crest,
.seam-down .wave-echo { stroke: var(--pl-band-ink); }

/* Leaving it, the lab's own ground rises back, inked with its accent. */
.seam-up { background: var(--pl-band-bg); }
.seam-up .wave-ground { color: var(--pl-bg); }
.seam-up .wave-crest,
.seam-up .wave-echo { stroke: var(--pl-accent); }

.wave-echo { opacity: 0.38; }

/* --- banded scheme ------------------------------------------------------- */
/* A scheme that paints its own ground must be full-bleed and must carry inline
   padding. An inset rectangle with flush text reads as a mistake — the ground
   has to run to the edge of the viewport, and the content has to sit in .wrap
   like everything else. */

.band {
  padding-block: clamp(0.5rem, 2vw, 1.5rem) clamp(1rem, 3vw, 2.5rem);
}

/* The seams already carry the entry and exit, so the first and last sections
   inside a band do not need their full padding on top of that. */
.band .section:first-child { padding-top: clamp(1rem, 2.5vw, 2rem); }
.band .section:last-child { padding-bottom: clamp(1.5rem, 3vw, 2.5rem); }

/* The card's title is the repository link, stretched over the whole card so
   the card stays clickable; links inside the body sit above the stretch. Kept
   as a div-plus-stretched-link rather than one big anchor so a card can carry
   links of its own (labloop credits autoresearch). */
.card { position: relative; transition: border-color 140ms ease-out; }
.card:hover { border-color: var(--pl-accent); }
.card-title a { text-decoration: none; }
.card-title a::after { content: ""; position: absolute; inset: 0; }
.card:hover .card-title h3 { text-decoration: underline; text-underline-offset: 3px; }
.card p a { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .card { transition: none; } }

/* --- brand mark ---------------------------------------------------------- */
/* Masked rather than inlined, so the mark takes the scheme's accent without
   duplicating 3 KB of path data in every page that shows it. */

/* THE MARK IS SHOWN IN COLOUR, so it is a background-image rather than a
   masked shape: a mask can only take one colour, and the colour build carries
   rose, butterscotch and emerald facets under the line. That costs the two
   builds below, because the outline is sumi on light grounds and chalk on
   dark ones, and a single file cannot be both.
   Root-relative on purpose: a url() inside a custom property resolves against
   the stylesheet that consumes it, so a relative path would break the pages
   in subdirectories. */
:root {
  --pl-mark: url("/assets/brand/mark-colour-dark.svg");
  --pl-mark-small: url("/assets/brand/mark-colour-small-dark.svg");
}
@media (prefers-color-scheme: light) {
  :root:not([data-scheme]) {
    --pl-mark: url("/assets/brand/mark-colour.svg");
    --pl-mark-small: url("/assets/brand/mark-colour-small.svg");
  }
}
[data-scheme="twilight"],
[data-scheme="blueprint"] {
  --pl-mark: url("/assets/brand/mark-colour-dark.svg");
  --pl-mark-small: url("/assets/brand/mark-colour-small-dark.svg");
}
[data-scheme="cel"],
[data-scheme="notepad"] {
  --pl-mark: url("/assets/brand/mark-colour.svg");
  --pl-mark-small: url("/assets/brand/mark-colour-small.svg");
}

/* The small build is the same drawing as the hero, one weight heavier, so
   the line holds at 30px. It is a weight change and nothing else: the back
   range stays in, because the header and the hero have to be one logo. */
.brand-mark {
  width: 30px;
  height: 30px;
  flex: none;
  background: var(--pl-mark-small) center / contain no-repeat;
}

/* --- utilities ----------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- mission ------------------------------------------------------------- */

/* Deliberately NOT lowercased, though it is set in the display face. This is
   a quotation, not a display role, and it contains an acronym — "ai systems"
   reads as a typo rather than as levity. brand.md carves this out: the
   lowercase transform is for display roles; quoted text keeps its capitals. */
.mission-statement {
  font-family: var(--pl-face-display);
  font-variation-settings: var(--pl-display-settings);
  font-weight: var(--pl-display-weight);
  letter-spacing: var(--pl-tracking-display);
  font-size: clamp(1.45rem, 2.8vw, 2rem);
  line-height: 1.28;
  text-wrap: balance;
  max-width: 34ch;
  margin: 0 0 1.6rem;
}

.mission-body { max-width: var(--pl-measure); }
.mission-body p { color: var(--pl-text-muted); margin: 0 0 1rem; }
.mission-body p:last-child { margin-bottom: 0; }

/* Short intro line under a section head. */
.section-intro {
  font-size: 1.12rem;
  color: var(--pl-text-muted);
  max-width: var(--pl-measure);
  margin: 0;
}

/* --- data tables --------------------------------------------------------- */
/* Figures are mono and tabular in both type systems: an elevation and an eval
   score are the same kind of fact, so they have to look like it. */

.data-table { border-collapse: collapse; width: 100%; min-width: 40rem; }

/* Even gutters, flush at both outer edges. Zeroing padding-right on a .num
   column instead would butt the figure straight into the next cell. */
.data-table th,
.data-table td {
  text-align: left;
  padding: 0.7rem 1.4rem;
  border-bottom: 1px solid var(--pl-rule);
  vertical-align: middle;
}
.data-table th:first-child,
.data-table td:first-child { padding-left: 0; }
.data-table th:last-child,
.data-table td:last-child { padding-right: 0; }

.data-table thead th {
  font-family: var(--pl-font-mono);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: var(--pl-tracking-label);
  text-transform: uppercase;
  color: var(--pl-text-muted);
  border-bottom-width: 2px;
  border-bottom-color: var(--pl-text);
  white-space: nowrap;
}

.data-table tbody th {
  font-family: var(--pl-face-display);
  font-variation-settings: var(--pl-display-settings);
  font-weight: var(--pl-display-weight);
  font-size: 1.04rem;
  white-space: nowrap;
}

.data-table td { color: var(--pl-text-muted); font-size: 0.94rem; }
.data-table .num { text-align: right; }
.data-table thead .num { text-align: right; }
.data-table tbody tr:last-child th,
.data-table tbody tr:last-child td { border-bottom: none; }

/* --- the range ----------------------------------------------------------- */
/* The model family, unnamed until there is something to name. One paper-plane
   glyph per planned model, masked so each takes the scheme's accent, placed
   by inline left/top/width because the composition is art-directed rather
   than laid out.
   Width and opacity both ascend with capability tier, which is the naming
   system drawn rather than stated: glider, delta, canard, hammer. Keep that
   order if the numbers are ever retuned, or the picture contradicts
   brand.md. (.peak is a fossil of the mountain-era glyphs; the class name
   outlived the drawing.) */

.range {
  position: relative;
  aspect-ratio: 1440 / 430;
  margin-top: 0.5rem;
}

.peak {
  position: absolute;
  aspect-ratio: 1;
  background: var(--pl-accent);
  -webkit-mask: var(--g) center / contain no-repeat;
  mask: var(--g) center / contain no-repeat;
}

.note {
  margin: 1.5rem 0 0;
  color: var(--pl-text-muted);
  font-size: 0.92rem;
  max-width: 62ch;
}
.note strong { color: var(--pl-text); font-weight: 600; }

.tag[data-state="wip"] {
  border-style: dashed;
  border-color: var(--pl-text-muted);
}

/* --- contact ------------------------------------------------------------- */

.contact-card {
  background: var(--pl-surface);
  border: 1px solid var(--pl-rule);
  padding: clamp(1.4rem, 3vw, 2.2rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem 2rem;
  flex-wrap: wrap;
}
.contact-card p { margin: 0; color: var(--pl-text-muted); max-width: 48ch; }

/* An email address is an identifier, so the label rule's uppercase would make
   it a different string. The button keeps the mono and drops the transform. */
.btn-mail { text-transform: none; letter-spacing: 0.02em; font-size: 0.92rem; }

/* --- preflight ----------------------------------------------------------- */
/* The dedicated pages, before there is anything to show on them. */

.preflight {
  padding-block: clamp(3rem, 8vw, 6rem) clamp(2rem, 5vw, 3.5rem);
  display: grid;
  gap: 1.35rem;
  align-content: start;
}
.preflight h1 { font-size: clamp(2.4rem, 5.6vw, 4.2rem); max-width: 16ch; }
.preflight .cta-row { margin-top: 0.4rem; }

/* --- research ------------------------------------------------------------- */
/* Long-form articles, generated from markdown by research/build.py. The
   register sits between the homepage and a model card: warm serif at reading
   measure, quiet chrome, the data in mono. Deliberately no wave art here;
   the words carry the page.

   CASE CARVE-OUT: the global heading rule lowercases display roles, but an
   article's title and headings are authored prose, not chrome, in the same
   way brand.md exempts pull quotes ("body voice printed large"). Forcing
   them through the transform would mangle acronyms and identifiers: "AI"
   becomes "ai", "ReDoS" becomes "redos". So content headings keep their
   capitals, and the same applies to titles listed on the index. */

.article { padding-block: clamp(2.5rem, 6vw, 4.5rem); }
.article-head,
.article-body,
.article-back { max-width: var(--pl-measure); }

.article-head { display: grid; gap: 0.9rem; margin-bottom: 2.25rem; }
.article-head h1 {
  font-size: clamp(2rem, 4.6vw, 3.2rem);
  text-transform: none;
}
.article-meta {
  font-family: var(--pl-font-mono);
  font-size: 0.78rem;
  color: var(--pl-text-muted);
  margin: 0;
}
.article-meta a { color: var(--pl-accent); }

.article-body h2,
.article-body h3,
.article-body h4 { text-transform: none; margin: 2.2rem 0 0.7rem; }
.article-body h2 { font-size: 1.5rem; }
.article-body h3 { font-size: 1.2rem; }
.article-body p { margin: 0 0 1rem; }
.article-body a { text-decoration-thickness: 1px; text-underline-offset: 2px; }
.article-body ul,
.article-body ol { margin: 0 0 1rem; padding-left: 1.4rem; }
.article-body li { margin-bottom: 0.35rem; }

.article-body blockquote {
  margin: 1.4rem 0;
  padding: 0.2rem 0 0.2rem 1.1rem;
  border-left: 3px solid var(--pl-accent);
  color: var(--pl-text-muted);
}
.article-body blockquote p:last-child { margin-bottom: 0; }

.article-body code {
  font-size: 0.88em;
  background: var(--pl-surface);
  padding: 0.1em 0.35em;
}
.article-body pre {
  background: var(--pl-surface);
  border: 1px solid var(--pl-rule);
  padding: 1rem 1.2rem;
  overflow-x: auto;
  margin: 1.4rem 0;
  font-size: 0.85rem;
  line-height: 1.55;
}
.article-body pre code { background: none; padding: 0; font-size: inherit; }

/* Markdown emits bare tables, so the table is its own scroll container:
   display block trades a little table semantics for never letting a wide
   table scroll the page sideways on a phone. */
.article-body table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 1.4rem 0;
}
.article-body th,
.article-body td {
  text-align: left;
  padding: 0.5rem 1.2rem 0.5rem 0;
  border-bottom: 1px solid var(--pl-rule);
}
.article-body th {
  font-family: var(--pl-font-mono);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: var(--pl-tracking-label);
  text-transform: uppercase;
  color: var(--pl-text-muted);
  border-bottom-width: 2px;
  border-bottom-color: var(--pl-text);
}

.article-body img { max-width: 100%; height: auto; }
.article-body hr { border: 0; border-top: 1px solid var(--pl-rule); margin: 2rem 0; }
.article-body .footnote { font-size: 0.88rem; color: var(--pl-text-muted); }

.article-back { margin: 2.5rem 0 0; font-family: var(--pl-font-mono); font-size: 0.8rem; }

/* The index: date in the margin, title and summary beside it. */
.research-list { list-style: none; margin: 1.5rem 0 0; padding: 0; }
.research-entry {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 0.35rem 1.5rem;
  padding-block: 1.4rem;
  border-top: 1px solid var(--pl-rule);
}
.research-entry:last-child { border-bottom: 1px solid var(--pl-rule); }
.research-date { font-size: 0.78rem; color: var(--pl-text-muted); margin: 0; padding-top: 0.35rem; }
.research-entry h2 { font-size: 1.25rem; text-transform: none; margin: 0 0 0.3rem; }
.research-entry h2 a { text-decoration: none; }
.research-entry h2 a:hover { text-decoration: underline; text-underline-offset: 3px; }
.research-entry p { margin: 0; color: var(--pl-text-muted); font-size: 0.95rem; max-width: 62ch; }
@media (max-width: 40rem) {
  .research-entry { grid-template-columns: 1fr; gap: 0.15rem; }
  .research-date { padding-top: 0; }
}

/* --- print ---------------------------------------------------------------- */
/* Article pages double as their own PDFs: build.py prints them through
   Chromium, so this block IS the PDF layout. One fixed light look, in
   literals on purpose; a PDF does not follow the reader's OS theme.
   The literals are harbour's ink, smoke, mist and rule — keep them in step
   with the palette by hand, since var() would resolve to the reader's
   scheme and defeat the point. */
@media print {
  @page { margin: 18mm 16mm; }
  body { background: #ffffff; color: #05192B; font-size: 11pt; }
  .site-header,
  .site-footer,
  .skip-link,
  .article-back,
  .article-pdf-link { display: none; }  /* the PDF linking to itself */
  .article { padding-block: 0; }
  .article-head h1 { color: #05192B; }
  .article-meta,
  .eyebrow { color: #829AA1; }
  .article-body a { color: #05192B; }
  .article-body blockquote { border-left-color: #05192B; color: #829AA1; }
  .article-body code,
  .article-body pre { background: #DEEAEE; }
  .article-body pre { border-color: #CADADF; }
  .article-body .footnote { color: #829AA1; }
  /* No scrollbars on paper: code wraps and tables lay out as tables again,
     so wide content folds instead of clipping at the page edge. */
  .article-body pre { white-space: pre-wrap; overflow-wrap: break-word; }
  .article-body table { display: table; width: 100%; }
}
