/* ==========================================================================
   Mzalendo shared component layer.
   One source of truth for cards, buttons, fields, tabs, badges, stats, tables,
   empty-states and timelines — so every page (members, legislation, sentiment,
   chat, home) looks consistent. All values come from core.css tokens.
   ========================================================================== */

/* ---- Section rhythm ---- */
.section { margin: var(--space-6) 0; }
.section--tight { margin: var(--space-5) 0; }
/* `.section` is the intended rhythm primitive but only ~17 of ~180 content
   templates ever adopted it; the rest name a section and rely on the component
   inside it to bring its own margin, which fails for the section box itself.
   Combined with `* { margin: 0 }` that produced 56 adjacent blocks under 16px
   apart across 25 pages (2026-08-02).

   Rather than editing ~100 templates, give any <section> that follows another
   the same gap. Scoped to DIRECT children of a container on purpose: a bare
   `section + section` would also hit sections nested inside cards, panels and
   tab bodies, where the spacing is deliberate. This is why `.house-header` and
   `.na-position-holders` on /mps-performance/national-assembly/ — neither of
   which has any CSS rule of its own — now separate correctly.

   `:where()` is load-bearing, not decoration. Unwrapped, this selector is
   (0,1,2) and OUTRANKS every component that declares its own margin at (0,1,0)
   — it would have silently forced `.section--tight` from 24px to 32px (two
   adjacent ones exist in tazabu/certified_hansard_index.html) and overridden
   `.ab-band`'s 2.8rem. `:where()` drops it to (0,0,0), so it still beats the
   `* { margin: 0 }` reset — components.css loads after core.css — while losing
   to anything that states a real intent. A fallback, not an override. */
:where(.container > section + section) { margin-top: var(--space-6); }
/* Named like a section, styled like nothing: these appear in templates but
   match no rule anywhere, so they contributed 0px. `.detail-section` alone is
   used in 10 templates and is a <div>, so the section fallback above cannot
   reach it. Bottom-only, matching `.committee-group` / `.region-section` /
   `.data-table` — a symmetric margin would ADD to `.site-content`'s row-gap
   rather than collapse, because flex items do not collapse margins. */
.detail-section,
.house-header,
.na-position-holders,
.senate-position-holders,
.senators-section,
/* `.metric-band` (entity/_metric_comparison.html) needs this even though it IS
   a <section>: the fallback above is `:where()`, so `.region-section`'s
   bottom-only `margin: 0 0 …` legitimately wins on the county pages and leaves
   the pair with 3px of ink. The band owning its trailing margin is the fix. */
.metric-band { margin-bottom: var(--space-6); }
.section__title,
.section-heading,
h2.header-two,
.leadership-heading,
.parliament-section--heading,
.our-work--section-heading { font-size: var(--text-xl); font-weight: var(--fw-bold); color: var(--colour-text); margin: 0 0 var(--space-4); }
.section__title--accent { border-left: 4px solid var(--colour-primary); padding-left: var(--space-3); }
.section__lead { color: var(--colour-text-muted); margin: 0 0 var(--space-5); }

/* ---- Buttons ---- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-5); border: 1px solid transparent; border-radius: var(--radius-pill);
  font: inherit; font-weight: var(--fw-semibold); line-height: 1; cursor: pointer; text-decoration: none;
  transition: background-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard),
    border-color var(--motion-fast) var(--ease-standard), box-shadow var(--motion-fast) var(--ease-standard),
    transform var(--motion-fast) var(--ease-standard); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }
.btn[disabled], .btn.is-disabled { opacity: .55; pointer-events: none; box-shadow: none; }
.btn--primary { background: var(--colour-primary); color: #fff; }
.btn--primary:hover { background: var(--colour-primary-dark); box-shadow: var(--shadow-md); }
.btn--secondary { background: var(--colour-secondary); color: #fff; }
.btn--secondary:hover { background: var(--colour-secondary-dark); box-shadow: var(--shadow-md); }
.btn--ghost { background: transparent; color: var(--colour-primary); border-color: var(--colour-border-strong); }
.btn--ghost:hover { border-color: var(--colour-primary); color: var(--colour-primary); background: var(--colour-primary-alpha-10); }
.btn--sm { padding: var(--space-2) var(--space-4); font-size: var(--text-sm); }
.btn--lg { padding: var(--space-4) var(--space-6); font-size: var(--text-lg); }

/* ---- Form fields ---- */
.field { font: inherit; padding: var(--space-3) var(--space-4); border: 1px solid var(--colour-border-strong);
  border-radius: var(--radius-pill); background: #fff; color: var(--colour-text); }
.field:focus { outline: none; border-color: var(--colour-primary); box-shadow: 0 0 0 3px var(--colour-primary-alpha-20); }
.field--block { width: 100%; }
.field-label { display: block; font-size: var(--text-sm); font-weight: 600; color: var(--colour-text-muted); margin-bottom: var(--space-1); }
.filter-bar,
.filter-form { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: end; margin: 0 0 var(--space-5); }
.filter-bar .grow,
.filter-form .grow { flex: 1 1 18rem; }
/* Self-sufficient children: bare label/input/select/button inside a filter bar
   get the canonical field/button treatment, so consumers need no per-element
   classes. .filter-form is the same component under its legacy tazabu name. */
.filter-bar > div,
.filter-form > div { display: flex; flex-direction: column; }
.filter-bar label,
.filter-form label { display: block; font-size: var(--text-xs); font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: .03em; color: var(--colour-text-muted); margin-bottom: var(--space-1); }
.filter-bar input, .filter-bar select,
.filter-form input, .filter-form select { padding: var(--space-2) var(--space-3); border: 1px solid var(--colour-border-strong); border-radius: var(--radius-md); font: inherit; background: var(--colour-bg); color: var(--colour-text); min-height: 2.5rem; transition: border-color var(--motion-fast, .15s) var(--ease-standard), box-shadow var(--motion-fast, .15s) var(--ease-standard); }
.filter-bar input:focus, .filter-bar select:focus,
.filter-form input:focus, .filter-form select:focus { outline: none; border-color: var(--colour-primary); box-shadow: 0 0 0 3px var(--colour-primary-alpha-20); }
.filter-bar button[type="submit"],
.filter-form button[type="submit"] { padding: var(--space-2) var(--space-5); min-height: 2.5rem; border: 1px solid transparent; border-radius: var(--radius-pill); background: var(--colour-primary); color: #fff; font: inherit; font-weight: var(--fw-semibold); line-height: 1; cursor: pointer; transition: background-color var(--motion-fast, .15s) var(--ease-standard), box-shadow var(--motion-fast, .15s) var(--ease-standard); }
.filter-bar button[type="submit"]:hover,
.filter-form button[type="submit"]:hover { background: var(--colour-primary-dark); box-shadow: var(--shadow-md); }
.filter-bar button[type="submit"]:focus-visible,
.filter-form button[type="submit"]:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }

/* ---- Cards ---- */
.card { background: var(--colour-bg); border: 1px solid var(--colour-border); border-radius: var(--radius-lg);
  overflow: hidden; transition: box-shadow .15s ease, transform .15s ease; }
.card--link { display: block; text-decoration: none; color: inherit; }
.card--link:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.card--link:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; box-shadow: var(--shadow-md); }
.card--pad { padding: var(--space-4) var(--space-5); }
.card__title { font-size: var(--text-lg); font-weight: var(--fw-bold); line-height: var(--leading-snug); color: var(--colour-text); margin: 0 0 var(--space-2); letter-spacing: -0.008em; }
.card__meta { font-size: var(--text-sm); color: var(--colour-text-muted); display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
.card__meta a { color: var(--colour-primary); text-decoration: none; }

/* Data/chart card grid — capped at 3 columns (charts read poorly at 4+), responsive. */
.card-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 560px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 920px) { .card-grid { grid-template-columns: repeat(3, 1fr); } }
.card-grid--wide { --card-min: 260px; }

/* Portrait/member card */
.member-card { display: block; text-decoration: none; color: inherit; background: #fff;
  border: 1px solid var(--colour-border); border-radius: var(--radius-lg); overflow: hidden;
  transition: box-shadow .15s ease, transform .15s ease; }
