/* ---------- fonts ----------
   Self-hosted, no CDN. Four families per the ruling that supersedes the
   brief's one-font rule. Variable where a variable file exists, so weight
   range costs no extra request. Files live in L3_render/static/fonts/ and are
   content-hashed by build.py alongside the stylesheet.

   SOURCE:
     SpaceGrotesk[wght].woff2        300-700  headings, figures, wordmark   — github.com/floriankarsten/space-grotesk
     IBMPlexSansVar-Roman.woff2      100-700  UI and body                  — github.com/IBM/plex (plex-sans-variable)
     IBMPlexMono-Regular.woff2       400      numerals                     — github.com/IBM/plex (plex-mono)
     IBMPlexMono-SemiBold.woff2      600      numerals, emphasis           — github.com/IBM/plex (plex-mono)
     SourceSerif4Var-Roman.woff2     200-900  analysis prose               — github.com/adobe-fonts/source-serif */

@font-face {
  font-family: "Space Grotesk";
  src: url("fonts/SpaceGrotesk[wght].woff2") format("woff2-variations");
  font-weight: 300 700;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("fonts/IBMPlexSansVar-Roman.woff2") format("woff2-variations");
  font-weight: 100 700;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/IBMPlexMono-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}

@font-face {
  font-family: "Source Serif 4";
  src: url("fonts/SourceSerif4Var-Roman.woff2") format("woff2-variations");
  font-weight: 200 900;
  font-display: swap;
}

/* candidatecasino.com — site.css
   Replaces L3_render/static/site.css. Hand-written, no preprocessor.
   Rationale: docs/visual-direction.md
   Order of sections: tokens, reset, layout, masthead, prose, board,
   chart, tables, modules, footer, desktop, print. */

/* ---------- tokens ---------- */

:root {
  /* four roles: display, UI/body, numerals, analysis prose */
  --font-display: "Space Grotesk", -apple-system, BlinkMacSystemFont, sans-serif;
  --font: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  --font-num: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
    monospace;
  --font-serif: "Source Serif 4", Georgia, "Times New Roman", serif;

  --fs-micro: 0.75rem;    /* 12px  — labels, source names */
  --fs-small: 0.8125rem;  /* 13px  — table cells, metadata */
  --fs-base: 0.9375rem;   /* 15px  — body prose */
  --fs-lead: 1.0625rem;   /* 17px  — opening sentence */
  --fs-h3: 1.125rem;      /* 18px */
  --fs-h2: 1.25rem;       /* 20px */
  --fs-h1: 1.6875rem;     /* 27px  — 34px on desktop */
  --fs-figure: 2.125rem;  /* 34px  — the headline number */

  /* space: 4px base, six steps */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;

  /* colour roles */
  --desk: #f0eee9;         /* margin beyond the content column */
  --paper: #fbf8f2;        /* the content column itself (5b) */
  --panel: #f5efe2;        /* boxes sitting on the paper (5b) */
  --card: #ffffff;         /* raised surface */
  --rule: #e3d8c0;         /* structural hairline */
  --rule-faint: #efe9db;   /* row separator, bar track */

  --ink: #24211c;          /* headings */
  --body: #3d4653;         /* prose */
  --meta: #6b6455;         /* timestamps, source names — 5.3:1 on paper */

  --gold: #e0b464;         /* accent rule, never text */
  --gold-ink: #8a6413;     /* accent as text — 5.0:1 on paper */
  --gold-label: #7a5a1e;   /* small-caps module labels */

  --felt: #0b2b22;         /* masthead ground */
  --felt-edge: #1c4436;

  --fresh: #1a7f37;
  --recent: #9a6700;
  --stale: #6e7781;
  --final: #24292f;

  --up: #1a7f37;
  --down: #b3261e;

  /* Party colour, set inline from data (P465) as style="--party:#RRGGBB".
     Absent colour falls back to neutral — a party with no P465 value
     renders grey rather than borrowing another party's hue.

     Fills and swatches use --party directly. STROKES DO NOT: a near-white
     party colour (SD's yellow) vanishes as a 2px line on white. Any line
     drawn in party colour must be darkened first — chart.py should mix 35%
     black into any hex whose relative luminance exceeds 0.6. Today chart.py
     does not use party colour at all (PRIMARY_COLOUR / SECONDARY_COLOUR),
     and the specimen follows it. */
  --party: var(--meta);

  --measure: 34rem;        /* prose measure */
  --radius: 9px;
}

/* ---------- reset ---------- */

* {
  box-sizing: border-box;
}

html {
  font-family: var(--font);
  font-size: 100%;
  line-height: 1.5;
  background: var(--desk);
  color: var(--body);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  padding: 0 var(--s4) var(--s7);
  max-width: 40rem;
  margin-inline: auto;
  background: var(--paper);
  border-inline: 1px solid var(--rule);
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  color: var(--ink);
  text-wrap: pretty;
  margin: 0;
}

h1 {
  font-size: var(--fs-h1);
  line-height: 1.15;
  letter-spacing: -0.015em;
}

h2 {
  font-size: var(--fs-h2);
  line-height: 1.25;
}

h3 {
  font-size: var(--fs-h3);
  line-height: 1.3;
}

p {
  margin: 0 0 var(--s3);
  max-width: var(--measure);
  text-wrap: pretty;
}

a {
  color: var(--gold-ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}

a:hover {
  color: var(--ink);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

time,
.num {
  font-variant-numeric: tabular-nums;
}

/* 44px minimum tap target — applied to controls, not to inline prose
   links, which would otherwise blow up line height inside paragraphs. */
nav a,
summary,
button,
input,
select,
td a,
th a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

p a {
  min-height: 0;
  display: inline;
}

/* ---------- masthead ---------- */

.masthead {
  background: var(--felt);
  border-bottom: 2px solid var(--gold);
  margin: 0 calc(-1 * var(--s4)) var(--s3);
}

nav[aria-label="breadcrumb"] {
  font-size: var(--fs-small);
  color: var(--fresh);
  margin-bottom: var(--s2);
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s1);
  align-items: center;
}

nav[aria-label="breadcrumb"] a,
nav[aria-label="breadcrumb"] a:visited {
  color: var(--fresh);
}

/* ---------- page head ----------
   The h1 and lede sit ABOVE .layout, not inside .main. On a phone .layout
   is not a grid, so anything inside .main follows the rail and the featured
   panel — which put the page's own question below the fold while a
   different market's heading sat above it. Hoisted, the read order is
   title, number, panel, body at every width. This is a deviation from the
   mock, which pairs the h1 and the figure side by side on desktop. */
.page-head {
  margin-bottom: var(--s5);
}

/* Title and headline number pair up, as in the mock: on a phone the number
   follows the lede directly; on desktop they sit at the same level. */
.page-head .hero-figure {
  margin: var(--s4) 0 0;
}

/* ---------- lede ---------- */

.lede {
  font-size: var(--fs-lead);
  line-height: 1.45;
  color: var(--body);
  margin-bottom: var(--s3);
}

.last-updated {
  font-size: var(--fs-micro);
  color: var(--meta);
  margin-bottom: var(--s5);
}

.last-updated time {
  font-family: var(--font-num);
}

/* ---------- prose ----------
   The two reading page types: the analysis post and the reference (prose)
   pages. An analysis post reads in the serif, a prose page reads in the
   sans. Both are prose, but they are not the same kind of prose. A post is
   an argument with a byline and a date it was true -- it wants the measure
   and the colour of something written. A methodology or privacy page is
   reference: looked up, quoted from, read a paragraph at a time, and it
   belongs to the same voice as the tables it explains. Setting both in the
   serif would make the reference pages feel like opinion; setting both in
   the sans would waste the only font in the stack that was loaded
   specifically for reading.

   Scoped to .post, not to <article>: the market and entity pages are
   articles too and must stay in the sans. */
.post {
  font-family: var(--font-serif);
}

.post p {
  font-size: 1.0625rem;
  line-height: 1.7;
  color: #2c3440;
  max-width: 93ch;  /* ~50% wider than reference prose's 62ch measure */
  margin: 0 0 var(--s4);
}

/* The opening sentence is the summary the whole page repeats in its meta
   description and its JSON-LD. It gets scale, not a box. */
.post .lede {
  font-size: 1.3125rem;
  line-height: 1.45;
  font-weight: 400;
  color: var(--ink);
  max-width: 66ch;  /* same 1.5x widening as .post p, off the old 44ch */
  margin: var(--s3) 0 var(--s4);
}

/* Headings stay in the display face -- the serif is for reading, not for
   structure, and keeping h2 in Space Grotesk is what makes a post's
   sections scan like the sections of every other page. */
.post h2 {
  margin-top: var(--s6);
}

/* A post headline is the longest single line on the site, and Swedish
   compounds it: "Socialdemokraternas" alone is 27px wider than the 310px
   content box at 360px, and h1 breaks nothing by default. Same case the
   rule above handles for `.titleline h1` on the entity pages -- a post has
   no titleline, so it needs the rule of its own. */
.post h1 {
  overflow-wrap: break-word;
  hyphens: auto;
  text-align: center;
}

/* The reading column (title, lede, sections) and the share rail sit in one
   grid so the rail can take the whitespace the wider measure above leaves
   at the paper's right edge, instead of trailing the post at the bottom.
   Single column below the desktop breakpoint -- the rail has nowhere to go
   beside the text once it's narrower than the text itself. */
.post-layout {
  display: grid;
  gap: var(--s6);
}

.post-share .share-row {
  flex-direction: column;
  align-items: stretch;
}

/* The closing sign-off, distinct from the top-of-page .provenance byline —
   same text (`byline`), a second place a reader who scrolled straight to
   the end still sees who to attribute the piece to. */
.post-signature {
  font-family: var(--font-sans);
  font-size: var(--fs-small);
  color: var(--meta);
  margin: var(--s5) 0 var(--s3);
}

/* Byline and timestamp are one line of provenance, not two paragraphs. The
   hairline above them closes the title block. */
.provenance {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  align-items: baseline;
  padding-top: var(--s3);
  border-top: 1px solid var(--rule);
  font-family: var(--font);
  font-size: var(--fs-micro);
  color: var(--meta);
  margin-bottom: var(--s5);
}

/* Deliberately not the mono face used by `.last-updated time`: a post's
   date is spelled out in words, and words set in Plex Mono read as a
   machine timestamp rather than as a publication date. */
.provenance time {
  font-variant-numeric: tabular-nums;
}

/* Contents for the reference pages. Methodology runs to eight sections and
   gets quoted section by section from other pages, so the headings need to
   be both a map and a set of link targets. No JavaScript: the ids come from
   the content file's headings at build time. Rendered only where there are
   more than four sections. */
.contents {
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  padding: var(--s3) 0;
  margin-bottom: var(--s5);
}

/* A prose page's rail (.hub-split's right column): the contents nav above
   the recent-analyses widget, when both are present -- same gap as the
   split itself uses between its own two columns. */
.prose-rail > * + * {
  margin-top: var(--s5);
}

.contents h2 {
  font-family: var(--font-display);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--meta);
  margin: 0 0 var(--s2);
  border-bottom: none;
}

