/* ============================================================
   THE BASIN — basin.la
   Design system. Tokens mirror the interactive maps
   (site/crime_map_template.html) so every surface reads as one
   publication: warm paper, ink, blue #2a78d6 / orange #eb6834.
   ============================================================ */

:root {
  color-scheme: light;
  --paper: #f9f9f7;            /* page */
  --surface: #fcfcfb;          /* cards, tables */
  --surface-2: #f3f2ee;        /* recessed panels */
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --muted: #898781;
  --faint: #b5b3ab;
  --border: rgba(11, 11, 11, 0.10);
  --border-strong: rgba(11, 11, 11, 0.22);
  --hairline: rgba(11, 11, 11, 0.14);
  --grid: #e1e0d9;
  --s1: #2a78d6;               /* data blue  — slot 1 */
  --s2: #eb6834;               /* data orange — slot 2 */
  --s3: #1baf7a;               /* green — Politics */
  /* CRIMSON, NOT THE OLD #e34948. The section colours carry real area on the
     section fronts now rather than a 7px dot, and at that size the old red sat
     16 degrees of hue from the elections orange: separation 38 where the other
     three sit 108-252 apart, and 15 once simulated for deuteranopia. Crimson
     measures 88 normal and 77/76 deuter/protan, the best of everything tried,
     and it is still a red, which is the colour a crime desk should be. The
     purples score better to normal vision and collapse to 12-25 for dichromats. */
  --s4: #b3243b;               /* crimson — Crime */
  /* TEAL, for Places. The four desks above had taken blue, orange, green and
     red, which leaves this hue and the violets. Run through the same checker
     the level palette uses: against the other four it separates 12.3 at worst
     (money, under protanopia), where the existing worst pair is elections
     against city-hall at 9.2 under deuteranopia. So the fifth desk does not
     become the tightest pair; the floor stays where it already was. It is also
     the right colour for the desk, being the one that maps the physical city. */
  --s5: #1e848f;               /* teal — Places */
  --s1-2: #86b6ef;            /* lighter step of the data blue */
  /* Two steps per hue. The MARK is the rule, the dot, the fill; the INK is the
     step that clears 4.5:1 as text on this paper, because the marks themselves
     run 2.67-4.19:1 and will not hold a headline. Every value below is measured,
     not chosen: see the ratio beside it. */
  --s1-ink: #2873cd;           /* 4.50:1 on paper */
  --s2-ink: #bc532a;           /* 4.52:1 */
  --s3-ink: #14825a;           /* 4.56:1 */
  --s4-ink: #b3243b;           /* 6.16:1, the mark already clears it */
  --s5-ink: #1d7c87;           /* 4.65:1 */
  --s2-band: rgb(246, 217, 204); /* the orange at .22 over paper; ink 14.73:1 on it */
  --s1-wash: rgba(42, 120, 214, 0.10);
  --s2-wash: rgba(235, 104, 52, 0.10);
  --s3-wash: rgba(27, 175, 122, 0.09);
  --s4-wash: rgba(179, 36, 59, 0.09);
  --s5-wash: rgba(30, 132, 143, 0.10);
  /* THE SAME INK, MEASURED ON ITS OWN WASH INSTEAD OF ON PAPER. A desk kicker
     set on that desk's tinted field is not on paper any more, and the tint
     costs it about 9%: the green went 4.56 to 4.17 the moment the plate went
     under it. These are the step that buys it back, six to nine per cent
     darker, hue held. The crimson is not restated because it measures 5.34 on
     its own wash and never needed one. Ratios are against the wash composited
     over paper, which is what the reader actually sees. */
  --s1-ink-wash: #2469bb;      /* 4.62:1 on --s1-wash */
  --s2-ink-wash: #af4d27;      /* 4.60:1 */
  --s3-ink-wash: #137a55;      /* 4.63:1 */
  --s4-ink-wash: var(--s4-ink); /* 5.34:1 unchanged */
  --s5-ink-wash: #1b737e;      /* 4.63:1 */
  --de-emph: #c9c7be;          /* gray series / context marks */
  --link: #1c5cab;
  /* The county pink as a desk colour. --lvl-4 is the mark; it measures 2.90:1
     on paper and cannot hold text, so it gets an ink step at the same hue the
     way every other desk hue does. #a75984 is the least darkening that clears
     4.5:1, so the ink still reads as the same colour as the rule beside it. */
  --lvl-4-ink: #a75984;        /* 4.52:1 on paper */
  --lvl-4-wash: rgba(204, 121, 167, 0.10);
  /* THE THREE MEDIA DESKS. Media holds three unlike things and now hands each
     one its own hue instead of running all three off the film pink, because a
     reader arriving at Games should be able to tell at a glance that they are
     not on Videos. The pink stays with the films, which is the colour the film
     pages and the film share cards already ship. The other two are picked out
     of the hues the five news desks and the pink have not taken: gold, which is
     the arcade colour and sits far enough off the elections orange to read as
     its own, and violet, which no desk on the site uses at all.

     Every value measured, same as the desks. -ink clears 4.5:1 as text on
     paper, -band is the mark at .20 over the plane for a tinted field, and -on
     is the type colour for a solid field of the mark. The violet is the one
     that flips: it is dark enough on paper to carry paper type, and light
     enough on the dark plane to carry ink, so -on is not the same value in
     both themes. That is measured rather than tidy. */
  --med-vid: #CC79A7;          /* 2.90:1 on paper — a mark, not text */
  --med-vid-ink: #a75984;      /* 4.52:1 on paper */
  --med-vid-wash: rgba(204, 121, 167, 0.10);
  --med-vid-band: #f0dfe7;     /* the pink at .20 over paper; ink 15.38:1 on it */
  --med-vid-on: #0b0b0b;       /* 6.43:1 on the pink */
  --med-gam: #E69F00;          /* 2.14:1 on paper */
  --med-gam-ink: #996a00;      /* 4.51:1 on paper */
  --med-gam-wash: rgba(230, 159, 0, 0.12);
  --med-gam-band: #f5e7c6;     /* the gold at .20 over paper; ink 16.05:1 */
  --med-gam-on: #0b0b0b;       /* 8.74:1 on the gold */
  --med-int: #7B52AB;          /* 5.48:1 on paper — clears text on its own */
  --med-int-ink: #7B52AB;      /* the mark already holds type */
  --med-int-wash: rgba(123, 82, 171, 0.10);
  --med-int-band: #e0d8e8;     /* the violet at .20 over paper; ink 14.21:1 */
  --med-int-on: #f9f9f7;       /* 5.48:1 on the violet; ink is 3.41 and fails */
  --warn-bg: #fdf4e6;
  --warn-bd: #e8c99a;
  --warn-ink: #7a5418;
  --nav-ink: #6e6c67;          /* 4.98:1 on paper */
  --nav-hover: #bb4716;        /* 4.95:1 on paper, the orange two steps down */
  /* The six levels of government, shared with the Your Government board so
     its sub-brand mark and its columns cannot drift apart. Okabe-Ito, run
     through the palette checker for colour-vision separation on this paper. */
  --lvl-1: #009E73; --lvl-2: #0072B2; --lvl-3: #E69F00;
  --lvl-4: #CC79A7; --lvl-5: #56B4E9; --lvl-6: #D55E00;
  --shadow: 0 1px 2px rgba(11,11,11,.05), 0 10px 30px rgba(11,11,11,.06);
  --serif: "Charter", "Iowan Old Style", "Georgia", "Times New Roman", serif;
  --sans: system-ui, -apple-system, "Segoe UI", "Helvetica Neue", sans-serif;
  --mono: ui-monospace, "SF Mono", "Menlo", "Consolas", monospace;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --paper: #0d0d0d;
    --surface: #1a1a19;
    --surface-2: #222220;
    --ink: #f4f3ef;
    --ink-2: #c3c2b7;
    --muted: #8f8d86;
    --faint: #5c5b55;
    --border: rgba(255, 255, 255, 0.10);
    --border-strong: rgba(255, 255, 255, 0.26);
    --hairline: rgba(255, 255, 255, 0.14);
    --grid: #2c2c2a;
    --s1: #3987e5;
    --s2: #d95926;
    --nav-ink: #9d9b93;        /* 6.98:1 on the dark plane */
    --nav-hover: #ef7a45;      /* 6.98:1, the orange opened up for dark */
    --lvl-1: #00885f; --lvl-2: #0068a8; --lvl-3: #c68000;
    --lvl-4: #c673a1; --lvl-5: #3a9bcf; --lvl-6: #a43000;
    --s1-2: #1c5cab;
    /* The green already clears 6.90:1 on the dark plane and needs no step.
       The crimson does not: 2.99:1, so it opens up for dark the way the
       orange does, and its ink step opens further to hold text. */
    --s3: #1baf7a;
    --s4: #b72f45;
    --s1-ink: #3987e5;         /* 5.34:1 on the dark plane */
    --s2-ink: #d95926;         /* 5.00:1 */
    --s3-ink: #1baf7a;         /* 6.90:1 */
    --s4-ink: #c55768;         /* 4.57:1 */
    /* The teal measures 4.40:1 on the dark plane and just misses, so it opens
       up one step the way the crimson does. */
    --s5: #208c97;             /* 4.87:1 on the dark plane */
    --s5-ink: #2293a0;         /* 5.32:1 */
    --s2-band: rgb(58, 30, 18);  /* the orange at .22 over the dark plane; ink 13.76:1 */
    --s1-wash: rgba(57, 135, 229, 0.14);
    --s2-wash: rgba(217, 89, 38, 0.14);
    --s3-wash: rgba(27, 175, 122, 0.15);
    --s4-wash: rgba(183, 47, 69, 0.18);
    --s5-wash: rgba(32, 140, 151, 0.15);
    /* Dark corrects the other way: the tint sits over the dark plane, so the
       ink that misses has to open up rather than close down. Three of the five
       already clear it on their own wash (blue 4.62, green 5.73, teal 4.59)
       and are not restated. */
    --s1-ink-wash: var(--s1-ink);
    --s2-ink-wash: #db602f;    /* 4.63:1 on --s2-wash, was 4.37 */
    --s3-ink-wash: var(--s3-ink);
    --s4-ink-wash: #ca6474;    /* 4.61:1 on --s4-wash, was 4.07 */
    --s5-ink-wash: var(--s5-ink);
    --de-emph: #45443f;
    --link: #6ea6ec;
    /* On the dark plane the mark already measures 5.89:1, so like the green it
       needs no separate ink step. */
    --lvl-4-ink: #c673a1;      /* 5.89:1 on the dark plane */
    --lvl-4-wash: rgba(198, 115, 161, 0.16);
    /* The three media hues, opened up against the dark plane rather than
       flipped. The pink and the gold are the board's own dark steps; the
       violet opens one step to clear text, and its -on flips to ink because
       at that lightness paper only reaches 3.68:1 on it. */
    --med-vid: #c673a1;        /* 5.89:1 on the dark plane */
    --med-vid-ink: #c673a1;
    --med-vid-wash: rgba(198, 115, 161, 0.16);
    --med-vid-band: #32212b;   /* ink 13.63:1 on it */
    --med-vid-on: #0b0b0b;     /* 5.97:1 on the pink */
    --med-gam: #c68000;        /* 6.01:1 on the dark plane */
    --med-gam-ink: #c68000;
    --med-gam-wash: rgba(198, 128, 0, 0.16);
    --med-gam-band: #32240a;   /* ink 13.58:1 on it */
    --med-gam-on: #0b0b0b;     /* 6.08:1 on the gold */
    --med-int: #9a6ccf;        /* 5.02:1 on the dark plane */
    --med-int-ink: #9a6ccf;
    --med-int-wash: rgba(154, 108, 207, 0.18);
    --med-int-band: #292034;   /* ink 14.00:1 on it */
    --med-int-on: #0b0b0b;     /* 5.08:1 on the violet */
    --warn-bg: #2a2113;
    --warn-bd: #5c4a24;
    --warn-ink: #e2c489;
    --shadow: 0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.35);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --paper: #0d0d0d;
  --surface: #1a1a19;
  --surface-2: #222220;
  --ink: #f4f3ef;
  --ink-2: #c3c2b7;
  --muted: #8f8d86;
  --faint: #5c5b55;
  --border: rgba(255, 255, 255, 0.10);
  --border-strong: rgba(255, 255, 255, 0.26);
  --hairline: rgba(255, 255, 255, 0.14);
  --grid: #2c2c2a;
  --s1: #3987e5;
  --s2: #d95926;
  --nav-ink: #9d9b93;        /* 6.98:1 on the dark plane */
  --nav-hover: #ef7a45;      /* 6.98:1, the orange opened up for dark */
  --lvl-1: #00885f; --lvl-2: #0068a8; --lvl-3: #c68000;
  --lvl-4: #c673a1; --lvl-5: #3a9bcf; --lvl-6: #a43000;
    --s1-2: #1c5cab;
  /* The green already clears 6.90:1 on the dark plane and needs no step.
     The crimson does not: 2.99:1, so it opens up for dark the way the
     orange does, and its ink step opens further to hold text. */
  --s3: #1baf7a;
  --s4: #b72f45;
  --s1-ink: #3987e5;         /* 5.34:1 on the dark plane */
  --s2-ink: #d95926;         /* 5.00:1 */
  --s3-ink: #1baf7a;         /* 6.90:1 */
  --s4-ink: #c55768;         /* 4.57:1 */
  --s5: #208c97;             /* 4.87:1 on the dark plane */
  --s5-ink: #2293a0;         /* 5.32:1 */
  --s2-band: rgb(58, 30, 18);  /* the orange at .22 over the dark plane; ink 13.76:1 */
  --s1-wash: rgba(57, 135, 229, 0.14);
  --s2-wash: rgba(217, 89, 38, 0.14);
  --s3-wash: rgba(27, 175, 122, 0.15);
  --s4-wash: rgba(183, 47, 69, 0.18);
  --s5-wash: rgba(32, 140, 151, 0.15);
  --s1-ink-wash: var(--s1-ink);
  --s2-ink-wash: #db602f;    /* 4.63:1 on --s2-wash, was 4.37 */
  --s3-ink-wash: var(--s3-ink);
  --s4-ink-wash: #ca6474;    /* 4.61:1 on --s4-wash, was 4.07 */
  --s5-ink-wash: var(--s5-ink);
  --de-emph: #45443f;
  --link: #6ea6ec;
  --lvl-4-ink: #c673a1;
  --lvl-4-wash: rgba(198, 115, 161, 0.16);
  --med-vid: #c673a1;
  --med-vid-ink: #c673a1;
  --med-vid-wash: rgba(198, 115, 161, 0.16);
  --med-vid-band: #32212b;
  --med-vid-on: #0b0b0b;
  --med-gam: #c68000;
  --med-gam-ink: #c68000;
  --med-gam-wash: rgba(198, 128, 0, 0.16);
  --med-gam-band: #32240a;
  --med-gam-on: #0b0b0b;
  --med-int: #9a6ccf;
  --med-int-ink: #9a6ccf;
  --med-int-wash: rgba(154, 108, 207, 0.18);
  --med-int-band: #292034;
  --med-int-on: #0b0b0b;
  --warn-bg: #2a2113;
  --warn-bd: #5c4a24;
  --warn-ink: #e2c489;
  --shadow: 0 1px 2px rgba(0,0,0,.5), 0 10px 30px rgba(0,0,0,.35);
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, iframe { max-width: 100%; }
a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 2.5px; }
a:hover { color: var(--ink); }
::selection { background: var(--s1-wash); }
:focus-visible { outline: 2px solid var(--s1); outline-offset: 2px; border-radius: 2px; }

/* 1280 is the width the papers this reads next to actually use: the BBC and
   the New York Times both sit around here and the LA Times wider still. At
   1140 the page looked cramped between them, every headline wrapped a line
   early, and the type read larger than it was because the measure was short. */
.wrap { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
.measure { max-width: 700px; margin-left: auto; margin-right: auto; }

/* ---------- masthead & nav ---------- */
.topbar {
  border-bottom: 1px solid var(--hairline);
  background: var(--paper);
}
.topbar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 10px 0;
  /* basin.js swaps the theme button's label from "Theme" to a glyph plus a word
     on load. The bar took its height from that button, so the swap moved the
     masthead and every page below it. 45px is the measured height with the
     dateline (20.6px) against the button (25px) plus the 10px padding, and it
     is reserved here so the label cannot decide the layout. */
  min-height: 45px; box-sizing: border-box;
  font-family: var(--sans); font-size: 0.78rem; color: var(--muted);
}
.topbar .dateline { letter-spacing: 0.02em; }
.theme-toggle {
  appearance: none; border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-2); font-family: var(--sans); font-size: 0.75rem;
  padding: 4px 10px; border-radius: 2px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  /* Widest label is the sun plus "Light" at 61.1px and the tallest is 24.5px.
     Both are fixed so a font that draws the glyph larger, which is what an
     emoji font does on Android, cannot resize the button or the bar. */
  min-width: 64px; height: 25px; justify-content: center;
  line-height: 1; box-sizing: border-box;
}
.theme-toggle:hover { border-color: var(--border-strong); color: var(--ink); }

.masthead { text-align: center; padding: 34px 0 22px; }
.masthead .brandmark { display: inline-flex; align-items: center; gap: 14px; text-decoration: none; color: var(--ink); }
/* the mark is ink with the accent as its only color, so it must not take the data blue */
.masthead .brandmark svg { width: 46px; height: 46px; flex: none; }
.masthead .wordmark {
  font-family: var(--serif); font-weight: 700; letter-spacing: -0.015em;
  font-size: clamp(2.4rem, 1.6rem + 3.4vw, 3.6rem); line-height: 1;
}
.masthead .tagline {
  margin-top: 10px; font-family: var(--sans); font-size: 0.82rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}
.masthead--compact { text-align: left; padding: 16px 0 12px; }
.masthead--compact .brandmark svg { width: 30px; height: 30px; }
.masthead--compact .wordmark { font-size: 1.55rem; }
.masthead--compact .tagline { display: none; }

/* The bar is a gray band and each section is a segment of it, so a segment
   fills with the signature orange under the cursor rather than only changing
   the colour of its type. */
.mainnav {
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  position: sticky; top: 0; z-index: 60;
  background: var(--surface-2);
}
.mainnav > ul {
  display: flex; align-items: center; justify-content: center; gap: 2px;
  list-style: none; margin: 0; padding: 0 16px;
}
.mainnav > ul::-webkit-scrollbar { display: none; }
.mainnav li { position: relative; }
/* The sections sit back in gray and come forward in the signature orange
   under the cursor. Both values are picked rather than reached for: the nav
   is 13px bold, which WCAG counts as small text, so it needs 4.5:1 and the
   --muted token only carries 3.41:1 on paper while the brandmark orange
   carries 3.04:1. These are the nearest steps of each that clear the bar. */
.mainnav a {
  display: block; padding: 11px 14px;
  font-family: var(--sans); font-size: 0.83rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--nav-ink); text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color 90ms linear, background-color 90ms linear;
}
/* THE LABEL IS PAPER, NOT WHITE. Flat white clears the bar on the light
   plane, but --nav-hover opens up to #ef7a45 in dark and white on that is
   2.79:1, which the 13px bold label fails badly. --paper is already the right
   colour in both: 4.95:1 on the light orange, 6.98:1 on the dark one, because
   in dark mode it IS the near-black the orange was chosen against. */
.mainnav > ul > li > a:hover, .mainnav > ul > li > a:focus-visible {
  background: var(--nav-hover); color: var(--paper);
}
.mainnav > ul > li > a[aria-current="true"],
.mainnav .nav-top[aria-current="true"] { color: var(--ink); border-bottom-color: var(--s2); }
/* THE CURRENT SECTION HOVERS LIKE EVERY OTHER ONE. This rule used to set
   color to --nav-hover, and because the attribute selector outranks the plain
   :hover above it, it won the cascade on color while that rule still supplied
   the background. The result on whichever section you were standing in was
   #bb4716 type on a #bb4716 field: a solid orange rectangle with the label
   invisible inside it. White is what the background was picked against. */
.mainnav > ul > li > a[aria-current="true"]:hover,
.mainnav > ul > li > a[aria-current="true"]:focus-visible,
.mainnav .nav-top[aria-current="true"]:hover,
.mainnav .nav-top[aria-current="true"]:focus-visible,
.nav-drop:hover .nav-top[aria-current="true"],
.nav-drop[data-open="true"] .nav-top[aria-current="true"] {
  background: var(--nav-hover); color: var(--paper);
}
.mainnav .nav-data > a { color: var(--link); }
.mainnav .nav-data > a:hover, .mainnav .nav-data > a:focus-visible { background: var(--nav-hover); color: var(--paper); }

/* Your Government leads with its own mark, at cap height beside the label. */
.nav-has-mark > a { display: flex; align-items: center; gap: 8px; }
.nav-has-mark svg { width: 21px; height: 14px; flex: none; display: block; }

/* THE TWO THAT OPEN. The parent is a button because it opens something. The
   menu is a plain list beneath it, ruled rather than floated: no shadow, no
   rounded corner, the same 2px ink rule the sections use. It opens on hover
   for a pointer, on focus-within for a keyboard with no script, and on click
   for touch, where hover does not exist. */