.member-card:hover,
.member-card:focus-visible { box-shadow: var(--shadow-md); transform: translateY(-2px); outline: 2px solid var(--colour-primary); outline-offset: 2px; }
.member-card:focus-visible .member-card__name { color: var(--colour-primary); }
.member-card:hover .member-card__name { color: var(--colour-primary); }
.member-card__img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; background: var(--colour-bg-subtle); display: block; }
.member-card__body { padding: var(--space-3) var(--space-4); }
.member-card__name { font-weight: 700; line-height: 1.25; }
.member-card__sub { font-size: var(--text-sm); color: var(--colour-text-muted); margin-top: var(--space-1); }

/* ---- Badges ---- */
.badge { display: inline-block; padding: 1px 9px; border-radius: var(--radius-pill);
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
  background: var(--colour-primary-alpha-10); color: var(--colour-primary); }
.badge--green { background: var(--colour-secondary-alpha-10); color: var(--colour-secondary-dark); }
.badge--muted { background: var(--colour-lighter); color: var(--colour-text-muted); }
.badge--solid { background: var(--colour-primary); color: #fff; }

/* ---- Tabs / sub-nav ---- */
.tabs { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5);
  border-bottom: 2px solid var(--colour-border); margin: 0 0 var(--space-5); }
.tabs a { display: inline-block; padding: var(--space-3) var(--space-1); color: var(--colour-text);
  text-decoration: none; font-weight: 600; border-bottom: 3px solid transparent; }
.tabs a:hover { color: var(--colour-primary); }
.tabs a.is-active { color: var(--colour-primary); border-bottom-color: var(--colour-primary); }
.tabs a:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; border-radius: var(--radius-sm); }
.tabs a .count { opacity: .6; font-weight: 400; margin-left: var(--space-1); }

/* Pill-style filter row (alt to underline tabs) */
.pills { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-5); }
.pills a { padding: var(--space-2) var(--space-4); border: 1px solid var(--colour-border-strong);
  border-radius: var(--radius-pill); text-decoration: none; color: var(--colour-text); font-size: var(--text-sm); font-weight: 600;
  transition: background-color var(--motion-fast) var(--ease-standard), border-color var(--motion-fast) var(--ease-standard), color var(--motion-fast) var(--ease-standard); }