/* Self-authored SVG diagrams (L3_render/diagrams.py), placed in a
   `<figure class="post-image">`. The svg itself already gets its scaling,
   border and background from the shared `svg[role="img"]` rule below (the
   chart svg uses the same role), and its caption already gets the shared
   `figcaption` rule -- no new rule needed for either. This class exists
   only to give the figure a bit more breathing room than the chart's own
   figure gets, since a post-image sits between paragraphs rather than next
   to a table twin. */
.post-image {
  margin: var(--s2) 0 var(--s5);
}

.post-image figcaption {
  font-style: italic;
}

.contents ol {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: var(--measure);
  counter-reset: toc;
}

.contents li {
  font-size: var(--fs-small);
  line-height: 1.4;
  margin-bottom: var(--s2);
  break-inside: avoid;
  counter-increment: toc;
}

/* Numbered because other pages cite these sections by position as well as
   by name, and because eight underlined links in two columns need
   something in the left margin to hold the rows apart. */
.contents li::before {
  content: counter(toc);
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
  margin-right: var(--s2);
}

.contents a {
  text-decoration: none;
}

.contents a:hover {
  text-decoration: underline;
}

section {
  margin-bottom: var(--s6);
}

/* .market-grid is a div, not a section, so the rule above never fires
   before whatever follows it — the year hub's "Tre val, en dag" section
   otherwise sits flush under the card grid. Same s6 gap .section-head
   already opens above a card grid, mirrored below it. */
.market-grid + section {
  margin-top: var(--s6);
}

/* Scoped to article sections: .featured is a <section> too, and must not
   inherit the gold underline. */
article > section > h2 {
  border-bottom: 2px solid var(--gold);
  display: inline-block;
  padding-bottom: 2px;
  margin-bottom: var(--s3);
}

section ul {
  padding-left: 1.1rem;
  max-width: var(--measure);
}

/* Visually hidden, still read: the board is a <details> grid rather than a
   <table>, so each value carries its own column name for screen readers and
   crawlers. The visible .board-cols headings are the sighted equivalent. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- module label ---------- */

.module-label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-label);
  margin-bottom: var(--s2);
}


/* ---------- party mark ---------- */

.party-swatch {
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  border-radius: 2px;
  background: var(--party);
  border: 1px solid rgba(0, 0, 0, 0.25);
  vertical-align: -0.05em;
}

/* Entity pages lead with the mark, not a number: a party page's subject is
   the party, and the chip is the same object the board rows already use, at
   heading scale. The h1 wraps beside it rather than under it, which matters
   at 360px with a name like Kristdemokraterna. */
.titleline {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s2);
}

.titleline .chip {
  width: 44px;
  height: 44px;
  font-size: 0.8125rem;
}

.titleline h1 {
  min-width: 0;
  overflow-wrap: break-word;
}

/* ---------- the board ---------- */

.board-figure {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s4);
  margin: 0 0 var(--s4);
}

.board-figure .leader {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s2);
}

.board-figure .figure {
  font-family: var(--font-display);
  font-size: var(--fs-figure);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.02em;
}

.board-figure .figure-note {
  font-size: var(--fs-micro);
  color: var(--meta);
}

.board {
  font-size: var(--fs-small);
}

.board th {
  text-align: left;
  font-size: var(--fs-micro);
  font-weight: 600;
  color: var(--meta);
  border-bottom: 1px solid var(--rule);
  padding: 0 0 var(--s2);
}

.board td {
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--rule-faint);
  vertical-align: middle;
}

.board td:last-child,
.board th:last-child {
  text-align: right;
}

.board .outcome {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-weight: 500;
}

.board .prob {
  font-weight: 700;
  color: var(--ink);
}

/* share bar — geometry, not decoration: encodes the same number */
.bar {
  display: block;
  height: 7px;
  background: var(--rule-faint);
  border-radius: 4px;
  overflow: hidden;
}

.bar > span {
  display: block;
  height: 7px;
  background: var(--party);
  border-radius: 4px;
}

.delta-up {
  color: var(--up);
  font-weight: 600;
  font-family: var(--font-num);
}

.delta-down {
  color: var(--down);
  font-weight: 600;
  font-family: var(--font-num);
}

.delta-flat {
  color: var(--ink);
  font-weight: 600;
  font-family: var(--font-num);
}

/* ---------- freshness ---------- */

.odds-updated {
  font-size: var(--fs-micro);
  margin: var(--s2) 0 0;
}

.odds-updated time {
  font-family: var(--font-num);
}

.odds-updated.fresh {
  color: var(--fresh);
}

.odds-updated.recent {
  color: var(--recent);
}

.odds-updated.stale {
  color: var(--stale);
}

.odds-updated.final {
  color: var(--final);
  font-weight: 600;
}

/* ---------- countdown ---------- */

.countdown {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--card);
  padding: var(--s3);
  margin-bottom: var(--s4);
}

.countdown .figure {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.countdown .caption {
  font-size: var(--fs-micro);
  color: var(--meta);
}

/* ---------- chart ----------
   RULE FOR chart.py: author the viewBox at the NARROWEST container the
   chart will occupy (~280px, i.e. a 360px viewport), never at desktop
   width. An SVG scales its absolute font-size attributes with the box, so
   a chart authored at 460px renders its 10px labels at 6px on a phone.
   Authored narrow, the same labels are 10px on a phone and scale up. */

svg[role="img"] {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

/* chart.py's own element classes -- every stroke, fill and label takes its
   colour from here, never from a hex literal in chart.py (design-brief.md
   -> "Chart colour lives in site.css, not in chart.py"). */
.chart-axis {
  stroke: var(--rule);
}

.chart-baseline {
  stroke: var(--ink);
}

.chart-gridline {
  stroke: var(--rule);
}

.chart-label {
  fill: var(--meta);
}

.chart-line-primary {
  fill: none;
  stroke: var(--ink);
}

.chart-line-secondary {
  fill: none;
  stroke: var(--meta);
  opacity: 0.7;
}

.chart-endpoint {
  fill: var(--ink);
}

.chart-endpoint-label {
  fill: var(--ink);
}

.chart-key-swatch {
  stroke: var(--meta);
}

figure {
  margin: 0 0 var(--s3);
}

figcaption {
  font-size: var(--fs-micro);
  color: var(--meta);
  margin-top: var(--s2);
}

/* ---------- data tables ---------- */

.data-table {
  font-size: var(--fs-small);
}

.data-table caption {
  text-align: left;
  font-size: var(--fs-micro);
  color: var(--meta);
  padding-bottom: var(--s2);
}

.data-table th {
  text-align: left;
  font-size: var(--fs-micro);
  font-weight: 600;
  color: var(--meta);
  border-bottom: 1px solid var(--rule);
  padding: 0 var(--s2) var(--s2) 0;
}

.data-table td {
  padding: var(--s2) var(--s2) var(--s2) 0;
  border-bottom: 1px solid var(--rule-faint);
}

.data-table td:last-child,
.data-table th:last-child {
  padding-right: 0;
}

.data-table time {
  font-family: var(--font-num);
  color: var(--meta);
  font-size: var(--fs-micro);
}

.results-table td:nth-child(n + 2),
.results-table th:nth-child(n + 2),
.source-table td:nth-child(n + 3),
.source-table th:nth-child(n + 3),
.chart-table td:nth-child(3),
.chart-table th:nth-child(3) {
  text-align: right;
}

/* the long tail of a 54-row result: same table, compressed rows */
.results-table tr.minor td {
  padding-top: 3px;
  padding-bottom: 3px;
  font-size: var(--fs-micro);
  color: var(--meta);
  border-bottom: 0;
}

.results-table tr.minor + tr:not(.minor) td {
  border-top: 1px solid var(--rule-faint);
}

/* The share bar is a table cell on results tables. It reads as geometry for
   the same number in the cell before it, so it takes flexible width on
   desktop and drops to its own line in the mobile reflow, which
   `.data-table td > .bar` already handles. */
.results-table td.share {
  width: 34%;
  padding-right: var(--s3);
}

.results-table td.seats {
  font-family: var(--font-num);
  font-weight: 600;
  color: var(--ink);
}

/* The long tail of an official result. A municipal result runs to 54 rows,
   most of them single-digit vote counts, and the brief permits collapsing
   the tail behind a summary provided it needs no JavaScript. <details>
   cannot live inside <tbody>, so the tail is its own table inside the
   disclosure — the rows are identical in structure to the ones above, which
   is what keeps the two halves reading as one result. */
.results-tail {
  margin-top: var(--s4);
  border-top: 1px solid var(--rule);
}

.results-tail > summary {
  padding: var(--s2) 0;
}

.results-tail .data-table {
  margin-top: var(--s2);
}

/* ---------- identity ---------- */

.identity {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s5);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s2) var(--s4);
  font-size: var(--fs-small);
}

