
:root {
  --paper: #f4f2ee;
  --surface: #ffffff;
  --ink: #161616;
  --rule: #e4e1da;
  --accent: #5B3E96;
  --muted: #5d5a54;
  /* Story cards need to read as objects sitting ON the page, not as regions of
     it. Paper is warm off-white, so the card goes to true white and takes a rule
     a shade darker than the page's hairlines — a card you have to hunt for is a
     card that is not doing its job. */
  --card: #ffffff;
  --card-rule: #d9d5cc;
  /* TWO different widths, and conflating them was the bug that made a 27-inch
     monitor render the site as a phone column.

     --measure is the READING measure: how wide a line of prose may run before the
     eye loses the return sweep. 34rem is right and stays right at every size.
     --shell is the PAGE: masthead, index grid, figures. It grows with the screen.

     One variable did both jobs, so the whole site was pinned at reading width. */
  --measure: 34rem;
  --shell: 100%;
  --gutter: 1.25rem;
  /* How wide a figure may run. Images are NOT bound by the reading measure —
     a photograph has no line length to respect, and an article whose pictures
     are as narrow as its text reads like a phone screen stretched. */
  --figure: 100%;
  /* Text in the first track, at the reading measure; everything else free to run
     the width of the shell. LEFT, not centred — the article is anchored to the same
     edge as the masthead and the index, so a reader's eye lands in one place on
     every page of the site. */
  --article-cols: min(var(--measure), 100%) 1fr;
  /* Three faces, three jobs — the article-layout handoff's type system.
     Display carries headlines only and never goes below 14px. Body is a reading
     serif with a true italic for deks and standfirsts. Labels are a sans, small,
     uppercase, letterspaced: kickers, datelines, nav, anything that is signage
     rather than prose. Inter stays defined for the cards, which are rendered by
     Pillow against theme.yaml and are a separate system. */
  --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --body: "Spectral", Georgia, "Times New Roman", serif;
  --label: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sans: var(--label);
  /* The handoff states its greys as ink at an opacity rather than as fixed hexes,
     which is what lets one set of rules hold in both themes. Keeping the ink as a
     raw triple is what makes that expressible. */
  --ink-rgb: 22, 22, 22;
  --hairline: rgba(var(--ink-rgb), .3);
  --label-ink: rgba(var(--ink-rgb), .5);
  --dek-ink: rgba(var(--ink-rgb), .72);
}
/* Tablet. */
@media (min-width: 48rem) {
  :root { --shell: 46rem; --gutter: 2rem; --measure: 44rem; --figure: 44rem; }
}
/* Desktop and up. The handoff's column is 957px and it is stated as final, so the
   measure stops growing here rather than at a per-breakpoint width — one column,
   one number, everywhere it fits. It is a long line by convention; it is paired
   with an 18px reading serif, which carries a longer line than a sans does. */
@media (min-width: 72rem) {
  :root {
    --shell: 72rem; --gutter: 2.5rem; --measure: 957px; --figure: 72rem;
  }
}
/* Large desktop. The page keeps growing past the reading column: the column stops
   at the handoff's 957px because that is a line length, but the SHELL carries on,
   because that is a page. Prose sits at the left of it rather than centred — which
   is what makes the index look, in his words, like it uses the screen. A centred
   column in a wide shell reads as a narrow site with padding. */