.nav-top {
  display: flex; align-items: center; gap: 6px;
  padding: 11px 14px; margin: 0; background: none; border: 0; border-radius: 0;
  border-bottom: 2px solid transparent; cursor: pointer;
  font-family: var(--sans); font-size: 0.83rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--nav-ink);
  transition: color 90ms linear, background-color 90ms linear;
}
.nav-top:hover, .nav-top:focus-visible,
.nav-drop:hover .nav-top, .nav-drop[data-open="true"] .nav-top {
  background: var(--nav-hover); color: var(--paper);
}
/* MEDIA'S PARENT IS A LINK, NOT A BUTTON. It is the one menu whose parent has
   somewhere of its own to go: Media holds three categories and the page that
   carries all three at once, so clicking the word goes there and the menu is
   the shortcut past it. .mainnav a already sets display:block and would beat a
   bare .nav-top on specificity, which would drop the caret out of the flex row,
   so the anchor case is spelled out rather than left to the cascade. */
.mainnav a.nav-top { display: flex; text-decoration: none; }
.nav-caret { width: 9px; height: 6px; flex: none; transition: transform 120ms ease; }
.nav-drop[data-open="true"] .nav-caret, .nav-drop:hover .nav-caret { transform: rotate(180deg); }

.nav-menu {
  position: absolute; top: 100%; left: 0; z-index: 70;
  min-width: 208px; margin: 0; padding: 5px 0 6px; list-style: none;
  background: var(--surface); border: 1px solid var(--border);
  border-top: 2px solid var(--s2); border-radius: 0;
  opacity: 0; visibility: hidden; transform: translateY(-3px);
  transition: opacity 110ms linear, transform 110ms ease, visibility 0s linear 110ms;
}
.nav-drop:hover .nav-menu,
.nav-drop:focus-within .nav-menu,
.nav-drop[data-open="true"] .nav-menu {
  opacity: 1; visibility: visible; transform: translateY(0);
  transition: opacity 110ms linear, transform 110ms ease, visibility 0s;
}
.nav-menu li { position: static; }
.nav-menu a {
  display: block; padding: 8px 16px; border-bottom: 0; white-space: nowrap;
  font-size: 0.79rem; letter-spacing: 0.03em; text-transform: none;
  font-weight: 500; color: var(--ink-2);
}
.nav-menu a:hover { color: var(--nav-hover); background: var(--surface-2); }
.nav-menu a[aria-current="true"] { color: var(--ink); font-weight: 700; }
/* The way to all of them, set apart at the foot of the menu it belongs to. */
.nav-menu .nav-all a { border-top: 1px solid var(--hairline); margin-top: 4px; padding-top: 10px; }
.nav-menu .nav-all a::after { content: " \2192"; color: var(--muted); }

/* The bar wraps rather than scrolls sideways. A scrolling strip has to clip
   its overflow, and clipping is exactly what an absolutely positioned menu
   cannot survive: the panel would be cut off at the edge of the strip it
   hangs from. Wrapping costs a second line on a phone and keeps the menus. */
@media (max-width: 940px) {
  .mainnav > ul { flex-wrap: wrap; row-gap: 0; padding: 2px 10px; }
  .mainnav > ul > li > a, .nav-top { padding: 9px 10px; font-size: 0.78rem; }
  .nav-has-mark svg { width: 18px; height: 12px; }
}
/* No hover on a touch screen, so the menus open only on tap, which the script
   handles by setting data-open. Without this the first tap would open a menu
   the browser then leaves stuck open under a synthetic hover. */
@media (hover: none) {
  .nav-drop:hover .nav-menu { opacity: 0; visibility: hidden; }
  .nav-drop[data-open="true"] .nav-menu { opacity: 1; visibility: visible; }
}

/* ---------- kickers, cards, index ---------- */
.kicker {
  font-family: var(--sans); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
  display: inline-flex; align-items: center; gap: 7px;
}
.kicker .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); flex: none; }
.kicker--money .dot { background: var(--s1); }
.kicker--elections .dot { background: var(--s2); }
.kicker--city-hall .dot { background: var(--s3); }
.kicker--crime .dot { background: var(--s4); }
/* Places shipped without one, so a teal desk announced itself with the same
   gray dot as no desk at all. */
.kicker--places .dot { background: var(--s5); }
.kicker--data .dot { background: var(--faint); }
/* THE WORD TAKES THE DESK COLOUR, NOT ONLY THE DOT BESIDE IT. A section front
   has said POLITICS in green since the desks were drawn (.sec .cat-head), but
   everywhere outside a .sec container the same word fell back to muted and the
   only colour left was seven pixels wide. These are the -ink steps, measured
   at 4.5:1 on paper, so the kicker is legible type rather than a tint. The
   .sec rules carry more specificity and still win where a section sets its
   own. */
.kicker--money { color: var(--s1-ink); }
.kicker--elections { color: var(--s2-ink); }
.kicker--city-hall { color: var(--s3-ink); }
.kicker--crime { color: var(--s4-ink); }
.kicker--places { color: var(--s5-ink); }

.home-section { padding: 34px 0 8px; }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  border-bottom: 2px solid var(--ink); padding-bottom: 8px; margin-bottom: 22px;
}
.section-head h2 { margin: 0; font-size: 1.25rem; letter-spacing: -0.01em; }
.section-head .more { font-family: var(--sans); font-size: 0.78rem; font-weight: 600; }

.story h3 { font-size: 1.28rem; line-height: 1.25; margin: 8px 0 6px; letter-spacing: -0.012em; }
.story h3 a { color: var(--ink); text-decoration: none; }
.story h3 a:hover { text-decoration: underline; text-decoration-thickness: 1.5px; }
.story .dek { color: var(--ink-2); font-size: 0.95rem; line-height: 1.5; margin: 0 0 8px; }
.story .meta { font-family: var(--sans); font-size: 0.74rem; color: var(--muted); }

.lead-story h3 { font-size: clamp(1.7rem, 1.3rem + 1.6vw, 2.3rem); line-height: 1.14; }
.lead-story .dek { font-size: 1.08rem; line-height: 1.55; }

.story-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
.story-grid--2 { grid-template-columns: repeat(2, 1fr); }
.front-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 36px; align-items: start; }
.front-grid .rail .story { padding: 16px 0; border-bottom: 1px solid var(--hairline); }
.front-grid .rail .story:first-child { padding-top: 0; }
.front-grid .rail .story:last-child { border-bottom: 0; }
.front-grid .rail .story h3 { font-size: 1.05rem; }
.divider { border: 0; border-top: 1px solid var(--hairline); margin: 26px 0; }

/* ---------- hero: the two atlases ---------- */
.hero { padding: 26px 0 6px; }
.hero-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 28px; }
.hero-grid--3 { grid-template-columns: repeat(3, 1fr); gap: 22px; }
@media (max-width: 900px) { .hero-grid--3 { grid-template-columns: 1fr 1fr; } }
/* Four across is tight, so the gap closes to buy back line length. It breaks
   to a 2x2 earlier than the three-up does, at the width where a card heading
   starts taking three lines. */
.hero-grid--4 { grid-template-columns: repeat(4, 1fr); gap: 18px; }
@media (max-width: 1100px) { .hero-grid--4 { grid-template-columns: 1fr 1fr; gap: 22px; } }
/* Each card is tied to its section by a rule along the top, in that section's
   dot colour and nothing else. The dot in the kicker is four pixels across and
   was the only colour on a wall of identical gray cards; a 3px rule reads at a
   glance without turning the grid into a paintbox. --cat defaults to the ink
   rule so an unmodified card still looks deliberate. */
.map-card {
  --cat: var(--border-strong);
  display: block; text-decoration: none; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border); border-radius: 2px;
  border-top: 3px solid var(--cat);
  overflow: hidden;
  transition: border-color 0.16s ease;
}
.map-card--money { --cat: var(--s1); }
.map-card--elections { --cat: var(--s2); }
.map-card--city-hall { --cat: var(--s3); }
.map-card--crime { --cat: var(--s4); }
.map-card:hover { border-color: var(--border-strong); border-top-color: var(--cat); }
.map-card:hover h3 { color: var(--cat); }
.map-card h3 { transition: color 0.16s ease; }
.map-card .shot { position: relative; aspect-ratio: 16 / 9.6; background: var(--surface-2); overflow: hidden; }
/* Centred, not top left. The tile PNG and this box are both 16:9.6 now, so
   object-fit has nothing to trim; when they disagreed the anchor threw the
   whole difference onto the right edge, which is what sliced the right-hand
   column off the calls map's legend. Centring means a future mismatch costs
   a little off both edges instead of a lot off one. */
.map-card .shot img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; display: block; }
.map-card .badge {
  position: absolute; top: 12px; left: 12px;
  font-family: var(--sans); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  background: var(--ink); color: var(--paper);
  padding: 4px 9px; border-radius: 2px; opacity: 0.92;
}
.map-card .body { padding: 16px 18px 18px; }
.map-card h3 { margin: 4px 0 6px; font-size: 1.45rem; letter-spacing: -0.015em; }
.map-card p { margin: 0; color: var(--ink-2); font-size: 0.94rem; line-height: 1.5; }
.map-card .cta {
  margin-top: 12px; font-family: var(--sans); font-size: 0.8rem; font-weight: 700; color: var(--link);
}
.map-card:hover .cta { text-decoration: underline; }

img.theme-dark-only { display: none; }
:root[data-theme="dark"] img.theme-light-only { display: none; }
:root[data-theme="dark"] img.theme-dark-only { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) img.theme-light-only { display: none; }
  :root:not([data-theme="light"]) img.theme-dark-only { display: block; }
}

/* ---------- signature map slot ---------- */
/* THE FOUR DESKS, side by side, each carrying its own colour from the rule at
   the top of the column down through the kicker and the row marks. The colour
   is the only thing telling a reader at a glance that these are four separate
   lists rather than one long one, so it runs the whole height. */
.catcols-sec { margin-top: 30px; }
/* ONE COLUMN PER DESK, and there are five of them. This was repeat(4) from
   when there were four, so Places came in and sat alone on a second row at a
   quarter of the width: survivable while each column was a thin rule over
   paper, obvious the moment they became tinted plates. The gap closes to buy
   the fifth column its line length back. */
.catcols { display: grid; grid-template-columns: repeat(5, 1fr); gap: 20px; }
/* THE SAME TREATMENT AS THE HERO: a tinted field per desk rather than a rule
   over paper. Four plates in a row is what makes these read as four separate
   lists at a glance, which the 3px rule was already trying to do with a
   fortieth of the area. Each binds all three steps the way .sec--* does, and
   takes the ink measured on its own wash for the type that sits on it. */
.catcol {
  min-width: 0; background: var(--cat-wash); padding: 16px 16px 18px;
}
.catcol--money { --cat: var(--s1); --cat-ink: var(--s1-ink-wash); --cat-wash: var(--s1-wash); }
.catcol--elections { --cat: var(--s2); --cat-ink: var(--s2-ink-wash); --cat-wash: var(--s2-wash); }
.catcol--city-hall { --cat: var(--s3); --cat-ink: var(--s3-ink-wash); --cat-wash: var(--s3-wash); }
.catcol--crime { --cat: var(--s4); --cat-ink: var(--s4-ink-wash); --cat-wash: var(--s4-wash); }
.catcol--places { --cat: var(--s5); --cat-ink: var(--s5-ink-wash); --cat-wash: var(--s5-wash); }
/* THE PLATE'S NAME, at the size a desk name is worth. These are the five
   headings the whole row is organised by and they were set two steps under the
   headlines beneath them, so the reader met the stories before finding out
   which desk they belonged to. Tracking comes in as the size goes up, or
   ELECTIONS runs past the column at the 5-up width. */
.catcol-h {
  display: block; margin: 4px 0 13px;
  font-family: var(--sans); font-size: clamp(1rem, 0.72rem + 0.8vw, 1.35rem);
  font-weight: 700; line-height: 1;
  letter-spacing: 0.045em; text-transform: uppercase;
  color: var(--cat-ink); text-decoration: none;
}
.catcol-h:hover { color: var(--ink); }
.catlead .thumb {
  display: block; aspect-ratio: 3 / 2; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin-bottom: 10px;
}
.catlead .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.catlead h3 {
  font-family: var(--serif); font-size: 1.04rem; line-height: 1.2;
  letter-spacing: -0.012em; margin: 0;
}
.catlead h3 a, .catrow h4 a { color: inherit; text-decoration: none; }
.catlead h3 a:hover, .catrow h4 a:hover { color: var(--cat-ink); }
.catlead .dek {
  color: var(--ink-2); font-size: 0.84rem; line-height: 1.5; margin: 7px 0 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.catlead .meta, .catrow .meta { color: var(--muted); font-size: 0.72rem; margin-top: 6px; }
.catrow {
  display: grid; grid-template-columns: 64px 1fr; gap: 11px; align-items: start;
  padding: 11px 0; border-top: 1px solid var(--hairline);
}
.catcol .catlead { padding-bottom: 3px; }
.catrow .thumb {
  display: block; aspect-ratio: 1 / 1; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin: 0;
}
.catrow .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A row with no picture gives its headline the full width rather than holding
   an empty square open. A tinted placeholder there read as an image that had
   failed rather than one that never existed. */
.catrow:not(:has(.thumb)) { grid-template-columns: 1fr; }
.catrow h4 {
  font-family: var(--serif); font-size: 0.88rem; font-weight: 600;
  line-height: 1.24; margin: 0; letter-spacing: -0.008em;
}
@media (max-width: 1180px) { .catcols { grid-template-columns: repeat(3, 1fr); gap: 22px; } }
@media (max-width: 1020px) { .catcols { grid-template-columns: 1fr 1fr; gap: 24px 22px; } }
@media (max-width: 620px) { .catcols { grid-template-columns: 1fr; } }

/* THE MAP ROW ON THE HERO'S GRID. Latest takes the first column, the width of
   the address box directly above it; the map and its rail take the second and
   third together, which is exactly the story plus the video. Same template and
   same gap as .homehero, so the two rows share their edges rather than nearly
   sharing them. */
.homerow {
  display: grid; grid-template-columns: 1fr 1.32fr 0.74fr; gap: 0 30px;
  align-items: start; margin: 28px 0 6px;
}
/* The row owns the spacing now, so the section inside it starts at its top
   edge rather than 28px below the list beside it. */
.homerow > .sigmap { margin: 0; }
.homerow > .latest { grid-column: 1; }
.homerow > .sigmap { grid-column: 2 / -1; }
/* Latest is a date cut of the same material the columns below sort by desk, so
   it is set as a plain list: no dek, and no tinted plate of its own. It has to
   read as a different way through the reporting rather than as a sixth desk.
   The colour it does carry is every desk's at once, in the rule under the
   heading and in the mark on each row, which is the distinction stated rather
   than left to the absence of colour. */
.latest { min-width: 0; }
/* THE COLUMN'S OWN MASTHEAD. It was a 0.62rem label, which is the size of a
   caption doing the job of a section title beside a map that is 500px tall,
   and the eye went to the map and never came back. At this size it holds the
   left third on its own.
   THE RULE UNDER IT IS THE FIVE DESK COLOURS IN ORDER, which is the one piece
   of colour that says what this list is: not a sixth desk, but every desk, cut
   by date. It is the same five marks as the logo above and as the plates
   below, so the colour is the site's own rather than decoration. Hard stops,
   no gradient: a blend between two desk hues is a colour that means nothing. */
.latest-h {
  font-family: var(--sans); font-size: clamp(1.35rem, 1rem + 1vw, 1.85rem);
  font-weight: 700; letter-spacing: 0.06em; line-height: 1;
  text-transform: uppercase; color: var(--ink);
  padding-bottom: 10px; margin-bottom: 2px; border-bottom: 0;
  background-image: linear-gradient(to right,
    var(--s1) 0 20%, var(--s2) 20% 40%, var(--s3) 40% 60%,
    var(--s4) 60% 80%, var(--s5) 80% 100%);
  background-repeat: no-repeat;
  background-size: 100% 4px;
  background-position: 0 100%;
}
/* Picture left, words right, on the same 1:1 square the desk rows below use so
   the two lists agree about what a small cover photo is. A piece with no
   photograph gets the full width rather than an empty square held open. */
.latest .story {
  padding: 11px 0; border-bottom: 1px solid var(--hairline);
  display: grid; grid-template-columns: 72px 1fr; gap: 13px; align-items: start;
}
.latest .story:not(:has(.thumb)) { grid-template-columns: 1fr; }
.latest .story:last-child { border-bottom: 0; }
/* .story .thumb sets 16/10 with a border and a bottom margin, at the same
   specificity as .latest .thumb and later in the file, so it won and these
   squares came out 72x45 rectangles. One more step of specificity settles it
   rather than a source-order trick that the next edit would undo. */
.latest .story .thumb {
  display: block; aspect-ratio: 1 / 1; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin: 0; border: 0;
}
.latest .story .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.latest .story h3:first-child { margin-top: 0; }
/* The headline takes the height the desk tag used to spend, and the body sits
   flush with the top of its cover photo so the two sides of the row start on
   the same line. justify-content keeps the date tucked under the headline
   rather than drifting to the foot of a 72px box on a one-line head. */
.latest .story-body {
  min-width: 0; display: flex; flex-direction: column; justify-content: flex-start;
}
.latest .story h3 {
  font-size: 1.09rem; line-height: 1.22; letter-spacing: -0.012em; margin: 0;
}
/* The desk sits inside the date line rather than above the headline, so it
   costs the row no height. .kicker's default 0.72rem and 0.14em tracking is
   set for a line of its own and would push the date to a second line in a
   column this narrow; these are the same marks at the meta line's own size. */
.latest .story .meta {
  font-size: 0.72rem; margin-top: 6px;
  display: flex; align-items: center; gap: 3px; flex-wrap: wrap;
}
.latest .story .meta .kicker {
  font-size: 0.62rem; letter-spacing: 0.09em; gap: 5px;
}
.latest .story .meta .kicker .dot { width: 6px; height: 6px; }
@media (max-width: 1120px) {
  .homerow { grid-template-columns: 0.9fr 1.1fr; }
  .homerow > .sigmap { grid-column: 2; }
}
@media (max-width: 860px) {
  .homerow { grid-template-columns: 1fr; gap: 26px 0; }
  .homerow > .latest, .homerow > .sigmap { grid-column: 1; }
}

.sigmap { margin: 28px 0 6px; }
/* The map holds two thirds and more reporting fills the last third, so the
   first fold is not one single link for a reader who does not want a map. */
.sigrow { display: grid; grid-template-columns: 2fr 1fr; gap: 26px; align-items: stretch; }
/* The rail runs the map's full height: the six stories nearly fill it and the
   closing link is pushed to the floor, so the two columns end on the same
   line instead of the rail stopping a third of the way up. */
.sigside { min-width: 0; display: flex; flex-direction: column; }
/* Sized off .latest-h, a step down: the two headings stand on the same line
   across the row, and this one labels a third of the width where that one
   labels a whole column. Full ink, not muted, now that it is a heading rather
   than a caption. */
.sigside-h {
  font-family: var(--sans); font-size: clamp(0.95rem, 0.75rem + 0.6vw, 1.2rem);
  font-weight: 700; letter-spacing: 0.06em; line-height: 1.1;
  text-transform: uppercase; color: var(--ink);
  padding-bottom: 9px; border-bottom: 2px solid var(--ink); margin-bottom: 4px;
}
.sigside .story { padding: 11px 0; border-bottom: 1px solid var(--hairline); }
.sigside .story:last-of-type { border-bottom: 0; }
.sigside .story h3 { font-size: 0.93rem; line-height: 1.24; margin: 4px 0 0; }
.sigside .story .meta { font-size: 0.72rem; margin-top: 4px; }
.sigside .story .kicker { font-size: 0.6rem; }
.sigside .more { display: inline-block; margin-top: auto; padding-top: 12px; }
/* Each of the other maps as a picture with its name under it, sized so four
   of them stand exactly as tall as the signature card beside them. */
.maprow {
  display: block; text-decoration: none; color: inherit;
  padding: 12px 0; border-bottom: 1px solid var(--hairline);
}
.sigside .maprow:last-of-type { border-bottom: 0; }
.maprow-shot {
  display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.maprow-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.maprow-t {
  display: block; margin-top: 8px;
  font-family: var(--serif); font-size: 0.95rem; font-weight: 600; line-height: 1.2;
}
.maprow:hover .maprow-t { color: var(--link); }
.maprow:hover .maprow-shot { border-color: var(--border-strong); }
@media (max-width: 860px) { .sigrow { grid-template-columns: 1fr; } }

.sigmap-card {
  display: grid; grid-template-columns: 1.55fr 1fr; gap: 0;
  text-decoration: none; color: var(--ink);
  border: 1px solid var(--border); border-radius: 2px; background: var(--surface);
}
/* THE CARD CARRIES THE MAP'S OWN DESK COLOUR, the way .catcol and .hh-feature
   already do. The shot is a map of the city and reads as a picture; under it
   sat a white box with a headline in it, and the two did not look like one
   card. The wash binds them and it tells the reader before they read a word
   that the signature map is the elections map. Class is stamped from
   MAP_META's kicker, so rotating SIGNATURE recolours the card with it. */
.sigmap-card--money { --cat: var(--s1); --cat-ink: var(--s1-ink-wash); --cat-wash: var(--s1-wash); }
.sigmap-card--elections { --cat: var(--s2); --cat-ink: var(--s2-ink-wash); --cat-wash: var(--s2-wash); }
.sigmap-card--city-hall { --cat: var(--s3); --cat-ink: var(--s3-ink-wash); --cat-wash: var(--s3-wash); }
.sigmap-card--crime { --cat: var(--s4); --cat-ink: var(--s4-ink-wash); --cat-wash: var(--s4-wash); }
.sigmap-card--places { --cat: var(--s5); --cat-ink: var(--s5-ink-wash); --cat-wash: var(--s5-wash); }
.sigmap-card:hover { border-color: var(--border-strong); }
.sigmap-card .shot {
  display: block; overflow: hidden; border-right: 1px solid var(--border);
  aspect-ratio: 16 / 9; background: var(--surface-2);
}
.sigmap-card .shot img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 42%; display: block;
}
/* In the two-thirds slot the card stacks: the taller shot needs the width more
   than the standfirst does, and side by side it was squeezed to a stripe. */
.sigrow .sigmap-card { grid-template-columns: 1fr; }
/* Exactly the sigmap- shot's own 1000x880, so nothing is cropped. At any
   other ratio object-fit trimmed the instrument's control bar and the card
   led with a half-row of cut-off form fields. */
.sigrow .sigmap-card .shot {
  aspect-ratio: 25 / 22; border-right: 0; border-bottom: 1px solid var(--border);
}
.sigrow .sigmap-card .shot img { object-fit: contain; object-position: 50% 50%; }
/* THE STANDFIRST TAKES WHATEVER HEIGHT IS LEFT. The rail beside it is the
   taller column, so the row stretches this card past its own content; with two
   auto rows the surplus was split between them, which letterboxed the map
   inside a shot box grown past its aspect ratio and left a bare strip of card
   between the picture and the tint. Pinning the shot to auto and giving the
   rest to the body puts the wash on the card's own bottom edge, and the map
   keeps the 25:22 it was cut at. */
.sigrow .sigmap-card { grid-template-rows: auto 1fr; }
.sigrow .sigmap-card .body { padding: 20px 22px 22px; align-self: stretch; }
/* The wash goes on the body only, never over the shot: the map's own colour
   scale is the thing being previewed and a tint across it would be a lie about
   what the reader is going to get. */
.sigmap-card .body {
  display: block; padding: 26px 28px 28px; align-self: center;
  background: var(--cat-wash, transparent);
}
.sigmap-card .t {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.05rem); letter-spacing: -0.018em; line-height: 1.12;
}
.sigmap-card .d {
  display: block; color: var(--ink-2); font-size: 0.97rem; line-height: 1.55; margin-top: 10px;
}
.sigmap-card .cta {
  display: block; font-family: var(--sans); font-size: 0.8rem; font-weight: 700;
  color: var(--cat-ink, var(--link)); margin-top: 16px;
}
.sigmap-card:hover .cta { text-decoration: underline; }
@media (max-width: 760px) {
  .sigmap-card { grid-template-columns: 1fr; }
  .sigmap-card .shot { border-right: 0; border-bottom: 1px solid var(--border); aspect-ratio: 16/10; }
  .sigmap-card .body { padding: 20px 20px 22px; }
}