.pills a:hover { border-color: var(--colour-primary); color: var(--colour-primary); }
.pills a:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }
.pills a.is-active { background: var(--colour-primary); color: #fff; border-color: var(--colour-primary); }
.pills a.is-active:hover { color: #fff; }
.pills a .count, .pills a .n { opacity: .6; font-weight: var(--fw-regular); margin-left: var(--space-1); }

/* ---- Meter (proportional magnitude bar) ---- */
.meter { height: 6px; background: var(--colour-lighter); border-radius: var(--radius-pill); overflow: hidden; margin-top: var(--space-2); }
.meter__fill { display: block; height: 100%; background: var(--colour-primary); border-radius: var(--radius-pill); }
.meter--green .meter__fill { background: var(--colour-secondary); }
.meter__stack { display: flex; height: 100%; border-radius: var(--radius-pill); overflow: hidden; }
.meter__seg { display: block; height: 100%; }
.meter__seg--mp { background: var(--colour-primary); }
.meter__seg--sen { background: var(--colour-secondary); }
.meter-legend { display: flex; gap: var(--space-4); font-size: var(--text-sm); color: var(--colour-text-muted); margin: 0 0 var(--space-4); }
.meter-legend i { display: inline-block; width: 11px; height: 11px; border-radius: 2px; margin-right: var(--space-1); vertical-align: middle; }
.meter-legend .mp { background: var(--colour-primary); }
.meter-legend .sen { background: var(--colour-secondary); }

/* ---- Small utilities ---- */
.text-muted { color: var(--colour-text-muted); }
.text-sm { font-size: var(--text-sm); }
.result-count { color: var(--colour-text-muted); font-size: var(--text-sm); margin: 0 0 var(--space-4); }

/* ---- Stats ---- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-4); margin: 0 0 var(--space-5); }
.stat { background: #fff; border: 1px solid var(--colour-border); border-radius: var(--radius-lg); padding: var(--space-5); text-align: center; box-shadow: var(--shadow-sm); }
.stat__num { display: block; font-size: var(--text-2xl); font-weight: var(--fw-extrabold); color: var(--colour-primary); line-height: 1.1; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.stat__label { display: block; margin-top: var(--space-1); font-size: var(--text-sm); color: var(--colour-text-muted); text-transform: uppercase; letter-spacing: .03em; }
/* Optional third line: a caveat about the figure ("Partial corpus — not a
   national total"). Sentence case, not the label's uppercase — it is prose. */
.stat__hint { display: block; margin-top: var(--space-2); font-size: var(--text-xs); color: var(--colour-text-muted); text-transform: none; letter-spacing: 0; }
/* An absent figure keeps the tile's geometry — same size, same slot, so a row
   of tiles still shares one baseline — but drops the emphasis. Rendering
   "Unknown" in extrabold brand red would give an absence the visual weight of
   a measurement. */
.stat__num--unknown { color: var(--colour-text-muted); font-weight: var(--fw-semibold); }

/* ---- Data table ---- */
.data-table { width: 100%; border-collapse: collapse; margin: 0 0 var(--space-6); font-variant-numeric: tabular-nums; }
/* `.data-table` declares a trailing margin only, so on /parties/ (and the two
   /research-and-knowledge/ aliases that render the same template) the bordered
   `.card` above it sat directly on the sticky <thead>. The section fallback
   cannot help — a <table> is not a <section>. Scoped to direct children of a
   content container: verified only party_index.html and party_detail.html put
   a `.data-table` there, so this is surgical rather than global.

   `.table-scroll` is listed too: wrapping the party table for the horizontal-
   overflow fix moved the table out of direct-child position, which silently
   stopped this selector matching. Whichever of the two is the container's
   child needs the leading gap. */
.container > * + .data-table,
.container > * + .table-scroll { margin-top: var(--space-6); }
/* Wide tables (e.g. member comparison) scroll horizontally instead of breaking layout on phones. */
@media (max-width: 600px) { .data-table { display: block; overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; } }
.data-table th, .data-table td { text-align: left; padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--colour-border); font-size: var(--text-sm); }
/* Scoped to `thead` on purpose. This is COLUMN-header styling — uppercase,
   0.72rem, muted — and it was applying to every `th`, including the
   `<th scope="row">` row headers that carry actual data (division subjects,
   party names). That shouted the data in tiny uppercase grey. Row headers are
   content and are styled as content below. */
.data-table thead th { text-transform: uppercase; font-size: 0.72rem; font-weight: var(--fw-bold); color: var(--colour-text-muted); letter-spacing: .03em; }
.data-table tbody th { font-weight: var(--fw-semibold); color: var(--colour-text); }
/* Sticky header for long scrolling tables (opt-in: <thead> stays put). */
.data-table thead th { position: sticky; top: 0; z-index: var(--z-base); background: var(--colour-bg); box-shadow: inset 0 -1px 0 var(--colour-border-strong); }
/* Zebra striping + row hover for scannability. */
.data-table tbody tr:nth-child(even) { background: var(--colour-bg-subtle); }
.data-table tbody tr:hover { background: var(--colour-primary-alpha-10); }
.data-table td.num, .data-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table tbody tr:last-child td { border-bottom: 0; }

/* ---- Empty state ---- */
.empty-state { text-align: center; padding: var(--space-7) var(--space-5); color: var(--colour-text-muted);
  border: 1px dashed var(--colour-border-strong); border-radius: var(--radius-lg); background: var(--colour-bg-subtle); }
.empty-state__icon { font-size: 2rem; color: var(--colour-border-strong); margin-bottom: var(--space-3); }
.empty-state__title { font-weight: 700; color: var(--colour-text); margin-bottom: var(--space-1); }
/* The explanatory sentence is centred, so without a cap it ran the full panel
   width (measured 145 cpl in a 1236px panel). */
.empty-state p { max-width: var(--container-prose); margin-inline: auto; }
/* Compact variant for an empty state that sits INSIDE a panel which already has
   its own heading — the full-size one spent 263px of box on 177 characters,
   which is its own kind of dead space. */
.empty-state--tight { padding: var(--space-5) var(--space-4); }
.empty-state--tight .empty-state__icon { font-size: 1.25rem; margin-bottom: var(--space-2); }

/* ---- Pagination ---- */
.pager { display: flex; gap: var(--space-3); align-items: center; justify-content: center; margin: var(--space-6) 0; }
.pager a { display: inline-flex; align-items: center; padding: var(--space-2) var(--space-4);
  border: 1px solid var(--colour-border-strong); border-radius: var(--radius-pill);
  color: var(--colour-primary); text-decoration: none; font-weight: var(--fw-semibold); transition: all var(--motion-fast) var(--ease-standard); }
.pager a:hover { background: var(--colour-primary); color: #fff; border-color: var(--colour-primary); }
.pager a:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }
.pager span { color: var(--colour-text-muted); font-size: var(--text-sm); }

/* ---- Vertical timeline ---- */
.vtimeline { list-style: none; padding: 0; margin: var(--space-4) 0 0 var(--space-2); border-left: 3px solid var(--colour-border); }
.vtimeline__item { position: relative; padding: 0 0 var(--space-5) var(--space-5); }
.vtimeline__item::before { content: ""; position: absolute; left: -9px; top: .2rem; width: 14px; height: 14px;
  border-radius: 50%; background: var(--colour-primary); border: 3px solid #fff; box-shadow: 0 0 0 1px var(--colour-border); }
.vtimeline__date { font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--colour-text-muted); }

/* ==========================================================================
   Design-system extensions (Appendix H) — layout primitives, card variants,
   avatars, semantic badges, grade pills, subnav. All token-driven.
   ========================================================================== */

/* ---- Layout primitives ---- */
/* `padding: 0` here meant the page <h1> touched the band's top and bottom
   edges on every page that does NOT opt into `has-brand-hero` — measured at 49
   of 75 pages, with `/ai-tools/` rendering the heading at offset 0. Only the
   branded variant below had ever set `padding-block`.

   `margin-bottom` is deliberately GONE: `.site-content` is a flex column and
   now owns the gap between the band and the content via `row-gap` (core.css).
   Flex-item margins do not collapse, so keeping both would give 64px. */
.page-band {
  background: var(--colour-bg-subtle);
  border-bottom: 1px solid var(--colour-border);
  /* ONE band height rule for the whole site (owner-directed, 2026-09-02).
     Measured before this line, at 1440px: `/electoral-reforms/roadmap/` was
     272px tall around 136px of content — half of it empty, forced by a
     `min-height: 17rem` — while `/mps-performance/national-assembly/
     13th-parliament/` was 80px around 80px of content, its title touching both
     edges. Both extremes were rejected.

     The band is now sized by what is in it plus this one padding, everywhere:
     no `min-height` on any page, and the branded hero uses this same value
     rather than a larger one of its own. Those two pages land at 200px and
     144px. Change the token here and every band moves together — that is the
     point; do not reintroduce a per-section height. Margins stay untouched:
     the gap BELOW the band is `.site-content`'s `row-gap` (core.css), which is
     the shared page rhythm, not this element's spacing. */
  padding-block: var(--space-6);
}

/* ============================================================
   Branded section hero — opt-in via the `has-brand-hero` body class.
   Replaces the flat near-white page band with a deep civic hero that
   carries the Kenyan tricolour motif (black · red · green) plus a faint
   dot texture, so section landing pages stop reading as white-on-white.
   Reusable: any page can add `has-brand-hero` to its body_class. */
/* The `.has-brand-hero .site-content { padding-top: 0 }` that used to sit here
   is gone: `.site-content` itself is now `padding: 0` (core.css), so every
   page starts flush, hero or not. */
.has-brand-hero .page-band {
  position: relative; isolation: isolate;
  /* No `padding-block` of its own: the hero is the same height as any other
     band (see `.page-band` above). It differs in colour, not in size. */
  border-bottom: 0;
  color: #fff;
  background:
    radial-gradient(130% 140% at 15% -25%, hsl(358 75% 41% / 0.5), transparent 62%),
    linear-gradient(160deg, hsl(358 77% 37%), hsl(358 80% 31%));
}
.has-brand-hero .page-band::before {              /* faint dot-grid texture, fading down */
  content: ""; position: absolute; inset: 0; z-index: -1;
  background-image: radial-gradient(hsl(0 0% 100% / 0.07) 1px, transparent 1.6px);
  background-size: 24px 24px;
  -webkit-mask-image: linear-gradient(180deg, #000, transparent 85%);
  mask-image: linear-gradient(180deg, #000, transparent 85%);
}
.has-brand-hero .page-band::after {               /* Kenyan tricolour signature rule */
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: linear-gradient(90deg,
    #000 0 32%, #fff 32% 34%,
    var(--colour-primary) 34% 64%, #fff 64% 66%,
    var(--colour-secondary) 66% 100%);
}
.has-brand-hero .page-heading { color: #fff; letter-spacing: -0.03em; }
.has-brand-hero .page-band .breadcrumbs,
.has-brand-hero .page-band .breadcrumbs a { color: hsl(0 0% 100% / 0.72); }
.has-brand-hero .page-band .breadcrumb-item.current { color: #fff; }
.has-brand-hero .page-band .breadcrumb-separator { color: hsl(0 0% 100% / 0.4); }

/* Eyebrow kicker sitting above a hero title. */
.hero-kicker {
  display: inline-flex; align-items: center; gap: .55em;
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs); font-weight: var(--fw-bold);
  text-transform: uppercase; letter-spacing: .14em;
  color: hsl(144 52% 72%);
}
.hero-kicker::before {
  content: ""; width: 1.7rem; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--colour-primary), var(--colour-secondary));
}
.prose { max-width: var(--container-prose); margin-inline: auto; }
.prose p { line-height: var(--leading-normal); }
/* Lead/intro paragraphs used to opt OUT of the reading-width cap here
   (`.page-intro--text.prose { max-width: none }`), on the reasoning that they
   should span the same width as the wide table below them. Measured, that
   produced 137-215 characters per line — `p.analysis-intro.prose` rendered at
   161 cpl *while carrying `.prose`*. An intro is still text and still has to
   be readable, so it is capped at its own (shorter) measure instead. Left-
   aligned, not centred: it must line up with the heading above it. */
.page-intro--text,
.section-intro--text,
.section__lead { max-width: var(--measure-lead); margin-inline: 0; }
/* Body copy in the /mps-performance/ section intros. Same family as the two
   `--text` classes above and it was simply missed when they were capped: it
   carried NO measure at all, so it inherited the shell width and measured 156
   cpl at the old 1320px container. Widening `--container-max` on 2026-08-02
   took it to 204 cpl, which is what made the omission visible. It is body
   copy, not a lead, so it gets the longer `--container-prose`. */
.parliament-section--text { max-width: var(--container-prose); }
/* Citation and advisory lines are set small, so the same pixel width buys far
   more characters — these measured 228-304 cpl uncapped.

   `--container-prose` is now `none` (core.css), so this rule no longer caps
   anything — these notes span their container and measure ~270 cpl at 1920px.
   That is the owner's explicit choice; read the core.css note before changing
   it. The declaration is kept rather than deleted so that restoring a measure
   site-wide remains a one-token edit.

   DO NOT flow these into CSS columns. That was tried across the whole site on
   2026-08-02 and REMOVED the same day by owner direction: "remove the multi col
   if the rest of the page is not multi col." Newspaper columns inside an
   otherwise single-column page read as a different document. */
.src-cite,
.prov-ribbon__body { max-width: var(--container-prose); }
/* Cap the TEXT, not the panel. `.note-panel` is a bordered, tinted, flex
   container — putting the measure on it shrank the whole amber banner to 704px
   inside a 1320px container, leaving 616px of white beside a half-width box.
   A max-width on a bordered box is not the same operation as a measure cap. */
.note-panel p { max-width: var(--container-prose); }
.wrap--narrow { max-width: var(--container-narrow); margin-inline: auto; }
/* Opt-in wide container for genuinely dense surfaces (large data tables,
   ranking grids) that legitimately need more room than prose. */
.container--wide { max-width: var(--container-wide); }
/* Horizontal scroll for bespoke tables that do not already provide their own
   scroll wrapper. (.mz-rank__tablewrap and .finance-table-wrap already do.) */
/* The wrapper takes over the table's block role. Once a `.data-table` is put
   inside a scroll wrapper, the table's own `margin-bottom` is trapped INSIDE
   the scrollport and no longer separates the wrapper from the next block —
   measured 0px on /parties/ between `.table-scroll` and the card grid below it
   the moment the wrapper was added. So the wrapper carries the trailing margin
   and the table drops its own to avoid stacking two. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 0 var(--space-6); }
.table-scroll > .data-table { margin-bottom: 0; }

/* Scroll affordance. Under 600px `.data-table` becomes its own horizontal
   scroller, so on a phone the later columns — including the per-row gazette
   SOURCE link the elections hero explicitly promises — sit off-canvas with
   nothing to say so. The content IS reachable; it just looks like the row ends
   at the viewport edge. These background-attachment gradients are the standard
   CSS-only cue: the shadows are painted against the scrollport (`scroll`) and
   the white covers (`local`) slide away as you scroll, so a shadow appears only
   on the side that has more content. No JS, no markup change. */
.table-scroll,
.data-table {
  background:
    linear-gradient(to right, var(--colour-bg) 30%, hsl(0 0% 100% / 0)) left center,
    linear-gradient(to left, var(--colour-bg) 30%, hsl(0 0% 100% / 0)) right center,
    linear-gradient(to right, hsl(0 0% 0% / 0.10), hsl(0 0% 0% / 0)) left center,
    linear-gradient(to left, hsl(0 0% 0% / 0.10), hsl(0 0% 0% / 0)) right center;
  background-repeat: no-repeat;
  background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}

/* Performance Rankings shipped a ranking table with EVERY column left-aligned —
   all 8 headers and all 8 cells measured `text-align: left`, over values
   spanning 0-2,142 across 50 rows, with no zebra. Digits that do not share a
   right edge cannot be compared down a column, which is the entire purpose of
   this table. Columns 3+ are the numeric ones (Overall, Contributions, Division
   participation, Bills, Committees, Initiative). Scoped by element+nth-child so
   it outranks the generated island bundle, which loads after this file. */
.mz-rank__table thead th:nth-child(n + 3),
.mz-rank__table tbody td:nth-child(n + 3) { text-align: right; }
.mz-rank__table tbody tr:nth-child(even) { background: var(--colour-bg-subtle); }

/* Performance Rankings scrolled the WHOLE PAGE on a phone: measured
   document.scrollWidth = 445px at a 390px viewport. The rankings table is not
   the cause — .mz-rank__tablewrap scrolls it. The cause is the filter row,
   whose controls carry `min-width: 14rem` + `min-width: 10rem` (= 384px before
   gaps and the container gutter) and so cannot fit a 390px screen.
   Those rules live in the GENERATED island bundle
   (apps/parliament/static/islands/mz-islands.css, built from wagtail/frontend/),
   which loads after this file via {% block extra_css %} — so this override is
   scoped by an extra class to win on specificity rather than by !important,
   and survives a future `npm run build` of the bundle. */
@media (max-width: 700px) {
  .mz-rank__filters .mz-rank__search,
  .mz-rank__filters .mz-rank__party { min-width: 0; flex: 1 1 100%; }
  .mz-rank__toolbar .mz-rank__filters { width: 100%; }
}
.grid--auto { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--col-min, 240px), 1fr)); gap: var(--space-4); }
.grid-12 { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--space-4); }