@media (min-width: 100rem) {
  :root { --shell: 84rem; --gutter: 3rem; --figure: 84rem; }
}
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #14131a;
    --surface: #1c1b23;
    --ink: #ece9e4;
    --ink-rgb: 236, 233, 228;
    --rule: #2f2d38;
    /* Same intent as light mode, inverted: the card lifts OFF the ground rather
       than sinking into it, and its rule is lighter than the page hairline. */
    --card: #1f1e27;
    --card-rule: #3a3746;
    --accent: #A78BDD;
    --muted: #a09caa;
  }
}
@font-face {
  font-family: "Inter var";
  src: url("/assets/Inter-Variable.ttf") format("truetype-variations");
  font-weight: 100 900;
  font-display: swap;
}
/* The display serif. SELF-HOSTED, not linked: this site ships `font-src 'self'`,
   so a Google Fonts URL would be blocked by the CSP and every headline would
   silently fall back to the sans — the failure would look like a design choice.
   Two subsets with unicode-range so a Latin-only page never downloads the rest;
   latin-ext carries the diacritics the slate needs (Davíð, Ružička, Šefčovič).
   OFL, licence bundled at assets/fonts/OFL-CormorantGaramond.txt. */
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/CormorantGaramond-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  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;
}
/* The reading face and the label face, from the article-layout handoff. Self-hosted
   for the same reason as the display serif: `font-src 'self'` blocks a Google Fonts
   URL, and the page would fall back to a system stack without erroring. */
@font-face {
  font-family: "Spectral";
  src: url("/assets/Spectral-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Spectral";
  src: url("/assets/Spectral-Italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Spectral";
  src: url("/assets/Spectral-SemiBold.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/IBMPlexSans-Regular.woff2") format("woff2");
  font-weight: 400 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/CormorantGaramond-latin-ext.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  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;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 18px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
.wrap { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
/* Every prose block on the site holds the reading measure and stays LEFT, so the
   index, the standing pages and the article all start at the same edge. */
.prose { max-width: var(--measure); margin-inline: 0; }

/* The ARTICLE is different, and this is what makes a desktop page read like a
   page instead of a phone column: a three-track grid whose middle track is the
   reading measure. Text sits in the middle track; figures span all three and
   run out to --figure. Capping images at the text measure was the actual
   complaint — a photograph has no line length to respect.

   The template repeats on `section` because the narrative emits
   article > section > figure, and a grandchild cannot participate in its
   grandparent's grid. Same columns on both levels keeps them aligned. */
article.prose {
  display: grid;
  grid-template-columns: var(--article-cols);
  max-width: none;
  margin-inline: 0;
}
article.prose > *,
article.prose > section > * { grid-column: 1; }
article.prose > section {
  display: grid;
  grid-template-columns: var(--article-cols);
  grid-column: 1 / -1;
}
article.prose > figure,
article.prose > section > figure,
article.prose > .deck,
article.prose > section > .deck {
  grid-column: 1 / -1;
  width: min(var(--figure), 100%);
  max-width: none;
  justify-self: center;
}
a { color: inherit; }
header.masthead {
  border-bottom: 1px solid var(--rule);
  padding: 1.5rem 0;
  margin-bottom: 3rem;
}
/* Phone-first: the masthead STACKS — brand on its own line, then the nav in
   tight rows beneath it. Six items in a row beside the brand wrapped into four
   ragged lines at 390px, each a 1.9rem row-gap apart, and the masthead swallowed
   half the first screen. The single row returns at 48rem (below, after .nav). */
.masthead .wrap {
  display: flex; flex-direction: column; align-items: flex-start; gap: .9rem;
}
/* Chrome is signage, so it takes the label face. Without this the masthead and nav
   inherit the body serif and the page loses the distinction the type system is for. */
.brand {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--label); font-weight: 500;
  letter-spacing: .01em; text-decoration: none;
  /* The name never breaks: "Atlas / Ahead" on two lines reads as two words that
     happen to be stacked, not as a brand. */
  white-space: nowrap;
}
.brand svg { color: var(--accent); flex: none; }
.nav { font-family: var(--label); font-size: 13px; letter-spacing: .1em;
  text-transform: uppercase; }
/* Muted by default so the nav recedes; the page you are ON takes the accent and
   full opacity. A nav where every item shouts equally tells a reader nothing. */
.nav a { color: var(--muted); text-decoration: none; opacity: .78; }
.nav a:hover { color: var(--ink); opacity: 1; }
.navgrp { position: relative; display: inline-flex; align-items: center; }
.navgrp > .navmenu {
  position: absolute; top: 100%; left: 0; z-index: 40;
  display: none; flex-direction: column; min-width: 168px;
  background: var(--paper); border: 1px solid var(--rule);
  padding: 6px 0; margin-top: 6px;
}
/* opens on hover AND on keyboard focus, so it is reachable without a pointer */
.navgrp:hover > .navmenu, .navgrp:focus-within > .navmenu { display: flex; }
.navgrp > .navmenu a {
  padding: 7px 14px; white-space: nowrap; opacity: .8;
  font-family: var(--label); font-size: 12px; letter-spacing: .08em;
}
.navgrp > .navmenu a:hover { opacity: 1; background: var(--rule); }
/* 🐛 The nav is a CONTAINED horizontal scroller (see the note below), and an
   absolutely-positioned menu inside `overflow-x: auto` is CLIPPED by it — the
   markup was right and the dropdown simply could not escape its container.
   Above the breakpoint the strip fits without scrolling, so overflow goes back
   to visible and the menu can hang. Below it, the children render inline in the
   strip instead, so nothing is ever unreachable. */
@media (min-width: 48rem) {
  .nav { overflow: visible; }
}
@media (max-width: 47.99rem) {
  .navgrp { gap: 1.4rem; }
  .navgrp > .navmenu {
    position: static; display: flex; flex-direction: row; gap: 1.4rem;
    background: none; border: 0; padding: 0; margin: 0; min-width: 0;
  }
  .navgrp > .navmenu a { padding: 0; font-size: 13px; letter-spacing: .1em; }
}
.nav a[aria-current="page"] {
  color: var(--accent); opacity: 1; font-weight: 620;
}
.kicker {
  font-family: var(--label);
  font-size: 12px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--label-ink); margin: 0 0 18px;
}

/* --- the article lead, per the layout handoff ------------------------------- */
/* Kicker row: section · hairline divider · status tag. */
.kickrow {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  font-family: var(--label); font-size: 12px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--label-ink); margin: 0 0 18px;
}
.kickrow .divider { width: 1px; height: 11px; background: var(--hairline); flex: none; }
/* 🚨 The handoff replaces the filled status badge with an outline chip. James
   looked at both on a real card and kept the fill (2026-08-10): on a card the
   badge is the only element carrying the verdict, and filled is what reads at
   thumbnail size. This is the one point where the site follows theme.yaml rather
   than the handoff, so the site and the cards still say the same thing. */
.kickrow .badge { margin: 0; }
.dateline {
  font-family: var(--label); font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--label-ink); margin: 0 0 34px;
}
.standfirst {
  font-family: var(--body); font-style: italic; font-size: 20px;
  line-height: 1.55; margin: 0 0 10px;
}
.contents {
  font-family: var(--label); font-size: 12px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--label-ink); margin: 0 0 30px;
}
.hairline { width: 34px; height: 1px; background: var(--hairline); margin: 0 0 30px; }
/* The hero breaks the column and runs the full viewport. It has to out-specify the
   article grid's figure rule, which caps every other figure at --figure — that rule
   is what kept the hero boxed at 957px on the first cut. */