/* ---------- the mayor's race scoreboard ---------- */
.scoreboard { margin: 40px 0 6px; border-top: 2px solid var(--ink); }
.sb-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 20px;
  flex-wrap: wrap; padding: 13px 0 16px; border-bottom: 1px solid var(--hairline);
}
.sb-head h2 { margin: 7px 0 0; font-size: 1.5rem; letter-spacing: -0.015em; }
.sb-clock { font-family: var(--sans); font-size: 0.76rem; color: var(--muted); text-align: right; }
.sb-clock .d {
  display: block; font-size: 2rem; font-weight: 700; color: var(--ink);
  letter-spacing: -0.02em; line-height: 1;
}
.sb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.sb-cand { padding: 20px 24px 22px; border-right: 1px solid var(--hairline); }
.sb-cand:last-child { border-right: 0; }
.sb-name { font-family: var(--serif); font-weight: 700; font-size: 1.35rem; letter-spacing: -0.012em; }
.sb-note { font-family: var(--sans); font-size: 0.72rem; color: var(--muted); margin-top: 3px; }
.sb-total {
  font-family: var(--sans); font-weight: 700; font-size: 2.5rem;
  letter-spacing: -0.025em; line-height: 1; margin-top: 16px;
}
.sb-totlab {
  font-family: var(--sans); font-size: 0.66rem; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--muted); margin-top: 5px;
}
.sb-bar { display: flex; gap: 2px; height: 14px; margin: 12px 0 14px; }
.sb-bar .seg { display: block; height: 100%; }
.sb-bar--against { height: 8px; margin: 7px 0 0; }
.sb-lines { font-family: var(--sans); font-size: 0.83rem; }
.sb-lines .ln, .sb-against .ln {
  display: flex; align-items: baseline; gap: 9px;
  padding: 6px 0; border-bottom: 1px solid var(--hairline);
}
.sb-lines .ln:last-child { border-bottom: 0; }
.sb-lines .sw { width: 10px; height: 10px; flex: none; border: 1px solid var(--border); }
.sb-lines .k, .sb-against .k { color: var(--ink-2); flex: 1; }
.sb-lines .v, .sb-against .v { font-variant-numeric: tabular-nums; font-weight: 700; }
.sb-against {
  margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--ink);
  font-family: var(--sans); font-size: 0.83rem;
}
.sb-against .ln { border-bottom: 0; padding: 0; }
.sb-foot {
  font-family: var(--sans); font-size: 0.74rem; color: var(--muted); line-height: 1.55;
  margin: 0; padding: 13px 0 16px; border-top: 1px solid var(--hairline); max-width: 82ch;
}
.sb-foot strong { color: var(--ink-2); font-weight: 600; }

/* ---------- the signature leaderboard ---------- */
.leaderboard { margin: 40px 0 6px; }
.lb-title { font-size: 1.5rem; letter-spacing: -0.015em; margin: 0 0 7px; }
.lb-dek { color: var(--ink-2); font-size: 0.95rem; line-height: 1.55; margin: 0 0 18px; max-width: 68ch; }
.lb-table { border-collapse: collapse; width: 100%; font-family: var(--sans); }
.lb-table td { padding: 9px 12px 9px 0; border-bottom: 1px solid var(--hairline); vertical-align: baseline; }
.lb-table tr:last-child td { border-bottom: 1px solid var(--border-strong); }
.lb-rank {
  font-size: 0.78rem; font-variant-numeric: tabular-nums; color: var(--muted);
  width: 2.4em; font-weight: 700;
}
.lb-name { font-family: var(--serif); font-size: 1.02rem; font-weight: 600; line-height: 1.25; }
.lb-sector {
  display: block; font-family: var(--sans); font-size: 0.68rem; font-weight: 400;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin-top: 2px;
}
.lb-bar { width: 34%; min-width: 90px; }
.lb-bar span { display: block; height: 11px; background: var(--s1); }
.lb-val {
  text-align: right; font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: 0.95rem; white-space: nowrap; width: 6.5em;
}
.lb-alias { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted); }
.lb-foot {
  font-family: var(--sans); font-size: 0.72rem; color: var(--muted);
  margin: 12px 0 0; line-height: 1.5;
}
.lb-table--full { font-size: 0.86rem; }
.lb-table--full thead th {
  text-align: left; font-size: 0.63rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted); padding: 0 12px 9px 0;
  border-bottom: 2px solid var(--ink); white-space: nowrap;
}
.lb-table--full thead th.num, .lb-table--full td.num { text-align: right; }
.lb-table--full td.num { font-variant-numeric: tabular-nums; color: var(--ink-2); white-space: nowrap; }

/* ---------- numbers strip ---------- */
.numstrip {
  margin: 34px 0 6px; border-top: 2px solid var(--ink); border-bottom: 1px solid var(--hairline);
  padding: 18px 0 16px;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 18px;
}
.numstrip .cell { text-align: center; border-right: 1px solid var(--hairline); }
.numstrip .cell:last-child { border-right: 0; }
.numstrip .v {
  font-family: var(--sans); font-weight: 700; font-size: clamp(1.3rem, 1rem + 1.2vw, 1.7rem);
  letter-spacing: -0.01em; color: var(--ink);
}
.numstrip .k {
  margin-top: 3px; font-family: var(--sans); font-size: 0.72rem;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted);
}

/* ---------- your block strip ----------
   The address box, on the front page and at the head of Money. Built like an
   almanac index line, not a signup box: 2px ink rule above, hairline below,
   the field underlined rather than boxed. No panel fill, no shadow, no radius,
   so it reads as part of the page furniture and not as an advertisement. */
.ybstrip {
  margin: 34px 0 6px; border-top: 2px solid var(--ink); border-bottom: 1px solid var(--hairline);
  padding: 20px 0 20px;
}
/* On a section page it sits straight under .cat-head, which already ends in the
   same 2px rule, so it takes that one instead of printing a second one 30px
   below it. */
.ybstrip:not(.ybstrip--home) { border-top: 0; margin-top: 14px; padding-top: 6px; }
/* THE FRONT PAGE'S FIRST SCREEN. Three things run side by side: the address
   box answers "what has this got to do with me", the lead story answers "why
   should I trust you", and the film answers "what is this place" faster than
   either, because it is the only one that moves. A reader needs all three
   before deciding to stay. Stacked, they were several scrolls apart.

   The film column is the narrowest of the three because it is 9:16 and its
   width sets the height of the whole band: at the third of the grid it would
   naively want, it stood two hundred pixels taller than the plate beside it
   and the hero stopped fitting on a laptop screen. */
.homehero {
  display: grid; grid-template-columns: 1fr 1.32fr 0.74fr; gap: 0 30px;
  margin: 14px 0 4px; padding-bottom: 22px;
  border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink);
  align-items: stretch;
}
/* THE ADDRESS BOX IS A BLOCK, NOT A COLUMN OF TEXT. Left on the open page it
   filled about half its cell and the rest was paper, which read as something
   missing rather than as space. As a tinted plate it fills the height it is
   given, and the tool stops looking like an orphaned paragraph next to a
   photograph. Warm grays out of the same ramp as the paper, opened by the
   orange from the brandmark: the one place on the page where the accent runs
   the full width of anything. */
.homehero .ybstrip {
  margin: 0; padding: 14px 24px 14px; border: 0;
  /* THE DESK COLOUR AS A FIELD, NOT AS A LINE. This plate carried a 3px orange
     rule over a warm gray ramp, which put the colour in three pixels and left
     the other six hundred beige. The -wash step is the one the system already
     defines for a tinted field, measured per theme, so the plate is orange at
     the strength the desks were drawn to be tinted at. */
  background: var(--s2-wash);
  /* STRETCH, NOT CENTRE. The film column is 9:16 and sets the height of the
     band, which left this plate 154px taller than its own contents and centred
     them in it: 77px of tint above the mark and 77px below the search field,
     reading as a plate that had failed to load rather than as one that was
     full. Distributed, the mark opens the plate and the field closes it. */
  display: flex; align-items: stretch;
}
.homehero .ybstrip-form {
  grid-template-columns: 1fr; gap: 9px; width: 100%;
  align-content: space-between;
}
/* THE SUB-BRAND LOCKUP. Six cells for six levels of government, cut to the
   masthead's own grammar so the tool reads as part of the publication rather
   than as a widget dropped onto it, and stacked over the name so the plate
   opens with a mark the way the page does. */
.govmark { display: block; text-decoration: none; color: inherit; }
.govmark-svg { display: block; width: 46px; height: 31px; margin-bottom: 7px; }
.govmark-name {
  display: block; font-family: var(--serif); font-weight: 700;
  letter-spacing: -0.021em; line-height: 1.03;
  font-size: clamp(1.85rem, 1.15rem + 1.9vw, 2.5rem);
}
.govmark:hover .govmark-name { color: var(--link); }
/* What the reader is about to get, in the colours they will meet on the
   board. It fills the foot of the plate with the one thing worth saying
   there, which is that this goes from a neighborhood council to Congress. */
.ybstrip-levels {
  display: flex; flex-wrap: wrap; gap: 3px 14px;
  margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--hairline);
}
.lvchip {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--sans); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.02em; color: var(--ink-2); white-space: nowrap;
}
.lvchip i { width: 9px; height: 9px; flex: none; }
.ybstrip-embed {
  margin: 6px 0 0; font-size: 0.76rem;
}
.ybstrip-embed a { color: var(--ink-2); text-decoration: none; }
.ybstrip-embed a::after { content: " \2192"; }
.ybstrip-embed a:hover { color: var(--nav-hover); }
.ybstrip--home .ybstrip-copy p { font-size: 0.9rem; max-width: 40ch; margin-top: 6px; line-height: 1.42; }
.ybstrip--home .ybstrip-in { font-size: 0.98rem; background: transparent; }
/* THE GOVERNMENT PLATE IS TWO TOOLS, NOT ONE. Your Government answers who
   represents an address; Roll Call answers whether they turn up. They share the
   hero's first column and split its height, each keeping its own desk tint so
   the join reads as two plates rather than as one plate with a footer. */
/* THE STACK MUST NOT SET THE ROW HEIGHT. The hero's height belongs to the
   feature column; this plate has always simply filled whatever that came to.
   Equal `1fr` rows sized both halves to the taller one's max-content, which made
   the stack the tallest thing in the band, stretched the hero by 250px and left
   a void under the video. So Your Government keeps the old behaviour of taking
   up the slack, and Roll Call is a compact band at the foot of the same plate:
   together they come to exactly what the single tile came to before. */
.hh-govstack { display: flex; flex-direction: column; min-width: 0; align-self: start; }
.homehero .hh-govstack .ybstrip { flex: 1 1 auto; min-height: 0; }
.hh-govstack .rcstrip { flex: 0 0 auto; }
/* THE MEDALLION MUST NOT SET THE PLATE'S HEIGHT, and on this plate that rules
   out putting it in the flow at all. Measured at 1440: the stack comes to 586
   and the film beside it to 584, so the column is already the tallest thing in
   the band by two pixels and has no slack to give. A champion in a second
   column narrowed the copy from 335px to 172px, which wrapped the dek and the
   figures onto four more lines and pushed the hero up by 85px -- the void under
   the video, exactly as warned above, arrived by reflow rather than by height.

   So it is taken out of flow and set in the corner the copy already leaves
   empty. The figures stop at x=232 of 359 and the link at x=138, so the bottom
   right of this plate is 119 x 81 of nothing. Nothing reflows because nothing
   moves; the medallion is sized to that hole and checked against it. */
.rcstrip {
  margin: 0; padding: 10px 24px 11px;
  background: var(--med-int-wash);
  border-top: 2px solid var(--med-int);
  display: flex; flex-direction: column; justify-content: center;
  position: relative;
  container-type: inline-size; container-name: rcplate;
}
.rcstrip-copy {
  display: flex; flex-direction: column; gap: 5px; justify-content: center;
}
.rcmark { display: block; text-decoration: none; color: inherit; }
.rcmark-name {
  display: block; font-family: var(--serif); font-weight: 700;
  letter-spacing: -0.021em; line-height: 1.03;
  font-size: clamp(1.35rem, 0.95rem + 1.1vw, 1.72rem);
}
.rcmark:hover .rcmark-name { color: var(--med-int); }
.rcstrip-dek {
  margin: 0; font-size: 0.84rem; color: var(--ink-2); max-width: 42ch; line-height: 1.4;
}
/* Figures divided by hairlines, the almanac strip the maps use, not three cards. */
.rcstrip-stats {
  display: flex; flex-wrap: wrap; gap: 4px 0;
  margin-top: 0; padding-top: 6px; border-top: 1px solid var(--hairline);
  font-family: var(--sans); font-size: 0.68rem; color: var(--ink-2);
}
.rcstrip-stats span { padding: 0 13px; border-right: 1px solid var(--hairline); }
.rcstrip-stats span:first-child { padding-left: 0; }
.rcstrip-stats span:last-child { border-right: 0; }
.rcstrip-stats b {
  display: block; font-size: 0.98rem; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.rcstrip-go { margin: 0; font-size: 0.76rem; }
.rcstrip-go a { color: var(--med-int); text-decoration: none; font-weight: 600; }
.rcstrip-go a::after { content: " \2192"; }
.rcstrip-go a:hover { text-decoration: underline; }

/* LAST MONTH'S BEST ATTENDANCE, in the corner. The board's medallion at a
   quarter of the size: label, ring, name, figure. Centred on the ring rather
   than ranged right, because a face reads as the centre of its own column. */
/* SIZED AGAINST THE PLATE, NOT THE WINDOW, which is why this is a container
   query. The hole in the corner is not a function of the viewport: the plate is
   383px wide from 1280 up, 331px at 1121 where the three-column hero is at its
   tightest, and 370-469 again once the hero drops to two columns. Measured
   free width in that corner runs 127 / 101 / 75. A viewport media query would
   have to guess at all three; asking the plate how wide IT is gets the answer
   right at every width and survives the hero being re-laid out later.

   The breakpoints below are CONTENT widths, which is what a size container
   query compares against -- not the 383/357/331 the plate measures from the
   outside. Written as the outer numbers they never matched, and every width
   quietly got the narrowest medallion. */
.rcchamp {
  position: absolute; right: 24px; bottom: 6px;
  width: var(--rcc-w, 70px);
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  text-decoration: none; color: inherit; text-align: center;
}
@container rcplate (min-width: 305px) { .rcchamp { --rcc-w: 92px; } }
@container rcplate (min-width: 332px) { .rcchamp { --rcc-w: 112px; } }
/* Two or three lines depending on which of those it got -- "AUGUST ATTENDANCE /
   CHAMP" at the wide end -- which is why the tracking is modest, the leading
   tight, and the word breaks are left to the browser. */
.rcchamp-label {
  font-family: var(--sans); font-size: 0.44rem; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--med-int);
  line-height: 1.1; margin-bottom: 1px;
}
@container rcplate (min-width: 332px) { .rcchamp-label { font-size: 0.46rem; } }
/* The rim is painted over the plate rather than wrapped around it: as a border
   it leaves a hairline of the ring's own background between itself and the
   clipped portrait. Same trick the attendance board uses. */
.rcchamp-ring {
  display: block; width: var(--rcc-ring, 29px); height: var(--rcc-ring, 29px);
  border-radius: 50%;
  background: var(--paper, #fff); overflow: hidden; position: relative;
  flex: none; --rim: 2.5px;
}
@container rcplate (min-width: 305px) { .rcchamp-ring { --rcc-ring: 31px; } }
@container rcplate (min-width: 332px) { .rcchamp-ring { --rcc-ring: 33px; } }
.rcchamp-ring::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: var(--rim) solid var(--med-int);
}
/* Pulled up the frame, because a headshot cropped to a circle on its centre
   puts the chin in the middle and the top of the head outside it. */
.rcchamp-ring img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 20%; display: block;
}
/* NOT nowrap. The champion is whoever it is, and Marqueece Harris-Dawson is
   twice the width of Heather Hutt; a name that cannot wrap runs out of the
   corner and over the figures to its left. It wraps, and the figure line is
   what gives way for it at the narrow end. */
.rcchamp-name {
  font-family: var(--serif); font-size: 0.62rem; font-weight: 700;
  line-height: 1.06; letter-spacing: -0.008em; margin-top: 1px;
  overflow-wrap: break-word; hyphens: auto;
}
@container rcplate (min-width: 332px) { .rcchamp-name { font-size: 0.68rem; } }
.rcchamp-fig {
  font-family: var(--sans); font-size: 0.52rem; font-weight: 700;
  color: var(--ink-2); font-variant-numeric: tabular-nums; line-height: 1.12;
  white-space: nowrap; display: none;
}
@container rcplate (min-width: 332px) { .rcchamp-fig { display: block; } }
/* "roll calls" is what the figure means, but the corner has no room for it and
   the plate says "roll calls" twice already, so it is dropped below the width
   where it fits rather than allowed to wrap the figure onto two lines. */
.rcchamp-unit { font-weight: 400; display: none; }
.rcchamp:hover .rcchamp-ring { --rim: 4px; }
.rcchamp:hover .rcchamp-name { color: var(--med-int); }

@media (max-width: 900px) {
  .rcstrip { padding: 18px 18px 20px; }
  .rcchamp { right: 16px; bottom: 16px; }
}
/* Below 860 the hero is a single column, so the plate's height is its own
   business again and the medallion can come back into the flow, where it does
   not have to be squeezed into someone else's white space. On its side: ring
   left, the three lines beside it, and the unit word back on the figure. */