.identity dt {
  color: var(--meta);
  margin: 0;
}

.identity dd {
  margin: 0;
  font-weight: 500;
  color: var(--ink);
}

/* The dl is already a two-column grid. Its dd may now carry a swatch and a
   hex, or a link, so the baseline needs settling and the swatch needs the
   gap it does not get from `vertical-align` alone. */
.identity dd {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}

.identity dd .num {
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
}

.byline {
  font-size: var(--fs-small);
  color: var(--meta);
}

.settled-statement {
  font-weight: 600;
  color: var(--ink);
}

.settled-tag {
  font-size: var(--fs-micro);
  color: var(--meta);
}

/* ---------- secondary boxes ----------
   Everything else on the page is full width, so the smaller modules —
   related analysis, related markets, settled results — sit together in one
   band rather than in a rail. Each box is about the width of the headline
   figure, and the band reflows to one column on a phone. Absent modules
   simply do not render, so a band with one box still looks intended. */
.box-row {
  display: grid;
  gap: var(--s4);
  margin-bottom: var(--s6);
}

/* A prose section paired with a list module beside it (country hub's
   electoral-system intro + analysis list, the Riksdag election hub's own
   "how it works" + analysis list) — one column on a phone, 2:1 once there
   is room for two, so the list reads as a sidebar rather than a second
   equal column. */
.hub-split {
  display: grid;
  gap: var(--s5);
  align-items: start;
}

.box {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s3) var(--s4) var(--s4);
}

.box h2 {
  font-size: 0.8125rem;
  font-weight: 700;
  margin: 0 0 var(--s2);
}

.box ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.box li + li {
  border-top: 1px solid var(--rule);
}

.box li a {
  display: flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--fs-small);
  line-height: 1.4;
  text-decoration: none;
}

.box li a:hover {
  text-decoration: underline;
}

.box .meta {
  font-size: var(--fs-micro);
  color: var(--meta);
  margin: var(--s2) 0 0;
}

/* Sources. Name and use are one unit per row, the name carrying the link
   and the use explaining why the source is trusted for what. A dl would be
   truer, but the template emits <li>s, so the split happens inside the
   item. */
.sources {
  list-style: none;
  padding: 0;
  max-width: var(--measure);
}

.sources li {
  padding: var(--s3) 0;
  border-top: 1px solid var(--rule-faint);
}

.sources li a {
  display: flex;
  align-items: center;
  min-height: 44px;
  font-weight: 600;
}

.sources li .use {
  display: block;
  font-size: var(--fs-small);
  color: var(--meta);
  margin-top: 2px;
}

/* ---------- modules ----------
   04-delta.css (docs/designs/04-hub-specimen.html): the section header, the
   market card grid, the all-markets panel that closes a grid, and the
   analysis card/list — the four components every hub mock uses and that had
   no rule until prompt 43. Every value resolves through an existing token;
   no new custom property was needed. */

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  border-bottom: 2px solid var(--felt-edge);
  padding-bottom: var(--s2);
  margin: var(--s6) 0 var(--s4);
}

.section-head h2 {
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.01em;
}

.section-head .note {
  flex: none;
  font-size: var(--fs-micro);
  color: var(--meta);
  text-align: right;
}

.market-grid {
  display: grid;
  gap: var(--s4);
  grid-template-columns: 1fr;
}

.market-card {
  display: block;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s4);
  color: inherit;
  text-decoration: none;
}

.market-card:hover {
  border-color: var(--gold);
  color: inherit;
}

.market-card .question {
  display: block;
  font-size: var(--fs-micro);
  font-weight: 500;
  color: var(--meta);
  min-height: 2.5em;
}

.market-card .outcome {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ink);
}

.market-card .price {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  margin-top: var(--s2);
}

.market-card .prob {
  font-family: var(--font-display);
  font-size: 1.625rem;
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.market-card .odds {
  font-family: var(--font-num);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--gold-ink);
}

.market-card .foot {
  margin-top: var(--s2);
  font-size: var(--fs-micro);
  color: var(--meta);
}

.market-card .foot .gap {
  font-family: var(--font-num);
}

.market-card.all-markets {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: var(--panel);
}

.market-card.all-markets p {
  font-size: var(--fs-small);
  color: var(--body);
  margin: var(--s2) 0 0;
  max-width: none;
}

.market-card.all-markets .cta {
  align-self: flex-start;
  margin-top: var(--s3);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--felt);
  border-bottom: 2px solid var(--gold);
  padding-bottom: 2px;
}

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

.analysis-list li {
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule);
}

.analysis-list li:last-child {
  border-bottom: none;
}

.analysis-list a {
  display: block;
  color: inherit;
  text-decoration: none;
}

.analysis-list h3 {
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 var(--s1);
}

.analysis-list a:hover h3 {
  text-decoration: underline;
}

.analysis-list .meta {
  font-size: var(--fs-micro);
  color: var(--meta);
}

.analysis-grid {
  display: grid;
  gap: var(--s4);
  grid-template-columns: 1fr;
}

.analysis-card {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: var(--s4);
}

.analysis-card a {
  display: block;
  color: inherit;
  text-decoration: none;
}

.analysis-card h3 {
  font-family: var(--font-serif);
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 var(--s1);
}

.analysis-card a:hover h3 {
  text-decoration: underline;
}

.analysis-card .meta {
  font-size: var(--fs-micro);
  color: var(--meta);
}

/* 07 delta — the two analysis lists. Additive to site.css + 04-delta.css.
   Specimens: 07-analysis-index-specimen.html (country hub, paper),
   07-analysis-felt-specimen.html (party page, felt).

   What this replaces: the `.box` holding a <ul> of post links, in the two
   places it appears. Those two places are not the same place.

   On the country hub the analysis list is a *destination*: `#analys` is one
   of the five things the masthead nav points at, and since prompt 45 moved
   parties and candidates out of the band, it is the only thing left in it.
   A box holding the page's fifth nav target, alone in a band built for
   leftovers, reads as a leftover. .analysis-index gives it the same
   full-width section treatment the market grid and the party grid have:
   .section-head, then ruled rows.

   Everywhere else — a party page, a candidate page, a place page, a market
   page — the list genuinely is secondary, one box among three. There the
   problem is the opposite: three identical panels of link lists, and the
   analysis one is the only one that leads to something written. .analysis-felt
   puts it on the masthead's ground so it is the one box in the band the eye
   lands on. It is the first time the felt appears below the header, and that
   is the point.

   Tokens: no new custom property. On-felt inks are the literals site.css
   already uses for text on the felt — see .mast-nav, .mast-tabs and .mover
   — and the row separator is --felt-edge, the same hairline the masthead
   rows use. The literal values themselves are named where .analysis-felt's
   rules live, further down this file (see below).

   Relation to 04-delta.css's .analysis-list: that rule is a stack of
   serif headlines with `date · read time` under each, for a column under a
   section head on the year hub and home. It stays and keeps its callers.
   These two are one line per post: a title and a date on the same baseline.
   Neither of these replaces it and none of the three should be nested.

   .analysis-index lives here, in `modules`, alongside the market grid and
   party grid it matches. .analysis-felt is on-felt and every one of its
   inks is a raw literal — the `modules` section carries none anywhere else
   (its own test enforces that), so .analysis-felt sits instead next to the
   masthead's own on-felt literals, further down this file, where that
   convention already lives. Same 07-delta.css, one rule split across two
   locations for that reason.

   ---------- into: modules ---------- */

/* Ruled rows, no container. The container was the box; on paper the rule and
   the gold underline carry the structure, which is what every other list on
   the site does. --rule-faint rather than --rule: at four or five rows a
   structural hairline per row reads as a table, and this is a contents page.
   The row is the tap target — no inner link, 44px minimum from the padding
   plus a one-line title. */
.analysis-index {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--s6);
}

.analysis-index a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  min-height: 44px;
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule-faint);
  color: inherit;
  text-decoration: none;
}

/* The serif, because it is the voice of the thing being linked to: posts are
   set in Source Serif 4 and this is the only list on a hub that leads to
   prose. --fs-base, not --fs-h3: these are links in a list, not headlines.

   The gold rule is a text decoration, not a border. Elsewhere on the site the
   underlined name is short and single-line and `border-bottom` is the same
   thing; here the title wraps by design, and a border draws across the flex
   item — the full column width, out to the date. A decoration follows the
   line boxes, so it underlines the last line only as far as the text goes. */