.hero, article.prose > figure.hero, article.prose > section > figure.hero {
  position: relative; margin: 0 0 40px; grid-column: 1 / -1;
  width: 100vw; max-width: none; margin-left: 50%; transform: translateX(-50%);
  justify-self: stretch;
}
.hero img {
  display: block; width: 100%; height: 70vh; max-height: 70vh;
  object-fit: cover; object-position: 50% 28%;
}
/* The fallback for a file too small to bleed: its own width, its own aspect,
   centred in the column, never scaled up. Sharp and smaller beats wide and
   mushy, and it is the only honest way to run the sole free photograph of an
   event that nobody else shot. */
.hero-inline, article.prose > figure.hero-inline {
  width: auto; margin: 0 0 40px; transform: none; grid-column: 1 / -1;
}
.hero-inline img { height: auto; max-height: none; object-fit: contain; }
/* The short sits in the reading column, above the article: a 9:16 frame capped
   so it never towers over the text it introduces. Its credit is the AI-narration
   disclosure, which is law wherever the short appears. */
figure.story-video {
  margin: 0 0 40px; max-width: var(--measure);
}
figure.story-video video {
  display: block; width: 100%; max-width: 340px; margin: 0 auto;
  aspect-ratio: 9 / 16; background: #000; border-radius: 4px;
  object-fit: cover;
}
/* Out-specifies `.hero figcaption`, which sits later in the sheet: an inline hero
   has margin to put its credit in, so it does not need a bar over the picture. */