@media (max-width: 860px) {
  .rcchamp {
    position: static; width: auto; margin-top: 10px; padding-top: 9px;
    border-top: 1px solid var(--hairline);
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    /* EXPLICIT rows, because the ring spans them with `1 / -1` and -1 counts
       lines of the EXPLICIT grid only. Left implicit it resolved to row 1
       alone, which stretched that row to the ring's height and left the label
       floating half a ring above the name. */
    grid-template-rows: auto auto auto;
    column-gap: 11px; align-items: center; text-align: left;
  }
  .rcchamp-ring { grid-column: 1; grid-row: 1 / -1; width: 44px; height: 44px; }
  .rcchamp-label, .rcchamp-name, .rcchamp-fig { grid-column: 2; }
  .rcchamp-label { margin-bottom: 0; font-size: 0.53rem; }
  .rcchamp-name { margin-top: 1px; font-size: 0.85rem; }
  /* Explicit, because the base rule hides the figure and the container query
     that shows it keys on the plate being wide -- and on a phone the plate is
     full-bleed but narrow. In this layout there is room for it either way. */
  .rcchamp-fig { font-size: 0.66rem; display: block; }
  .rcchamp-unit { display: inline; }
}
/* Narrow enough that the copy and the medallion would both be squeezed side by
   side, so the champion drops under the copy and turns on its side: ring left,
   the three lines of type beside it. The hero has already gone single-column by
   here, so this is the one place the plate can afford the extra height -- and
   the label stays, because the label is the thing that says what the face is. */
@media (max-width: 460px) {
  .rcchamp-ring { width: 40px; height: 40px; }
  .rcchamp-name { font-size: 0.8rem; }
}

/* Deepened two steps off the brandmark orange: the brand value carries only
   3.2:1 against white and this button holds text. */
.homehero .btn-primary { background: #c74e1b; border-color: #c74e1b; }
.homehero .btn-primary:hover { background: #b34412; border-color: #b34412; }

/* A TINTED PLATE PER COLUMN, so the first screen says which desks it is made
   of before a word is read: the address box orange, the story its own desk,
   the film the pink it already flags itself with. Three fields of colour
   rather than three lines over three fields of paper. The desk is bound the
   way .sec--* binds it, off the kicker the column already prints, so filing a
   piece under a desk stays the only thing anyone has to do. */
.hh-feature:has(.kicker--money) { --cat: var(--s1); --cat-ink: var(--s1-ink-wash); --cat-wash: var(--s1-wash); }
.hh-feature:has(.kicker--elections) { --cat: var(--s2); --cat-ink: var(--s2-ink-wash); --cat-wash: var(--s2-wash); }
.hh-feature:has(.kicker--city-hall) { --cat: var(--s3); --cat-ink: var(--s3-ink-wash); --cat-wash: var(--s3-wash); }
.hh-feature:has(.kicker--crime) { --cat: var(--s4); --cat-ink: var(--s4-ink-wash); --cat-wash: var(--s4-wash); }
.hh-feature:has(.kicker--places) { --cat: var(--s5); --cat-ink: var(--s5-ink-wash); --cat-wash: var(--s5-wash); }
/* The kicker is the one line of desk-coloured type standing on the desk's own
   tint, so on this plate it takes the step measured there. Everywhere else the
   kicker is on paper and keeps the paper step. */
.hh-feature > .kicker { color: var(--cat-ink); }
/* STRETCH, and let the picture take up the slack. At align-self:start the
   column stopped 135px short of the film beside it and the hero ended on a
   ragged line with a hole under the story. Stretched, the thumb is the one
   child that can grow, which is the right answer twice over: the lead art gets
   bigger and the three columns finish together at any width. */
.hh-feature {
  align-self: stretch; padding: 22px 24px 26px; min-width: 0;
  background: var(--cat-wash, var(--surface-2));
  display: flex; flex-direction: column;
}
/* Wider than 16:9 so the story column stops overshooting the plate beside it.
   The plate cannot shrink below its own contents, so the picture is what
   gives. The aspect ratio is the floor it is drawn at and the height it
   reserves before the image decodes; flex-grow takes it past that only when
   there is height going spare. */
.hh-feature .thumb {
  display: block; aspect-ratio: 2 / 1; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin-bottom: 12px;
  flex: 1 1 auto; min-height: 0;
}
.hh-feature .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A DRAWN MARK, not a photograph: an interactive that composes its own front
   page picture out of its own data. No background of its own, so the plate
   shows through and the meet-fit leaves margin rather than letterbox bands;
   no overflow crop, because the whole point is that nothing gets cut off. */
.hh-feature .thumb--mark {
  background: transparent; border-radius: 0; overflow: visible;
  display: flex; align-items: center; justify-content: center;
  /* The mark's own aspect, not the photograph's 2/1. Stacked on a phone there
     is no spare height for flex-grow to hand over, so the box sat at 2/1 with a
     1.24 mark meet-fitted inside it and 128px of empty plate down either side.
     Matching the viewBox means the grid fills the box on a phone and still has
     room to grow on the open page. */
  aspect-ratio: 856 / 688;
}
.hh-mark, .hh-mark svg { display: block; width: 100%; height: 100%; }
/* Turf, not the Politics desk's --s3, and the same values the index page sets
   locally: the mark and the page it points at have to be the same green. */
.hh-mark--golf { --turf: #2c7a4b; --turf-fill: #b9dcc4; }
:root[data-theme="dark"] .hh-mark--golf { --turf: #7fd3a1; --turf-fill: #244f37; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hh-mark--golf { --turf: #7fd3a1; --turf-fill: #244f37; }
}
.hh-mark--golf svg g { fill: var(--turf-fill); stroke: var(--turf); }
/* A featured INTERACTIVE puts a themed pair in here, and the rule above sets
   display at (0,2,1) while img.theme-dark-only { display: none } a few thousand
   lines up is only (0,1,1) — so the later, stronger rule silently won and both
   the light and the dark picture were served, stacked. Same trap .medthumb
   documents; restated here because this box does need its own display. */
.hh-feature .thumb img.theme-dark-only { display: none; }
/* An interactive tile is composed by build_interactive_shots.py with the
   element's TOP-LEFT corner pinned, and .thumb is flex:1 1 auto here, so its
   box runs taller than the 5:3 tile and object-fit crops the sides. Centred,
   that cut the first column's course names in half. Anchor the same corner the
   shot was composed around and the crop falls off the far edge instead, where
   an array is supposed to continue. */
.hh-feature .thumb img.theme-light-only,
.hh-feature .thumb img.theme-dark-only { object-position: 0 0; }
:root[data-theme="dark"] .hh-feature .thumb img.theme-light-only { display: none; }
:root[data-theme="dark"] .hh-feature .thumb img.theme-dark-only { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hh-feature .thumb img.theme-light-only { display: none; }
  :root:not([data-theme="light"]) .hh-feature .thumb img.theme-dark-only { display: block; }
}
.hh-feature h3 {
  font-family: var(--serif); font-size: clamp(1.2rem, 0.95rem + 0.95vw, 1.62rem);
  line-height: 1.16; letter-spacing: -0.016em; margin: 6px 0 0;
}
.hh-feature h3 a { color: inherit; text-decoration: none; }
.hh-feature h3 a:hover { color: var(--link); }
/* Clamped so the story column cannot run so far past the address plate that
   the two halves of the first screen stop reading as a pair. */
.hh-feature .dek {
  color: var(--ink-2); font-size: 0.92rem; line-height: 1.5; margin: 8px 0 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.hh-feature .meta { color: var(--muted); font-size: 0.76rem; margin-top: 8px; }

/* ---------- the film column ---------- */
.hh-film {
  align-self: start; padding: 22px 24px 26px; min-width: 0;
  background: var(--med-vid-wash);
  /* THE FLAG SITS ON ITS OWN COLOUR NOW, which costs it contrast: --med-vid-ink
     measures 4.52 on paper and 4.11 once the pink tint is under it. Seven per
     cent darker holds the hue and measures 4.63. Light only. In dark mode the
     wash is over the dark plane and the flag already measures 4.88, so it keeps
     the token it shares with the film pages. */
  --hh-flag-ink: #9b537b;
}
/* Dark needs no step: the tint is over the dark plane there, where the shared
   token already measures 4.88. Both selectors, because the theme is a system
   preference until the reader touches the toggle and an explicit choice after. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hh-film { --hh-flag-ink: var(--med-vid-ink); }
}
:root[data-theme="dark"] .hh-film { --hh-flag-ink: var(--med-vid-ink); }
/* THE COLUMN'S HEADING, not a caption on it. At 0.68rem it was the smallest
   type in the hero labelling the one thing in the hero that moves, and it read
   as a badge stuck on the frame. It is the third column's masthead and it is
   set like one, against the address box's own title on the left. */
.hh-film-flag {
  display: inline-block; font-family: var(--sans);
  font-size: clamp(0.95rem, 0.75rem + 0.6vw, 1.2rem);
  font-weight: 700; letter-spacing: 0.05em; line-height: 1.1;
  text-transform: uppercase;
  color: var(--hh-flag-ink, var(--med-vid-ink)); text-decoration: none; margin-bottom: 11px;
}
.hh-film-flag:hover { color: var(--link); }
/* The frame reserves its own height from the aspect ratio, so the hero does
   not jolt taller when the poster finally decodes. */
.hh-film-frame {
  position: relative; aspect-ratio: 9 / 16; overflow: hidden; border-radius: 2px;
  background: #0d0d0d; margin-bottom: 12px;
}
.hh-film-vid { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The whole frame is the link. The button sits above it in the stack so a
   click on the control does not also navigate. */
.hh-film-hit { position: absolute; inset: 0; z-index: 1; }
.hh-film-btn {
  position: absolute; right: 9px; bottom: 9px; z-index: 2;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: rgba(8, 8, 8, 0.62); color: #f4f3ef;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity 0.16s ease, background 0.16s ease;
}
.hh-film-btn svg { width: 15px; height: 15px; fill: currentColor; }
/* Idle it stays out of the picture; it appears on hover, on keyboard focus,
   and whenever the film is paused, because a paused film with no visible way
   to restart it reads as broken rather than as paused. */
.hh-film-frame:hover .hh-film-btn,
.hh-film-btn:focus-visible,
.hh-film--paused .hh-film-btn { opacity: 1; }
.hh-film-btn:hover { background: rgba(8, 8, 8, 0.82); }
.hh-film-btn .ic-play { display: none; }
.hh-film--paused .hh-film-btn .ic-play { display: block; }
.hh-film--paused .hh-film-btn .ic-pause { display: none; }
.hh-film h3 {
  font-family: var(--serif); font-size: 1.02rem; line-height: 1.2;
  letter-spacing: -0.012em; margin: 0;
}
.hh-film h3 a { color: inherit; text-decoration: none; }
.hh-film h3 a:hover { color: var(--link); }
.hh-film .meta { color: var(--muted); font-size: 0.72rem; margin-top: 7px; }
/* The way to the rest of the videos. The flag above the frame used to be the
   only route and it was a 0.68rem label doing the job of a button. */
.hh-film-more { margin-top: 12px; }
/* Hidden in the three-column layout, where the film's own first frame already
   carries the same sentence in readable type. It comes back in the horizontal
   card below, which has the width for it and needs the depth. */
.hh-film-dek { display: none; }

/* BELOW THE THREE-COLUMN WIDTH the film does not simply narrow, it turns on
   its side. A 9:16 frame in a one-column layout is most of a phone screen tall
   for one excerpt, which is not what it is worth. As a horizontal card it keeps
   the motion and costs a couple of hundred pixels. */
@media (max-width: 1120px) {
  .homehero { grid-template-columns: 0.9fr 1.1fr; }
  .hh-film {
    grid-column: 1 / -1; display: grid; grid-template-columns: 132px 1fr;
    gap: 0 18px; align-items: center;
    /* The plate is what separates this from the story above it now, so the
       hairline that used to do the job would be a second, weaker line inside
       a field that already has an edge. */
    margin-top: 20px;
  }
  .hh-film-flag { grid-column: 2; align-self: end; margin: 0 0 4px; }
  .hh-film-frame { grid-row: 1 / span 4; grid-column: 1; margin: 0; }
  .hh-film h3, .hh-film-dek, .hh-film .meta { grid-column: 2; }
  .hh-film-dek {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden; max-width: 64ch;
    color: var(--ink-2); font-size: 0.86rem; line-height: 1.5; margin: 7px 0 0;
  }
}
@media (max-width: 860px) {
  .homehero { grid-template-columns: 1fr; gap: 0; }
  .homehero .ybstrip, .hh-feature, .hh-film { padding: 20px 18px 22px; }
  .hh-feature { margin-top: 20px; }
}
@media (max-width: 520px) {
  .hh-film { grid-template-columns: 104px 1fr; gap: 0 14px; }
  .hh-film-dek { -webkit-line-clamp: 2; font-size: 0.8rem; }
}
/* A reader who has asked the system for less motion gets the poster. The film
   is still one click away and the button says so. */
@media (prefers-reduced-motion: reduce) {
  .hh-film-btn { opacity: 1; transition: none; }
}
.ybstrip-form {
  display: grid; grid-template-columns: 1fr minmax(300px, 380px); gap: 32px; align-items: start;
}
.ybstrip-copy .kicker { text-decoration: none; }
.ybstrip-copy p {
  margin: 8px 0 0; max-width: 56ch; color: var(--ink-2);
  font-size: 1.02rem; line-height: 1.55;
}
.ybstrip--home .ybstrip-copy p { font-size: 1.1rem; color: var(--ink); }
.ybstrip-lab {
  display: block; font-family: var(--sans); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted); margin-bottom: 7px;
}
.ybstrip-row { display: flex; gap: 10px; align-items: stretch; }
.ybstrip-in {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--serif); font-size: 1.05rem; color: var(--ink);
  background: transparent; border: 0; border-bottom: 2px solid var(--border-strong);
  border-radius: 0; padding: 6px 2px;
}
.ybstrip-in::placeholder { color: var(--faint); }
.ybstrip-in:focus { outline: 0; border-bottom-color: var(--s2); }
.ybstrip-row .btn { flex: 0 0 auto; align-self: stretch; }
.ybstrip-note {
  margin: 9px 0 0; font-family: var(--sans); font-size: 0.74rem; color: var(--muted);
}
@media (max-width: 800px) {
  .ybstrip-form { grid-template-columns: 1fr; gap: 18px; }
}

/* ---------- article page ---------- */
.article-head { padding: 42px 0 10px; }
.article-head h1 {
  font-size: clamp(1.9rem, 1.35rem + 2.6vw, 3rem);
  line-height: 1.12; letter-spacing: -0.018em; margin: 10px 0 14px;
}
.article-head .dek {
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.22rem);
  line-height: 1.5; color: var(--ink-2); font-style: italic; margin: 0 0 18px;
}
.byline {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px;
  font-family: var(--sans); font-size: 0.78rem; color: var(--muted);
  border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
  padding: 10px 0; margin-bottom: 34px;
}
.byline .who { color: var(--ink-2); font-weight: 600; }

.article-body { padding-bottom: 30px; }
.article-body > p, .article-body > h2, .article-body > h3, .article-body > ul,
.article-body > ol, .article-body > blockquote, .article-body > .tablewrap,
.article-body > .note { max-width: 700px; margin-left: auto; margin-right: auto; }
.article-body > p { margin: 0 auto 1.25em; }
.article-body > p:first-of-type::first-letter {
  font-size: 3.1em; line-height: 0.82; float: left;
  padding: 0.08em 0.09em 0 0; font-weight: 700; color: var(--ink);
}
.article-body h2 {
  font-size: 1.45rem; letter-spacing: -0.012em; line-height: 1.25;
  margin: 2em auto 0.7em;
}
.article-body strong { font-weight: 700; }
.article-body ul, .article-body ol { padding-left: 1.3em; margin: 0 auto 1.25em; }
.article-body li { margin-bottom: 0.4em; }
.article-body code {
  font-family: var(--mono); font-size: 0.85em;
  background: var(--surface-2); border: 1px solid var(--border);
  padding: 0.08em 0.35em; border-radius: 2px;
}
.article-body blockquote {
  border-left: 3px solid var(--s2); margin: 1.6em auto; padding: 0.2em 0 0.2em 1.2em;
  color: var(--ink-2); font-style: italic; font-size: 1.05em;
}

/* tables */
.tablewrap { overflow-x: auto; margin: 1.6em auto; }
.article-body table, .datatable {
  border-collapse: collapse; width: 100%;
  font-family: var(--sans); font-size: 0.86rem; line-height: 1.45;
}
.article-body th, .datatable th {
  text-align: left; font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); font-weight: 700;
  border-bottom: 2px solid var(--ink); padding: 6px 12px 6px 0;
}
.article-body td, .datatable td {
  border-bottom: 1px solid var(--hairline); padding: 7px 12px 7px 0;
  vertical-align: top;
}
.article-body td[align="right"], .article-body th[align="right"],
.datatable td.num, .datatable th.num {
  text-align: right; font-variant-numeric: tabular-nums;
}
.article-body tr:last-child td { border-bottom: 1px solid var(--border-strong); }

/* ---------- photography ---------- */
figure.art-hero { max-width: 820px; margin: 0 auto 34px; }
figure.art-hero img {
  display: block; width: 100%; aspect-ratio: 21 / 9; object-fit: cover;
  border-radius: 2px; border: 1px solid var(--border);
}
figure.art-hero figcaption {
  font-family: var(--sans); font-size: 0.72rem; color: var(--muted); margin-top: 8px;
}
/* A photograph set inside the body, on the same 820px measure the exhibits use
   so a picture and a chart line up down the page. 3:2 rather than the hero's
   21:9, because this one is read rather than skimmed past. */
figure.art-inline { max-width: 820px; margin: 2.4em auto; }
figure.art-inline img {
  display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: 2px; border: 1px solid var(--border);
}
/* A plate is a drawing, not a photograph: cropping it to 3:2 throws away the
   part of the map the caption is pointing at. Plates keep their own aspect and
   carry their own paper, so they stay light on the dark plane by design. */
figure.art-inline.plate img {
  aspect-ratio: auto; object-fit: contain; background: #f9f9f7;
}
figure.art-inline figcaption {
  font-family: var(--sans); font-size: 0.72rem; color: var(--muted); margin-top: 8px;
}
.portrait-row {
  display: flex; gap: 22px; flex-wrap: wrap; justify-content: center;
  max-width: 700px; margin: 2em auto 0.6em;
}
.portrait { width: 150px; text-align: center; }
.portrait img {
  display: block; width: 150px; height: 178px; object-fit: cover; object-position: top;
  border-radius: 2px; border: 1px solid var(--border); background: var(--surface-2);
}
.portrait .p-name { font-family: var(--serif); font-weight: 700; font-size: 0.94rem; margin-top: 9px; line-height: 1.25; }
.portrait .p-role { font-family: var(--sans); font-size: 0.7rem; color: var(--muted); margin-top: 2px; line-height: 1.35; }
.portrait-credit {
  max-width: 700px; margin: 0 auto 1.8em; text-align: center;
  font-family: var(--sans); font-size: 0.68rem; color: var(--muted);
}
.story .thumb {
  display: block; aspect-ratio: 16 / 10; overflow: hidden;
  border-radius: 2px; border: 1px solid var(--border); margin-bottom: 12px;
  background: var(--surface-2);
}
.story .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lead-story .thumb { aspect-ratio: 16 / 9; border-radius: 2px; }
.cat-list .story.has-thumb {
  display: grid; grid-template-columns: 210px 1fr; gap: 22px; align-items: start;
}
.cat-list .story.has-thumb .thumb { margin-bottom: 0; }
@media (max-width: 640px) {
  .cat-list .story.has-thumb { grid-template-columns: 1fr; }
  .portrait { width: 130px; }
  .portrait img { width: 130px; height: 154px; }
}

/* figures / charts — a ruled exhibit, not a card */
figure.chart {
  max-width: 820px; margin: 2.4em auto;
  background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
  border-top: 2px solid var(--ink);
}
figure.chart .chart-head { padding: 11px 0 12px; }
figure.chart .chart-title {
  font-family: var(--sans); font-weight: 700; font-size: 1rem; color: var(--ink);
  margin: 0 0 3px; letter-spacing: -0.008em; line-height: 1.3;
}
figure.chart .chart-sub {
  font-family: var(--sans); font-size: 0.79rem; color: var(--muted);
  margin: 0; line-height: 1.45; max-width: 68ch;
}
figure.chart .chart-plot {
  border-top: 1px solid var(--hairline); padding-top: 16px;
}
figure.chart svg { display: block; width: 100%; height: auto; overflow: visible; }
/* An exhibit whose plot is a rendered plate rather than inline SVG. The plates are
   drawn dark, so on light paper they carry their own field and need no page-side
   surface; the 2px radius is the site's image radius, not a card corner. */
figure.chart--plate .chart-plot { padding-top: 0; border-top: 0; }
figure.chart--plate img { display: block; width: 100%; height: auto; border-radius: 2px; }
figure.chart--plate { border-top-width: 2px; }
/* A plate rendered as a THEMED PAIR lands in here, and the rule directly above
   sets display at (0,2,1) while img.theme-dark-only { display: none } far above
   is only (0,1,1) — so the later, stronger rule wins and BOTH plates are served,
   stacked. Third time this trap has bitten: .medthumb and .hh-feature .thumb
   both document it. Any box that sets display on its own img has to restate the
   switch. */
figure.chart--plate img.theme-dark-only { display: none; }
:root[data-theme="dark"] figure.chart--plate img.theme-light-only { display: none; }
:root[data-theme="dark"] figure.chart--plate img.theme-dark-only { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) figure.chart--plate img.theme-light-only { display: none; }
  :root:not([data-theme="light"]) figure.chart--plate img.theme-dark-only { display: block; }
}