/* ---- Card variants ---- */
.card--row { display: flex; gap: var(--space-4); align-items: stretch; padding: var(--space-4) var(--space-5); }
.card--feature { padding: var(--space-5) var(--space-6); border-left: 4px solid var(--colour-primary); }
.card__media { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; background: var(--colour-bg-subtle); }
.card__body { padding: var(--space-4) var(--space-5); }
.card__footer { padding: var(--space-3) var(--space-5); border-top: 1px solid var(--colour-border); display: flex; gap: var(--space-3); align-items: center; }
.card__tag-row { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }

/* ---- Avatar ---- */
.avatar { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; background: var(--colour-bg-subtle); flex: none; }
.avatar--lg { width: 72px; height: 72px; }
.avatar--initial { display: grid; place-items: center; color: #fff; font-weight: var(--fw-extrabold); background: var(--colour-primary); }

/* ---- Committee directory (row card: small chair photo left, details right —
   the chair is a supporting detail, not the headline) ---- */
/* `align-items: flex-start` let each card shrink to its own content, so a row
   of three ended at three different depths and the House badge floated at a
   different height in every card. Stretch the card and push the badge row to
   the bottom, so the badges line up across a row and the grid reads as rows
   rather than as ragged columns. */
.committee-card { align-items: stretch; }
.committee-card__avatar { border: 2px solid var(--colour-bg-subtle); box-shadow: var(--shadow-sm); align-self: flex-start; }
.committee-card__body { min-width: 0; display: flex; flex-direction: column; flex: 1; }
.committee-card__title { margin-bottom: var(--space-1); }
.committee-card .card__tag-row { margin-top: auto; padding-top: var(--space-2); }
.committee-group { margin-bottom: var(--space-6); }
.committees-search { width: min(24rem, 100%); }

/* ---- Semantic badges (extend .badge) ---- */
.badge--success { background: var(--colour-success-bg); color: var(--colour-success); }
.badge--warning { background: var(--colour-warning-bg); color: var(--colour-warning); }
.badge--info { background: var(--colour-info-bg); color: var(--colour-info); }
.badge--danger { background: var(--colour-danger-bg); color: var(--colour-danger); }

/* ---- Grade pill (scorecards): meaning, not alarm — A/B green … D/E/F red ---- */
.grade { display: inline-grid; place-items: center; min-width: 2.4rem; padding: var(--space-1) var(--space-3);
  border: 3px solid currentColor; border-radius: var(--radius-md); font-size: var(--text-2xl);
  font-weight: var(--fw-extrabold); line-height: 1; color: var(--colour-primary); }
.grade--a, .grade--b { color: var(--colour-success); }
.grade--c { color: var(--colour-warning); }
.grade--d, .grade--e, .grade--f { color: var(--colour-danger); }

/* ---- Shared sub-nav (alias of .tabs for section sub-navigation) ---- */
.subnav { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5);
  border-bottom: 2px solid var(--colour-border); margin: 0 0 var(--space-5); }
.subnav a { display: inline-block; padding: var(--space-3) var(--space-1); color: var(--colour-text);
  text-decoration: none; font-weight: var(--fw-semibold); border-bottom: 3px solid transparent; }
.subnav a:hover { color: var(--colour-primary); }
.subnav a:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; border-radius: var(--radius-sm); }
.subnav a.is-active { color: var(--colour-primary); border-bottom-color: var(--colour-primary); }

/* ---- Vote-decision chips (voting pages) ---- */
.vote-chip { display: inline-flex; align-items: center; gap: var(--space-1); padding: 1px 9px; border-radius: var(--radius-pill); font-size: var(--text-sm); font-weight: var(--fw-semibold); }
.vote-chip--yes { background: var(--colour-success-bg); color: var(--colour-success); }
.vote-chip--no { background: var(--colour-danger-bg); color: var(--colour-danger); }
.vote-chip--abstain, .vote-chip--absent { background: var(--colour-lighter); color: var(--colour-text-muted); }

/* ---- House (chamber) tabs — National Assembly | Senate. Never mix Houses. ----
   Outlined-pill treatment (matches the look formerly bespoke to Bills' .leg-toggle,
   now the one shared style used everywhere a page has real chamber data). */