figure.hero-inline figcaption {
  position: static; background: none; color: var(--muted);
  padding: .5rem 0 0; font-size: 12px;
}
/* A portrait cropped to 70vh of a wide viewport becomes a face at arm's length.
   Biasing the crop upward is the deck's own rule for photo slides and it helps,
   but the real fix is a landscape hero — the handoff says the crop is TBD by the
   final photo, and this one was chosen as a card portrait, not a page hero. */
/* 🚨 The handoff shows no credit on the hero. Images are claims here — licence and
   attribution are mandatory and carry no waiver — so the credit is added rather
   than inherited. It sits on the image because a full-bleed photo has no margin to
   put it in. */
.hero figcaption {
  position: absolute; left: 0; bottom: 0; margin: 0;
  font-family: var(--label); font-size: 11px; letter-spacing: .08em;
  color: rgba(255, 255, 255, .82); background: rgba(0, 0, 0, .45);
  padding: .35rem .7rem;
}
h1 {
  font-family: var(--serif);
  font-size: clamp(2.4rem, 7vw, 64px); line-height: 1.12;
  margin: 0 0 22px; font-weight: 500;
}
h2 {
  font-family: var(--serif);
  font-size: 34px; line-height: 1.2; margin: 3rem 0 .75rem; font-weight: 500;
}
/* Standing pages can carry a third level; long-form deliberately cannot (the
   contract allows h2 only — a narrative needing sub-sub-structure is two pieces).
   Sized so the step down from h2 is unmistakable at a glance, because an h3 that
   reads as a small h2 makes a page look like it has lost its hierarchy. */
h3 {
  font-family: var(--serif);
  font-size: 26px; line-height: 1.25; margin: 2.25rem 0 .5rem; font-weight: 600;
}
/* The dek is the handoff's italic standfirst voice, and it keeps a shorter measure
   than the body: an italic line this size stops being readable long before 957px. */
.dek {
  font-family: var(--body); font-style: italic;
  font-size: 19px; line-height: 1.5; color: var(--dek-ink);
  max-width: 640px; margin: 0 0 28px;
}
p { margin: 0 0 1.15rem; }
.badge {
  display: inline-block; padding: .3rem .6rem; border-radius: 3px;
  font-size: .6875rem; letter-spacing: .12em; font-weight: 600;
  color: #fff; text-transform: uppercase;
}
.fn { text-decoration: none; color: var(--accent); font-weight: 600; padding: 0 .1em; }
.fn sup { font-size: .7em; }
figure { margin: 2rem 0; }
figure img { width: 100%; height: auto; display: block; border-radius: 2px; }
figcaption { font-size: .8125rem; color: var(--muted); margin-top: .5rem; }
.stat {
  font-size: clamp(2.2rem, 8vw, 3.2rem); font-weight: 680; line-height: 1.05;
  color: var(--accent); margin: 1.5rem 0 .5rem;
}
.panel {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: 4px; padding: 1.5rem; margin: 2.5rem 0;
}
.panel h2 { margin-top: 0; }
.deck {
  display: grid; grid-auto-flow: column; grid-auto-columns: 78%;
  gap: .75rem; overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: 1rem; margin: 1.5rem 0;
}
.deck img {
  width: 100%; height: auto; scroll-snap-align: center;
  border: 1px solid var(--rule); border-radius: 3px;
}
@media (min-width: 48rem) { .deck { grid-auto-columns: 42%; } }
ol.sources { padding-left: 1.4rem; font-size: .9375rem; }
ol.sources li { margin-bottom: .6rem; word-break: break-word; }
ol.sources a { color: var(--accent); }
/* The article's receipts. Ruled off from the prose so it reads as the record
   rather than a last paragraph, and muted because it is for checking, not
   reading — the inline links in the prose are the reading path. */