.analysis-index .title {
  font-family: var(--font-serif);
  font-size: var(--fs-base);
  line-height: 1.35;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  text-wrap: pretty;
}

.analysis-index a:hover .title {
  text-decoration-color: var(--gold-ink);
}

/* Day and month, mono, right. Not the year: the list is short and current,
   and the full date is on the post. Absent date → the element is absent and
   the title takes the full width; do not print a dash. */
.analysis-index .when {
  flex: none;
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
  font-variant-numeric: tabular-nums;
}

/* 08-delta.css (docs/designs/08-footer-specimen.html): the sharing row. Last
   thing in an article, on the paper, marked off by the same hairline
   .criteria uses rather than by a box -- it is an end, not a module. The
   label is --gold-label, the small-caps gold that reads on paper.

   Destinations are intent URLs, resolved at build time with the canonical URL
   and the page title already escaped. No JS, so no copy-to-clipboard button
   and no share counts: the canonical URL is printed under the row instead,
   selectable in one gesture, which is what the copy button would have done. */
.share {
  border-top: 1px solid var(--rule);
  margin-top: var(--s6);
  padding-top: var(--s4);
}

.share .label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-label);
  margin-bottom: var(--s3);
}

.share-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

/* .spread-pill's shape, in the neutral inks: 44px here, because unlike the
   footer line these are controls and they are the reason the row exists. */
.share-row a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 15px;
  border: 1px solid var(--rule);
  border-radius: 22px;
  background: var(--card);
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--gold-ink);
  text-decoration: none;
}

.share-row a:hover {
  border-color: var(--gold);
  background: var(--panel);
  color: var(--ink);
}

/* The canonical URL, printed. Mono, --meta, breaks anywhere rather than
   overflowing at 360px, and selects in one gesture. */
.share .url {
  display: block;
  margin-top: var(--s3);
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
  overflow-wrap: anywhere;
  user-select: all;
}

/* ---------- */

/* 06-delta.css (docs/designs/06-parties-specimen.html): the party grid and
   the candidate list that replace the country hub's `#parties`/`#candidates`
   box lists. Tokens: no new custom property. The cell is --panel with a
   --rule hairline, which is what .box already is; the difference is the
   grid, the figures and the internal rule, not a new surface. Two ornaments
   are used exactly as they ship: .chip.sm (26px, dashed white ring) for the
   party mark, and .bar for the share bar. Neither is edited here.

   One additive rule does touch a .chip: .chip sets its glyph to white, which
   is correct over eight of the party colours and wrong over SD's yellow and
   MP's green. site.css's token comment already names the 0.6 relative
   luminance threshold for this, for strokes. Rather than edit .chip, the
   party mark carries data-ink="dark" when the fill's luminance exceeds 0.6
   and an additive rule flips the glyph to --ink. The same fix is wanted on
   the board and the entity pages; that is a separate task and it edits
   .chip. */

/* Eight parties, so four across at desktop rather than three: two rows of
   four reads as a set, and 3 + 3 + 2 reads as a ranking with a remainder.
   One column on a phone, two from 40em, four from 60em. */
.party-grid {
  display: grid;
  gap: var(--s3);
  grid-template-columns: 1fr;
  margin-bottom: var(--s6);
}

/* The cell recedes rather than lifts: --panel is the tone the page already
   uses for a module sitting on paper, and the hairline is inset rather than a
   border so the grid stays flush with the ruled sections above and below it.
   A white --card fill with a coloured top edge was the other candidate and it
   read as a web card on a page that is otherwise printed. */
.party-cell {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  background: var(--panel);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--rule);
  padding: var(--s3);
  color: inherit;
  text-decoration: none;
}

/* Gold on hover, same as .spot and .market-card. The whole cell is the
   target — no inner link, and at 360px the cell is 302px wide and about
   132px tall, so the 44px minimum is satisfied by the cell itself. */
.party-cell:hover {
  box-shadow: inset 0 0 0 1px var(--gold);
  color: inherit;
}

.party-cell .who {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

/* The gold underline marks the name as the way through, the same as
   .featured-link and .spot .cta. It hugs the text: the name is a flex item
   in a row here, so it already does. */
.party-cell .name {
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
  border-bottom: 2px solid var(--gold);
}

/* .chip sets its glyph to white. Over SD's yellow and MP's green the
   abbreviation disappears. The flag comes from the fill's relative luminance, computed
   where the colour is read, not from a list of party keys. */
.party-cell .chip[data-ink="dark"] {
  color: var(--ink);
}

/* Absent for a party whose leader QID resolves to no person entity — no
   line, no placeholder. Two spokespersons render as one line, joined by the
   same middot the rest of the site uses for a list of two. */
.party-cell .leader {
  font-size: var(--fs-micro);
  line-height: 1.35;
  color: var(--meta);
}

/* margin-top: auto pins the bar and the figures under it to the bottom of the
   cell, so the bars and the percentages sit on a common line across a row
   whatever the identity block above them did. This exists for the
   absent-leader case, which is live: a cell with no leader line is one child
   shorter, and without this it pulls its bar up 24px and breaks the row it
   sits in. Same idiom as .spot .cta in 05-delta.css. */
.party-cell .bar {
  margin-top: auto;
}

/* The internal rule is what gives the cell structure without a border doing
   it: identity above, arithmetic below. --rule rather than --rule-faint,
   because --rule-faint is the bar track and the two sit 8px apart. */
.party-cell .figures {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2);
  border-top: 1px solid var(--rule);
  padding-top: var(--s2);
  font-variant-numeric: tabular-nums;
}

.party-cell .share {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
}

/* Seats and open markets, mono, in one string. Two facts of different kinds
   in one line is deliberate: the seat count is settled history and the market
   count is what the site has to say about it now, and the section note above
   the grid says which is which. Keep it short — at four columns the cell has
   about 175px of content and "107 mandat · 4 marknader" does not fit on one
   line. The words are in the section note; the cell has the numbers. */
.party-cell .counts {
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
  white-space: nowrap;
}

/* Candidates stay a list. There are six of them and they carry one fact each
   beyond the name, so a card each would be eight-tenths whitespace. Two
   columns of rows at desktop, one on a phone. */
.person-list {
  display: grid;
  grid-template-columns: 1fr;
  margin-bottom: var(--s6);
}

.person-row {
  display: grid;
  grid-template-columns: 3px 1fr auto;
  align-items: center;
  gap: 0 var(--s3);
  min-height: 44px;
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--rule);
  color: inherit;
  text-decoration: none;
}

.person-row:hover .name {
  border-bottom-color: var(--gold-ink);
}

/* The party colour as a rail rather than a chip: a row is 48px tall and a
   26px chip in it competes with the name for the eye. The rail is the same
   information at the same place in every row, and it is the only party
   colour on the row, so a candidate with no party membership takes --gold
   and does not read as belonging to a grey party. */
.person-row .rail {
  align-self: stretch;
  background: var(--party);
}

.person-row .text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* align-self, not justify-self: the parent is a flex column, where
   justify-self is ignored and the underline would run the full width of the
   row and read as a divider. */
.person-row .name {
  align-self: flex-start;
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  border-bottom: 2px solid var(--gold);
}

/* Party and birth year. Absent party renders the line without it rather than
   with "no party": the rail is already saying it. */
.person-row .sub {
  font-size: var(--fs-micro);
  line-height: 1.35;
  color: var(--meta);
}

.person-row .count {
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--meta);
  text-align: right;
  white-space: nowrap;
}

/* ---------- resolution criteria ----------
   Settlement mechanics: read by few, needed exactly when it is needed. Set
   in the serif at a generous measure so it reads as reference prose rather
   than as a wall, and marked off by a gold hairline instead of a box. */
.criteria {
  border-top: 1px solid var(--rule);
  padding-top: var(--s4);
}

.criteria p {
  font-family: var(--font-serif);
  font-size: 0.9375rem;
  line-height: 1.72;
  color: #2c3440;
  max-width: 64ch;
  margin: 0 0 var(--s3);
}

.criteria p:first-of-type {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
}

.criteria p + p {
  padding-top: var(--s3);
  border-top: 1px solid var(--rule-faint);
}

/* ---------- disclosure ---------- */

details {
  border-top: 1px solid var(--rule-faint);
}

summary {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--gold-ink);
  cursor: pointer;
}

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

/* 08-delta.css (docs/designs/08-footer-specimen.html), candidate B, "the
   ledger": felt full width, 2px gold rule on the edge facing the paper,
   nothing below it -- the second and last place the felt appears, closing
   the page the way the masthead opens it. One centred wordmark under the
   bulb-dot rule, the whole link inventory as a single ruled line, the
   account row, then the closing line.

   Tokens: no new custom property. On-felt inks are the literals site.css
   already uses for text on the felt -- #eef3ef and #fff (.mast-nav,
   .mast-tabs), #8fae9f (.mast-tabs, .mast-movers .label) -- and the rules
   are --felt-edge and --gold, as in the masthead. */

/* Full-bleed felt band. Inline margins match .masthead's; the negative bottom
   margin eats body's --s7 bottom padding so the felt runs to the end of the
   document. body's border-inline stops at the padding box, so the band meets
   both rules cleanly at every width. */
.site-foot {
  background: var(--felt);
  border-top: 2px solid var(--gold);
  margin: var(--s7) calc(-1 * var(--s4)) calc(-1 * var(--s7));
  padding: var(--s5) var(--s4) var(--s4);
  font-size: var(--fs-small);
  color: #b7cbc0;
  text-align: center;
}