/* An interactive map offered inside an article. The still is a facade that
   basin.js replaces with the live map on click, so it carries an affordance
   saying so rather than pretending to be a picture. Same 2px ink rule and same
   figcaption as every other exhibit: it is an exhibit that happens to move. */
figure.chart--map { border-top-width: 2px; }
figure.chart--map .chart-plot { padding-top: 0; border-top: 0; }
.map-facade { display: block; position: relative; text-decoration: none; color: inherit; }
.map-facade img { display: block; width: 100%; height: auto; border-radius: 2px; }
.map-facade img.theme-dark-only { display: none; }
:root[data-theme="dark"] .map-facade img.theme-light-only { display: none; }
:root[data-theme="dark"] .map-facade img.theme-dark-only { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .map-facade img.theme-light-only { display: none; }
  :root:not([data-theme="light"]) .map-facade img.theme-dark-only { display: block; }
}
.map-facade-cta {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 26px 16px 13px;
  background: linear-gradient(to top, var(--paper) 18%, transparent);
  font-family: var(--sans); font-size: 0.84rem;
}
.map-facade-cta b { font-weight: 700; }
.map-facade-cta span { color: var(--link); font-weight: 600; }
.map-facade-cta span::after { content: " \2192"; }
.map-facade:hover .map-facade-cta span { text-decoration: underline; }
/* Once upgraded, the iframe stands in for the still at the same measure. The
   height is measured, not a ratio: the embed puts its own title, dek and
   control rail above the canvas, 357px of it at article width, and that rail
   does not shrink with the frame. At 640 the map got 283px and was cut off at
   the bottom edge; 780 leaves it a map worth panning. The legend below the
   canvas is reachable by scrolling inside the frame, the same as on its page. */
.map-live { display: block; width: 100%; height: 780px; border: 0; border-radius: 2px; }
@media (max-width: 640px) { .map-live { height: 660px; } }
figure.chart figcaption {
  font-family: var(--sans); font-size: 0.7rem; color: var(--muted); line-height: 1.45;
  margin-top: 12px; padding-top: 9px; border-top: 1px solid var(--hairline);
}
figure.chart .legend {
  display: flex; flex-wrap: wrap; gap: 16px; margin: 0 0 12px;
  font-family: var(--sans); font-size: 0.75rem; color: var(--ink-2);
}
figure.chart .legend .key { display: inline-flex; align-items: center; gap: 7px; }
figure.chart .legend .sw {
  width: 10px; height: 10px; border-radius: 0; flex: none;
  border: 1px solid var(--border);
}

/* chart svg text */
.c-lab { font-family: var(--sans); font-size: 12px; fill: var(--ink-2); }
.c-lab-strong { font-family: var(--sans); font-size: 12px; font-weight: 700; fill: var(--ink); }
.c-val { font-family: var(--sans); font-size: 11.5px; font-weight: 600; fill: var(--ink); font-variant-numeric: tabular-nums; }
.c-tick { font-family: var(--sans); font-size: 10.5px; fill: var(--muted); font-variant-numeric: tabular-nums; }
.c-note { font-family: var(--sans); font-size: 10.5px; fill: var(--muted); }
.c-grid { stroke: var(--grid); stroke-width: 1; }
.c-axis { stroke: var(--faint); stroke-width: 1; }

/* chart hover */
.c-hit { fill: transparent; cursor: default; }
.c-hit:hover + .c-mark, .c-mark:hover { filter: brightness(0.92); }
#basin-tip {
  /* Anchored at the viewport's origin and moved with a transform. It used to be
     moved with left/top, and because pointermove fires continuously the browser
     logged a layout shift for every pixel the cursor travelled across a chart.
     A transform is composited, so it moves without reflowing anything. Without
     left/top the static position also parked it thousands of pixels down a long
     article, which is why the first tip of a page jumped into place. */
  position: fixed; left: 0; top: 0; transform: translate3d(-9999px, -9999px, 0);
  z-index: 999; pointer-events: none; will-change: transform;
  background: var(--ink); color: var(--paper);
  font-family: var(--sans); font-size: 0.76rem; line-height: 1.4;
  padding: 6px 10px; border-radius: 2px; max-width: 260px; opacity: 0; transition: opacity 0.08s;
}
#basin-tip.show { opacity: 0.97; }
#basin-tip .t { font-weight: 700; }

/* notes & panels */
.note {
  background: var(--warn-bg); border: 0; border-left: 3px solid var(--warn-bd); color: var(--warn-ink);
  border-radius: 0; padding: 12px 16px; font-size: 0.92rem; margin: 1.5em auto;
  font-family: var(--sans);
}
.getdata {
  max-width: 700px; margin: 2.4em auto 1em;
  border: 1px solid var(--border); border-left: 4px solid var(--s1);
  background: var(--surface); border-radius: 0; padding: 18px 22px;
}
.getdata h2 { margin: 0 0 6px; font-size: 1.05rem; }
.getdata p { font-size: 0.92rem; color: var(--ink-2); margin: 0 0 10px; }
.getdata .files { font-family: var(--sans); font-size: 0.84rem; }
.getdata .files li { margin-bottom: 0.5em; }
.getdata .files code { background: var(--surface-2); }
.getdata .cta-row { margin-top: 12px; display: flex; gap: 12px; flex-wrap: wrap; }

.btn {
  display: inline-block; font-family: var(--sans); font-weight: 700; font-size: 0.82rem;
  padding: 8px 16px; border-radius: 2px; text-decoration: none; border: 1px solid transparent;
}
.btn-primary { background: var(--s1); color: #fff; }
.btn-primary:hover { background: var(--link); color: #fff; }
.btn-ghost { border-color: var(--border-strong); color: var(--ink-2); }
.btn-ghost:hover { color: var(--ink); border-color: var(--ink); }

/* ---------- keep reading ----------
   This replaced prev/next, which was two lines of serif under a small-caps
   label: no card, no rule, no chevron, nothing that said it could be tapped.
   Each row is one target now, 56px minimum, which is the smallest thing a
   thumb hits reliably, and the whole row is the hit area rather than the
   words. The arrow is the affordance; it is decorative to a screen reader,
   which reads the section heading and the headline instead. */
.readnext {
  max-width: 700px; margin: 2.4em auto 0; padding-top: 16px;
  border-top: 2px solid var(--ink);
}
.readnext h2 {
  margin: 0 0 6px; font-family: var(--sans); font-size: 0.95rem;
  letter-spacing: -0.01em;
}
.rn-card {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: 3px 14px; min-height: 56px; padding: 12px 2px;
  border-bottom: 1px solid var(--border);
  text-decoration: none; color: var(--ink);
}
.rn-card .kicker { grid-column: 1; font-size: 0.64rem; }
.rn-t {
  grid-column: 1; font-family: var(--serif); font-weight: 600;
  font-size: 1.02rem; line-height: 1.3;
}
.rn-go { grid-column: 2; grid-row: 1 / span 2; color: var(--faint); font-size: 1.15rem; }
.rn-card:hover { background: var(--surface-2); }
.rn-card:hover .rn-t { text-decoration: underline; }
.rn-card:hover .rn-go { color: var(--s1-ink); }
.rn-card:focus-visible { outline: 2px solid var(--s1-ink); outline-offset: 2px; }

/* One card partway down the body, because everything else on an article page
   sits below 90% of it and a reader who leaves at 40% never sees any of it. */
.rn-inline {
  max-width: 700px; margin: 2em auto; padding: 13px 16px;
  background: var(--surface-2); border-left: 3px solid var(--s2);
  font-family: var(--sans);
}
.rn-lbl {
  display: block; font-size: 0.64rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 4px;
}
.rn-inline a {
  display: block; font-family: var(--serif); font-weight: 600;
  font-size: 1.02rem; line-height: 1.3; color: var(--ink); text-decoration: none;
}
.rn-inline a:hover { text-decoration: underline; }

/* ---------- the sticky next bar ----------
   Touch widths only. Held one-handed, the reachable arc is the bottom of the
   screen; the masthead nav is behind a dropdown in the far corner and the
   foot of a 16,000px article is never arrived at, so nothing on the page was
   in the arc at any point during a read. This is, and it carries one link. */
.nextbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 900;
  display: none; align-items: center; gap: 8px;
  padding: 8px 10px; padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  background: var(--paper); border-top: 2px solid var(--ink);
  box-shadow: 0 -6px 20px rgba(11, 11, 11, 0.10);
  font-family: var(--sans);
  transform: translateY(110%); visibility: hidden;
  transition: transform 0.18s ease-out, visibility 0s linear 0.18s;
}
.nextbar.show {
  transform: translateY(0); visibility: visible;
  transition: transform 0.18s ease-out, visibility 0s;
}
.nb-link {
  flex: 1 1 auto; min-width: 0; min-height: 44px;
  display: flex; flex-direction: column; justify-content: center;
  text-decoration: none; color: var(--ink);
}
.nb-lbl {
  font-size: 0.6rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); line-height: 1.6;
}
.nb-t {
  font-family: var(--serif); font-weight: 600; font-size: 0.95rem; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.nb-x {
  flex: 0 0 auto; width: 44px; height: 44px; padding: 0;
  border: 0; background: none; color: var(--muted);
  font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.nb-x:hover { color: var(--ink); }
@media (max-width: 760px) { .nextbar:not([hidden]) { display: flex; } }
@media (prefers-reduced-motion: reduce) { .nextbar { transition: none; } }

/* ---------- category pages ---------- */
.cat-head { padding: 40px 0 8px; border-bottom: 2px solid var(--ink); margin-bottom: 8px; }
.cat-head h1 { margin: 8px 0 10px; font-size: clamp(1.8rem, 1.4rem + 1.8vw, 2.6rem); letter-spacing: -0.015em; }
.cat-head p { color: var(--ink-2); max-width: 640px; margin: 0 0 18px; }

/* THE SECTION FRONT. Lead, grid, rail, in the proportions a broadsheet section
   page has always used: the lead takes about a third, the rest of the desk runs
   beside it two abreast, and the desk's own tools stand in the rail. What this
   replaced was a full-width band of product tiles above a single narrow column
   of stories, so every section but Crime opened on four cards and put its own
   lead story below the fold. */
.catgrid {
  display: grid; grid-template-columns: 1.32fr 1.24fr 1fr; gap: 0 26px;
  padding-top: 26px;
}
/* The gutters are ruled, which is what makes three columns of different things
   read as one page. The columns stretch rather than sitting at the top of the
   row so each rule runs the full height of the tallest of them; on start
   alignment they stopped wherever their own column ran out and read as
   underlines on their way down. */
.catgrid-rest, .catgrid-rail { border-left: 1px solid var(--hairline); padding-left: 26px; }
.sec--money { --cat: var(--s1); --cat-ink: var(--s1-ink); --cat-wash: var(--s1-wash); }
.sec--elections { --cat: var(--s2); --cat-ink: var(--s2-ink); --cat-wash: var(--s2-wash); }
.sec--city-hall { --cat: var(--s3); --cat-ink: var(--s3-ink); --cat-wash: var(--s3-wash); }
.sec--crime { --cat: var(--s4); --cat-ink: var(--s4-ink); --cat-wash: var(--s4-wash); }
.sec--places { --cat: var(--s5); --cat-ink: var(--s5-ink); --cat-wash: var(--s5-wash); }

/* THE DESK'S COLOUR, CARRIED THROUGH THE WHOLE PAGE rather than spent on one
   7px dot. It runs on the things that are already structure — the rules, the
   masthead, the rail plate — and never on body copy, so the page reads as
   colour-coded rather than decorated. Everything that holds text uses the -ink
   step, which is measured to clear 4.5:1; the marks themselves do not. */
.sec .cat-head {
  border-bottom: 0; padding-bottom: 0; position: relative;
}
/* The house device from the maps page: a short bar of the desk colour leading
   into the ink rule. Wider here, because on a section front it is the one
   place the desk gets to announce itself. */
.sec .cat-head::after {
  content: ""; display: block; height: 3px; margin-top: 18px;
  background: linear-gradient(90deg, var(--cat) 0 190px, var(--hairline) 190px 100%);
}
/* Media is the one section whose head rule carries three colours rather than
   one, because it is the one section that holds three unlike things. They run
   in the order the columns below them run, so the rule is a key to the page
   rather than decoration. */
.sec--media:not(.sec--videos):not(.sec--games):not(.sec--interactives) .cat-head::after {
  background: linear-gradient(90deg,
    var(--med-vid) 0 110px, var(--med-gam) 110px 220px,
    var(--med-int) 220px 330px, var(--hairline) 330px 100%);
}
.sec .cat-head h1 { color: var(--cat-ink); }
.sec .cat-head .kicker { color: var(--cat-ink); }
.sec .cat-head p { max-width: 640px; }

/* The column rules pick up the desk tint instead of neutral hairline, which is
   what stops the three columns reading as a table someone drew on the page. */
.sec .catgrid-rest, .sec .catgrid-rail { border-left-color: var(--cat-wash); }
.sec .catbig { border-top: 3px solid var(--cat); padding-top: 14px; }

/* The rail is the one full plate of colour on the page. It is the column that
   is not reporting, so it is the one that can carry a tint without putting
   anything behind a wash that a reader is trying to read. */
.sec .catgrid-rail {
  background: linear-gradient(178deg, var(--cat-wash) 0%, transparent 62%);
  padding: 0 18px 18px 22px; border-left-width: 1px;
}
.sec .catrail-sec, .sec .catrail-sub, .sec .ybstrip.ybstrip--rail { border-top-color: var(--cat); }
.sec .catrail-h, .sec .catrail-sub .sub-label { color: var(--cat-ink); }
.sec .catrail-sub .sub-btn { background: var(--cat-ink); border-color: var(--cat-ink); }
.sec .catrail-sub .sub-btn:hover { filter: brightness(0.9); }
.sec .ybstrip--rail .ybstrip-in:focus { border-bottom-color: var(--cat); }
.sec .ybstrip--rail .btn-primary { background: var(--cat-ink); border-color: var(--cat-ink); }

/* The grid cards. A hairline of the desk colour under each date, which ties
   six otherwise loose cards to the page without touching the headlines. */
.sec .catcell .meta, .sec .catbig .meta {
  border-top: 2px solid var(--cat-wash); padding-top: 7px;
}
.sec .catcell:hover .meta, .sec .catbig:hover .meta { border-top-color: var(--cat); }
.sec .cat-more { border-top-color: var(--cat); }
.sec .cat-more .section-head h2 { color: var(--cat-ink); }
.sec .cat-list .story { border-bottom-color: var(--cat-wash); }
.sec .cat-list .kicker { color: var(--cat-ink); }
@media (max-width: 1060px) {
  .sec .catgrid-rail { padding: 0 16px 16px; }
}
@media (max-width: 760px) {
  .sec .cat-head::after { background: linear-gradient(90deg, var(--cat) 0 120px, var(--hairline) 120px 100%); }
}
/* No section kicker on any card here: every story on the page is the same
   desk, and the head above says so once. The colour arrives on hover instead,
   which is where it means something. */
.catgrid h2 a, .catgrid h3 a { color: inherit; text-decoration: none; }
.catgrid h2 a:hover, .catgrid h3 a:hover { color: var(--cat); }
.catgrid .meta { font-family: var(--sans); font-size: 0.73rem; color: var(--muted); margin-top: 7px; }

/* The lead. One picture at the size a lead deserves and a headline that can be
   read from across the room; the only place on a section page a dek runs. */
.catbig { min-width: 0; }
.catbig .thumb {
  display: block; aspect-ratio: 3 / 2; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin-bottom: 14px;
}
.catbig .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.catbig h2 {
  font-family: var(--serif); font-weight: 700; margin: 0;
  font-size: clamp(1.5rem, 1.05rem + 1.5vw, 2.05rem); line-height: 1.13;
  letter-spacing: -0.019em;
}
.catbig .dek { color: var(--ink-2); font-size: 1rem; line-height: 1.55; margin: 11px 0 0; }

/* The rest of the desk, two abreast. Headline and date only: at this width a
   dek turns each cell into a paragraph and the column stops scanning. */
.catgrid-rest {
  display: grid; grid-template-columns: 1fr 1fr; gap: 26px 22px;
  align-content: start; min-width: 0;
}
.catcell { min-width: 0; }
.catcell .thumb {
  display: block; aspect-ratio: 16 / 10; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin-bottom: 10px;
}
.catcell .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.catcell h3 {
  font-family: var(--serif); font-weight: 700; margin: 0;
  font-size: 1.12rem; line-height: 1.21; letter-spacing: -0.013em;
}
/* An odd story would otherwise leave half the last row as paper, which reads
   as a card that failed to render. Spanning it fills the row and gives the
   piece a wider measure, the way a section page runs its last item across. */
.catgrid-rest .catcell:last-child:nth-child(odd) {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: 150px 1fr; gap: 16px; align-items: start;
}
.catgrid-rest .catcell:last-child:nth-child(odd) .thumb { margin-bottom: 0; }
.catgrid-rest .catcell:last-child:nth-child(odd):not(:has(.thumb)) { grid-template-columns: 1fr; }

/* The rail. A vertical rule sets it off rather than a panel: it is the third
   column of the same page, not a box parked beside it. */
.catgrid-rail { min-width: 0; }
.catrail-sec { border-top: 2px solid var(--ink); padding-top: 12px; }
.catrail-h {
  font-family: var(--serif); font-weight: 700; font-size: 1.2rem;
  letter-spacing: -0.014em; margin: 0 0 4px;
}
.railrow {
  display: block; text-decoration: none; color: inherit;
  padding: 13px 0; border-bottom: 1px solid var(--hairline);
}
.railrow:last-child { border-bottom: 0; }
.railrow .kicker { font-size: 0.62rem; margin-bottom: 5px; }
.railrow-t {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: 1.02rem; line-height: 1.22; letter-spacing: -0.012em; color: var(--ink);
}
.railrow:hover .railrow-t { color: var(--cat); }
.railrow-d {
  display: block; margin-top: 5px; color: var(--ink-2);
  font-size: 0.83rem; line-height: 1.45;
  /* Card copy is written for a half-width tile and runs seven lines in a rail.
     Three is enough to say what the thing is; the page it opens says the rest. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* The address box in the rail: one column, no second half to put a field in.
   Doubled class because the section-page rule above it is .ybstrip:not(.ybstrip--home),
   which counts as two classes and was quietly winning the border and the spacing. */
.ybstrip.ybstrip--rail {
  margin: 0 0 26px; padding: 12px 0 0;
  border-top: 2px solid var(--ink); border-bottom: 0;
}
.ybstrip--rail .ybstrip-form { grid-template-columns: 1fr; gap: 14px; }
.ybstrip--rail .ybstrip-copy p { font-size: 0.9rem; margin-top: 7px; }
.ybstrip--rail .ybstrip-in { font-size: 0.95rem; }
.ybstrip--rail .ybstrip-note { font-size: 0.71rem; }
/* The signup, set like the footer's: stacked, because a field and a button
   side by side in a 300px column leaves room for neither. */
.catrail-sub { margin-top: 26px; border-top: 2px solid var(--ink); padding-top: 14px; }
.catrail-sub .sub-form { margin: 0; padding-top: 0; border-top: 0; max-width: none; }
.catrail-sub .sub-label { font-family: var(--serif); font-size: 1.2rem; font-weight: 700;
  letter-spacing: -0.014em; text-transform: none; color: var(--ink); }
.catrail-sub .sub-blurb { font-size: 0.86rem; margin: 6px 0 12px; }
.catrail-sub .sub-row { flex-direction: column; align-items: stretch; gap: 9px; }
.catrail-sub .sub-input { flex: 0 0 auto; }
.catrail-sub .sub-btn { align-self: flex-start; }
.catrail-sub .sub-fine { font-size: 0.7rem; }

/* Anything the grid could not take. */
.cat-more { margin-top: 42px; border-top: 2px solid var(--ink); padding-top: 10px; }
.cat-more .section-head { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }

/* The rail drops under the two story columns first: it is the one column that
   is not reporting, so it is the one that can wait. Below that the grid goes
   to a single column and the page becomes one thread. */
@media (max-width: 1060px) {
  .catgrid { grid-template-columns: 1.15fr 1fr; }
  .catgrid-rail {
    grid-column: 1 / -1; border-left: 0; padding-left: 0;
    margin-top: 34px; padding-top: 6px;
    /* auto-fit rather than a fixed two-up: Politics carries three blocks here
       and a two-up stranded the third under the first with a column of paper
       beside it. */
    display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 0 26px; align-items: start;
  }
  .catgrid-rail > * { margin-top: 26px; }
  .catgrid-rail > .ybstrip.ybstrip--rail { margin-bottom: 0; }
}
@media (max-width: 760px) {
  .catgrid { grid-template-columns: 1fr; gap: 30px; }
  .catgrid-rest { gap: 24px 20px; border-left: 0; padding-left: 0; }
  .catgrid-rail { grid-template-columns: 1fr; }
}
@media (max-width: 460px) {
  .catgrid-rest { grid-template-columns: 1fr; }
  .catgrid-rest .catcell:last-child:nth-child(odd) { grid-template-columns: 1fr; }
  .catgrid-rest .catcell:last-child:nth-child(odd) .thumb { margin-bottom: 10px; }
}

/* ---------- the maps landing page ---------- */
/* The section rule under the head is 2px of flat ink everywhere else on the
   site. Here it carries the three data colours instead, in the order the
   grid below runs them, which is the whole of the colour added to this page
   above the cards. It is a 2px line: the restraint is the point. */
.cat-head--maps {
  border-bottom: 0;
  padding-bottom: 0;
  position: relative;
}
/* Still the house rule: 2px, solid, full width, ink. The only change is a
   short orange lead-in at the head of it, which is the printed-tab device the
   section fronts already use and the one place on this page colour is allowed
   to sit on its own. Four colours were tried here first, one per section, and
   that turned the top of the page into a paintbox and stopped reading as a
   rule at all. */
.cat-head--maps::after {
  content: ""; display: block; height: 2px; margin-top: 20px;
  background: linear-gradient(90deg, var(--s2) 0 84px, var(--ink) 84px 100%);
}
/* The claim runs wider than a standfirst normally would because it is two
   paragraphs of argument rather than a one-line label. */
.cat-head--maps .lede {
  max-width: 720px; font-size: 1.06rem; line-height: 1.62;
  color: var(--ink-2); margin: 0 0 20px;
}
/* The receipt for the sentence above it. Numbers in the serif at display
   size, labels in small caps underneath, on the recessed panel so the strip
   reads as a footing for the head rather than a fourth paragraph. */
.scale-strip {
  display: flex; flex-wrap: wrap; gap: 0; list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--border); border-radius: 2px; overflow: hidden;
  background: var(--surface-2);
}
.scale-strip li {
  flex: 1 1 150px; padding: 12px 18px 13px;
  border-right: 1px solid var(--border);
}
.scale-strip li:last-child { border-right: 0; }
.scale-strip .n {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: 1.5rem; letter-spacing: -0.02em; line-height: 1.1; color: var(--ink);
}
/* One accent, on the first figure only. Colouring all three would make the
   strip the loudest thing on a page whose job is to show maps. */
.scale-strip li:first-child .n { color: var(--s2); }
.scale-strip .l {
  display: block; margin-top: 3px;
  font-family: var(--sans); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
@media (max-width: 560px) {
  .scale-strip li { flex: 1 1 100%; border-right: 0; border-bottom: 1px solid var(--border); }
  .scale-strip li:last-child { border-bottom: 0; }
}
.cat-list .story { padding: 22px 0; border-bottom: 1px solid var(--hairline); max-width: 780px; }

/* ---------- map pages ---------- */
/* The embedded map carries its own headline and standfirst, so the chrome no
   longer prints a second copy above it: two stacked titles pushed the map
   itself most of the way off the first screen. The h1 stays in the document
   for the outline and for assistive technology, just not on screen. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.map-page-head { padding: 20px 0 14px; }
.map-page-head h1 { margin: 6px 0 8px; font-size: clamp(1.5rem, 1.2rem + 1.5vw, 2.2rem); letter-spacing: -0.015em; }
.map-page-head p { color: var(--ink-2); margin: 0; max-width: 820px; }
.map-frame-wrap { border-top: 1px solid var(--hairline); background: var(--paper); }
.map-frame { display: block; width: 100%; height: calc(100vh - 58px); min-height: 560px; border: 0; }
.map-page-foot {
  font-family: var(--sans); font-size: 0.78rem; color: var(--muted);
  padding: 10px 0 26px; display: flex; gap: 18px; flex-wrap: wrap;
}
/* An instrument page is one iframe between the nav and the source line, so its
   category colour has three pixels above the frame and a rule below it to live
   on. Nothing above the frame grows: a second stacked heading here pushed the
   instrument itself off the first screen, which is why there is none. */
.sec--interactives .map-frame-wrap { border-top: 3px solid var(--cat); }
.sec--interactives .map-page-foot { border-top: 1px solid var(--cat-wash); }
.sec--interactives .map-page-foot .medflag {
  display: inline-block; background: var(--cat); color: var(--cat-on);
  text-decoration: none;
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; padding: 3px 8px 2px; margin-right: 10px;
}

/* ---------- data catalog ---------- */
.data-hero { padding: 44px 0 6px; max-width: 780px; }
.data-hero h1 { margin: 8px 0 12px; font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.8rem); letter-spacing: -0.018em; }
.data-hero .lede { font-size: 1.1rem; line-height: 1.6; color: var(--ink-2); }
.pillar { margin: 30px 0 6px; }
.pillar-head { border-bottom: 2px solid var(--ink); padding-bottom: 7px; margin-bottom: 18px; }
.pillar-head h2 { margin: 0; font-size: 1.2rem; }
.ds-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.ds-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 2px;
  padding: 18px 20px; display: flex; flex-direction: column;
}
/* Free tier. Marked with the same orange rule the site uses for an active
   choice, so it reads as a state of the same card rather than a different
   product. No badge, no pill. */
.ds-card--free { border-top: 2px solid var(--s2); }
.ds-dl { font-weight: 600; }
.ds-card h3 { margin: 2px 0 6px; font-size: 1.12rem; letter-spacing: -0.01em; }
.ds-card .blurb { font-size: 0.92rem; color: var(--ink-2); margin: 0 0 12px; line-height: 1.5; flex: 1; }
.ds-card .spec {
  font-family: var(--sans); font-size: 0.74rem; color: var(--muted);
  display: flex; gap: 14px; flex-wrap: wrap; border-top: 1px solid var(--hairline);
  padding-top: 10px; margin-bottom: 12px;
}
.ds-card .spec b { color: var(--ink-2); font-weight: 600; }
.ds-card .links { font-family: var(--sans); font-size: 0.8rem; display: flex; gap: 14px; flex-wrap: wrap; }

/* ---------- about ---------- */
.prose { max-width: 700px; margin: 0 auto; padding: 10px 0 40px; }
.prose h1 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.8rem); letter-spacing: -0.018em; margin: 34px 0 16px; }
.prose h2 { font-size: 1.35rem; margin: 1.8em 0 0.6em; letter-spacing: -0.01em; }
.prose p { margin: 0 0 1.2em; }