.sources-section {
  margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--rule);
}
.sources-section h2 { font-size: 1rem; margin-bottom: .75rem; }
footer.site {
  border-top: 1px solid var(--rule); margin-top: 4rem; padding: 2rem 0 3rem;
  font-size: .875rem; color: var(--muted);
}
/* The sourcing line is a promise, not a signature — it reads better standing
   beside the way back into the site than alone at the bottom of a page. */
.footer-nav {
  display: flex; gap: 1.9rem; flex-wrap: wrap; margin-bottom: 1rem;
  font-family: var(--label); font-size: 13px; letter-spacing: .1em;
  text-transform: uppercase;
}
.footer-nav a { color: var(--muted); text-decoration: none; opacity: .78; }
.footer-nav a:hover { color: var(--ink); opacity: 1; }
.footer-nav a[aria-current="page"] { color: var(--accent); opacity: 1; }
.lede-card {
  display: block; text-decoration: none;
  border: 1px solid var(--card-rule);
  border-radius: 4px; padding: 1.75rem; background: var(--card);
  transition: border-color .15s ease, transform .15s ease;
}
.lede-card .kicker { color: var(--accent); font-family: var(--label); }
.lede-card h2 {
  font-family: var(--serif); font-size: 1.75rem; line-height: 1.15;
  font-weight: 600; margin: 0 0 .5rem;
}
.lede-card .dek { font-size: 1.0625rem; margin-bottom: 0; }
/* The lead story wears the accent on its edge. Only the lead: an index where
   every card is emphasised has no lead, and the point of the rail is that a
   reader can see which story we are standing behind. The class is set in the
   markup rather than inferred with :first-child, so "which story leads" stays a
   decision the builder makes and not an accident of source order. */
.lede-card.is-lead { border-left: 3px solid var(--accent); }
@media (prefers-reduced-motion: no-preference) {
  .lede-card:hover { transform: translateY(-2px); }
}
.lede-card:hover { border-color: var(--accent); }

/* --- navigation ------------------------------------------------------------ */
/* Phone: ONE row that scrolls, not two that wrap.
   Measured at 390px before this: six items wrapped into a ragged second row that
   read as a leftover rather than a designed row, the masthead ate 144px — 17% of
   the viewport before a word of the story — and every link was a 21px tap target,
   less than half the 44px a thumb needs. Wrapping fixed none of that; it traded
   height for reachability and lost both.
   A single strip fixes both at once: real padding gives 44px targets, and one row
   is shorter than two even with the padding. The overflow is CONTAINED — the page
   body still never scrolls sideways, which is the rule that matters. */