.site-foot a {
  color: #eef3ef;
  text-decoration: none;
}

.site-foot a:hover {
  color: #fff;
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

/* The one ornament the site allows itself, and the second and last place it
   appears: the same values as .featured .bulbs, capped short and centred so
   it reads as a rule over the wordmark rather than as a border. */
.site-foot .bulbs {
  height: 6px;
  max-width: 15rem;
  margin: 0 auto var(--s4);
  background: radial-gradient(circle at 3px 3px, #e0b464 1.7px, transparent 2.1px)
    0 0 / 18px 6px repeat-x;
  opacity: 0.85;
}

/* .mast-brand is display:flex in site.css, which would stretch the box to the
   full width here; inline-flex keeps it the size of the wordmark and lets
   text-align centre it. Same component otherwise -- same border, same two
   lines, no second wordmark rule anywhere in the stylesheet. */
.site-foot .mast-brand {
  display: inline-flex;
}

.foot-line {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s1) var(--s4);
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--felt-edge);
}

/* One nav, one flat list of links, and the separator is space -- no dots, no
   pipes, no pseudo-elements. A drawn separator cannot survive the wrap: as a
   ::before it orphans at the start of the next row, as an ::after at the end
   of the previous one, and this line wraps to three rows at 360px by design.
   Space separates cleanly at every width; the gold on this row is the hover
   decoration, and the bulb rule above it. */
.foot-line a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
}

/* 40px, not the site's 44px: these are 13px links on one wrapping line and a
   44px box per link makes the line 88px tall the moment it wraps on a phone.
   The row spacing (--s1) keeps a 40px box clear of its neighbour above, and
   this is the footer, not a control. */

/* The accounts. Pills rather than a second dotted line, on the darker felt
   .mast-clock uses, at .mover's size -- the shape the site already has for a
   small standalone chip of mono text on the felt. Words, not marks. */
.foot-social {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s4);
}

.foot-social .label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #8fae9f;
  margin-right: var(--s1);
}

.foot-social a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 13px;
  border: 1px solid var(--felt-edge);
  border-radius: 18px;
  background: #0f3729;
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #b7cbc0;
}

.foot-social a:hover {
  border-color: var(--gold);
  background: var(--felt);
  color: #fff;
  text-decoration: none;
}

/* Closing line: the disclaimer and the copyright, mono, because it is the
   same register as the odds timestamps -- reference, not voice. Two spans on
   desktop, stacked centred on a phone. */
.foot-legal {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--felt-edge);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s1) var(--s3);
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: #8fae9f;
}

/* 07-delta.css's second half (docs/designs/07-analysis-felt-specimen.html):
   .analysis-index, the paper half of this delta, lives in `modules` above,
   next to the market grid and party grid it matches. This half is on-felt
   and every ink in it is a raw literal, which the `modules` section's own
   test forbids (it asserts every value there resolves through a token) —
   so it sits here instead, beside .mast-nav/.mast-tabs/.mover, the masthead
   rules that already set felt text in these same raw hex values. Still the
   .box-row cell a .box takes; the band needs no layout change.

   Tokens: no new custom property. #fff, #eef3ef and #8fae9f are the
   literals site.css already uses for text on the felt (.mast-nav,
   .mast-tabs, .mover); the row separator is --felt-edge, the same hairline
   the masthead rows use. This is the first time the felt appears below the
   masthead — deliberately the only module it appears in below it. */

/* Reads as a card dealt onto the page: gold rule across the top, the same
   2px gold the masthead closes with, and square top corners so the rule is
   an edge rather than a stroke around a pill. Sits in .box-row and takes the
   same grid cell a .box takes, so the band needs no change. */
.analysis-felt {
  background: var(--felt);
  border-top: 2px solid var(--gold);
  border-radius: 0 0 var(--radius) var(--radius);
  padding: var(--s3) var(--s4) var(--s4);
}

.analysis-felt .head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--felt-edge);
}

/* .box h2 is 13px/700 in the UI face; this keeps the size and takes the
   display face, because on the felt it is a title on a card and not a label
   on a panel. */
.analysis-felt h2 {
  font-family: var(--font-display);
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  margin: 0;
}

.analysis-felt .note {
  flex: none;
  font-size: var(--fs-micro);
  color: #8fae9f;
}

.analysis-felt a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  min-height: 44px;
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--felt-edge);
  text-decoration: none;
}

.analysis-felt a:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

/* Decoration rather than border, for the same reason as .analysis-index
   .title: the title wraps and a border would run to the date column. */
.analysis-felt .title {
  font-family: var(--font-serif);
  font-size: var(--fs-base);
  line-height: 1.35;
  color: #eef3ef;
  text-decoration: underline;
  text-decoration-color: var(--gold);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  text-wrap: pretty;
}

/* Gold as text is legal here and only here: --gold-ink exists because gold
   fails on paper, and on the felt the plain --gold is the accent that reads.
   The date is the one place on the card where the gold repeats. */
.analysis-felt .when {
  flex: none;
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.analysis-felt a:hover .title {
  color: #fff;
}

/* ---------- masthead stack (ported from mock 5b + 5c) ---------- */

/* Mobile-first: brand and countdown share row one, the nav gets its own
   scrolling row beneath. No JS, so no hamburger — the nav scrolls
   horizontally, the same pattern as .mast-tabs below it. */
.mast-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  padding: var(--s2) var(--s4);
  border-bottom: 1px solid var(--felt-edge);
}

.mast-brand {
  order: 1;
}

.mast-clock {
  order: 2;
  margin-left: auto;
  min-width: 0;
}

.mast-nav {
  order: 3;
  flex: 1 0 100%;
  flex-wrap: nowrap;
  overflow-x: auto;
  white-space: nowrap;
  gap: 0 var(--s4);
  border-top: 1px solid var(--felt-edge);
  padding-top: var(--s1);
  scrollbar-width: none;
}

.mast-nav::-webkit-scrollbar {
  display: none;
}

.mast-nav a {
  flex: none;
}

.mast-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  border: 1px solid #b8893c;
  border-radius: 5px;
  padding: 6px 11px 5px;
  text-decoration: none;
  flex: none;
}

.mast-brand .l1 {
  font-family: var(--font-display);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.14em;
  color: #fff;
}

.mast-brand .l2 {
  font-family: var(--font-display);
  font-size: 0.53125rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.34em;
  color: var(--gold);
  margin-top: 3px;
}

.mast-nav {
  display: flex;
  font-size: var(--fs-small);
  font-weight: 500;
}
.mast-nav a {
  color: #b7cbc0;
  text-decoration: none;
}

.mast-nav a[aria-current] {
  color: #fff;
  border-bottom: 2px solid var(--gold);
}

.mast-clock {
  display: flex;
  align-items: center;
  gap: var(--s1);
  min-height: 32px;
  padding: 0 13px;
  border: 1px solid var(--felt-edge);
  background: #0f3729;
  border-radius: 16px;
  font-size: var(--fs-micro);
  font-weight: 500;
  color: #b7cbc0;
  white-space: nowrap;
}

.mast-clock strong {
  font-family: var(--font-display);
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

/* row 2 — sibling markets in this election */
.mast-tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s5);
  align-items: center;
  padding: 0 var(--s4);
  min-height: 44px;
  font-size: var(--fs-small);
  font-weight: 500;
  border-bottom: 1px solid var(--felt-edge);
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}

.mast-tabs::-webkit-scrollbar {
  display: none;
}

.mast-tabs a {
  color: #8fae9f;
  text-decoration: none;
  flex: none;
}

.mast-tabs a[aria-current] {
  color: #fff;
  border-bottom: 2px solid var(--gold);
  padding: 12px 0;
}

/* row 3 — 24h movers ticker */
.mast-movers {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s4);
  min-height: 44px;
  background: #082019;
  border-bottom: 1px solid var(--felt-edge);
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}

.mast-movers::-webkit-scrollbar {
  display: none;
}

.mast-movers .label {
  flex: none;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #8fae9f;
}

.mover {
  flex: none;
  border: 1px solid var(--felt-edge);
  border-radius: 4px;
  padding: 4px 7px;
  font-family: var(--font-num);
  font-size: var(--fs-micro);
  color: #eef3ef;
  font-variant-numeric: tabular-nums;
  /* A mover is an <a> on the sitewide ticker (each links to its own
     market) and a plain <span> on the per-market one — same look either
     way, no underline, no colour change from being a link. */
  text-decoration: none;
}

.mover .up {
  color: #5fc98e;
}

.mover .down {
  color: #ff7c8b;
}

.mover .flat {
  color: #eef3ef;
}

/* ---------- elections board ----------
   05-delta.css (docs/designs/05-elections-specimen.html): the
   three-elections module, arrangement A — the felt board with three spots.
   Placed here, not in the "modules" section above, because its on-felt
   text is deliberately raw hex (see below) — the same convention the
   masthead stack just above already uses, and "modules" is policed for
   zero raw hex by test_delta_css_section_carries_no_raw_hex.
   No new custom property. Two things worth naming because they are not
   tokens and could look like oversights:

   - On-felt text has no token in site.css. The masthead sets #fff and
     #b7cbc0 as raw hex (.mast-nav a, .mast-clock), and this follows the
     same two values for the same purpose rather than inventing
     --felt-ink / --felt-meta.
   - The gold edge on a felt panel is #b8893c in .featured. Here it is
     rgba(224,180,100,0.55), which resolves visually to the same edge over
     --felt and is derived from --gold rather than restating it. site.css
     already uses rgba(224,180,100,.35) and rgba(224,180,100,.4) for
     exactly this, so this follows the existing habit.

   The chip stack derives its two under-chips from the face colour with
   filter: brightness(), so a stack works with any --party and with the
   neutral felt fallback. No second set of colours to keep in sync. */