.house-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0 0 var(--space-3); }
.house-tab { padding: var(--space-2) var(--space-4); border: 1px solid var(--colour-border-strong);
  border-radius: var(--radius-pill, 999px); text-decoration: none; color: var(--colour-text);
  font-size: var(--text-sm, .9rem); font-weight: var(--fw-semibold, 600); white-space: nowrap;
  transition: border-color var(--motion-fast, .15s) var(--ease-standard), color var(--motion-fast, .15s) var(--ease-standard); }
.house-tab:hover { border-color: var(--colour-primary); color: var(--colour-primary); }
.house-tab:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }
.house-tab.is-active { background: var(--colour-primary); color: #fff; border-color: var(--colour-primary); }
@media (max-width: 480px) { .house-tabs { display: flex; width: 100%; }
  .house-tab { flex: 1; text-align: center; } }

/* Timeframe (year) filter — sits beside the house tabs on metric pages. */
.timeframe-filter { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-3); font-size: var(--text-sm); }
.timeframe-filter select { padding: 6px 10px; border: 1px solid var(--colour-border, #ddd); border-radius: var(--radius-md, 8px); background: #fff; font-family: inherit; }

/* ---- Explorer scope bar: chamber (NA | Senate) + time window, shared across analysis pages ---- */
.scope-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-5); margin: 0 0 var(--space-3); }
.scope-bar > * { margin-bottom: 0; }
.scope-bar__periods { margin-bottom: 0; }
.scope-bar__custom { margin-bottom: 0; }
.scope-bar__window { margin: 0 0 var(--space-5); }
.scope-bar__notice { margin: 0 0 var(--space-2); color: var(--colour-warning); }