/* ---------- footer ---------- */
.site-footer { margin-top: 60px; border-top: 2px solid var(--ink); background: var(--surface); }
.footer-contours { display: block; width: 100%; height: 64px; color: var(--faint); opacity: 0.6; }
.footer-grid {
  display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 28px;
  padding: 8px 0 34px; font-family: var(--sans); font-size: 0.84rem;
}
.footer-grid .fbrand .wordmark { font-family: var(--serif); font-weight: 700; font-size: 1.5rem; color: var(--ink); }
.footer-grid .fbrand p { color: var(--muted); font-size: 0.82rem; max-width: 300px; line-height: 1.55; }
.footer-grid h4 {
  font-size: 0.7rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); margin: 6px 0 10px;
}
.footer-grid ul { list-style: none; margin: 0; padding: 0; }
.footer-grid li { margin-bottom: 7px; }
.footer-grid a { color: var(--ink-2); text-decoration: none; }
.footer-grid a:hover { color: var(--ink); text-decoration: underline; }
.footer-fine {
  border-top: 1px solid var(--hairline); padding: 14px 0 22px;
  font-family: var(--sans); font-size: 0.74rem; color: var(--muted);
  display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}

/* ---------- responsive ---------- */
@media (max-width: 940px) {
  .front-grid { grid-template-columns: 1fr; }
  .story-grid { grid-template-columns: 1fr 1fr; }
  .numstrip { grid-template-columns: repeat(3, 1fr); row-gap: 20px; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
  .sb-grid { grid-template-columns: 1fr; }
  .sb-cand { border-right: 0; border-bottom: 1px solid var(--hairline); }
  .lb-bar { display: none; }
}
@media (max-width: 640px) {
  body { font-size: 16px; }
  .hero-grid { grid-template-columns: 1fr; }
  .story-grid, .story-grid--2 { grid-template-columns: 1fr; }
  .numstrip { grid-template-columns: repeat(2, 1fr); }
  .ds-grid { grid-template-columns: 1fr; }
  .masthead { padding: 26px 0 16px; }
  figure.chart { padding: 14px 14px 10px; }
  .article-body > p:first-of-type::first-letter { font-size: 2.7em; }
}
@media print {
  .mainnav, .topbar, .theme-toggle, .site-footer,
  .readnext, .rn-inline, .nextbar { display: none !important; }
}

/* ---------------------------------------------------------------- subscribe
   Printed-form grammar, per DESIGN_LANGUAGE.md: 2px ink rule above, small-caps
   label, UNDERLINED input (never boxed), square chrome, no shadow anywhere. */
.sub-form {
  max-width: 700px; margin: 2.4em auto 1em; padding-top: 16px;
  border-top: 2px solid var(--ink); font-family: var(--sans);
}
.sub-label {
  display: block; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 8px;
}
.sub-blurb {
  font-family: var(--serif); font-size: 0.95rem; color: var(--ink-2);
  margin: 0 0 14px; line-height: 1.5; max-width: 52ch;
}
.sub-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.sub-input {
  flex: 1 1 240px; min-width: 0; font-family: var(--sans); font-size: 0.92rem;
  color: var(--ink); background: transparent; padding: 6px 2px;
  border: 0; border-bottom: 1px solid var(--border-strong); border-radius: 0;
  appearance: none;
}
.sub-input::placeholder { color: var(--faint); }
.sub-input:focus { outline: 0; border-bottom: 2px solid var(--s2); padding-bottom: 5px; }
.sub-btn { border: 0; cursor: pointer; font-family: var(--sans); line-height: 1.2; }
.sub-btn[disabled] { opacity: 0.65; cursor: default; }
/* The signup posts in the background, so this is the only thing that tells the
   reader it worked. It replaces the row rather than sitting under it. */
.sub-done {
  margin: 4px 0 0; font-family: var(--sans); font-size: 0.92rem; font-weight: 600;
  color: var(--s3-ink); line-height: 1.45;
}
.sub-fine {
  font-size: 0.74rem; color: var(--muted); margin: 12px 0 0; line-height: 1.5;
}
.sub-fine a { color: var(--ink-2); }

/* footer field: same grammar, compact, no top rule (the grid already divides) */
.fsub .sub-form { margin: 0; padding-top: 0; border-top: 0; max-width: none; }
.fsub .sub-blurb { font-size: 0.82rem; margin-bottom: 10px; font-family: var(--sans); }
.fsub .sub-row { flex-direction: column; align-items: stretch; gap: 8px; }
/* .sub-input carries flex-basis 240px for the side-by-side row. Stacking the
   footer field flips the main axis, where a basis is a HEIGHT, so the input
   grew into a 240px block and stretched the whole footer column with it. */
.fsub .sub-input { flex: 0 0 auto; }
.fsub .sub-btn { align-self: flex-start; }
.fsub .sub-fine { font-size: 0.7rem; }

/* modal: a plate that slides up, not a rounded card that floats */
.sub-modal { position: fixed; inset: 0; z-index: 90; }
.sub-modal[hidden] { display: none; }
.sub-modal-backdrop { position: absolute; inset: 0; background: rgba(11, 11, 11, 0.42); }
.sub-modal-panel {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: min(560px, 100%); background: var(--paper);
  border-top: 2px solid var(--ink); border-left: 1px solid var(--border);
  border-right: 1px solid var(--border); border-radius: 0;
  padding: 22px 26px 26px; animation: sub-rise 0.22s ease-out;
}
@keyframes sub-rise { from { transform: translate(-50%, 14px); opacity: 0; }
                        to { transform: translate(-50%, 0); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .sub-modal-panel { animation: none; } }
.sub-modal-panel h2 { font-size: 1.35rem; margin: 6px 0 0; }
.sub-modal-panel .sub-form { border-top: 0; padding-top: 10px; margin: 0; }
.sub-modal-x {
  position: absolute; top: 10px; right: 12px; background: none; border: 0;
  font-size: 1.5rem; line-height: 1; color: var(--muted); cursor: pointer; padding: 4px 6px;
}
.sub-modal-x:hover { color: var(--ink); }
@media (max-width: 720px) {
  .sub-modal-panel { width: 100%; padding: 18px 18px 22px; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

/* ---------------------------------------------------------------- corrections */
.corr {
  display: grid; grid-template-columns: 110px 1fr; gap: 18px;
  padding: 14px 0; border-bottom: 1px solid var(--hairline);
}
.corr:first-of-type { border-top: 2px solid var(--ink); }
.corr-date {
  font-family: var(--sans); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); padding-top: 3px;
}
.corr-head { font-weight: 700; margin-bottom: 4px; }
.corr p { margin: 0; font-size: 0.94rem; color: var(--ink-2); }
.corr-none {
  border-top: 2px solid var(--ink); padding-top: 14px;
  color: var(--muted); font-size: 0.94rem;
}
@media (max-width: 640px) { .corr { grid-template-columns: 1fr; gap: 4px; } }

/* ============================================================
   COUNCIL DISTRICT PAGES
   An atlas plate: ink rule above every block, hairline between
   every cell, figures in the sans, prose in the serif. No card,
   no shadow, no rounded chrome, no pill.
   ============================================================ */

.dhead { border-top: 2px solid var(--ink); padding-top: 16px; margin-top: 26px; }
.dhead h1 {
  font-size: clamp(2rem, 1.4rem + 3vw, 3.2rem); letter-spacing: -0.022em;
  margin: 10px 0 8px; line-height: 1.05;
}
.dmeta, .dhoods {
  font-family: var(--sans); margin: 0; line-height: 1.5;
}
.dmeta {
  font-size: 0.86rem; font-weight: 700; color: var(--ink-2);
  display: flex; align-items: center; flex-wrap: wrap; gap: 0;
}
.dmeta .sep {
  display: inline-block; width: 1px; height: 12px; margin: 0 11px;
  background: var(--border-strong);
}
.dhoods {
  font-size: 0.72rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); margin-top: 7px;
}
.dlede-1 {
  max-width: 62ch; font-size: 1.06rem; color: var(--ink-2); margin: 14px 0 4px;
}

/* the almanac strip */
.dstrip {
  margin: 26px 0 0; border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--hairline); padding: 16px 0 14px;
  display: grid; grid-template-columns: repeat(5, 1fr);
}
.dstrip .cell {
  padding: 0 14px; border-right: 1px solid var(--hairline); min-width: 0;
}
.dstrip .cell:first-child { padding-left: 0; }
.dstrip .cell:last-child { border-right: 0; padding-right: 0; }
.dstrip .v {
  font-family: var(--sans); font-weight: 700; color: var(--ink);
  font-size: clamp(1.35rem, 1rem + 1.4vw, 1.85rem); letter-spacing: -0.018em;
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.dstrip .k {
  margin-top: 5px; font-family: var(--sans); font-size: 0.61rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted); line-height: 1.35;
  min-height: 2.7em;
}
.dstrip .c {
  margin-top: 6px; font-family: var(--sans); font-size: 0.7rem; color: var(--ink-2);
  font-variant-numeric: tabular-nums; display: flex; align-items: center; flex-wrap: wrap;
}
.dstrip .c .sep {
  display: inline-block; width: 1px; height: 9px; margin: 0 8px;
  background: var(--border-strong);
}

/* the opener */
.dlede {
  max-width: 44em; margin: 30px 0 4px; padding-left: 14px;
  border-left: 2px solid var(--s2);
}
.dlede p {
  margin: 0; font-size: clamp(1.1rem, 0.98rem + 0.5vw, 1.32rem); line-height: 1.5;
  color: var(--ink);
}

/* district picker: real permalinks, active carries the orange rule */
.dpicker { margin: 32px 0 6px; border-top: 1px solid var(--hairline); padding-top: 12px; }
.dlab {
  font-family: var(--sans); font-size: 0.61rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 9px;
}
.dps { display: flex; flex-wrap: wrap; gap: 0; }
.dp {
  font-family: var(--sans); font-size: 0.88rem; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink-2); text-decoration: none;
  padding: 5px 0 6px; min-width: 46px; text-align: center;
  border-right: 1px solid var(--hairline); border-bottom: 2px solid transparent;
}
.dp:first-child { min-width: 40px; }
.dp:last-child { border-right: 0; }
.dp:hover { color: var(--ink); background: var(--surface-2); }
.dp--on { color: var(--ink); border-bottom-color: var(--s2); }

/* sections */
.dsec { margin-top: 46px; }
.dsec h2 {
  font-size: 1.4rem; letter-spacing: -0.012em; margin: 0 0 6px; padding-top: 14px;
  border-top: 2px solid var(--ink);
}
.dsec-blurb {
  font-family: var(--sans); font-size: 0.83rem; color: var(--muted); margin: 0 0 4px;
  max-width: 66ch; line-height: 1.5;
}

/* the metric rows */
.dtable { margin-top: 16px; }
/* The value tracks are sized to the widest figure these tables actually carry,
   measured rather than guessed: the district column needs 187px for CD 4's
   $101,792,466,259 and the citywide column 168px for $834,932,956,252, both on
   the 2025 assessment roll. At 124px they overlapped the column beside them.
   Fixed widths rather than max-content because every table on the page shares
   one geometry, which is what makes the cell hairlines run as continuous
   vertical rules down the plate. Re-measure before adding a wider measure: a
   figure too long for its track overlaps its neighbor, and that is deliberate,
   since a number silently clipped to a plausible shorter number is worse. */
.drow {
  display: grid; grid-template-columns: minmax(0, 1fr) 200px 180px 116px;
  border-top: 1px solid var(--hairline); align-items: start;
}
.drow--head {
  border-top: 1px solid var(--border-strong); border-bottom: 1px solid var(--hairline);
}
.drow.drow--head .l, .drow.drow--head .n {
  font-family: var(--sans); font-size: 0.61rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--muted); padding-top: 7px; padding-bottom: 7px;
  white-space: normal; line-height: 1.35;
}
.drow .l { padding: 12px 18px 12px 0; min-width: 0; }
.drow .lab {
  font-family: var(--sans); font-size: 0.94rem; font-weight: 700; color: var(--ink);
  line-height: 1.3;
}
.drow .den {
  font-family: var(--sans); font-size: 0.73rem; color: var(--muted); line-height: 1.45;
  margin-top: 4px; max-width: 58ch;
}
.drow .den b { color: var(--ink-2); }
.drow .n {
  font-family: var(--sans); font-weight: 700; font-size: 1.02rem; color: var(--ink-2);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
  padding: 12px 14px; border-left: 1px solid var(--hairline); min-width: 0;
}
.drow .n--me { color: var(--s1); font-size: 1.16rem; }
.drow .nlab {
  display: block; font-size: 0.61rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--muted); margin-top: 3px;
}
.drow.drow--head .n { border-left: 1px solid var(--hairline); text-align: right; }

/* caveat: tinted ground and a 3px left rule, never a pill */
.dcav {
  background: var(--warn-bg); border-left: 3px solid var(--warn-bd); color: var(--warn-ink);
  font-family: var(--sans); font-size: 0.78rem; line-height: 1.5;
  padding: 9px 14px; margin: 0;
}
.dcav b { font-weight: 700; }
.dcav--alt {
  background: var(--s1-wash); border-left-color: var(--s1); color: var(--ink-2);
}
.dsrc {
  border-top: 1px solid var(--hairline); margin-top: 0; padding-top: 9px;
}
.dsrc p, .dsrc-inline {
  font-family: var(--sans); font-size: 0.7rem; color: var(--muted); line-height: 1.45;
  margin: 0 0 5px;
}

/* the June council contest */
.dracewrap { margin-top: 26px; border-top: 1px solid var(--border-strong); padding-top: 12px; }
.drace {
  display: grid; grid-template-columns: 1fr 220px 58px; align-items: center;
  gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--hairline);
}
.drace .rn { font-family: var(--sans); font-size: 0.88rem; color: var(--ink); }
.drace .rb { background: var(--surface-2); height: 13px; }
.drace .rb span { display: block; height: 13px; background: var(--s1); }
.drace .rv {
  font-family: var(--sans); font-size: 0.85rem; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.dracewrap .dsrc-inline { margin-top: 10px; }

/* method */
.dmethod { max-width: 68ch; margin-top: 14px; }
.dmethod p { font-size: 0.95rem; color: var(--ink-2); margin: 0 0 1em; }

/* ---------- the council-district grid ---------- */
/* Fifteen tiles, three across. Each is a photograph of the place, who holds
   it, and the five headline figures -- the same five the strip on a district
   page opens with, so the grid and the page cannot tell different stories.
   The four desk colours cycle through it; on a three-wide grid a four-colour
   cycle lands them on diagonals, so no row and no column ever repeats one. */
.dhead--grid { border-top: 0; padding-top: 0; margin-top: 22px; }
.dhead--grid::after {
  content: ""; display: block; height: 3px; margin-top: 16px;
  background: linear-gradient(90deg,
    var(--s1) 0 25%, var(--s2) 25% 50%, var(--s3) 50% 75%, var(--s4) 75% 100%);
}
.dgrid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px;
  margin-top: 22px;
}
.dtile {
  --cat: var(--s1);
  display: flex; flex-direction: column; min-width: 0;
  text-decoration: none; color: inherit;
  background: var(--surface);
  border: 1px solid var(--border); border-top: 3px solid var(--cat);
  transition: border-color .16s ease, transform .16s ease;
}
.dtile--c0 { --cat: var(--s1); --cat-ink: var(--s1-ink); --cat-wash: var(--s1-wash); }
.dtile--c1 { --cat: var(--s2); --cat-ink: var(--s2-ink); --cat-wash: var(--s2-wash); }
.dtile--c2 { --cat: var(--s3); --cat-ink: var(--s3-ink); --cat-wash: var(--s3-wash); }
.dtile--c3 { --cat: var(--s4); --cat-ink: var(--s4-ink); --cat-wash: var(--s4-wash); }
.dtile:hover { border-color: var(--cat); }
.dtile:hover .dtile-name { color: var(--cat-ink); }