.nav {
  display: flex; flex-wrap: nowrap; align-items: center;
  gap: 1.4rem;
  overflow-x: auto;
  /* 🐛 A full-bleed negative margin was the first attempt and it made the PAGE
     scroll sideways — measured, 390px viewport against a wider document. The
     contained scroller is the whole point; a bleed that escapes its container is
     the bug it was meant to avoid. The strip stays inside the shell padding.
     And `width: 100%` is load-bearing, not tidiness: the masthead is a COLUMN
     flex container, so the nav sized itself to its content — 580px inside a 390px
     wrap — and `overflow-x` had nothing to clip against. Measured, both times. */
  width: 100%;
  min-width: 0;
  scrollbar-width: none;
  /* The fade is the affordance: on a touch screen there is no scrollbar, so the
     only way to say "there is more this way" is to show the row not ending. */
  -webkit-mask-image: linear-gradient(to right, #000 88%, transparent 100%);
  mask-image: linear-gradient(to right, #000 88%, transparent 100%);
}
.nav::-webkit-scrollbar { display: none; }
.nav a {
  white-space: nowrap;
  /* 44px of tap target, from padding rather than a height, so the text stays on
     the same baseline as the brand beside it. */
  padding: .72rem 0;
}
/* The single brand-left, nav-right row needs ~900px before six items fit without
   wrapping — inside the 46rem tablet shell it cannot, so the strip holds through
   tablet and the row returns with the 72rem shell. Nothing scrolls or fades there:
   with room for every item, an affordance for hidden ones would be a lie. */
@media (min-width: 72rem) {
  .masthead .wrap {
    flex-direction: row; align-items: center;
    justify-content: space-between; gap: 1rem;
  }
  .nav {
    gap: 1.9rem; overflow-x: visible; width: auto;
    -webkit-mask-image: none; mask-image: none;
  }
  .nav a { padding: 0; }
}

/* --- tables ------------------------------------------------------------------
   The findings page is the first here to carry one. Wide content scrolls inside
   its own box; the page body never does. Figures are numbers in columns, so they
   get tabular figures and align right — a column of counts that does not line up
   is harder to compare than the same column in prose. */
.tablewrap { overflow-x: auto; margin: 1.5rem 0; }
/* An unstyled <hr> rendered as a stray dot mid-page. A rule between sections is a
   full-width hairline or it is nothing. */
.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 3rem 0; }
.prose table {
  border-collapse: collapse; width: 100%; font-family: var(--label);
  font-size: .88rem; line-height: 1.45;
}
.prose th, .prose td {
  padding: .5rem .9rem .5rem 0; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--rule); white-space: nowrap;
}
.prose th {
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); font-weight: 650; border-bottom-color: var(--ink);
}
.prose td + td, .prose th + th { text-align: right; font-variant-numeric: tabular-nums; }
/* A caveat belongs to the table above it, so it sits tight under it rather than
   floating in the paragraph rhythm. */
.prose blockquote {
  margin: .6rem 0 1.6rem; padding-left: 1rem;
  border-left: 3px solid var(--rule); color: var(--muted); font-size: .92rem;
}

/* --- section labels --------------------------------------------------------- */
.section-label {
  font-family: var(--label); font-size: .75rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
  margin: 3.5rem 0 1.25rem; padding-bottom: .6rem;
  border-bottom: 1px solid var(--rule);
}

/* --- the index -------------------------------------------------------------- */
.index { display: grid; gap: 1.5rem; }
/* Two up, and it stays two up. Three columns turned each story into a tile;
   at two, a card is wide enough for a headline to read as a headline. */
@media (min-width: 48rem) {
  .index { grid-template-columns: repeat(2, 1fr); }
  /* A lone story spans both columns. Left in one cell it reads as the first of
     several that failed to load, rather than as the only one we have published —
     and while the slate is being cleared one at a time, that is the common case. */
  .lede-card.is-only { grid-column: 1 / -1; }
}

/* --- the cards, on their own page ------------------------------------------ */
.cardpage {
  display: grid; gap: 1.25rem; grid-template-columns: 1fr;
  margin: 2rem 0; max-width: none;
}
@media (min-width: 48rem) { .cardpage { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 72rem) { .cardpage { grid-template-columns: repeat(3, 1fr); } }
.cardpage img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--rule); border-radius: 3px;
}
.cardpage figure { margin: 0; }
.cardpage figcaption {
  font-size: .75rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin-top: .5rem;
}
.backlink {
  display: inline-block; margin-top: 2.5rem; font-size: .9375rem;
  color: var(--accent); text-decoration: none; font-weight: 600;
}
/* Understated text links, not buttons — the handoff is explicit, and it is right:
   two purple buttons at the top of an article read as an app's call to action and
   pull rank on the headline. James kept the accent hue on links, so these carry it;
   the handoff's contribution here is the shape, not the colour. */
.readmore {
  font-family: var(--label); font-size: 13px; letter-spacing: .04em;
  color: var(--accent); text-decoration: none;
  border-bottom: 1px dotted var(--accent); padding-bottom: 1px;
}
.readmore:hover { border-bottom-style: solid; }
.readlinks { display: flex; gap: 28px; flex-wrap: wrap; margin: 0 0 30px; }
/* The article still leads: same colour, more weight, no box. */
.readmore-primary { font-weight: 500; }
/* The seam between sourced fact and our argument, marked by the builder rather
   than by a sentence in the prose. Small, quiet, and impossible to miss once you
   know what it means. */