.elections-board {
  background: radial-gradient(120% 90% at 50% 0%, var(--felt-edge) 0%, var(--felt) 70%);
  border: 1px solid rgba(224, 180, 100, 0.55);
  border-radius: 0;
  padding: var(--s4);
  margin: var(--s5) calc(-1 * var(--s4));
}

/* The board's own header, not .section-head: that rule is a paper rule with
   a felt-coloured underline, and neither its colour nor its 2px weight
   works on felt. Same arrangement — heading left, note right, rule under
   both — at gold on a dark ground. A board on a hub takes this header and
   no .section-head above it; two headers for one module is the thing to
   avoid. */
.elections-board .head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  border-bottom: 1px solid rgba(224, 180, 100, 0.35);
  padding-bottom: var(--s3);
  margin-bottom: var(--s4);
}

.elections-board .head h2 {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: #fff;
}

/* Date and polling hours. Two lines at desktop, and it must never wrap into
   the heading, hence flex: none. Same size and tracking as .featured
   .marquee span, which is the site's other on-felt label. */
.elections-board .head .when {
  flex: none;
  font-size: 0.59375rem;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--gold);
  text-align: right;
}

/* One column on a phone, three from 40em. Not the 1/2/3 stepping of
   .market-grid: there are exactly three elections, always, and a 2 + 1
   arrangement reads as one of them being different in kind. Three or one. */
.spots {
  display: grid;
  gap: var(--s4);
  grid-template-columns: 1fr;
}

/* One spot, one election, one click target — a single <a>, so it inherits
   neither colour nor underline, same convention as .featured-link and
   .market-card. At 360px a spot is 302px wide and about 230px tall, so the
   44px minimum is satisfied by the spot itself; no inner link. */
.spot {
  /* :root sets --party to --meta, so a spot with no party colour would come
     out grey. The two reference spots are felt, not a party, so the spot
     resets --party for itself; the priced spot's inline --party overrides
     this the same way it does everywhere else on the site. */
  --party: var(--felt-edge);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  background: var(--felt);
  border: 1px solid rgba(224, 180, 100, 0.55);
  border-radius: var(--radius);
  padding: var(--s3);
  color: inherit;
  text-decoration: none;
}

.spot:hover {
  border-color: var(--gold);
  color: inherit;
}

/* Predicted / Reference. The one word that says whether the numbers below
   are a market's opinion or the electoral system's arithmetic, and the
   distinction the whole module exists to make. Gold on the priced election,
   on-felt meta on the other two. */
.spot .tag {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #b7cbc0;
}

.spot.priced .tag {
  color: var(--gold);
}

/* The chip stack. Three circles, the back two offset down-right, the face
   carrying a count and the unit it counts. It is a denomination, in the
   poker sense: how much of this election there is to read — five markets,
   twenty councils, two hundred and ninety councils. It is never a
   probability. A probability on the face would need a label, and the label
   is the reason this module was redrawn. */
.spot .denom {
  position: relative;
  width: 84px;
  height: 84px;
  margin: var(--s3) 0;
}

.spot .denom span {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--party);
}

/* The two chips under the face, derived from the face colour rather than
   given their own values, so the stack works for any --party and for the
   neutral fallback the region and municipal spots take. */
.spot .denom .back {
  transform: translate(5px, 6px);
  filter: brightness(0.72);
}

.spot .denom .mid {
  transform: translate(2px, 3px);
  filter: brightness(0.86);
}

.spot .denom .face {
  display: grid;
  place-items: center;
  align-content: center;
  border: 9px dashed rgba(255, 255, 255, 0.7);
  box-shadow: 0 0 0 3px rgba(224, 180, 100, 0.25);
}

/* The dashed ring and the gold halo are brighter on the priced spot — same
   ornament, more of it, which is the whole of the hierarchy between the
   three spots. Everything else about them is identical. */
.spot.priced .denom .face {
  box-shadow: 0 0 0 3px rgba(224, 180, 100, 0.55);
}

.spot .denom .num {
  position: static;
  background: none;
  border-radius: 0;
  font-family: var(--font-display);
  font-size: 1.625rem;
  font-weight: 700;
  line-height: 1;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.spot.priced .denom .num {
  color: #fff;
}

/* 7.5px uppercase, held inside the chip's dashed ring by the padding — the
   longest unit word is "municipalities" and at 8px it touched the ring.
   Wider than the ring even at this size, so it wraps rather than
   overflowing past the circle on one side — `overflow-wrap` is what keeps
   it centred instead of spilling out unevenly; `text-align` is explicit
   here too rather than relying on the inherited value, since a wrapped
   word is exactly the case a stray override elsewhere would show on. */
.spot .denom .unit {
  position: static;
  background: none;
  border-radius: 0;
  margin-top: 2px;
  padding: 0 8px;
  font-size: 0.46875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  overflow-wrap: anywhere;
  color: #b7cbc0;
}

.spot.priced .denom .unit {
  color: rgba(255, 255, 255, 0.85);
}

.spot h3 {
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 700;
  line-height: 1.25;
  color: #fff;
  margin: 0;
}

/* One line saying what the election decides. Every spot has one and they
   are the same shape: what the council does, then its threshold. This is
   the line that was missing when the face carried a bare percentage — a
   reader who does not already know what a region council is got a number
   and no subject. Taken from the election's own opening_sentence and
   how_it_works; not written per page. */
.spot .tagline {
  font-size: var(--fs-small);
  line-height: 1.35;
  color: #b7cbc0;
  margin: var(--s1) 0 var(--s3);
}

/* What is inside: market titles on the priced election, the largest places
   on the other two. Pills rather than a list because they are examples, not
   the set — the spot's link is the set. Wraps to as many lines as it needs;
   a spot with two pills and a spot with four are not aligned to each other
   and do not need to be, because the aligned thing is the way-through
   below. */
.spot .pills {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 3px;
  margin-bottom: var(--s3);
}

.spot .pills span {
  font-size: 0.6875rem;
  color: #b7cbc0;
  border: 1px solid var(--felt-edge);
  border-radius: 20px;
  padding: 2px 6px;
}

/* margin-top: auto puts every way-through on the same baseline across the
   row, whatever the pills above it did. Gold underline, not a button: the
   whole spot is already the target and this marks where it goes. Its text
   names the destination and does not repeat the count on the chip. */
.spot .cta {
  margin-top: auto;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--gold);
  border-bottom: 2px solid var(--gold);
  padding-bottom: 2px;
}

/* The board's footnote. Says which of the three has markets and where the
   other two get their numbers, which is the one thing a reader can
   otherwise only infer from the absence of a percentage. */
.elections-board .foot {
  font-size: var(--fs-micro);
  color: #b7cbc0;
  text-align: center;
  max-width: none;
  margin: var(--s4) 0 0;
}

/* ---------- featured panel ---------- */

/* Reads as an extension of the felt header: same dark ground, inset from
   the header's edges, no gap between it and the movers row above. */
.featured {
  margin: var(--s4);
  border: 1px solid #b8893c;
  border-radius: 10px;
  background: #0f3729;
  padding-bottom: var(--s4);
}

/* election_hub's elections_row panel is the one .featured usage that sits
   directly in .main rather than inside the already full-bleed .masthead, so
   its own margin:s4 only insets it further instead of reaching the page
   edges. Break it out the same way .elections-board does, square corners
   included; the masthead-housed featured panel (chrome.j2) is untouched. */
.main > .featured {
  margin-inline: calc(-1 * var(--s4));
  border-radius: 0;
}

/* The whole panel is one click target — a single <a> wrapping everything,
   not per-element links, so it must not look or behave like inline text. */
.featured-link {
  display: block;
  color: inherit;
  text-decoration: none;
}

/* bulb-dot rule — the one ornament, reserved for this panel */
.featured .bulbs {
  height: 6px;
  margin: 8px 10px 0;
  background: radial-gradient(circle at 3px 3px, #e0b464 1.7px, transparent 2.1px)
    0 0 / 18px 6px repeat-x;
  opacity: 0.85;
}

.featured .marquee {
  text-align: center;
  padding: var(--s3) var(--s4) 0;
}

.featured .marquee span {
  display: inline-block;
  font-size: 0.59375rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--gold);
  border-top: 1px solid rgba(224, 180, 100, 0.4);
  border-bottom: 1px solid rgba(224, 180, 100, 0.4);
  padding: 5px 9px;
}

.featured h2 {
  color: #fff;
  font-size: var(--fs-h1);
  line-height: 1.2;
  letter-spacing: -0.015em;
  margin: var(--s3) 0 var(--s4);
  text-align: center;
  padding: 0 var(--s4);
}

.featured .body {
  padding: 0 var(--s4);
}

.bloc-legend {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-micro);
  font-weight: 600;
  margin-bottom: 7px;
  color: #d8c9a8;
}

/* `.left`/`.right` are layout order, not a political side -- any real
   colour comes inline from the outcome's own party mapping (P465), same
   convention as `.bloc-card`/`.bloc-bar` below. An outcome with no such
   mapping (this panel's usual case) keeps this rule's neutral colour. */