/* ---- Site-wide detail / drill-down drawer (right side panel; bottom sheet on mobile) ---- */
.lmodal { position: fixed; inset: 0; z-index: var(--z-modal, 1000); }
.lmodal[hidden] { display: none; }
.lmodal__backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.45); }
.lmodal__dialog { position: fixed; top: 0; right: 0; bottom: 0; z-index: 1; width: min(480px, 100%);
  display: flex; flex-direction: column; background: var(--colour-bg, #fff);
  box-shadow: -8px 0 40px rgba(0,0,0,.25); animation: lmodal-slide-in .22s cubic-bezier(.16,1,.3,1); }
.lmodal__header { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: var(--space-3) var(--space-5); border-bottom: 1px solid var(--colour-border, #e6e6e6); }
.lmodal__title { margin: 0; font-size: 1.05rem; color: var(--colour-text); }
.lmodal__close { background: none; border: 0; font-size: 1.9rem; line-height: 1; color: var(--colour-text-muted); cursor: pointer; }
.lmodal__close:hover { color: var(--colour-primary); }
.lmodal__body { overflow-y: auto; padding: var(--space-4) var(--space-5); flex: 1; }
.lmodal__loading, .lmodal__error { padding: 2.5rem 0; color: var(--colour-text-muted); text-align: center; }
body.lmodal-open { overflow: hidden; }
@keyframes lmodal-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
/* Opt-in wide panel for reading a WHOLE document (a Bill runs to 20,000+ words).
   480px is right for a drill-down list and wrong for prose; a trigger asks for
   this with data-drawer-size="wide". Capped at 68ch of text so the line length
   stays readable rather than growing with the panel. */
.lmodal--wide .lmodal__dialog { width: min(980px, 100%); }
.lmodal--wide .lmodal__body > * { max-width: 68ch; }
.lmodal--wide .lmodal__body > .document-reader-layout,
.lmodal--wide .lmodal__body > .section { max-width: none; }
.lmodal--wide .lmodal__body .ocr-doc { max-width: 68ch; }
@media (max-width: 640px) {
  .lmodal__dialog { top: auto; left: 0; right: 0; width: 100%; height: 82vh; border-radius: 14px 14px 0 0;
    animation: lmodal-slide-up .22s cubic-bezier(.16,1,.3,1); }
  /* A document needs the height more than a drill-down list does. */
  .lmodal--wide .lmodal__dialog { height: 92vh; }
  @keyframes lmodal-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
}
/* Drill-down list (members/bills/etc. inside the drawer) */
/* Drawer breakdown: a compact figure and a section heading above the row list.
 * Moved here from `boundaries/css/region.css` when the Electoral Reforms tabs
 * adopted the same drawer — one copy, because two pages' drawers drifting
 * apart is exactly what a shared component is meant to prevent. */
.drawer-metric__figure { display: flex; align-items: baseline; gap: var(--space-2); margin: 0 0 var(--space-2); }
.drawer-metric__num { font-size: var(--text-3xl); font-weight: var(--fw-extrabold); color: var(--colour-primary); line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.drawer-metric__unit { font-size: var(--text-sm); color: var(--colour-text-muted); }
.drawer-section-title { font-size: var(--text-sm); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .04em; color: var(--colour-text-muted); margin: var(--space-5) 0 var(--space-2); }

.drill-list { list-style: none; margin: 0; padding: 0; }
.drill-list__item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--colour-border, #eee); }
.drill-list__item a { color: var(--colour-text); text-decoration: none; font-weight: 600; }
.drill-list__item a:hover { color: var(--colour-primary); }
.drill-list__meta { font-size: var(--text-sm); color: var(--colour-text-muted); }
.drill-list__count { color: var(--colour-text-muted); font-size: var(--text-sm); margin: 0 0 var(--space-3); }
/* A note that follows a shut <details>: its border-top would otherwise land on
   this line and the two would read as one control. */
.drill-list__count--after-collapse { margin-top: var(--space-4); }
/* Clickable stat (opens the count's breakdown in the drawer). */
.stat--link { text-decoration: none; color: inherit; cursor: pointer; transition: box-shadow .15s, transform .15s; }
.stat--link:hover { box-shadow: var(--shadow-md, 0 4px 14px rgba(0,0,0,.12)); transform: translateY(-1px); }
.stat--link .stat__num { color: var(--colour-primary); }
.drill-list__item--stack { flex-direction: column; align-items: flex-start; gap: 3px; }
.drill-list__item--stack a { font-weight: 500; line-height: 1.35; }
/* Presiding-officer speech: shown for the record, de-emphasised, and badged so
   it reads as not-counted-in-metrics. */
.drill-list__item--presiding { opacity: .72; }
/* Where a drawer's grouping came from. Small and quiet, but never optional on a
   machine-derived figure: the number is published BECAUSE the reader can see how
   it was assembled and open every row behind it. */
.drill-list__provenance {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--colour-border);
  font-size: var(--text-xs);
  line-height: 1.5;
  color: var(--colour-text-muted);
}
.drill-list__badge {
  display: inline-block; font-size: var(--text-xs, .72rem); font-weight: 600;
  line-height: 1; padding: 3px 7px; border-radius: 999px;
  color: var(--colour-text-muted); background: var(--colour-surface-2, #f1f1f3);
  border: 1px solid var(--colour-border, #e3e3e6); letter-spacing: .02em;
}

/* ---- Data export actions (Download CSV / Print-PDF) + print styles ---- */
.data-actions { display: flex; gap: var(--space-2); margin: 0 0 var(--space-4); flex-wrap: wrap; }
.data-actions__btn { display: inline-flex; align-items: center; gap: .35rem; padding: .4rem .85rem;
  border: 1px solid var(--colour-border-strong, #ccc); border-radius: var(--radius-pill, 999px);
  background: #fff; color: var(--colour-text); font: inherit; font-size: var(--text-sm); font-weight: 600;
  cursor: pointer; text-decoration: none; }
.data-actions__btn:hover { border-color: var(--colour-primary); color: var(--colour-primary); }
.data-actions__btn:focus-visible { outline: 2px solid var(--colour-primary); outline-offset: 2px; }
@media print {
  .site-header, .site-footer, #lmodal, .data-actions, .main-nav, .mobile-menu, .breadcrumbs,
  .timeframe-filter, .house-tabs, .tabs, .desktop-search-form, .filter-bar { display: none !important; }
  body { background: #fff; }
  .container { max-width: none !important; width: 100% !important; }
}

/* ---- Data & Downloads page (/data/) ----
   Download/feed catalogue built from the shared card + button components. */
.page-data .prose a,
.page-data .card__meta a { color: var(--colour-primary); font-weight: var(--fw-semibold); }
.page-data .prose a:hover,
.page-data .card__meta a:hover { text-decoration: underline; }
.page-data .data-stats { margin-top: var(--space-5); }
.page-data .data-jump { margin: var(--space-3) 0 var(--space-6); }
/* On this page card__meta holds descriptive sentences, not the meta chip row,
   so flow it as normal text instead of the shared flex layout. */
.page-data .card__meta { display: block; }

.dl-card { display: flex; flex-direction: column; }
.dl-card > .card--pad { flex: 1 1 auto; }
.dl-card .card__title { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
/* Actions pinned to the card base: a row of "open page" buttons, then a subtle
   direct-CSV line beneath. */
.dl-card__foot { padding: var(--space-3) var(--space-5) var(--space-4);
  border-top: 1px solid var(--colour-border); display: flex; flex-direction: column; gap: var(--space-2); }
.dl-card__open { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.dl-card__csv { margin: 0; font-size: var(--text-sm); color: var(--colour-text-muted); }
.dl-card__csv a { color: var(--colour-primary); font-weight: var(--fw-semibold); }
.dl-card__csv a:hover { text-decoration: underline; }
.dl-card__actions { flex-wrap: wrap; }
.dl-card__icon { display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: var(--radius-md); flex: none;
  background: var(--colour-primary-alpha-10); color: var(--colour-primary);
  font-size: var(--text-sm); }
.dl-card__icon--feed { background: var(--colour-secondary-alpha-10); color: var(--colour-secondary-dark); }

/* ---- Text links ----
   Affordance contract: every anchor a user must discover is affordant iff
   (a) it is a block/card with hover elevation + focus ring, (b) a button/chip
   with fill or border, or (c) it carries link colour with an at-rest underline
   (inline prose) or at-rest colour + hover underline (record titles in lists).
   Bare unclassed links inside .site-content get this automatically (core.css);
   any CLASSED inline link must add .text-link (or provide its own affordance
   per the contract). Quiet-at-rest is reserved for chrome: nav, tabs,
   breadcrumbs, pagination, footer, and whole-card links. */
.text-link {
  color: var(--colour-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.text-link:hover,
.text-link:focus-visible {
  color: var(--colour-link-hover);
  text-decoration-thickness: 2px;
}

/* ---- Generic JS-optional tabs ----
   Pair a <nav class="tabs"> of #hash links with sibling
   <section class="tab-panel" id="..."> panels inside a .tab-panels wrapper.
   The panel matching the URL hash shows; with no hash the .tab-panel--default
   one shows. Pure CSS — works with JS off; parliament.js only syncs the active
   underline. (Promoted out of the page-scoped mp.css so any page can reuse it.) */
.tab-panel { display: none; }
.tab-panel:target { display: block; }
.tab-panels:not(:has(:target)) .tab-panel--default { display: block; }

/* ---- Provenance affordance (trust-building, never a warning) ----
   Tier 1: a calm source ribbon leading a data page. Tier 2: a muted citation
   line under a table/chart. Both token-only. */
.prov-ribbon {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4) var(--space-5); margin: var(--space-5) 0;
  border: 1px solid var(--colour-border); border-left-width: 4px;
  border-radius: var(--radius-md); background: var(--colour-bg-subtle);
  font-size: var(--text-sm); line-height: var(--leading-normal);
}
.prov-ribbon--certified { background: var(--colour-success-bg); border-color: var(--colour-success); }
.prov-ribbon--preview { background: var(--colour-warning-bg); border-color: var(--colour-warning); }
.prov-ribbon__body { color: var(--colour-text); }
.prov-ribbon__body strong { color: var(--colour-text); }

.src-cite { font-size: var(--text-xs); color: var(--colour-text-muted); margin: var(--space-2) 0 var(--space-5); }
.src-cite a { color: var(--colour-link); }
.src-cite a:hover, .src-cite a:focus-visible { color: var(--colour-link-hover); }

/* Non-interactive pill chip (e.g. a step in a path); interactive pills use .pills a. */
.pill {
  padding: var(--space-2) var(--space-4); border: 1px solid var(--colour-border-strong);
  border-radius: var(--radius-pill); font-size: var(--text-sm); font-weight: 600;
  color: var(--colour-text); background: var(--colour-bg-subtle);
}

/* ---- Feature icon tile ----
   A tinted square holding a Font Awesome glyph, for feature/tool cards. Lifts to
   solid primary when its enclosing .card--link is hovered. */
.feature-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.6rem; height: 2.6rem; flex: none;
  border-radius: var(--radius-md);
  background: var(--colour-primary-alpha-10); color: var(--colour-primary);
  font-size: var(--text-lg);
}
.card--link:hover .feature-icon { background: var(--colour-primary); color: var(--colour-bg); }

/* Official Source Library — paginated two-column directory of sources; contents collapsed until expanded. */
/* Soft green-tinted page wash near the top, fading to white — breaks the flat
   all-white field without hurting text contrast. */
body.page-analysis-official-sources { background: linear-gradient(180deg, hsl(150 34% 97.5%), var(--colour-bg) 460px); }

/* Quarantine note: an amber advisory panel (semantic warning tone) with an
   icon rail, so the governance caveat reads as a deliberate notice. */
/* The trailing margin follows the convention every other shared block here uses
   (`.filter-bar`, `.data-table`, `.committee-group`, `.section__lead`): a note
   panel is a block in the flow, not a decoration glued to what follows it.
   Without it, /analysis/official-sources/ rendered this bordered box sitting
   directly on the search form below it. */
.note-panel { display: flex; gap: var(--space-4); align-items: flex-start; padding: var(--space-4) var(--space-5); margin: 0 0 var(--space-6); border: 1px solid hsl(45 80% 82%); border-left: 4px solid var(--colour-warning); background: linear-gradient(120deg, var(--colour-warning-bg), hsl(45 100% 97%)); border-radius: var(--radius-lg); }
.note-panel__icon { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 2.4rem; height: 2.4rem; border-radius: var(--radius-md); background: hsl(45 90% 88%); color: var(--colour-warning); font-size: var(--text-lg); }
.note-panel p { margin: 0; }
.note-panel strong { color: var(--colour-warning); }

/* Metric chips — the counts summary as coloured pills instead of a grey run-on line. */
.metric-chips { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin: var(--space-5) 0 var(--space-4); }
.metric-chip { display: inline-flex; align-items: baseline; gap: .45em; padding: var(--space-2) var(--space-4); border-radius: var(--radius-pill); font-size: var(--text-sm); font-weight: var(--fw-semibold); border: 1px solid transparent; }
.metric-chip b { font-size: var(--text-lg); font-weight: var(--fw-extrabold); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.metric-chip--docs { background: var(--colour-secondary-alpha-10); color: var(--colour-secondary-dark); border-color: var(--colour-secondary-alpha-20); }
.metric-chip--excerpts { background: var(--colour-primary-alpha-10); color: var(--colour-primary-dark); border-color: var(--colour-primary-alpha-20); }
.metric-chips__note { font-size: var(--text-sm); color: var(--colour-text-muted); }

.source-doc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); align-items: start; }
@media (max-width: 640px) { .source-doc-grid { grid-template-columns: 1fr; } }
.source-doc { position: relative; font-size: var(--text-sm); transition: box-shadow var(--motion-base) var(--ease-standard), transform var(--motion-base) var(--ease-standard), border-color var(--motion-base) var(--ease-standard); }
.source-doc::before {                             /* green→red accent rail, revealed on hover/open */
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--colour-secondary), var(--colour-primary));
  opacity: 0; transition: opacity var(--motion-base) var(--ease-standard);
}
.source-doc:hover { transform: translateY(-2px); border-color: var(--colour-secondary-alpha-20); box-shadow: 0 10px 26px hsl(150 40% 18% / 0.13); }
.source-doc[open] { border-color: var(--colour-secondary-alpha-20); }
.source-doc:hover::before, .source-doc[open]::before { opacity: 1; }
.source-doc__summary { cursor: pointer; list-style: none; display: flex; align-items: flex-start; gap: var(--space-3); }
.source-doc__summary::-webkit-details-marker { display: none; }
.source-doc__icon { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 2.4rem; height: 2.4rem; border-radius: var(--radius-md); background: var(--colour-secondary-alpha-10); color: var(--colour-secondary-dark); font-size: var(--text-base); }
.source-doc__head { flex: 1 1 auto; min-width: 0; }
.source-doc__chevron { flex: none; color: var(--colour-text-muted); margin-top: .3rem; transition: transform var(--motion-base) var(--ease-standard); }
.source-doc[open] .source-doc__chevron { transform: rotate(180deg); }
.source-doc[open] .source-doc__icon { background: var(--colour-secondary); color: #fff; }
.source-doc__name { display: block; font-weight: var(--fw-bold); color: var(--colour-text); }
.source-doc__meta { display: block; margin-top: 2px; color: var(--colour-text-muted); }
.source-doc__body { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--colour-border); }
.source-excerpts { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.source-excerpt { border: 1px solid var(--colour-border); border-radius: var(--radius-md); background: var(--colour-bg); }
.source-excerpt__summary { cursor: pointer; padding: var(--space-2) var(--space-3); font-size: var(--text-sm); color: var(--colour-text); display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: baseline; }
.source-excerpt__summary::-webkit-details-marker { color: var(--colour-text-muted); }
.source-excerpt__locator { font-weight: var(--fw-bold); color: var(--colour-text-muted); white-space: nowrap; }
.source-excerpt__snippet { color: var(--colour-text-muted); }
.source-excerpt[open] .source-excerpt__summary { border-bottom: 1px solid var(--colour-border); }
.source-excerpt[open] .source-excerpt__snippet { display: none; }
.source-excerpt__full { margin: 0; padding: var(--space-3); font-size: var(--text-sm); }
.source-pagination { display: flex; gap: var(--space-3); align-items: center; justify-content: center; flex-wrap: wrap; margin-top: var(--space-5); }
.source-pagination__status { color: var(--colour-text-muted); font-size: var(--text-sm); }


/* ---- Entity metrics: bullet-style KPI cards (member/region/party/committee) ----
   Form follows the job: each metric is a magnitude PLUS its position in a peer
   distribution, which is a bullet chart, not a chart-chart. One entity means one
   series, so no legend and no categorical palette — brand fill, neutral ink.
   Borrows the compare page's grammar (figure adjacent to a proportional bar). */
/* Hidden ONLY once the radar island has actually mounted (it sets data-mounted
   on itself). A failed or skipped mount leaves the cards visible, which is the
   whole point of a progressive-enhancement fallback. */
.island[data-mounted] + .metric-grid--islanded { display: none; }
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--space-4); margin: 0 0 var(--space-4); }
.metric-card { background: var(--colour-bg); border: 1px solid var(--colour-border); border-radius: var(--radius-lg); padding: var(--space-4) var(--space-4) var(--space-3); box-shadow: var(--shadow-sm); }
.metric-card__eyebrow { display: block; font-size: var(--text-xs); font-weight: var(--fw-semibold); letter-spacing: 0.04em; text-transform: uppercase; color: var(--colour-text-muted); margin: 0 0 var(--space-2); }
.metric-card__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); }
.metric-card__figure { font-size: var(--text-2xl); font-weight: var(--fw-extrabold); line-height: var(--leading-tight); color: var(--colour-text); font-variant-numeric: tabular-nums; }
.metric-card__rank { flex: none; font-size: var(--text-xs); font-weight: var(--fw-semibold); color: var(--colour-text-muted); background: var(--colour-bg-subtle); border: 1px solid var(--colour-border); border-radius: var(--radius-pill); padding: 2px var(--space-2); white-space: nowrap; }
/* The bullet track: the share of peers strictly below this entity. No median
   reference line — ties are heavy enough here (most members sponsor no Bills)
   that the median is not at the 50% mark, so a fixed tick would lie. */
.metric-card__track { position: relative; height: 8px; margin: var(--space-3) 0 0; background: var(--colour-lighter); border-radius: var(--radius-pill); }
.metric-card__fill { display: block; height: 100%; min-width: 4px; background: var(--colour-primary); border-radius: var(--radius-pill); }
.metric-card__foot { font-size: var(--text-xs); color: var(--colour-text-muted); margin: var(--space-2) 0 0; font-variant-numeric: tabular-nums; }
/* A refused metric. The reason IS the content; there is never a number here. */
.metric-card--refused { background: var(--colour-bg-subtle); box-shadow: none; }
.metric-card--refused .metric-card__figure { font-size: var(--text-lg); font-weight: var(--fw-semibold); color: var(--colour-text-muted); }
.metric-card__reason { font-size: var(--text-xs); color: var(--colour-text-muted); margin: var(--space-2) 0 0; line-height: var(--leading-snug); }
/* Meta + the collapsed honesty note. Compact by default, one click for the full
   statement — the wording is load-bearing but it must not out-shout the figures. */
.metric-meta { font-size: var(--text-xs); color: var(--colour-text-muted); margin: 0 0 var(--space-4); }
.metric-disclosure { border-top: 1px solid var(--colour-border); margin-top: var(--space-4); padding-top: var(--space-3); }
.metric-disclosure > summary { cursor: pointer; font-size: var(--text-sm); font-weight: var(--fw-semibold); color: var(--colour-text-muted); list-style: none; display: flex; align-items: center; gap: var(--space-2); }
.metric-disclosure > summary::-webkit-details-marker { display: none; }
.metric-disclosure > summary::before { content: "+"; display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; height: 1.1rem; border: 1px solid var(--colour-border); border-radius: var(--radius-sm); font-weight: var(--fw-bold); }
.metric-disclosure[open] > summary::before { content: "\2212"; }
/* A summary may carry a figure alongside its label — the drill panel's sources
   block says how many rows are sourced there, so provenance is legible without
   expanding anything. `auto` pushes it to the far edge of the flex summary. */
.metric-disclosure > summary .drill-list__meta { margin-left: auto; }
.metric-disclosure dl { margin: var(--space-3) 0 0; font-size: var(--text-sm); color: var(--colour-text-muted); max-width: var(--container-prose); }
.metric-disclosure dt { font-weight: var(--fw-semibold); color: var(--colour-text); margin-top: var(--space-2); }
.metric-disclosure dd { margin: 0 0 var(--space-1); }
@media (max-width: 420px) {
  .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
  .metric-card { padding: var(--space-3); }
  .metric-card__figure { font-size: var(--text-xl); }
  /* The median line is the first thing to go when space is tight; the track and
     the rank pill still carry the comparison. */
  .metric-card__foot { display: none; }
}

/* Topic pills on the classic member profile stay ANCHORS (drawer.js binds on
   [data-drawer-url] and preventDefaults, so they open the member's own drawer
   with JS and still navigate without it). Only the no-drill fallback needs a
   rule, so it sits in .pills exactly as an anchor does. */
.pills .pill-static { font: inherit; cursor: default; }

/* Screen-reader-only text. The public stylesheets had no such helper — the only
   one in the tree was `.w-sr-only`, inside Wagtail's admin bundle, which is not
   loaded on any public page. Used to voice a shared competition rank ("=3" ->
   "joint third") without repeating it visually. */
.u-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- Density kit (`dk-`) — MZ-20260907-EW2027 ----
   Client direction 2026-09-07: "less wordy and more visuals ... more compact
   saving on space so one can see more in one page than having to scroll so much
   just to see." These four pieces are the answer: a stat strip, a sparkbar row,
   a tile cartogram, and a lane ribbon. Each one puts a whole comparison in the
   space a paragraph would have taken.

   Two things deliberately NOT here, because they already exist and a second
   copy would drift: chip rails use `.pills`, and the detail drawer is `.lmodal`.

   Positions and magnitudes arrive as CSS custom properties on the element
   (`style="--dk-r:3; --dk-c:7"`). That keeps the numbers in the template, where
   the view can put real data in them, and keeps the rules here. It is also what
   the design lint permits: `lint_design_system.py` flags `<style>` BLOCKS, not
   `style=""` attributes, so a data-driven property is legal and a hand-written
   colour is not. Nothing below hard-codes a colour; every value is a token. */
.dk-strip {
  display: flex; flex-wrap: wrap; gap: 0;
  margin: 0 0 var(--space-4);
  border: 1px solid var(--colour-border); border-radius: var(--radius-lg);
  background: var(--colour-bg); overflow: hidden;
}
/* Each stat pays for itself in one line of vertical space. `flex: 1 1 8rem`
   lets four sit on a phone and eight on a laptop without a media query. */
.dk-strip__item {
  flex: 1 1 8rem; min-width: 0;
  padding: var(--space-3) var(--space-3);
  border-left: 1px solid var(--colour-border);
}
.dk-strip__item:first-child { border-left: 0; }
.dk-strip__label {
  display: block; font-size: var(--text-xs); font-weight: var(--fw-semibold);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--colour-text-muted); margin: 0 0 var(--space-1);
}
.dk-strip__figure {
  display: block; font-size: var(--text-xl); font-weight: var(--fw-extrabold);
  line-height: var(--leading-tight); color: var(--colour-text);
  font-variant-numeric: tabular-nums;
}
.dk-strip__note {
  display: block; font-size: var(--text-xs); color: var(--colour-text-muted);
  margin: var(--space-1) 0 0;
}
/* A figure we do not hold. It still occupies its slot and says so, because
   dropping the slot would hide the gap (AGENTS.md 0). */
.dk-strip__figure--absent {
  font-size: var(--text-sm); font-weight: var(--fw-semibold);
  color: var(--colour-text-muted);
}

/* Sparkbars: a magnitude per bar, `--dk-h` a 0-1 share of the tallest. Bars,
   not a line, because these series are counts of discrete things (seats, Bills,
   sittings) and a line between them would imply values in between. */
.dk-spark {
  display: flex; align-items: flex-end; gap: 2px;
  height: 2.5rem; margin: var(--space-2) 0 0;
}
.dk-spark__bar {
  flex: 1 1 0; min-width: 2px;
  height: calc(var(--dk-h, 0) * 100%); min-height: 2px;
  background: var(--colour-primary-alpha-50); border-radius: var(--radius-sm);
}
/* The bar the reader came for — this year, this member, this county. */
.dk-spark__bar--now { background: var(--colour-primary); }
/* A period with no figure. Drawn as a full-height ghost, never as a short bar:
   a missing value must not read as a small one. */
.dk-spark__bar--absent {
  height: 100%;
  background: repeating-linear-gradient(
    45deg, var(--colour-lighter) 0 3px, transparent 3px 6px
  );
  border: 1px dashed var(--colour-border);
}
.dk-spark__axis {
  display: flex; justify-content: space-between;
  font-size: var(--text-xs); color: var(--colour-text-muted);
  margin: var(--space-1) 0 0; font-variant-numeric: tabular-nums;
}

/* Tile cartogram. Every county gets the same square, so Vihiga is as clickable
   as Marsabit; see apps/boundaries/county_tiles.py for why, and for the row and
   column each county sits in. `--dk-rows`/`--dk-cols` come from TILE_ROWS and
   TILE_COLS, so adding a tile cannot leave a stale grid behind. */
.dk-tiles {
  display: grid;
  grid-template-columns: repeat(var(--dk-cols, 12), minmax(0, 1fr));
  grid-template-rows: repeat(var(--dk-rows, 14), auto);
  gap: 2px; margin: 0 0 var(--space-3);
  max-width: 34rem;
}
.dk-tile {
  grid-row: var(--dk-r); grid-column: var(--dk-c);
  aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); border: 1px solid var(--colour-border);
  font-size: var(--text-xs); font-weight: var(--fw-semibold);
  line-height: 1; text-align: center; text-decoration: none;
  color: var(--colour-text); overflow: hidden;
}
a.dk-tile:hover, a.dk-tile:focus-visible {
  outline: 2px solid var(--colour-primary); outline-offset: 1px;
  text-decoration: none;
}
/* Five bands and no more: a reader compares a square against the key, and
   cannot tell a 3%-darker square from its neighbour. Band 0 is "no figure" and
   is an OUTLINE, never the palest fill — tile_band() reserves it for exactly
   that, so an absent county cannot be mistaken for a low one. */
.dk-tile--b0 { background: transparent; border-style: dashed; color: var(--colour-text-muted); }
.dk-tile--b1 { background: var(--colour-primary-alpha-10); }
.dk-tile--b2 { background: var(--colour-primary-alpha-20); }
.dk-tile--b3 { background: var(--colour-primary-alpha-50); }
.dk-tile--b4 { background: var(--colour-primary-alpha-80); color: var(--colour-bg); }
.dk-tile--b5 { background: var(--colour-primary); color: var(--colour-bg); }
/* The two darkest bands carry light ink, so the focus ring needs a light rim to
   stay visible against them. */
a.dk-tile--b4:focus-visible, a.dk-tile--b5:focus-visible { outline-color: var(--colour-bg); }
.dk-tiles__key {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--text-xs); color: var(--colour-text-muted);
  margin: 0 0 var(--space-4);
}
.dk-tiles__swatch {
  width: 1rem; height: 1rem; border-radius: var(--radius-sm);
  border: 1px solid var(--colour-border);
}