.analysis-eyebrow {
  font-family: var(--label); font-size: 11px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--accent); font-weight: 500;
  margin: 3rem 0 -.4rem;
}

/* --- the signup form -------------------------------------------------------- */
.signup {
  display: flex; flex-direction: column; gap: .6rem;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: 4px; padding: 1.5rem; margin: 2rem 0;
}
.signup label {
  font-size: .75rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.signup input[type="email"] {
  font: inherit; font-size: 1rem; padding: .7rem .8rem;
  border: 1px solid var(--rule); border-radius: 3px;
  background: var(--paper); color: var(--ink); width: 100%;
}
.signup input[type="email"]:focus-visible, .signup button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.signup button {
  font: inherit; font-weight: 640; font-size: .9375rem; cursor: pointer;
  padding: .7rem 1.2rem; border-radius: 3px; align-self: flex-start;
  border: 1px solid var(--accent); background: var(--accent); color: var(--paper);
}
.signup button:hover { filter: brightness(1.08); }
/* The honeypot. Moved off-screen rather than `display: none`, because the naive
   bots this catches skip fields that are display:none — and because a screen
   reader is told to ignore it by aria-hidden, which is the honest way to hide
   something from a person without hiding it from a machine. */
.hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; margin: 0;
}

/* --- the ledger, the orgs, the archive, search ------------------------------ */
/* The ledger counts published work only, so it is typeset as a record rather than
   as a marketing statistic: no oversized numerals, no coloured panel. */
.ledger {
  display: grid; gap: 1px; margin: 1.5rem 0;
  background: var(--rule); border: 1px solid var(--rule);
}
@media (min-width: 34rem) { .ledger { grid-template-columns: repeat(2, 1fr); } }
/* The grid draws its rules as 1px gaps letting the container colour through, so an
   odd count leaves a filled-but-empty cell at the end — it reads as a tile that
   failed to load rather than as the end of the list. A trailing odd tile spans the
   row instead. The count changes every time a case is published, so this cannot be
   left to look right at one particular number. */
.tally:last-child:nth-child(odd) { grid-column: 1 / -1; }
.tally { background: var(--surface); padding: .9rem 1rem; }
.tally dt {
  font-size: .75rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}