.bloc-bar {
  display: flex;
  height: 13px;
  border-radius: 7px;
  overflow: hidden;
  gap: 2px;
  border: 1px solid rgba(224, 180, 100, 0.35);
}

.bloc-cards {
  display: grid;
  gap: var(--s3);
  margin-top: var(--s3);
}

.bloc-card {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 56px;
  background: var(--felt);
  border: 1px solid var(--felt-edge);
  border-radius: 9px;
  padding: 10px 12px;
  text-decoration: none;
}

.bloc-card .name {
  display: block;
  font-size: var(--fs-small);
  font-weight: 600;
  color: #fff;
}

.bloc-card .price {
  font-family: var(--font-display);
  display: block;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.bloc-card .price span {
  font-weight: 400;
  color: #b99a63;
}

/* 05-delta.css: arrangement B, the row for a row of an election rather than
   a row of a bloc. .featured, .bulbs, .marquee, .featured h2, .bloc-cards,
   .bloc-card and .chip above are all shipping rules, used unchanged; these
   four are what a row of an election needs beyond them: a second meta line
   under the name, a right-hand column, the way-through in it, and the
   words for an election that has no market. */
.bloc-card .text {
  flex: 1;
}

.bloc-card .sub {
  display: block;
  font-size: var(--fs-micro);
  color: #b7cbc0;
  margin-top: 2px;
}

.bloc-card .right {
  flex: none;
  text-align: right;
}

.bloc-card .way {
  display: block;
  font-size: var(--fs-micro);
  font-weight: 700;
  color: var(--gold);
  border-bottom: 2px solid var(--gold);
  padding-bottom: 2px;
}

/* "no market" is a fact about the election, not a missing value, and it is
   set in the meta colour so it does not read as a price. */
.bloc-card .absent {
  display: block;
  font-size: var(--fs-micro);
  color: #b7cbc0;
  margin-top: 5px;
}

/* The row for the election whose own page this is. Same panel, gold edge,
   no link — docs/prompts/44: it carries aria-current and is a <div>. */
.bloc-card[aria-current] {
  border-color: var(--gold);
}

/* poker-chip party mark: 8-segment dashed inner ring via conic-gradient */
.chip {
  position: relative;
  font-family: var(--font-display);
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--party);
  border: none;
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 0.6875rem;
  font-weight: 700;
}
/* inset: 0 keeps this the same box as .chip itself, so "circle closest-side"
   sizes the mask off the chip's own radius (no farthest-corner sqrt(2) skew)
   and the ring lands at the same proportional inset from the edge whatever
   the chip's width — .sm, the board's default size and .titleline's larger
   one all read alike instead of each needing its own tuned ring. */
.chip::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    conic-gradient(
      rgba(255,255,255,0.65) 0%     7.5%,
      transparent            7.5%   12.5%,
      rgba(255,255,255,0.65) 12.5%  20%,
      transparent            20%    25%,
      rgba(255,255,255,0.65) 25%    32.5%,
      transparent            32.5%  37.5%,
      rgba(255,255,255,0.65) 37.5%  45%,
      transparent            45%    50%,
      rgba(255,255,255,0.65) 50%    57.5%,
      transparent            57.5%  62.5%,
      rgba(255,255,255,0.65) 62.5%  70%,
      transparent            70%    75%,
      rgba(255,255,255,0.65) 75%    82.5%,
      transparent            82.5%  87.5%,
      rgba(255,255,255,0.65) 87.5%  95%,
      transparent            95%    100%
    );
  mask: radial-gradient(circle closest-side, transparent 68%, #000 68% 100%);
  z-index: 0;
}

.chip.sm {
  width: 26px;
  height: 26px;
  font-size: 0.59375rem;
}

/* Same ring, thinner band: at 26px the 32%-of-radius band the other sizes
   use (~4.2px) reads heavy, so .sm gets its own inner stop for a ~3px band
   instead of scaling proportionally with the rest. */
.chip.sm::after {
  mask: radial-gradient(circle closest-side, transparent 77%, #000 77% 100%);
}

/* ---------- headline number ---------- */

.hero-figure {
  border-left: 4px solid var(--party);
  padding-left: var(--s4);
  margin: 0 0 var(--s5);
}

.hero-figure .label {
  font-size: var(--fs-micro);
  font-weight: 500;
  color: var(--meta);
}

.hero-figure .figure {
  font-family: var(--font-display);
  font-size: 2.875rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin: var(--s1) 0 2px;
}

.hero-figure .who {
  font-size: 0.90625rem;
  font-weight: 500;
  color: var(--ink);
}

.hero-figure .who .delta {
  font-size: var(--fs-micro);
  font-weight: 500;
}

/* Direction, not a fixed tone (item 14 of the user's feedback round) —
   a 24h drop must not read as green just because `--fresh` and `--up`
   happen to share one value. */
.hero-figure .who .delta.up {
  color: var(--up);
}

.hero-figure .who .delta.down {
  color: var(--down);
}

.hero-figure .who .delta.flat {
  color: var(--ink);
}

.hero-figure .spread {
  font-size: var(--fs-small);
  color: #5a5648;
  margin-top: var(--s2);
}

/* gold pill — one per page, the board's headline fact */
.spread-pill {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--gold-label);
  background: #f7eccf;
  border: 1px solid #ddc492;
  border-radius: 14px;
  padding: 5px 11px;
}

/* ---------- expandable board ---------- */

.board-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  border-bottom: 2px solid var(--felt-edge);
  padding-bottom: 7px;
  margin-bottom: 0;
}

.board-head h2 {
  font-family: var(--font-display);
  font-size: 0.8125rem;
  font-weight: 700;
  margin: 0;
}

.board-note {
  font-size: var(--fs-micro);
  color: var(--meta);
  margin: var(--s2) 0 0;
}

/* Column headings mirror the summary grid at every breakpoint, so they must
   track the same three track definitions. */
.board-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 68px 52px 14px;
  gap: var(--s2);
  padding: var(--s3) 0 var(--s2);
  border-bottom: 1px solid var(--rule);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--meta);
}

.board-cols .r {
  text-align: right;
}

.board-cols .col-bar,
.board-cols .col-spread {
  display: none;
}

.board-row {
  border-bottom: 1px solid var(--rule);
  border-top: 0;
}

.board-row > summary {
  list-style: none;
  cursor: pointer;
  min-height: 44px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 68px 52px 14px;
  align-items: center;
  gap: var(--s2);
  padding: 12px 0;
  font-weight: 400;
  color: inherit;
}

.board-row > summary::-webkit-details-marker {
  display: none;
}

.board-row .who {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ink);
}

/* Always black, link or not, visited or not — an explicit rule on the <a>
   itself (the global `a { color }` rule, or the browser's default visited
   colour) would otherwise win over the inherited colour above. */
.board-row .who a,
.board-row .who a:visited {
  color: inherit;
  text-decoration: none;
}

/* Swedish party names are long compounds; let them wrap rather than force
   the grid track wider than the viewport. */
.board-row .who span:last-child {
  min-width: 0;
  overflow-wrap: break-word;
}

.board-row .pct {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.board-row .dec {
  font-family: var(--font-num);
  font-size: var(--fs-small);
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.board-row .chev {
  font-size: 1.375rem;
  line-height: 1;
  color: var(--felt);
  text-align: right;
}

.board-row[open] .chev {
  transform: rotate(180deg);
}

.board-row .detail {
  padding: 0 0 var(--s3);
}

.board-row .kv {
  display: flex;
  justify-content: space-between;
  font-size: var(--fs-micro);
  color: #5a5648;
  margin-top: var(--s1);
}

.board-row .kv .num {
  font-family: var(--font-num);
}

.bar-share {
  position: relative;
  height: 10px;
  background: var(--rule-faint);
  border-radius: 5px;
  margin-bottom: var(--s2);
}

/* The summary bar exists only where the 7-track grid has a column for it
   (see 72em). Hidden by default so the 4-track grid never gets a 5th child. */
.board-row > summary .bar-share {
  display: none;
}

.bar-share .fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 10px;
  background: var(--party);
  border-radius: 5px;
}

.board-row .detail .data-table {
  margin-top: var(--s3);
}

.board-row .detail .data-table th,
.board-row .detail .data-table td {
  font-size: var(--fs-micro);
}

/* ---------- analysis panel ---------- */

.analysis-panel {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: var(--s4) var(--s4) var(--s5);
  margin-top: var(--s5);
}

.analysis-panel h3 {
  font-family: var(--font-serif);
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 0 0 var(--s2);
  max-width: 44ch;
}

.analysis-panel p {
  font-family: var(--font-serif);
  font-size: 0.875rem;
  line-height: 1.6;
  color: #2c3440;
  margin: 0;
}

/* ---------- mobile table reflow ---------- */

@media (max-width: 39.999em) {
  .data-table,
  .data-table thead,
  .data-table tbody,
  .data-table tr,
  .data-table th,
  .data-table td {
    display: block;
  }

  .data-table thead {
    display: none;
  }

  .data-table tr {
    padding: var(--s2) 0;
    border-bottom: 1px solid var(--rule);
  }

  .data-table td {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--s4);
    border-bottom: 0;
    padding: 2px 0;
    text-align: left;
  }

  /* The bar encodes the same number as the text, so it must survive the
     reflow. As a flex child with no basis it would collapse to 0 — give it
     its own full-width line under the figure. */
  .data-table td > .bar {
    flex: 1 0 100%;
    margin-top: var(--s1);
  }

  /* label each cell from the header text carried in data-label */
  .data-table td::before {
    content: attr(data-label);
    color: var(--meta);
    font-size: var(--fs-micro);
    flex: none;
  }

  .data-table td:first-child {
    font-weight: 600;
    color: var(--ink);
  }

  .data-table td:first-child::before {
    content: none;
  }

  .board-row .spread {
    display: none;
  }

  /* compressed tail stays one line per row even reflowed */
  .results-table tr.minor {
    display: flex;
    justify-content: space-between;
    padding: 2px 0;
    border-bottom: 0;
  }

  .results-table tr.minor td {
    display: inline;
    padding: 0;
  }

  .results-table tr.minor td::before {
    content: none;
  }

  .results-table tr.minor td:not(:first-child)::after {
    content: " ";
  }
}