.dtile-shot {
  display: block; position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  background: var(--surface-2);
}
.dtile-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* No photograph yet. A numbered plate in the tile's own colour, which is a
   design and not a gap: the grid reads the same on the day fifteen pictures
   arrive as it does today with none. */
.dtile-shot--bare {
  background:
    linear-gradient(152deg, var(--cat-wash) 0%, transparent 78%),
    var(--cat-wash), var(--surface-2);
}
/* The plate is ruled like the rest of the site rather than left blank: two
   hairlines of the tile's own colour, and the number set at plate size. */
.dtile-shot--bare::after {
  content: ""; position: absolute; left: 15px; right: 15px; bottom: 14px;
  border-bottom: 2px solid var(--cat); opacity: .5;
}
.dtile-shot--bare .dtile-num {
  inset: 0; width: auto; height: auto; display: flex;
  align-items: center; justify-content: center;
  background: transparent; color: var(--cat-ink); opacity: .42;
  font-size: 5.4rem; letter-spacing: -0.04em; border: 0;
}
/* Over a photograph the number is a medallion in the corner, the way a plate
   in an atlas is numbered. */
.dtile-num {
  position: absolute; left: 0; bottom: 0;
  min-width: 44px; height: 34px; padding: 0 11px;
  display: flex; align-items: center; justify-content: center;
  background: var(--cat); color: #fff;
  font-family: var(--serif); font-weight: 700; font-size: 1.18rem;
  letter-spacing: -0.01em; line-height: 1;
}

.dtile-id { display: block; padding: 13px 15px 12px; }
.dtile-name {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: 1.12rem; letter-spacing: -0.014em; line-height: 1.2;
  transition: color .16s ease;
}
.dtile-mem {
  display: block; font-family: var(--sans); font-size: 0.8rem; font-weight: 600;
  color: var(--ink-2); margin-top: 4px;
}
.dtile-hoods {
  display: block; font-family: var(--sans); font-size: 0.72rem;
  color: var(--muted); margin-top: 3px; line-height: 1.35;
  /* Two lines, so a district with three long neighbourhood names cannot make
     its tile taller than the two beside it. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* The five figures. Equal fifths divided by hairlines, sitting on a wash of
   the tile's colour, and pushed to the bottom of the tile with margin-top:auto
   so all three in a row line up however tall the names above them run. */
.dtile-stats {
  display: grid; grid-template-columns: repeat(5, 1fr);
  margin-top: auto; border-top: 1px solid var(--border);
  background: var(--cat-wash);
}
.dcell {
  display: block; padding: 9px 3px 10px; text-align: center; min-width: 0;
  border-right: 1px solid var(--border);
}
.dcell:last-child { border-right: 0; }
.dcell .v {
  display: block; font-family: var(--sans); font-weight: 700; font-size: 0.94rem;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: var(--ink);
  line-height: 1.1;
}
.dcell .k {
  display: block; font-family: var(--sans); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-2);
  margin-top: 5px; line-height: 1.25;
  /* A wrapped label would make one cell taller than the four beside it and
     take the whole row of ranks out of line. The fix is to reserve the second
     line rather than to forbid it: clipping AFFORDABLE to AFFORDA... keeps the
     row tidy by making the label useless, which is the wrong trade. */
  min-height: 1.5rem;
  /* Belt and braces: no future label can spill out of the tile. */
  overflow-wrap: anywhere;
}
.dcell .u {
  display: block; font-family: var(--sans); font-size: 0.55rem;
  color: var(--muted); line-height: 1.25; min-height: 1.4rem; overflow-wrap: anywhere;
}
.dcell .r {
  display: block; font-family: var(--sans); font-size: 0.58rem; font-weight: 700;
  color: var(--cat-ink); margin-top: 4px;
}

/* The city, above the grid: the scale every tile is read against. */
/* THE CITY LINE IS A SHADED BAND, built like the nav strip in the header: one
   flat fill edge to edge of the bar, a hairline top and bottom, and nothing
   else. It is the scale all fifteen tiles are read against, so it is the one
   band above the grid that carries a fill.
   The fill is the brandmark orange rather than one of the four desk hues,
   because the desk colours cycle through the tiles below and an orange row
   among them would read as belonging to a desk rather than to the paper.
   It is a flat colour, not a gradient: a gradient faded out by the third cell
   and left half the bar on plain paper, which read as a tint that had failed
   rather than as a band. */
.dcity {
  display: grid; grid-template-columns: minmax(0, 1fr) 2fr; gap: 0 20px;
  align-items: center; margin-top: 24px;
  border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
  background: var(--s2-band);
  padding: 6px 20px 8px;
}
.dcity-l {
  display: flex; align-items: center; gap: 13px;
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.5rem, 1.15rem + 1.1vw, 2rem);
  line-height: 1.1; letter-spacing: -0.02em; color: var(--ink);
}
/* The masthead brandmark, at the size of the word beside it. It inherits ink
   through currentColor the way it does in the masthead, so the one orange cell
   is the only colour it brings onto a band that is already orange. */
.dcity-mark { flex: none; display: block; }
.dcity-mark svg {
  display: block; width: 1.05em; height: 1.05em; color: var(--ink);
}
.dtile-stats--city { background: transparent; border-top: 0; margin-top: 0; }
.dtile-stats--city .dcell { padding: 10px 3px 11px; border-right-color: var(--border); }
.dtile-stats--city .dcell .v { font-size: 1.16rem; }
.dtile-stats--city .dcell .k { color: var(--ink-2); }
/* --muted carries 2.98:1 on paper and drops to 2.69:1 on the band. It is a
   deliberate de-emphasis everywhere else on the site and stays that way; on a
   fill this warm the unit line takes the next step up instead. */
.dtile-stats--city .dcell .u { color: var(--ink-2); }

.dgrid-credit {
  font-family: var(--sans); font-size: 0.7rem; color: var(--muted);
  margin: 14px 0 0;
}

@media (max-width: 1000px) {
  .dgrid { grid-template-columns: repeat(2, 1fr); gap: 18px; }
  .dcity { grid-template-columns: 1fr; }
  .dcity-l { padding-bottom: 6px; }
}
@media (max-width: 620px) {
  .dgrid { grid-template-columns: 1fr; }
  .dtile-shot { aspect-ratio: 2 / 1; }
}
/* Under about 380px of tile the five cells stop holding a number and a label
   side by side, so they go to a 3+2 block instead of shrinking further. */
@media (max-width: 460px) {
  .dtile-stats, .dtile-stats--city { grid-template-columns: repeat(3, 1fr); }
  .dcell:nth-child(3) { border-right: 0; }
  .dcell:nth-child(4), .dcell:nth-child(5) { border-top: 1px solid var(--border); }
}

.dmethod--index { border-top: 1px solid var(--hairline); padding-top: 14px; margin-top: 26px; }

@media (max-width: 900px) {
  .dstrip { grid-template-columns: repeat(2, 1fr); row-gap: 16px; }
  .dstrip .cell:nth-child(2n) { border-right: 0; padding-right: 0; }
  .dstrip .cell:nth-child(2n+1) { padding-left: 0; }
  .drow { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .drow .n { white-space: normal; font-size: 0.95rem; }
  .drow.drow--head .n { font-size: 0.58rem; letter-spacing: 0.09em; }
  .drow .l, .drow--head .l { grid-column: 1 / -1; padding-right: 0; }
  .drow.drow--head .l { padding-bottom: 0; }
  .drow .n:first-of-type { border-left: 0; }
  .drow .n { text-align: left; padding-left: 0; padding-right: 12px; border-left: 0; }
  .drow .n + .n { border-left: 1px solid var(--hairline); padding-left: 12px; }
  .drace { grid-template-columns: 1fr 58px; }
  .drace .rb { display: none; }
}

/* Three value columns cannot hold a fourteen-digit figure on a phone. At 375
   the citywide assessed value wants about 134px against the 89px a third of the
   row gives it, so the two money columns ran into each other. Shrinking the
   type far enough would take it below the smallest size on the scale, and
   letting the number wrap would break it across lines, which is worse on a
   figure than either. So below this width the rank moves up beside the measure
   name and the two values take half the row each. The measure tables only:
   the index table carries short values and drops its later columns instead. */
@media (max-width: 560px) {
  .dtable:not(.dtable--index) .drow { grid-template-columns: 1fr 1fr; }
  /* The label keeps the full width so the description is not squeezed into
     half a phone screen. The rank sits in the top right of the same row, and
     only the measure name is inset to clear it. */
  .dtable:not(.dtable--index) .drow .l { grid-column: 1 / -1; grid-row: 1; }
  .dtable:not(.dtable--index) .drow .lab { padding-right: 88px; }
  .dtable:not(.dtable--index) .drow .n:nth-child(2) { grid-column: 1 / 2; grid-row: 2; }
  .dtable:not(.dtable--index) .drow .n:nth-child(3) { grid-column: 2 / 3; grid-row: 2; }
  .dtable:not(.dtable--index) .drow .n:nth-child(4) {
    grid-column: 2 / 3; grid-row: 1; justify-self: end; align-self: start;
    text-align: right; border-left: 0; padding-left: 0; padding-right: 0;
  }
}

/* Half a row on the smallest phones still in use is about 135px, and the
   citywide assessed value wants 157px of it at the size above. One step down
   on the value type is enough and is the last width at which these tables
   fit at all. */
@media (max-width: 360px) {
  .dtable:not(.dtable--index) .drow .n { font-size: 0.78rem; }
}

/* ---------- the neighborhood grid ----------

   The same tile as the council-district grid with two differences, both
   forced by there being 114 of these rather than 15.

   The picture is a locator map instead of a photograph. A photograph of Palms
   does not tell a reader where Palms is, and 114 licensed pictures is a
   different project from 15. The locator is generated from the same boundary
   file the panel is built from, so it cannot disagree with the page.

   The grid runs four across rather than three. A locator carries no detail to
   lose at a smaller size, unlike a photograph, and 114 tiles three across is
   thirty-eight rows of scrolling. */
.dgrid--nb { grid-template-columns: repeat(4, 1fr); gap: 16px; }
.dtile--nb { --cat: var(--s1); --cat-ink: var(--s1-ink); --cat-wash: var(--s1-wash); }
/* A map is not a photograph and must not be cropped to fill the frame: `cover`
   would cut the ends off the city and put the neighborhood somewhere it is
   not. */
.dtile-shot--map { aspect-ratio: 7 / 5; background: var(--surface); }
.dtile-shot--map img { object-fit: contain; padding: 4px; }
/* Four figures on a neighborhood tile, not the district tile's five. */
.dtile--nb .dtile-stats { grid-template-columns: repeat(4, 1fr); }
.dtile--nb .dtile-mem { font-variant-numeric: tabular-nums; }

/* The locator ships as two files rather than one with a media query inside it,
   because an SVG loaded through <img> is its own document and cannot see the
   theme toggle on this one. Same pattern as every other two-tone graphic here. */
.dhead-map .nbmap-dark, .dtile-shot--map .nbmap-dark { display: none; }
:root[data-theme="dark"] .dhead-map .nbmap-light,
:root[data-theme="dark"] .dtile-shot--map .nbmap-light { display: none; }
:root[data-theme="dark"] .dhead-map .nbmap-dark,
:root[data-theme="dark"] .dtile-shot--map .nbmap-dark { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .dhead-map .nbmap-light,
  :root:not([data-theme="light"]) .dtile-shot--map .nbmap-light { display: none; }
  :root:not([data-theme="light"]) .dhead-map .nbmap-dark,
  :root:not([data-theme="light"]) .dtile-shot--map .nbmap-dark { display: block; }
}

/* A withheld figure is not a missing one, and must not read as a data error.
   It is set in the muted tone rather than the ink one and keeps the column's
   tabular alignment so the table does not jump around it. */
.dna { color: var(--muted); font-style: italic; }
.dback {
  font-family: var(--sans); font-size: 0.82rem; margin: 16px 0 0;
}
.dback .sep { margin: 0 8px; color: var(--faint); }

/* The page's own locator sits beside the title rather than above it: the name
   and the shape answer the same question and belong on one line of sight. */
.dhead--nb { display: grid; grid-template-columns: 1fr 220px; gap: 24px; align-items: center; }
.dhead--nb .dhead-t { min-width: 0; }
.dhead-map { border: 1px solid var(--border); background: var(--surface); padding: 6px; }
.dhead-map img { width: 100%; height: auto; }
.dhead-map .nbmap-light, .dtile-shot--map .nbmap-light { display: block; }
@media (max-width: 720px) {
  .dhead--nb { grid-template-columns: 1fr; }
  .dhead-map { max-width: 220px; }
}

/* The grid's filter. Sized and set like the rest of the furniture rather than
   like a search engine: it narrows a list that is already on the page. */
.nbfilter { margin: 20px 0 0; }
.nbfilter-l {
  display: block; font-family: var(--sans); font-size: 0.61rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted);
  margin-bottom: 7px;
}
.nbfilter-i {
  width: 100%; max-width: 420px; box-sizing: border-box;
  font-family: var(--sans); font-size: 0.95rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border-strong);
  padding: 9px 12px; border-radius: 0;
}
.nbfilter-i:focus-visible { outline: 2px solid var(--s1); outline-offset: 1px; }
/* [hidden] is display:none in the UA sheet, which loses to .dtile's own
   display:flex. The filter sets the attribute; this is what makes it bite. */
.dtile--nb[hidden] { display: none; }
.nbfilter-c {
  font-family: var(--sans); font-size: 0.78rem; color: var(--ink-2);
  margin: 8px 0 0; font-variant-numeric: tabular-nums;
}

@media (max-width: 1100px) { .dgrid--nb { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 820px)  { .dgrid--nb { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .dgrid--nb { grid-template-columns: 1fr; } }

/* ---------- media: the film desk ----------
   Media is the one section that is not a news desk, so it does not get a fifth
   hue. It runs on the two house colours doing the two jobs they already do
   everywhere else: the blue is structure — rules, frames, the slate — and the
   orange is the only thing on the page a reader can act on, which is play.
   That is the same division as a chart, where --s1 is the subject and --s2 is
   the opposed quantity, and it keeps the palette at four validated desk hues
   instead of five.

   The hard problem here is that these films are not one shape. They run from
   1920x1080 through 1080x1116 to 1600x2338 portrait, and a grid that assumes
   16:9 either letterboxes a portrait film into a slot or crops the ticker off
   the top of it. So: the grid crops deliberately to a common window and prints
   the true dimensions underneath, and the lead and the film pages carry each
   film at its own ratio, uncropped. --ar is set per film. */
.sec--media { --cat: var(--lvl-4); --cat-ink: var(--lvl-4-ink); --cat-wash: var(--lvl-4-wash);
              --cat-band: var(--med-vid-band); --cat-on: var(--med-vid-on); }
/* The three sub-desks. Each binds the same four names the news desks bind, so
   every component below — the column head, the tile, the thumbnail fill, the
   view-all bar — reads --cat and cannot be told which of the three it is in.
   That is what keeps a game page, the games column on the front and the games
   index from drifting to three different golds. */
.sec--videos { --cat: var(--med-vid); --cat-ink: var(--med-vid-ink); --cat-wash: var(--med-vid-wash);
               --cat-band: var(--med-vid-band); --cat-on: var(--med-vid-on); }
.sec--games { --cat: var(--med-gam); --cat-ink: var(--med-gam-ink); --cat-wash: var(--med-gam-wash);
              --cat-band: var(--med-gam-band); --cat-on: var(--med-gam-on); }
.sec--interactives { --cat: var(--med-int); --cat-ink: var(--med-int-ink); --cat-wash: var(--med-int-wash);
                     --cat-band: var(--med-int-band); --cat-on: var(--med-int-on); }
/* Every kicker on the section is the film desk's own, not the news desk the
   subject happens to belong to. A grid of cards carrying blue, crimson and
   green dots reads as four sections on one page; the label still says what the
   film is about, which is the part that was doing the work. */
.sec--media .kicker { color: var(--cat-ink); }
.sec--media .kicker .dot { background: var(--cat); }

/* THE PLATE. The same double rule the maps sit in — an outer hairline, a
   margin of surface, an inner hairline — so a film reads as the same kind of
   object as a map rather than as an embed someone dropped on the page. */
.filmplate {
  border: 1px solid var(--border-strong); background: var(--surface);
  padding: 7px; border-radius: 0;
}
.filmstage {
  position: relative; aspect-ratio: var(--ar, 16 / 9);
  border: 1px solid var(--hairline); background: var(--surface-2);
  overflow: hidden;
}
.filmstage > video, .filmstage > img {
  display: block; width: 100%; height: 100%; object-fit: contain; background: #000;
}
/* THE SLATE. A film can's label: what it is on the left, how long it runs on
   the right, ruled off from the picture. Mono for the timecode because it is a
   measurement, and tabular so 0:54 and 2:48 line up down a column of cards. */
.filmslate {
  display: flex; align-items: baseline; gap: 14px;
  padding: 0 1px 8px; border-bottom: 2px solid var(--cat, var(--lvl-4)); margin-bottom: 7px;
}
.filmslate .what {
  font-family: var(--sans); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--cat-ink, var(--lvl-4-ink));
}
.filmslate .tc {
  font-family: var(--mono); font-size: 0.72rem; font-variant-numeric: tabular-nums;
  color: var(--muted); white-space: nowrap;
}

/* THE PLAY MARK. Square, ruled, orange — not a circle and not a pill. It is
   the one control on the page, so it is the one orange thing on the page. */
.playmark {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgba(11, 11, 11, 0.28); border: 0; cursor: pointer; padding: 0;
  transition: background 0.16s ease;
}
.playmark i {
  display: grid; place-items: center; width: 66px; height: 66px;
  border: 2px solid var(--cat-ink); background: rgba(249, 249, 247, 0.92);
  transition: background 0.16s ease;
}
.playmark i::after {
  content: ""; width: 0; height: 0; margin-left: 5px;
  border-left: 19px solid var(--cat-ink);
  border-top: 12px solid transparent; border-bottom: 12px solid transparent;
  transition: border-left-color 0.16s ease;
}
.playmark:hover { background: rgba(11, 11, 11, 0.14); }
.playmark:hover i { background: var(--cat-ink); }
.playmark:hover i::after { border-left-color: var(--paper); }
.playmark:focus-visible { outline: 2px solid var(--cat-ink); outline-offset: -4px; }
@media (max-width: 620px) { .playmark i { width: 52px; height: 52px; }
  .playmark i::after { border-left-width: 15px; border-top-width: 10px; border-bottom-width: 10px; } }

/* The runtime, printed into the corner of the picture the way a contact sheet
   carries a frame number. Square, ruled, no shadow. */
.filmstage .tcmark {
  position: absolute; left: 10px; bottom: 10px;
  font-family: var(--mono); font-size: 0.68rem; font-variant-numeric: tabular-nums;
  background: var(--ink); color: var(--paper); opacity: 0.9;
  padding: 3px 7px; border-radius: 2px; letter-spacing: 0.02em;
}

/* NOT YET LIVE. An honest state rather than a dead play button: the poster
   sits back, and the slate says what is actually happening. */
.filmstage--soon > img { opacity: 0.52; filter: saturate(0.6); }
.filmstage--soon .holdmark {
  position: absolute; inset: 0; display: grid; place-items: center; padding: 20px;
}
.filmstage--soon .holdmark span {
  font-family: var(--sans); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; text-align: center;
  color: var(--paper); background: var(--ink); opacity: 0.94;
  padding: 7px 12px; border-radius: 2px; border-left: 3px solid var(--cat);
}

/* THE FILM PAGE. The picture first at its own ratio, then the reporting under
   it in a measure that can actually be read. */
.filmpage { max-width: 1100px; margin: 0 auto; }
/* Sized from the film's own ratio against the height of the screen, so a
   portrait film comes out portrait-sized instead of a metre tall, and a 16:9
   film still fills the measure. --arn is the ratio as a number, set per film. */
.filmpage .filmplate {
  margin: 18px auto 0;
  max-width: calc((100vh - 230px) * var(--arn, 1.78));
  min-width: min(300px, 100%);
}
/* The two layouts. A wide film runs head-over-picture down the middle; a tall
   one sits beside its own headline, which is the only way the shape and the
   measure both get what they need. */
.filmpage-pic { max-width: calc((100vh - 230px) * var(--arn, 1.78)); margin: 0 auto; min-width: min(300px, 100%); }
.filmpage-pic .filmplate { max-width: none; }
.filmpage-top--tall {
  display: grid; grid-template-columns: minmax(0, auto) minmax(330px, 1fr);
  gap: 34px; align-items: start; padding-top: 26px;
}
.filmpage-top--tall .filmpage-pic { margin: 0; }
.filmpage-top--tall .filmpage-pic .filmplate { margin-top: 0; }
.filmpage-intro { border-top: 3px solid var(--cat-ink); padding-top: 14px; min-width: 0; }
.filmpage-intro h1 {
  margin: 7px 0 10px; font-size: clamp(1.7rem, 1.25rem + 1.7vw, 2.4rem); letter-spacing: -0.018em;
}
.filmpage-intro .dek { color: var(--ink-2); font-size: 1.04rem; line-height: 1.55; margin: 0 0 16px; }
@media (max-width: 900px) {
  .filmpage-top--tall { grid-template-columns: 1fr; gap: 20px; }
  .filmpage-top--tall .filmpage-pic { margin: 0 auto; }
}
.filmpage-head { padding: 34px 0 0; }
.filmpage-head h1 {
  margin: 8px 0 10px; font-size: clamp(1.8rem, 1.35rem + 1.9vw, 2.6rem); letter-spacing: -0.018em;
}
.filmpage-head .dek { color: var(--ink-2); font-size: 1.1rem; line-height: 1.55; max-width: 68ch; margin: 0; }
.filmpage-body { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 34px; margin: 26px 0 8px; }
.filmpage-note { border-top: 2px solid var(--ink); padding-top: 12px; }
.filmpage-note > p:first-child { margin-top: 0; }
.filmpage-rail { border-top: 3px solid var(--cat); padding-top: 12px; background: linear-gradient(178deg, var(--cat-wash) 0%, transparent 68%); padding-left: 15px; padding-right: 15px; padding-bottom: 16px; }
.filmpage-rail h2 {
  font-family: var(--sans); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--cat-ink); margin: 0 0 9px;
}
/* THE COMPANION ARTICLE, sitting at the head of the rail. It carries the
   article's own desk colour rather than the film pink around it, because a
   card that matches its surroundings reads as more of the same page and the
   whole job of this one is to lead somewhere else. It sits above the rail
   heading on purpose: a reader who has just finished the film is deciding what
   to do next right here, and a line in a list below the fold is not an answer
   to that. */