.tally dd { margin: .25rem 0 0; display: flex; align-items: baseline; gap: .6rem; }
.tally b { font-size: 1.6rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.tally span { font-size: .8125rem; color: var(--muted); line-height: 1.35; }
.ledger-note { font-size: .875rem; color: var(--muted); max-width: var(--measure); }
.orgs { list-style: none; padding: 0; margin: 1rem 0; }
.orgs li { padding: .5rem 0; border-bottom: 1px solid var(--rule); }
.orgs li:last-child { border-bottom: 0; }

/* --- a section and its pieces ----------------------------------------------- */
/* The hub's contents. Numbered because the pieces are written to be read in order,
   and the numeral is set in the accent so the sequence reads as the offer rather
   than as list furniture. */
.section-list {
  list-style: none; counter-reset: piece; padding: 0; margin: 1.5rem 0;
  max-width: var(--measure);
}
.section-list li {
  counter-increment: piece; position: relative;
  padding: 1rem 0 1rem 2.75rem; border-bottom: 1px solid var(--rule);
}
.section-list li:last-child { border-bottom: 0; }
.section-list li::before {
  content: counter(piece); position: absolute; left: 0; top: 1.05rem;
  font-size: .8125rem; font-weight: 600; letter-spacing: .04em;
  color: var(--accent); font-variant-numeric: tabular-nums;
}
.section-list a { text-decoration: none; display: block; }
.section-list strong { display: block; font-size: 1.125rem; line-height: 1.3; }
.section-list a:hover strong { color: var(--accent); }
.section-list .dek {
  display: block; font-size: .9375rem; color: var(--muted);
  line-height: 1.45; margin-top: .25rem;
}
/* Where a piece came from, above its headline. */
.crumb {
  font-size: .75rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 .75rem;
}
.crumb a { color: var(--accent); text-decoration: none; }
.crumb a:hover { text-decoration: underline; }
/* And where to go next, under its last line. */
.piece-nav {
  display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; align-items: baseline;
  margin: 2.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--rule);
  max-width: var(--measure); font-size: .9375rem;
}
.piece-nav .prev::before { content: "\2190\00a0"; }
.piece-nav .next::after { content: "\00a0\2192"; }
.piece-nav .up { margin-left: auto; color: var(--muted); }
.search { display: flex; flex-wrap: wrap; gap: .5rem; margin: 1.5rem 0 2rem; }
.search label { flex: 1 0 100%; font-size: .8125rem; color: var(--muted); }
.search input {
  flex: 1 1 14rem; padding: .6rem .75rem; font: inherit; font-size: .9375rem;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule); border-radius: 2px;
}
.search input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.search button {
  padding: .6rem 1.1rem; font: inherit; font-size: .9375rem; cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent); color: var(--paper);
  border-radius: 2px;
}
.search button:hover { filter: brightness(1.08); }
.archive { list-style: none; padding: 0; margin: 0; }
.archive li {
  display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.1rem 0; border-bottom: 1px solid var(--rule);
}
.archive li a { text-decoration: none; display: block; flex: 1; }
.archive li a:hover strong { color: var(--accent); }
.archive strong { display: block; font-size: 1.125rem; line-height: 1.3; }
.archive .kicker { display: block; margin-bottom: .2rem; }
.archive .dek { display: block; font-size: .9375rem; margin: .25rem 0 0; }
.worldmap { margin: 2rem 0; max-width: var(--figure); }
.worldmap svg { width: 100%; height: auto; display: block; }
/* Countries with nothing published are the ground, not the subject: readable, and
   quiet enough that the marked ones are the only thing the eye goes to. */
.worldmap .land { fill: var(--rule); }
.worldmap .hit { fill: var(--accent); }
.worldmap .dot { fill: var(--accent); stroke: var(--paper); stroke-width: 1.5; }
.worldmap a:hover .hit, .worldmap a:hover .dot { fill: var(--ink); }
/* A link inside an SVG gets no focus ring by default in several browsers, which
   makes the map unusable by keyboard — the exact failure a no-JavaScript map is
   supposed to avoid. */
.worldmap a:focus-visible .hit, .worldmap a:focus-visible .dot {
  fill: var(--ink); outline: 2px solid var(--ink);
}
.countries {
  list-style: none; padding: 0; margin: 1rem 0 0;
  display: flex; flex-wrap: wrap; gap: .5rem;
}
.countries a {
  display: inline-block; padding: .35rem .7rem; text-decoration: none;
  border: 1px solid var(--rule); border-radius: 2px; font-size: .9375rem;
}
.countries a:hover { border-color: var(--accent); color: var(--accent); }
.countries .hint { color: var(--muted); font-size: .8125rem; }
.worldmap figcaption {
  font-size: .8125rem; color: var(--muted); margin-top: .6rem;
  max-width: var(--measure);
}
.archive .tag {
  flex: none; align-self: center; font-size: .6875rem; letter-spacing: .06em;
  text-transform: uppercase; padding: .25rem .5rem; border-radius: 2px;
  background: var(--rule); color: var(--ink);
}

/* --- desktop type ---------------------------------------------------------- */
@media (min-width: 72rem) {
  body { font-size: 1.1875rem; line-height: 1.7; }
  h1 { font-size: 4rem; line-height: 1.08; letter-spacing: -0.005em; }
  h2 { font-size: 1.75rem; }
  .lede-card h2 { font-size: 2rem; }
  .dek { font-size: 1.35rem; line-height: 1.4; }
  header.masthead { padding: 2rem 0; margin-bottom: 4rem; }
  /* A figure that has broken out of the measure needs room to breathe, or it
     reads as an interruption rather than part of the piece. */
  article.prose > section > figure { margin: 3rem 0; }
  article.prose > section > figure figcaption { text-align: left; }
}