/* Lane ribbon: several series of dated events sharing ONE horizontal day axis,
   so "the reform hearing falls after the voter-registration close" is visible
   rather than stated. `--dk-x` is the event's position along that axis, 0-100,
   computed by the view from real dates — never eyeballed here. */
.dk-lanes { margin: 0 0 var(--space-4); }
.dk-lane {
  display: grid; grid-template-columns: 7rem minmax(0, 1fr);
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) 0; border-top: 1px solid var(--colour-border);
}
.dk-lane__label {
  font-size: var(--text-xs); font-weight: var(--fw-semibold);
  color: var(--colour-text-muted); text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dk-lane__track {
  position: relative; height: 1.25rem;
  background: var(--colour-lighter); border-radius: var(--radius-pill);
}
.dk-lane__mark {
  position: absolute; top: 50%; left: calc(var(--dk-x, 0) * 1%);
  width: 0.75rem; height: 0.75rem; margin: -0.375rem 0 0 -0.375rem;
  border-radius: var(--radius-pill);
  background: var(--colour-primary); border: 1px solid var(--colour-bg);
}
/* A span, not a moment — a registration window, a campaign period. */
.dk-lane__span {
  position: absolute; top: 50%; left: calc(var(--dk-x, 0) * 1%);
  width: calc(var(--dk-w, 0) * 1%); height: 0.5rem; margin-top: -0.25rem;
  min-width: 2px;
  background: var(--colour-primary-alpha-50); border-radius: var(--radius-pill);
}
/* A date the law fixes and nobody may move. Warning ink, because missing one is
   what the page is warning about. */
.dk-lane__mark--statutory { background: var(--colour-warning); }
/* Today. One per track rather than one line drawn across the whole block: the
   label column is 7rem wide and is NOT part of the axis, so a single element
   spanning every lane would sit 7rem to the right of the date it names. Each
   lane takes the same `--dk-x`, so they still line up down the page. */
.dk-lane__now {
  position: absolute; top: -2px; bottom: -2px; left: calc(var(--dk-x, 0) * 1%);
  width: 2px; margin-left: -1px; background: var(--colour-text-muted);
}
.dk-lane__axis {
  display: flex; justify-content: space-between;
  font-size: var(--text-xs); color: var(--colour-text-muted);
  margin: var(--space-2) 0 0 7rem; font-variant-numeric: tabular-nums;
}
@media (max-width: 520px) {
  /* The label stops being a column and becomes a line of its own; the track
     keeps its full width, which is the part that carries the comparison. */
  .dk-lane { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .dk-lane__axis { margin-left: 0; }
  .dk-strip__item { flex-basis: 6rem; padding: var(--space-2); }
  .dk-strip__figure { font-size: var(--text-lg); }
}