.filmread {
  display: block; text-decoration: none; color: var(--ink);
  background: var(--cat-wash); padding: 14px 15px 17px; margin: 0 0 20px;
  border-radius: 2px;
}
.filmread--money { --cat-wash: var(--s1-wash); --cat-ink: var(--s1-ink-wash); }
.filmread--elections { --cat-wash: var(--s2-wash); --cat-ink: var(--s2-ink-wash); }
.filmread--city-hall { --cat-wash: var(--s3-wash); --cat-ink: var(--s3-ink-wash); }
.filmread--crime { --cat-wash: var(--s4-wash); --cat-ink: var(--s4-ink-wash); }
.filmread--places { --cat-wash: var(--s5-wash); --cat-ink: var(--s5-ink-wash); }
.filmread-flag {
  display: block; font-family: var(--sans); font-size: 0.63rem; font-weight: 700;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--cat-ink);
  margin-bottom: 10px;
}
.filmread-shot {
  display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 2px;
  background: var(--surface-2); margin-bottom: 11px;
}
.filmread-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.filmread-h {
  display: block; font-family: var(--serif); font-size: 1.06rem; font-weight: 700;
  line-height: 1.22; letter-spacing: -0.012em;
}
.filmread-meta {
  display: block; font-family: var(--sans); font-size: 0.72rem;
  color: var(--ink-2); margin-top: 7px;
}
/* The cue is set as a button would be, without being one: a rule under a line
   of sans, which is the same move the section heads make. */
.filmread-cue {
  display: inline-block; margin-top: 12px; padding-bottom: 3px;
  font-family: var(--sans); font-size: 0.79rem; font-weight: 700;
  color: var(--cat-ink); border-bottom: 2px solid var(--cat-ink);
}
.filmread:hover .filmread-h { color: var(--cat-ink); }
.filmread:hover .filmread-shot { opacity: 0.92; }

.filmpage-rail ul { list-style: none; margin: 0; padding: 0; }
.filmpage-rail li { padding: 7px 0; border-bottom: 1px solid var(--hairline); font-size: 0.9rem; }
.filmpage-rail li:last-child { border-bottom: 0; }
.filmfoot {
  font-family: var(--sans); font-size: 0.78rem; color: var(--muted);
  border-top: 1px solid var(--hairline); padding: 12px 0 30px; margin-top: 10px;
  display: flex; gap: 18px; flex-wrap: wrap; justify-content: space-between;
}
@media (max-width: 900px) { .filmpage-body { grid-template-columns: 1fr; gap: 22px; } }

/* ---------- media index: three columns ----------
   The section holds three unlike things — films, games and instruments — and
   they are set as three ruled columns rather than one grid of tiles. Each
   column is a colour now rather than three shades of the same pink: the column
   binds its own --cat and every part of it below reads that name, so the whole
   thing turns one colour by changing one line. The columns collapse to two and
   then to one, in that order, because the middle breakpoint still has room for
   a pair.

   The front shows four of each and then hands the reader the way to the rest,
   which is what the view-all bar at the foot is. A column that printed all
   nine interactives ran three screens past the games column beside it and made
   the page a scroll rather than a front. */
.medcols {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px;
  margin-top: 34px;
}
.medcol--videos { --cat: var(--med-vid); --cat-ink: var(--med-vid-ink); --cat-wash: var(--med-vid-wash);
                  --cat-band: var(--med-vid-band); --cat-on: var(--med-vid-on); }
.medcol--games { --cat: var(--med-gam); --cat-ink: var(--med-gam-ink); --cat-wash: var(--med-gam-wash);
                 --cat-band: var(--med-gam-band); --cat-on: var(--med-gam-on); }
.medcol--interactives { --cat: var(--med-int); --cat-ink: var(--med-int-ink); --cat-wash: var(--med-int-wash);
                        --cat-band: var(--med-int-band); --cat-on: var(--med-int-on); }
/* The colour carries real area here rather than a rule and a dot: a tinted
   field down the whole column, a solid flag at its head and a solid bar at its
   foot. Still no shadow, no radius and no floating card — the field is
   delimited by a 4px rule of its own colour on top, which is the same printed
   tab device the section fronts use, only wider. */
.medcol {
  background: var(--cat-wash); padding: 0 14px 14px;
  display: flex; flex-direction: column;
}
/* The heading is set INTO the colour rather than beside it: a solid band of
   the category's hue across the head of the column with the label and the line
   under it reversed out in --cat-on, which is measured per hue and comes out
   ink on two of the three and paper on the violet. It bleeds to the column
   edges through the padding, so the colour reaches the rules rather than
   floating inside them. */
.medcol-head {
  margin: 0 -14px 2px; padding: 12px 14px 13px;
  background: var(--cat); color: var(--cat-on);
}
.medcol-head h2 {
  margin: 0; font-family: var(--sans); font-size: 0.8rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: inherit;
}
.medcol-head p {
  font-family: var(--sans); font-size: 0.74rem; line-height: 1.45;
  color: inherit; margin: 7px 0 0;
}
.medlist { list-style: none; margin: 0; padding: 0; flex: 1 1 auto; }
.medlist li { border-bottom: 1px solid var(--cat-wash); }
.medlist li:last-child { border-bottom: 0; }
/* Picture first, then the words. The row is one target rather than a headline
   with a picture beside it, because a thumbnail nobody can click is furniture. */
.medlist a {
  display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 12px;
  align-items: start; padding: 12px 0; text-decoration: none; color: inherit;
}
/* The same plate the films and the maps sit in: a hairline, a margin of
   surface, and the picture inside it. Not a rounded tile and not a shadow. */
.medthumb {
  display: block; position: relative; aspect-ratio: 5 / 3; overflow: hidden;
  border: 1px solid var(--cat); background: var(--surface);
}
.medthumb img, .medthumb svg { width: 100%; height: 100%; }
.medthumb svg { display: block; }
/* No display on the img, deliberately. The themed pair is switched by
   img.theme-dark-only { display: none } a few hundred lines up, and that rule
   carries the same specificity as this one — so setting display here, later in
   the file, silently won and both the light and the dark tile were served on
   every page. vertical-align kills the inline baseline gap that leaves. */
.medthumb img { object-fit: cover; object-position: 50% 50%; vertical-align: top; }
/* The games draw their own, out of their own datasets, so the marks follow the
   theme instead of shipping as two baked pictures. */
.medthumb svg { object-fit: contain; padding: 6px; box-sizing: border-box; }
.thumb-ink { fill: var(--cat); }
.thumb-ink-2 { fill: var(--cat); opacity: 0.42; }
.thumb-dim { fill: var(--de-emph); }
.thumb-rule { stroke: var(--cat-ink); stroke-width: 3; }
.thumb-hair { stroke: var(--border-strong); stroke-width: 2; }
.thumb-plate { fill: var(--cat-band); }
.thumb-mark { fill: var(--cat); }
.thumb-label {
  font-family: var(--sans); font-weight: 700; letter-spacing: 0.06em;
  fill: var(--cat-ink);
}
/* Type set on a solid mark rather than beside one, so it takes the measured
   --cat-on for the hue it is sitting on. */
.thumb-on { font-family: var(--sans); font-weight: 700; fill: var(--cat-on); }
.medtext { display: block; min-width: 0; }
.medtext b {
  font-family: var(--serif); font-weight: 700; font-size: 1rem; line-height: 1.25;
  color: var(--ink); display: block;
}
.medlist a:hover .medtext b { color: var(--cat-ink); }
.medtext span {
  font-family: var(--sans); font-size: 0.76rem; line-height: 1.5;
  color: var(--ink-2); margin-top: 5px; display: block;
}
/* THE WAY TO THE REST. A solid bar of the column's own colour across the foot
   of it, which is the one control on this page and is coloured like one. Square,
   no radius, no shadow: it is a printed tab, not a button in a product. */
.medmore {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding: 10px 12px; text-decoration: none;
  background: var(--cat); color: var(--cat-on);
  font-family: var(--sans); font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.11em; text-transform: uppercase;
}
.medmore .n { font-variant-numeric: tabular-nums; letter-spacing: 0.04em; opacity: 0.78; }
.medmore:hover { background: var(--cat-ink); color: var(--paper); }
@media (max-width: 940px) { .medcols { grid-template-columns: 1fr 1fr; gap: 22px; } }
@media (max-width: 620px) { .medcols { grid-template-columns: 1fr; gap: 20px; } }

/* ---------- a media category page ----------
   One of the three, on its own, in its own colour: a lead item on a solid
   field of the hue and then everything else two up as large tiles with a real
   preview on each. This is the shape the film section already used for films;
   it is generalised here so games and instruments get the same treatment
   rather than a third list. */
.medhead { padding: 34px 0 0; }
.medhead .flag {
  display: inline-block; background: var(--cat); color: var(--cat-on);
  font-family: var(--sans); font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; padding: 5px 11px 4px;
}
.medhead h1 { margin: 12px 0 10px; font-size: clamp(1.8rem, 1.4rem + 1.8vw, 2.6rem); letter-spacing: -0.018em; color: var(--cat-ink); }
.medhead .lede { color: var(--ink-2); max-width: 680px; margin: 0 0 4px; font-size: 1.06rem; line-height: 1.6; }
.medhead::after {
  content: ""; display: block; height: 4px; margin-top: 18px;
  background: linear-gradient(90deg, var(--cat) 0 240px, var(--cat-wash) 240px 100%);
}
/* THE LEAD. A solid field of the category's colour, which is the one slab on
   the page and is what marks the lead without a badge, a shadow or a size
   trick. The plate keeps its own near-white ground so the picture reads as a
   print mounted on a coloured mat. Type on the field takes --cat-on, measured
   per hue, and changes weight rather than opacity. */
.medlead {
  background: var(--cat); color: var(--cat-on);
  padding: 22px 24px 26px; margin: 26px 0 0;
}
.medlead-grid { display: grid; grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr); gap: 30px; align-items: start; }
.medlead-pic { display: block; text-decoration: none; }
.medlead-body { border-top: 2px solid currentColor; padding-top: 14px; min-width: 0; }
.medlead-body .kicker { color: inherit; }
.medlead-body .kicker .dot { background: currentColor; }
.medlead-body h2 {
  font-family: var(--serif); font-weight: 700; margin: 6px 0 10px;
  font-size: clamp(1.55rem, 1.1rem + 1.5vw, 2.15rem); line-height: 1.12; letter-spacing: -0.019em;
}
.medlead-body h2 a { color: inherit; text-decoration: none; }
.medlead-body h2 a:hover { text-decoration: underline; text-underline-offset: 4px; }
.medlead-body .dek { color: inherit; font-size: 1.02rem; line-height: 1.55; margin: 0 0 14px; }
.medlead .medplate { border-color: currentColor; }
@media (max-width: 1000px) { .medlead-grid { grid-template-columns: 1fr; gap: 20px; } }
@media (max-width: 620px) { .medlead { padding: 18px 16px 20px; } }

/* THE PLATE, for anything that is not a film: the same double rule the films
   and the maps sit in, so a game and an instrument read as the same kind of
   object as a film rather than as a screenshot dropped on the page. */
.medplate {
  border: 1px solid var(--cat); background: var(--surface); padding: 7px;
}
/* The preview box. 5:3 is what the instrument screenshots and the game plates
   are cut at; films are 16:9 and say so on the section rather than being
   cropped into somebody else's box. */
.medshot {
  display: block; position: relative; aspect-ratio: var(--shot-ar, 5 / 3);
  overflow: hidden; border: 1px solid var(--hairline); background: var(--surface-2);
}
.sec--videos { --shot-ar: 16 / 9; }
.medshot img, .medshot svg { display: block; width: 100%; height: 100%; }
.medshot img { object-fit: cover; object-position: 50% 0%; vertical-align: top; }
.medshot svg { object-fit: contain; padding: 14px; box-sizing: border-box; }
.medlead .medshot { aspect-ratio: var(--shot-ar, 5 / 3); }

/* THE TILES. Two up, because these are pictures and a picture at a quarter of
   the page stops being one. */
.medgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px 26px; margin: 30px 0 10px; }
.medcard { min-width: 0; }
.medcard > a { display: block; text-decoration: none; color: inherit; }
/* A film's length, set on the title's own line at the right of it. It used to
   run under the dek as "1:48 - 1600x2338 - silent", which is a spec sheet on a
   card whose whole job is to get somebody to press play. The length is the one
   figure that changes that decision, so it sits where the eye already is. */
.medcard-h, .medlead-h {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
}
.medcard-h h3, .medlead-h h2 { margin: 0; min-width: 0; }
.medcard-time {
  flex: none; font-family: var(--sans); font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.04em; color: var(--cat-ink); font-variant-numeric: tabular-nums;
}
.medcard-body { border-top: 3px solid var(--cat); padding-top: 11px; margin-top: 13px; }
.medcard:hover .medcard-body { border-top-color: var(--cat-ink); }
.medcard h3 {
  font-family: var(--serif); font-weight: 700; margin: 5px 0 7px;
  font-size: 1.32rem; line-height: 1.18; letter-spacing: -0.015em; color: var(--ink);
}
.medcard:hover h3 { color: var(--cat-ink); }
.medcard p { margin: 0; color: var(--ink-2); font-size: 0.93rem; line-height: 1.5; }
/* The spec line. Not set in caps: a film's is three measurements and reads as
   a caption, but a game's is a sentence about its own pool and an instrument's
   names a dataset, and those shout in caps and wrap to three lines. The figure
   inside carries the weight instead, which is the same division the exhibits
   use between a number and its label. */
.medcard .meta, .medlead-body .meta {
  font-family: var(--sans); font-size: 0.76rem; letter-spacing: 0.01em;
  color: var(--cat-ink); margin: 9px 0 0; line-height: 1.4;
}
.medlead-body .meta { color: inherit; }
.medcard .meta .n, .medlead-body .meta .n {
  font-weight: 700; font-variant-numeric: tabular-nums;
}
@media (max-width: 860px) { .medgrid { grid-template-columns: 1fr; gap: 26px; } }

/* The three ways out of a category page, at the foot: the other two categories
   and the way back to all of it, each in its own colour so the reader can see
   where they are going before they read the label. */
.medswitch { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 34px 0 10px; }
.medswitch a {
  display: block; text-decoration: none; padding: 13px 14px;
  border-top: 4px solid var(--cat); background: var(--cat-wash); color: var(--ink);
}
.medswitch a:hover { background: var(--cat-band); }
.medswitch b {
  display: block; font-family: var(--sans); font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--cat-ink);
}
.medswitch span {
  display: block; margin-top: 5px; font-family: var(--sans); font-size: 0.78rem;
  line-height: 1.45; color: var(--ink-2);
}
@media (max-width: 620px) { .medswitch { grid-template-columns: 1fr; gap: 10px; } }

/* ---------------------------------------------------------------- golf figures
   Two drawn exhibits for a-house-across-the-street: a street map and a pair of
   traced boundaries. Both are SVG files rather than generated marks, so their
   internal classes are styled here. Everything is token-based, so they theme
   with the rest of the site. Scoped to .fig-lacc / .fig-weho and used nowhere
   else. */
.fig-lacc text, .fig-weho text { font-variant-numeric: tabular-nums; }
.fig-lacc .rd { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.fig-lacc .rd.minor { stroke: var(--grid); }
.fig-lacc .rd.major { stroke: var(--de-emph); }
.fig-lacc .club-p { fill: var(--s3); fill-opacity: 0.9; stroke: var(--s3-ink); stroke-width: 2; }
.fig-lacc .mall-p { fill: var(--s1); fill-opacity: 0.9; stroke: var(--s1-ink); stroke-width: 1.6; }
.fig-lacc .house-p { fill: var(--s2); stroke: var(--paper); stroke-width: 2.5; }
.fig-lacc .cn { font-family: var(--serif); font-weight: 600; font-size: 33px;
                letter-spacing: -0.016em; fill: var(--ink); }
.fig-lacc .cs { font-family: var(--serif); font-size: 21px; fill: var(--ink-2); }
.fig-lacc .cl { font-family: var(--mono); font-size: 13px; letter-spacing: 0.18em; fill: var(--muted); }
.fig-lacc .cv { font-family: var(--serif); font-size: 29px; fill: var(--ink-2); }
.fig-lacc .ct { font-family: var(--serif); font-size: 52px; font-weight: 600; letter-spacing: -0.026em; }
.fig-lacc .club .ct { fill: var(--s3-ink); }
.fig-lacc .mall .ct { fill: var(--s1-ink); }
.fig-lacc .house .ct { fill: var(--s2-ink); }
.fig-lacc .foot { font-family: var(--mono); font-size: 13px; fill: var(--muted); }
.fig-weho text { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; }
.fig-weho .cp { fill: var(--s3); fill-opacity: 0.9; stroke: var(--s3-ink); stroke-width: 1.2; }
.fig-weho .wp { fill: var(--s1); fill-opacity: 0.9; stroke: var(--s1-ink); stroke-width: 1.2; }
.fig-weho .ln { font-weight: 700; font-size: 29px; fill: var(--ink); letter-spacing: -0.015em; }
.fig-weho .ls { font-size: 19px; fill: var(--ink-2); font-style: italic; }
.fig-weho .lk { font-size: 12px; letter-spacing: 0.18em; fill: var(--muted); }
.fig-weho .lv { font-size: 27px; fill: var(--ink); }
.fig-weho .lt { font-size: 40px; font-weight: 700; letter-spacing: -0.022em; }
.fig-weho .clubs .lt { fill: var(--s3-ink); }
.fig-weho .weho .lt { fill: var(--s1-ink); }
:root:where(:not([data-theme="light"])) .fig-lacc .club-p { fill-opacity: 0.78; }
:root:where(:not([data-theme="light"])) .fig-lacc .mall-p { fill-opacity: 0.82; }
:root[data-theme="dark"] .fig-lacc .club-p { fill-opacity: 0.78; }
:root[data-theme="dark"] .fig-lacc .mall-p { fill-opacity: 0.82; }

/* Masthead profile (miles-morton.html). NYT-masthead shape: big name, title
   beneath, 2px ink rule, one portrait floated right with the text wrapping it,
   stacking full width on a phone. Lives here rather than inline in the body so
   the page ships the same cached stylesheet as everything else. */
.profile-head { margin: 26px 0 0; }
.profile-name { font-size: clamp(2.5rem, 1.7rem + 3.6vw, 3.7rem); line-height: 1.0; letter-spacing: -0.022em; margin: 10px 0 6px; }
.profile-title { font-family: var(--sans); font-size: 0.82rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--muted); margin: 0 0 18px; }
.profile-rule { border: 0; border-top: 2px solid var(--ink); margin: 0 0 26px; }
.profile-portrait { float: right; width: 292px; margin: 4px 0 20px 28px; }
.profile-portrait img { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; border-radius: 2px; border: 1px solid var(--border); }
.profile-portrait figcaption { font-family: var(--sans); font-size: 0.7rem; line-height: 1.4; color: var(--muted); margin-top: 8px; }
.profile-clear { clear: both; }
.profile-disclosure { border-top: 1px solid var(--border); padding-top: 16px; }
.profile-plainlink { color: var(--ink); text-decoration: none; }
.profile-plainlink:hover { text-decoration: underline; }
@media (max-width: 720px) {
  .profile-portrait { float: none; width: 100%; margin: 20px 0; }
}