/* ---------- desktop ---------- */

@media (min-width: 40em) {
  body {
    max-width: 60rem;
    padding-inline: var(--s5);
  }

  /* 08-delta.css: the footer's felt band matches .masthead's inline margins
     at this width; with no drawn separator, space does the separating on
     .foot-line -- wider where the row holds on one or two lines. */
  .site-foot {
    margin-inline: calc(-1 * var(--s5));
    padding: var(--s6) var(--s5) var(--s5);
  }

  .foot-line {
    gap: var(--s2) var(--s5);
  }

  h1 {
    font-size: 2.125rem;
  }

  .masthead {
    margin-inline: calc(-1 * var(--s5));
  }

  .mast-top,
  .mast-tabs,
  .mast-movers {
    padding-inline: var(--s5);
  }

  /* one row again: brand, nav, clock */
  .mast-top {
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: var(--s5);
    min-height: 58px;
  }

  .mast-nav {
    order: 2;
    flex: 0 1 auto;
    border-top: 0;
    padding-top: 0;
    margin-right: auto;
  }

  .mast-clock {
    order: 3;
    margin-left: 0;
  }

  .contents ol {
    columns: 2;
    column-gap: var(--s5);
  }

  /* Authored at 280px so phone labels are 10px; capped here so the same
     labels do not scale past the surrounding prose on desktop. Specificity
     raised past the svg[role="img"] rule later in this block. */
  .analysis-panel figure svg[role="img"] {
    max-width: 380px;
  }

  /* Same cap, same reason, the market chart's own scale (460px keeps its
     largest label at or under body prose -- design-brief.md). Specificity
     raised past the svg[role="img"] rule later in this block, same as the
     analysis-panel rule above. */
  .market-chart svg[role="img"] {
    max-width: 460px;
  }

  .featured {
    margin: var(--s5);
  }

  .main > .featured {
    margin-inline: calc(-1 * var(--s5));
  }

  /* Title left, headline number right, at the same level — the mock's
     pairing. The body below runs full width. */
  .page-head {
    display: grid;
    grid-template-columns: 1.2fr 0.8fr;
    gap: var(--s5);
    align-items: start;
  }

  .page-head .hero-figure {
    margin-top: 0;
  }

  /* Rail comes FIRST in source so a phone meets the headline number before
     the criteria; on desktop order puts it back in the right column. */
  svg[role="img"] {
    max-width: 100%;
  }

  .bloc-cards {
    grid-template-columns: 1fr 1fr;
  }

  .featured h2 {
    font-size: 1.75rem;
  }

  /* Five columns from 640px: outcome, bar, consensus, odds, chevron. Both
     the outcome and the bar are flexible, so the row fills the full measure
     instead of leaving a dead gap before the numbers. Venue spread and gap
     wait for the seven-column set below. */
  .board-row > summary,
  .board-cols {
    grid-template-columns: minmax(0, 1.1fr) minmax(110px, 1.3fr) 84px 60px 18px;
    gap: var(--s3);
  }

  .board-row > summary {
    padding: 13px 0;
  }

  .board-row > summary .bar-share {
    display: block;
    margin-bottom: 0;
  }

  .board-cols .col-bar {
    display: block;
  }

  .board-row .detail > .bar-share {
    display: none;
  }

  .board-row .spread {
    display: none;
  }

  .board-row .pct {
  font-family: var(--font-display);
    font-size: 1.1875rem;
  }

  .board-row .spread {
    font-family: var(--font-num);
    font-size: var(--fs-micro);
    text-align: right;
    color: #5a5648;
  }

  /* The second of the row's two `.spread` spans -- the point-width figure,
     adjacent in the DOM to the range span above -- reads bolder and larger,
     the same distinction the two carried under their old, separate class
     names. */
  .board-row .spread + .spread {
    font-weight: 600;
    font-size: var(--fs-small);
  }

  .board-row .detail {
    padding: 2px 0 18px 14px;
    border-left: 2px solid var(--rule);
    margin-bottom: 6px;
  }

  .hero-figure {
    margin-bottom: 0;
  }

  .analysis-panel {
    padding: var(--s5) var(--s5) var(--s6);
  }

  .analysis-panel h3 {
    font-size: 1.4375rem;
  }

  .analysis-panel p {
    font-size: 0.9375rem;
  }

  .market-grid,
  .analysis-grid {
    grid-template-columns: 1fr 1fr;
  }

  /* 05-delta.css: the three-elections board, three spots side by side. */
  .spots {
    grid-template-columns: 1fr 1fr 1fr;
    gap: var(--s5);
  }

  .elections-board {
    padding: var(--s5) var(--s5) var(--s4);
    margin-inline: calc(-1 * var(--s5));
  }
}

@media (min-width: 60em) {
  .market-grid,
  .analysis-grid {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

/* An analysis post gets its own, wider paper at this width, so the 93ch
   measure above has somewhere to sit with margin on both sides instead of
   hugging the left edge -- and so the share rail has a column to sit in,
   beside the text, rather than trailing it at the foot of the post. */
@media (min-width: 60em) {
  body.post-page {
    max-width: 74rem;
    padding-inline: var(--s6);
  }

  .post-layout {
    grid-template-columns: minmax(0, 1fr) 11rem;
  }
}

/* 06-delta.css (docs/designs/06-parties-specimen.html). */
@media (min-width: 40em) {
  .party-grid {
    grid-template-columns: 1fr 1fr;
  }

  .person-list {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--s5);
  }
}

@media (min-width: 60em) {
  .party-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ---------- wide: the mock's full board ---------- */

/* Seven columns: outcome, bar, consensus, odds, spread, gap, chevron. The
   fixed tracks total 548px + 48px of gaps; the bar takes the remainder, so
   the row always fills the measure and the number columns still line up
   across rows (each <details> is its own grid, so only the bar may flex).
   Gated at 52em, the width where 596px + a 110px bar first fits. */
@media (min-width: 52em) {
  .box-row {
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  }

  .hub-split {
    grid-template-columns: 2fr 1fr;
  }

  .analysis-panel .cols {
    display: grid;
    grid-template-columns: 1fr 400px;
    gap: var(--s5);
    align-items: start;
  }

  .analysis-panel .cols figure {
    margin: 0;
  }

  .board-row > summary,
  .board-cols {
    grid-template-columns: 200px minmax(110px, 1fr) 90px 70px 100px 70px 18px;
    gap: var(--s2);
  }

  .board-cols .col-spread {
    display: block;
  }

  .board-row .spread {
    display: block;
  }

  .post .provenance,
  .contents {
    max-width: var(--measure);
  }
}

@media (min-width: 72em) {
  body {
    max-width: 72rem;
  }
}

/* ---------- print ---------- */

@media print {
  .mast-nav,
  .mast-tabs,
  .mast-movers,
  .mast-clock {
    display: none;
  }

  body {
    max-width: none;
    background: #fff;
  }

  details {
    display: block;
  }

  /* A post is the page type most likely to be quoted from a PDF. Keep the
     contents list out of it -- printed anchors are dead -- and let
     sections break where they must. */
  .contents {
    display: none;
  }

  .post h2 {
    break-after: avoid;
  }

  details > *:not(summary) {
    display: block !important;
  }

  /* 08-delta.css: the felt is a screen surface; on paper it is a solid block
     of ink across the last page. The footer still prints -- the URL of the
     site and the disclaimer are the reason -- as the rule and the type only.
     The share row is a set of controls; on paper it is nothing. The URL
     stays -- printed, it is the only way back to the page. */
  .site-foot {
    background: none;
    color: var(--body);
    border-top: 1px solid var(--ink);
    margin: var(--s5) 0 0;
    padding: var(--s3) 0 0;
  }

  .site-foot a {
    color: var(--body);
  }

  .site-foot .bulbs {
    display: none;
  }

  .site-foot .mast-brand {
    border-color: var(--ink);
  }

  .site-foot .mast-brand .l1 {
    color: var(--ink);
  }

  .site-foot .mast-brand .l2 {
    color: var(--body);
  }

  .share-row,
  .share .label,
  .foot-social {
    display: none;
  }
}

/* site.css's print block above forces every child of an open <details> to
   display:block, which is right for the board rows and wrong for this one:
   the tail is a <table>, and blocking it drops its columns so the two
   halves of one official result print as two differently-shaped tables.
   Restore table layout inside the tail only. A citable archive gets quoted
   by print and PDF, and on a place page the result is the whole page. */
@media print {
  .results-tail table,
  .results-tail thead,
  .results-tail tbody,
  .results-tail tr,
  .results-tail th,
  .results-tail td {
    display: revert !important;
  }
}
