/* Component primitives — cross-tab patterns that aren't tied to
   any single tab and aren't a chart shape themselves.

   First pass: chart-card and its descriptive helpers.
   - .chart-card / @media / .chart-card h3 / .chart-card .csub — the
     bordered, shadowed box every chart sits inside. Padding shrinks
     on narrow viewports.
   - .chart-card-head / .chart-card-toggles — the header row
     pattern for cards carrying inline mode + sort toggles on the
     right of the title.

   The cog popover (.chart-customise, .cc-*), toggle-group, sort-btn,
   area-select, and other component primitives move in subsequent
   0c-e PRs. */

/* ────────────────────────────────────────────────────────────
   Stack — vertical flex column primitive.

   The base owns `display:flex` + `flex-direction:column` only;
   the gap is set via a `.stack--gap-N` modifier that maps to a
   --space-* token. Replaces twelve bespoke list-wrapper classes
   (.schools-list, .la-cards-list, .la-bullets-list, .bullets-list,
   .trust-school-list, .panel-bullets-list, .area-school-list,
   .compare-distress-stack, .cp-list, .filter-list,
   .compare-list) that all encoded the same flex-column-gap recipe
   with arbitrary pixel values from {0, 4, 6, 8, 10, 12}px. Those
   selectors stay in place (JS still tags them for tab-specific
   styling hooks); the flex-column-gap declarations move here.

   Modifiers snap to the existing --space-* scale; the original
   6px and 10px gaps map to gap-2 (8px) and gap-3 (12px) — a 2px
   visual shift on .compare-distress-stack, .la-bullets-list,
   and .trust-school-list, accepted to keep the primitive on the
   canonical 4px spacing grid. New list-shaped UI should use
   `.stack .stack--gap-N` rather than introduce a new bespoke
   selector. */
.stack {
  display: flex;
  flex-direction: column;
}
.stack--gap-0 { gap: 0; }
.stack--gap-1 { gap: var(--space-1); }  /* 4px */
.stack--gap-2 { gap: var(--space-2); }  /* 8px (covers 6-8 callsites) */
.stack--gap-3 { gap: var(--space-3); }  /* 12px (covers 10-12 callsites) */

/* ────────────────────────────────────────────────────────────
   Card — canonical bordered + rounded + shadowed surface on
   var(--card). Replaces six bespoke selectors (.chart-card,
   .x2-chart-card, .groups-dash-card, .compare-block, .la-card,
   .school-card) that each encoded the same recipe with arbitrary
   drift across padding (14-22px) and radius (4 / 6 / var(--r-md))
   and shadow (some on, some off). Default padding/radius use
   design tokens; variants opt in via modifiers.

   Variant-specific paddings (e.g. .school-card's grid-cell 16px
   20px vs the default 20px) stay on the variant selector, since
   the recipe alone owns "bordered surface" and not "geometry of
   the specific card's children".

   The list-item variant is used by .bullet (stack of rows that
   read as a list, not a deck of cards) — no shadow, smaller
   radius. */
.card {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  padding: var(--space-5);
  /* Defensive — prevents grid-blowout when a card sits in a
     1fr column and its content (e.g. a long URL or a wide table)
     would otherwise force the column wider than its share. */
  min-width: 0;
}
/* List-item variant — vertically stacked rows that read as a
   list, not a deck of cards. No shadow, smaller radius, lighter
   visual weight. Used by .bullet today; .trust-school and
   .area-school-item are PR2 candidates. */
.card--list-item {
  box-shadow: none;
  border-radius: var(--r-sm);
}
/* Banner variant — left-stripe accent rather than a flat-bordered
   card. Used for "this is about X" advisory blocks (schools-banner
   today; profile-banner / compare-hero kept their full borders as
   hero cards, so they stay on plain .card). The left-stripe colour
   is set via --card-stripe (default var(--rule)); per-instance
   variants override --card-stripe on the original selector.
   No shadow — banners read as authored prose rather than as a
   deck-of-cards surface, so they sit visually quieter on the
   page. */
.card--banner {
  border-left-width: 3px;
  border-left-color: var(--card-stripe, var(--rule));
  border-radius: var(--r-sm);
  box-shadow: none;
}

/* ────────────────────────────────────────────────────────────
   Disclosure — generic <details>/<summary> chrome. Owns the
   summary list-style suppression, the rotating ▸ caret, and the
   [open] transform. Replaces the same recipe duplicated across
   ten triangle-caret selectors (.chart-notes, .chart-subnote,
   .lens-section, .adv-classification, .panel-theme-summary,
   .panel-characteristics--collapsible, .sv-narrative-block,
   .la-schools-disclosure, .filter-collapsible). All migrated:
   chart-notes / chart-subnote in the initial primitive PR;
   lens-section / adv-classification / sv-narrative-block in
   the second PR; panel-theme-summary (via panel-theme-group),
   panel-characteristics--collapsible, la-schools-disclosure in
   the third PR; filter-collapsible opts in via .disclosure--quiet.
   The ⓘ info-icon variant on .inspector-disclaimer is a documented
   exception (different glyph, not a triangle caret).
   Tab-specific styling (border, padding, headline font, count
   suffix slot) stays on the original selector; variants opt in
   via modifiers. Caret hover-darken is the sole source — the
   previous unified hover rule in base.css has retired now that
   no selectors opt out of the primitive. */
.disclosure > summary {
  cursor: pointer;
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::marker { content: ""; }
.disclosure > summary::before {
  content: "▸";
  font-size: var(--text-nano);
  color: var(--ink-3);
  transition: transform var(--t-fast);
  display: inline-block;
  flex-shrink: 0;
}
.disclosure[open] > summary::before {
  transform: rotate(90deg);
}
.disclosure > summary:hover::before {
  /* Sole source for caret hover-darken now that every triangle
     disclosure flows through this primitive. The previous
     unified caret-hover rule in base.css covering the holdouts
     has retired. */
  color: var(--ink);
}
/* Quiet — for inline disclosures inside chart notes/subnotes:
   smaller caret to match the smaller surrounding type scale. */
.disclosure--quiet > summary::before {
  font-size: var(--text-9);
}

/* ────────────────────────────────────────────────────────────
   Chip — small rounded labelled badge. Inline-flex so a chip
   can sit anywhere in a flow and align with surrounding text
   or icons. Default chrome: bordered, var(--card) fill, neutral
   ink. Variants opt in via modifiers.

   .chip alone is the bare base; almost every callsite picks
   either .chip--pill (rectangular 4px corners, used for filter
   toggles where the chip is one of a row of mutually-compatible
   options) or .chip--round (999px corners, used for category
   tags where the pill shape signals labelness — added in a
   later PR).

   First-pass migration: replaces the duplicate chrome on
   `.area-pill, .filter-pill` (compare.css:2238) and `.map-pill`
   (map.css:201), all three of which had identical bodies.
   Callsite-specific extras (toolbar context, segmented-row
   border collapse on .map-pill) stay on the original selectors;
   the legacy class is kept on the JS markup so any in-the-wild
   selectors targeting it still resolve.

   The .chip--graded variant for grade chips
   (.sv-grade-chip / .ilacs-traj-pill / .map-popup-grade /
   .ilacs-jn-chip) lands in PR2 (below); the .chip--round
   category-tag family lands in PR3. */
.chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--rule);
  background: var(--card);
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  padding: 4px 10px;
  cursor: pointer;
  font-family: inherit;
}
.chip--pill { border-radius: var(--r-sm); }
/* Active state — promoted to use `--accent` (the lighter blue
   formerly applied only to `.map-pill.on`). The earlier
   `--surface-strong` (near-black) read heavier than the rest of
   the site's interactive treatment and produced a visible mismatch
   between the Compare "View by" row (chip default) and the Map
   toolbar (override). Ink is `--paper`, not `--surface-strong-ink`:
   dark-mode --accent is a light blue where white fails AA (2.6:1);
   --paper is near-white on navy in light mode (11.1:1) and
   near-black on the light blue in dark mode (7.0:1) — same
   both-theme rationale as .toggle-btn.on below and .tab.on in
   base.css. */
.chip--on {
  background: var(--accent);
  color: var(--paper);
  border-color: var(--accent);
}
.chip:hover:not(.chip--on) { color: var(--ink); }
.chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* .chip--graded — grade-coloured inline marker. Reads `--g` from
   the call-site (set on a `style` attr) to compose its colour
   triple: a 25%-tint background, a 45%-tint matching border, and
   a 4px-wide grade-coloured left stripe. The tinted recipe came
   from `.sv-grade-chip` (school card grade strip), `.ilacs-traj-
   pill` (ILACS/SEND trajectory), `.map-popup-grade` (map popup
   grade strip) and `.ilacs-jn-chip` (per-judgement narrative
   summary line), which were four near-identical 12-25-line rules
   in three different tab files. Differences (padding tightness,
   stripe width, tint depth, font-size, font-weight) are exposed
   as custom properties so a caller can dial them via a thin
   variant rule.

   Tunable knobs (defaults match the dominant `.sv-grade-chip` /
   `.ilacs-traj-pill` recipe):
     --chip-tint         25%   (background mix strength)
     --chip-border-tint  45%   (border mix strength)
     --chip-stripe       4px   (left-stripe width)
     --chip-py           3px   (vertical padding)
     --chip-px           8px   (right padding)
     --chip-pl           4px   (left padding inside the stripe)
     --chip-gap          7px   (gap between inner spans)
     font-size: var(--text-xs) (= 11px)

   Intentionally NOT a sub-modifier of `.chip` — the base `.chip`
   adds cursor:pointer + 4×10 padding + 12px font for the
   filter-pill family, which would clash with these display-only
   graded markers. `.chip--graded` is composed alongside `.chip`
   on the JS markup; both rules apply, with the graded recipe
   overriding the base where they conflict. Follows the same
   compose-with-legacy-class migration pattern as the prior
   filter-pill consolidation (.chip / .chip--pill). */
.chip--graded {
  display: inline-flex;
  align-items: center;
  gap: var(--chip-gap, 7px);
  padding: var(--chip-py, 3px) var(--chip-px, 8px) var(--chip-py, 3px) var(--chip-pl, 4px);
  border-radius: 3px;
  background: color-mix(in oklch, var(--g, var(--rule)) var(--chip-tint, 25%), var(--card));
  border: 1px solid color-mix(in oklch, var(--g, var(--rule)) var(--chip-border-tint, 45%), var(--card));
  border-left: var(--chip-stripe, 4px) solid var(--g, var(--rule));
  color: var(--ink-2);
  font-size: var(--text-xs);
  font-family: inherit;
}
/* Anchor variant — graded chip wrapped in an <a>. Hover deepens
   the background tint (35% vs the 25% default) so the link
   affordance reads without changing the chip's silhouette.
   Matches the prior `a.ilacs-traj-pill:hover` body, generalised. */
a.chip--graded {
  text-decoration: none;
  transition: background var(--t-fast), border-color var(--t-fast);
}
a.chip--graded:hover {
  color: var(--ink);
  background: color-mix(in oklch, var(--g, var(--rule)) 35%, var(--card));
}

/* ────────────────────────────────────────────────────────────
   Cards-grid — responsive grid wrapper for card stacks.

   The corpus-rationalisation research identified eight bespoke
   grid wrappers (.bigpicture-grid, .compare-grid, .compare-
   finance-layout, .trust-insights-grid, .trust-history-charts,
   .sf-breakdown-grid, .groups-dash-cards) that all encode
   `display: grid; gap: 12-24px` with a 1-col mobile / 2-col
   desktop pattern. Breakpoints differ (900-1280px) but the shape
   is identical. (Two were dead CSS — .aar-insights-grid and
   .groups-dash-cards — and were deleted during the PR2
   consolidation.) The 1-col-always .compare-grid sits inside this
   family as `--cards-grid-cols: 1`.

   `.cards-grid` owns the recipe. Defaults to single-column;
   `--cards-grid-cols` / `--cards-grid-gap` custom properties let
   the caller dial in column count and gap without forking the
   selector. A canonical 1280px breakpoint promotes the single
   column to multi.
   ──────────────────────────────────────────────────────────── */
.cards-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--cards-grid-gap, var(--space-4));
}
@media (min-width: 1280px) {
  .cards-grid {
    grid-template-columns: repeat(var(--cards-grid-cols, 2), 1fr);
  }
}

/* Charts */
/* Recipe inherited from .card via composition on the JS markup
   (`class="card chart-card"`). Selector retained for chart-card-
   specific declarations: h3 styling, csub spacing, the narrow-
   viewport padding override, and the bottom-margin that puts
   air between stacked chart-cards on tabs that don't use a grid
   container (bigpicture-grid nulls this margin to 0). */
.chart-card {
  /* chart-card-only: padding override drops 2px on the horizontal
     axis to match the historical 20px 22px footprint; default
     .card uses 20px 20px. Kept as a thin compositional override
     so the chart's plot area aligns with the prior layout grid. */
  padding: var(--space-5) 22px;
  margin-bottom: var(--space-6);
}
@media (max-width: 760px) {
  .chart-card { padding: 16px 14px; margin-bottom: var(--space-4); border-radius: var(--r-sm); }
}
.chart-card h3 { font-size: 17px; margin-bottom: var(--space-1); }
.chart-card .csub { color: var(--ink-3); font-size: 13px; margin-bottom: var(--space-4); }
/* Tight-margin axisnote variant — sits directly below an SVG
   chart (e.g. scatter.js per-area renderer) where the default
   16px bottom margin is the wrong shape. Used as
   `.csub.csub--scatter-axisnote`. */
.csub--scatter-axisnote {
  margin-top: 6px;
  margin-bottom: 0;
  font-size: 11.5px; /* @approved: dense-table half-px; intentional between --text-xs (11px) and --text-sm (12px) for scatter axis notes */
}
/* ────────────────────────────────────────────────────────────
   Cog popover (chart-customise + cc-multiselect). The little ⚙
   icon that sits beside a chart-card's inline toggles and opens
   a small panel with chart-local filter shortcuts.
   ──────────────────────────────────────────────────────────── */

/* Customise disclosure — a tiny ⚙ icon docked beside a chart's
   inline sort / view toggles, which stay visible at all times
   (cheap, frequently-used); the ⚙ pops out a small
   panel with chart-local filter shortcuts (Phase / Region) that
   write through to the same global state the sidebar uses, so a
   phase set here also updates the sidebar and the active-filter
   chip strip. */
.chart-customise {
  position: relative;
  display: inline-flex; align-items: center;
}
.chart-customise-summary {
  list-style: none;
  cursor: pointer;
  color: var(--ink-2);
  padding: 4px 8px;
  border-radius: var(--r-sm);
  font-size: 14px;
  line-height: 1;
  user-select: none;
  display: inline-flex; align-items: center;
}
.chart-customise-summary::-webkit-details-marker { display: none; }
.chart-customise-summary::marker { content: ""; }
.chart-customise-summary:hover { color: var(--ink); background: var(--rule); }
.chart-customise-summary:focus-visible {
  outline: var(--focus-ring); outline-offset: var(--focus-ring-offset);
}
/* When the disclosure is open, float the filter panel out below
   the icon as a small popover. Closed = hidden via [hidden]-style
   semantics (display:none on the inner panel). */
.chart-customise:not([open]) > .chart-customise-panel { display: none; }
.chart-customise[open] > .chart-customise-panel {
  position: absolute;
  right: 0; top: calc(100% + 4px);
  z-index: 5;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  padding: 10px 12px;
  box-shadow: var(--shadow);
  min-width: min(280px, calc(100vw - 32px));
  max-width: min(520px, calc(100vw - 32px));
  max-height: 70vh;
  overflow-y: auto;
  display: flex; flex-direction: column;
  gap: 10px;
}
/* Each filter dimension stacks vertically — label on its own line
   above the pills row — so long pill sets (Region has 9, FSM has
   5) wrap below their label cleanly rather than reflowing the
   label into the first pill. */
.chart-customise-group {
  display: flex; flex-direction: column;
  gap: var(--space-1);
}
.chart-customise-group-label {
  color: var(--ink-3);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 11px;
}
.chart-customise-pills {
  display: flex; flex-wrap: wrap;
  gap: var(--space-1);
}
.chart-customise-pills .filter-pill {
  font-size: 12px;
}
/* Multi-select dropdown inside the cog popover — used for
   dimensions with ≥6 values (Region) where laying out every value
   as a pill would dominate the popover height. Summary button
   shows "All <noun>" / "<first>" / "<first> +N more"; opens to
   a vertical checkbox list. Same data-cc-<dim> click semantics
   as the pill rows, so the existing delegated handler picks up
   item clicks without per-row wiring. */
.cc-multiselect {
  position: relative;
  display: inline-block;
  min-width: 220px;
}
.cc-multiselect-summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2);
  border: 1px solid var(--rule);
  background: var(--card);
  color: var(--ink-2);
  border-radius: var(--r-sm);
  padding: 4px 8px;
  font: inherit; font-size: 12px;
  line-height: 1.3;
}
.cc-multiselect-summary::-webkit-details-marker { display: none; }
.cc-multiselect-summary::marker { content: ""; }
.cc-multiselect-summary:hover { color: var(--ink); border-color: var(--ink-3); }
.cc-multiselect-summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
.cc-multiselect-caret {
  color: var(--ink-3);
  font-size: var(--text-nano);
  line-height: 1;
  transition: transform var(--t-fast);
}
.cc-multiselect[open] .cc-multiselect-caret { transform: rotate(180deg); }
.cc-multiselect-panel {
  position: absolute;
  top: calc(100% + 4px); left: 0;
  z-index: 6;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  min-width: 100%;
  max-height: 240px;
  overflow-y: auto;
  padding: var(--space-1);
  display: flex; flex-direction: column;
  gap: 0;
}
.cc-multiselect:not([open]) > .cc-multiselect-panel { display: none; }
/* @applies-to button */
.cc-multiselect-item {
  border: 0; background: transparent;
  text-align: left;
  cursor: pointer;
  padding: 6px 10px;
  border-radius: 3px;
  font: inherit; font-size: 12px;
  color: var(--ink-2);
  display: flex; align-items: center; gap: var(--space-2);
}
.cc-multiselect-item:hover { background: var(--surface-hover, var(--rule)); color: var(--ink); }
.cc-multiselect-item.on { color: var(--ink); font-weight: 500; }
.cc-multiselect-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px;
  border: 1px solid var(--ink-3);
  border-radius: 3px;
  color: var(--ink);
  font-size: var(--text-nano); line-height: 1;
  flex: 0 0 auto;
}
.cc-multiselect-item.on .cc-multiselect-check {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--g-strong-ink, #fff);
}
/* Foot of the popover — reset + escape-hatch links, separated
   from the filter rows by a thin rule for visual rhythm. */
.chart-customise-foot {
  display: flex; flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  border-top: 1px dashed var(--rule);
  padding-top: var(--space-2);
  margin-top: 2px;
}
.chart-customise-reset,
.chart-customise-link {
  border: 0; background: transparent;
  color: var(--accent);
  font: inherit; font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  padding: 2px 4px;
}
.chart-customise-reset:hover,
.chart-customise-link:hover { text-decoration: underline; }
.chart-customise-reset:focus-visible,
.chart-customise-link:focus-visible {
  outline: var(--focus-ring); outline-offset: var(--focus-ring-offset); border-radius: 3px;
}
.chart-customise-reset { color: var(--ink-2); }

/* ────────────────────────────────────────────────────────────
   Segmented controls: .toggle-group (mode toggle on chart-card
   headers + the Trusts/LAs/Inspectors view picker), .sort-btn
   (compact ▲/▼ direction toggle), .area-select (custom-styled
   <select> sitting in the same vertical stack as the toggles),
   and the subtle :active press feedback on the named primary CTAs.
   ──────────────────────────────────────────────────────────── */

/* Generic mode toggle (two or three buttons). Used by the chart-card
   header on Big picture's by-X grade charts. */
.toggle-group {
  display: flex;
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  overflow: hidden;
  flex-shrink: 0;
}
/* @applies-to button */
.toggle-btn {
  border: 0;
  background: var(--card);
  color: var(--ink-2);
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  line-height: 1.2;
}
.toggle-btn + .toggle-btn { border-left: 1px solid var(--rule); }
/* Active = accent navy, not --surface-strong — reads as interactive
   chrome rather than data (home-rebuild "All navy"; same selection
   language as .chip--on and the active tab). Text is --paper for
   both-theme AA: dark --accent is a light blue where white fails
   contrast (see the .tab.on note in base.css). */
.toggle-btn.on { background: var(--accent); color: var(--paper); }
.toggle-btn:hover:not(.on) { color: var(--ink); }
/* Compact sort-direction toggle — two narrow arrow buttons, used
   in chart-card-toolbar headers where the chunkier .toggle-group
   would dominate. ▲ = best at top, ▼ = worst at top. */
.sort-toggle {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: var(--r-sm);
  overflow: hidden;
  flex-shrink: 0;
}
/* @applies-to button */
.sort-btn {
  border: 0;
  background: var(--card);
  color: var(--ink-3);
  width: 22px; height: 22px;
  padding: 0;
  font-size: var(--text-nano);
  line-height: 1;
  cursor: pointer;
  font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
}
.sort-btn + .sort-btn { border-left: 1px solid var(--rule); }
/* Active = accent fill + --paper ink — joins the interactive-chrome
   selection language ("chrome = accent, data = grade scale") already
   carried by .toggle-btn.on, .chip--on and the active tab, replacing
   the heavier --surface-strong. --paper keeps both-theme AA where
   white fails on dark mode's light-blue --accent (see the
   .toggle-btn.on note above). */
.sort-btn.on { background: var(--accent); color: var(--paper); }
.sort-btn:hover:not(.on) { color: var(--ink); }
/* Word-label variant of the toggle button (e.g. the trust geography
   "Any phase / Same phase" control). The glyph buttons above are a fixed
   22px square; text labels size to their content instead. */
.sort-btn--text {
  width: auto; height: auto;
  padding: 4px 10px;
  font-size: var(--text-xs);
}

/* Area selector used alongside the toggle groups on the by-X charts.
   Sits in the same vertical stack as the toggles and visually mirrors
   them (same height, border-radius, font). */
.area-select {
  font-family: inherit;
  font-size: var(--text-sm);
  line-height: 1.2;
  padding: 6px 26px 6px 10px;
  border: 1px solid var(--rule);
  border-radius: var(--r-md);
  background-color: var(--card);
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%), linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: calc(100% - 14px) calc(50% - 2px), calc(100% - 9px) calc(50% - 2px);
  background-size: 5px 5px;
  background-repeat: no-repeat;
  -webkit-appearance: none;
  appearance: none;
  color: var(--ink-2);
  cursor: pointer;
  /* Cap visual width at 220px but let long option labels widen the
     selected-value display rather than truncating mid-word. */
  min-width: 180px;
  max-width: 100%;
}
.area-select:hover { color: var(--ink); }
.area-select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Subtle press feedback on the most-clicked primary CTAs. Doesn't
   touch every button — only the named CTAs whose hover state
   already has motion or colour change so :active is an obvious
   extension. */
.ask-popup-toggle:active:not(:disabled),
.ask-submit:active:not(:disabled),
.contact-submit:active:not(:disabled),
.reset-btn:active:not(:disabled),
.pagination button:active:not(:disabled) {
  transform: translateY(1px);
}

/* ────────────────────────────────────────────────────────────
   Generic legend — used by every chart card that has a colour key.
   gradeLegendHtml() in the JS is the typed wrapper; the raw
   markup is <div class="legend"><span><span class="dot"/>label</span>…</div>.
   ──────────────────────────────────────────────────────────── */

.legend {
  display: flex; gap: 14px; flex-wrap: wrap; margin-top: 14px;
  padding-top: var(--space-3); border-top: 1px dashed var(--rule);
  font-size: 12px; color: var(--ink-2);
}
.legend span { display: inline-flex; align-items: center; gap: 6px; }
/* Custom-property pass-through: callers set `--dot-bg` inline to thread
   a runtime colour (e.g. SEND_OUTCOME_COLOUR map lookup, var(--g-*)
   token name built at runtime) into the swatch without an inline
   `style="background:..."` — that pattern is the largest single cluster
   in the audit and the whitelist explicitly accepts `style="--\w+:..."`.
   Fallback to `currentColor` lets a dotless caller still render
   something sensible. */
.legend .dot { width: 9px; height: 9px; border-radius: 2px; background: var(--dot-bg, currentColor); }
/* Inline group label sitting inline with the swatches — names the
   semantic axis of the legend (e.g. "Area SEND:", "Children's
   services (ILACS):"). Used when a single .legend row carries
   swatches for multiple distinct outcome systems and the reader
   needs to know which dataset each swatch belongs to. */
.legend-group-label {
  color: var(--ink-3);
  font-size: 12px;
  margin-right: var(--space-1);
}
/* Panel band-swatch legend — the small "Well above → Well below"
   key that sits under a `.panel-characteristics` block on the
   trust / LA / inspector panels. The swatch chips reuse the
   existing `.panel-char-seg[data-band="…"]` colour ramp from
   compare.css; the `.panel-band-legend__chip` modifier just
   resizes them from track-segment dimensions to a 10×8 inline
   swatch tile. */
.panel-band-legend {
  font-size: var(--text-nano);
  color: var(--ink-3);
  margin-top: 6px;
}
.panel-band-legend__chip {
  display: inline-block;
  width: 10px; height: 8px;
  vertical-align: middle;
  border-radius: 1px;
}
.panel-band-legend__arrow { margin: 0 6px 0 8px; }

/* Soft "unknown" suffix on the .panel-coverage--fallback line —
   a `<span>` that sits inline after the headline and reads in
   muted ink-3 so the eye lands on the count, not the disclaimer. */
.panel-coverage--fallback__suffix { color: var(--ink-3); }

/* Chip-row wrapper inside `.panel-info-block` — adds the 8px top
   margin that sets the sub-judgement chip strip apart from the
   headline / overall line that sits above. Used on the LA panel
   ILACS block. */
.panel-info-block .ilacs-chip-row { margin-top: var(--space-2); }

/* Looser top-margin variant of the .panel-info-block__meta line —
   used on the ILACS panel inside the school side-panel where the
   meta line sits below a chip row and the default 4px gap reads
   as too tight. Overrides the base rule in tabs/compare.css. */
.panel-info-block__meta--mt { margin-top: var(--space-2); }

/* Trust-history timeline tip value + popover date — both render
   in the muted --ink-2 to set the lift-date value / popover
   header date apart from the bolder primary text. Sibling rules
   for the rest of the popover live in tabs/compare.css; these two
   are split out here because they were inline-style holdouts in
   timeline.js and folding them in keeps the file shape the same. */
.trust-history-timeline-tip-row .trust-history-timeline-tip-value {
  color: var(--ink-2);
}
.trust-history-popover-header .trust-history-popover-header-date {
  color: var(--ink-2);
}


/* ────────────────────────────────────────────────────────────
   Horizontal scroller with edge-fade affordance (.hscroll).
   One recipe for every "wider than the viewport, scroll sideways"
   surface (mobile-layout triage, 2026-07). The classic scroll-
   shadow stack: two background-attachment:local "cover" layers
   that ride with the content, over two attachment:scroll shadow
   layers pinned to the container edges — so a soft inner shadow
   appears only on the side(s) with more content, and disappears
   when the scroller is flush at that end. No JS, works in both
   themes, invisible when nothing overflows.

   Opt in by adding class="hscroll" to the scroller (timeline
   scroller, subjects-table wrapper), or — for markup owned by
   no-touch modules — by joining this selector list (.tabs in
   index.js, .panel-schools-toolbar in schools-list-panel.js).
   Set --hscroll-bg when the scroller sits on a non-card surface
   (e.g. the master timeline's --paper-2 band).
   ──────────────────────────────────────────────────────────── */
.hscroll,
.tabs,
.panel-schools-toolbar {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--hscroll-bg, var(--card)) 45%, transparent) left / var(--space-6) 100% no-repeat local,
    linear-gradient(to left,  var(--hscroll-bg, var(--card)) 45%, transparent) right / var(--space-6) 100% no-repeat local,
    linear-gradient(to right, color-mix(in srgb, var(--ink) 16%, transparent), transparent) left / var(--space-3) 100% no-repeat scroll,
    linear-gradient(to left,  color-mix(in srgb, var(--ink) 16%, transparent), transparent) right / var(--space-3) 100% no-repeat scroll,
    var(--hscroll-bg, var(--card));
}

/* ────────────────────────────────────────────────────────────
   Ask popup (Claude Q&A widget). Self-contained, fixed-position,
   overlays every tab. Lives in components.css because the trigger
   is global and no per-tab CSS depends on it.
   ──────────────────────────────────────────────────────────── */

/* Global natural-language Q&A popup — fixed bottom-right on
   every tab. The toggle is a pill-shaped accent button that's
   always visible; clicking expands a panel above it with the
   input + recent-answer slot. Click outside or Esc to close. */
.ask-popup {
  position: fixed;
  right: var(--space-6);
  bottom: var(--space-6);
  /* Above Leaflet's controls (z-index 1000) so the toggle stays
     visible on the Map tab — the attribution control at the
     bottom-right was previously covering it. */
  z-index: 1100;
  /* Use the site's own accent rather than a Claude coral — keeps
     the widget visually unified with the tab underline and other
     blue CTAs on the page. The "Ask Claude" + "Powered by Claude"
     wordmarks still attribute the model; we don't need the colour
     to do that work too. */
  --claude-accent: var(--accent);
  --claude-accent-strong: color-mix(in srgb, var(--accent) 86%, black);
}
.ask-popup-toggle {
  display: flex; align-items: center; gap: var(--space-2);
  /* Card-surface pill, not an accent fill: since the one-navy
     unification an accent pill is the exact colour of the Home
     page's bottom search band — the launcher floated navy on navy.
     The card treatment holds on every surface the fixed pill can
     sit over (paper, alt bands, the navy band, the Map) in both
     themes; the hairline border + neutral shadow carry the edge. */
  border: 1px solid var(--rule);
  background: var(--card);
  color: var(--ink);
  border-radius: var(--r-full);
  padding: 11px 18px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow), 0 6px 18px rgba(20, 20, 30, 0.18);
  transition: background 0.12s, transform 0.1s, box-shadow 0.1s;
}
.ask-popup-toggle:hover { background: var(--surface-hover); transform: translateY(-1px); }
.ask-popup-toggle:focus-visible { outline: 2px solid var(--claude-accent); outline-offset: 3px; }
.ask-popup[data-open="true"] .ask-popup-toggle { display: none; }
.ask-popup-panel {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 460px;
  max-width: calc(100vw - 48px);
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: 10px;
  box-shadow: 0 14px 36px rgba(0,0,0,0.22);
  padding: 14px 16px 16px;
}
.ask-popup-head {
  display: flex; align-items: center;
  margin-bottom: 6px;
}
.ask-popup-head h3 {
  margin: 0; font-size: 14px; color: var(--ink);
  display: flex; align-items: center; gap: var(--space-2);
}
/* Close-button family — one base + two size modifiers replaces the
   five bespoke per-callsite classes (.ask-popup-close, .map-side-
   panel-close, .sidebar-close, .filter-search-clear, .af-chip-x)
   that previously each had their own near-identical rule block.
   Sourced from the JS primitive `closeButton({ size, extraClass })`
   in site/js/components/button.js — the factory owns the
   `close-btn` base class, picks the size modifier from
   `size: "lg" | "chip"` (default = 16px base), and appends any
   positioning hook (e.g. "sidebar-close") via `extraClass`.
   Positioning lives on parent selectors (.map-side-panel-inner,
   .filter-search, .af-chip) so the button base stays unaware of
   where it sits. */
/* @applies-to button */
.close-btn {
  margin-left: auto;
  border: 0; background: transparent;
  color: var(--ink-3);
  font-size: 16px; /* @approved: icon-glyph × button; optical size for the × character, between --text-md (14px) and --text-lg (17px) */ line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
  border-radius: var(--r-sm);
  font-family: inherit;
}
.close-btn:hover {
  color: var(--ink);
  background: var(--surface-hover);
}
/* Larger close button for the high-prominence panel headers (Ask
   popup, Map side panel, mobile sidebar dialog). 18px > the 16px
   default; visual weight matches the panel's H3. */
.close-btn--lg {
  font-size: 18px; /* @approved: larger icon glyph for panel headers; intentional visual weight step above 16px base close button */
}
/* Smaller close button inside an active-filter chip pill. Pill-
   matching border-radius + hover-bg shifts to `--rule` so the
   button stays visible against the chip's own `--card` background. */
.close-btn--chip {
  font-size: 14px;
  padding: 2px 6px;
  border-radius: var(--r-full);
  margin-left: var(--space-1);
}
.close-btn--chip:hover {
  background: var(--rule);
}
@media (max-width: 760px) {
  .close-btn--chip {
    font-size: 16px; /* @approved: icon-glyph × enlarged for mobile touch targets */
    padding: 8px 10px;
  }
}
/* The sidebar close is only useful when the sidebar is acting as
   a modal overlay. On desktop the sidebar is a permanent column,
   so the toggle in the tab bar is the canonical hide affordance
   and a duplicate × button in the header would be noisy. */
.sidebar-close { display: none; }
@media (max-width: 1000px) {
  .sidebar-close { display: inline-flex; }
}
.ask-footer {
  margin: 10px 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--rule-2);
  font-size: 11px;
  color: var(--ink-3);
  text-align: right;
}
.ask-footer a {
  color: var(--ink-2);
  text-decoration: none;
  font-weight: 500;
}
.ask-footer a:hover { color: var(--claude-accent); text-decoration: underline; }
.ask-experimental {
  font-size: var(--text-nano);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-3);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 1px 6px;
}
.ask-input-row {
  display: flex; gap: var(--space-2);
}
.ask-input {
  flex: 1 1 auto;
  border: 1px solid var(--rule);
  background: var(--paper-2);
  border-radius: 5px;
  padding: 8px 12px;
  font: inherit;
  font-size: var(--text-base);
  color: var(--ink);
  min-width: 0;
}
.ask-input:focus {
  outline: none;
  border-color: var(--claude-accent);
  background: var(--card);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--claude-accent) 14%, transparent);
}
/* @applies-to button */
.ask-submit {
  border: 1px solid var(--claude-accent);
  background: var(--claude-accent);
  color: #fff;
  border-radius: 5px;
  padding: 0 18px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  min-width: 80px;
  transition: background 0.12s, border-color 0.12s;
}
.ask-submit:hover:not(:disabled) {
  background: var(--claude-accent-strong);
  border-color: var(--claude-accent-strong);
}
.ask-submit:disabled { opacity: 0.6; cursor: progress; }
.ask-help {
  margin: 0 0 10px;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.5;
}
.ask-help em { font-style: normal; color: var(--ink-2); }
/* Sample-prompt chips beneath the input. Click to fill the input
   and submit — discovery aids that show off three different feature
   surfaces (count-sort ranking, multi-filter discovery, free-text
   search). */
.ask-samples {
  margin: 0 0 10px;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  align-items: center;
}
.ask-samples-label {
  font-size: 11px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-right: 2px;
}
/* @applies-to button */
.ask-sample {
  border: 1px dashed var(--rule);
  background: var(--card);
  color: var(--ink-2);
  border-radius: 12px;
  padding: 4px 10px;
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  line-height: 1.3;
  text-align: left;
}
.ask-sample:hover {
  color: var(--claude-accent);
  border-color: var(--claude-accent);
  border-style: solid;
  background: color-mix(in srgb, var(--claude-accent) 6%, var(--card));
}
.ask-response {
  margin-top: var(--space-3);
  padding: 10px 12px;
  border-radius: 5px;
  font-size: 13px;
  line-height: 1.55;
}
@media (max-width: 640px) {
  .ask-popup { left: var(--space-3); right: var(--space-3); bottom: var(--space-3); }
  .ask-popup-toggle { margin-left: auto; }
  .ask-popup-panel { width: auto; }
}
/* Below ~600px the full "Ask Claude" pill covers real content
   (section-nav pills, right-edge chart chrome), so it collapses to
   an icon-only FAB: a circular speech-bubble button at the WCAG
   touch-target floor. The label span stays in the DOM (visually
   hidden, sr-only recipe) so the accessible name is unchanged. The
   icon is a currentColor mask, same technique as .theme-toggle-icon. */
@media (max-width: 600px) {
  .ask-popup-toggle {
    justify-content: center;
    inline-size: calc(var(--touch-target-min) + var(--space-2));
    block-size: calc(var(--touch-target-min) + var(--space-2));
    padding: 0;
  }
  .ask-popup-toggle::before {
    content: "";
    inline-size: var(--space-5);
    block-size: var(--space-5);
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/%3E%3C/svg%3E") center / contain no-repeat;
  }
  .ask-popup-toggle .ask-toggle-label {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
    white-space: nowrap; border: 0;
  }
}
.ask-response--loading {
  background: color-mix(in srgb, var(--ink-3) 8%, var(--card));
  color: var(--ink-3);
}
.ask-response--ok {
  background: color-mix(in srgb, var(--g-strong) 8%, var(--card));
  border-left: 3px solid var(--g-strong);
  color: var(--ink-2);
}
.ask-response--err {
  background: color-mix(in srgb, var(--g-urgent) 8%, var(--card));
  border-left: 3px solid var(--g-urgent);
  color: var(--ink-2);
}
.ask-summary { margin: 0 0 6px; color: var(--ink); }
.ask-applied {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--ink-3);
}
/* @applies-to button */
.ask-clear {
  background: transparent; border: 0; padding: 0;
  color: var(--claude-accent); font: inherit; font-size: var(--text-xs);
  cursor: pointer; text-decoration: underline;
  margin-left: var(--space-1);
}
.ask-spinner {
  display: inline-block;
  width: 10px; height: 10px;
  border: 2px solid color-mix(in srgb, var(--ink-3) 30%, transparent);
  border-top-color: var(--claude-accent);
  border-radius: 50%;
  margin-right: var(--space-2);
  vertical-align: -1px;
  animation: ask-spin 0.7s linear infinite;
}
@keyframes ask-spin {
  to { transform: rotate(360deg); }
}
/* Specific facts pulled by Claude from the cached stats payload —
   one bullet per number / named entity. Margin matches the gap
   between summary and the now-relocated "View configured" footer. */
.ask-facts {
  margin: 6px 0 6px;
  padding-left: 18px;
  font-size: var(--text-xs);
  color: var(--ink-2);
  line-height: 1.5;
}
.ask-facts li { margin: 0 0 3px; }
.ask-facts li:last-child { margin-bottom: 0; }
/* Follow-up chips — clicking re-asks Claude with the chip's text.
   Wrap so 3 short questions land on one or two lines. The "Try
   next:" label sits inline so a single chip doesn't look orphaned. */
.ask-follow-ups {
  margin: 0 0 6px;
  font-size: var(--text-xs);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
}
.ask-follow-ups-label {
  color: var(--ink-3);
  margin-right: 2px;
}
.ask-follow-up {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-full);
  padding: 2px 10px;
  font: inherit;
  font-size: var(--text-xs);
  color: var(--claude-accent);
  cursor: pointer;
  line-height: 1.5;
  text-align: left;
}
.ask-follow-up:hover {
  background: color-mix(in srgb, var(--claude-accent) 8%, var(--card));
  border-color: var(--claude-accent);
}
.ask-follow-up:focus-visible {
  outline: 2px solid var(--claude-accent);
  outline-offset: 1px;
}
/* "Refining (turn N) · Start over" hint above the input, visible
   only when a multi-turn conversation is in progress. Sized below
   the input row so it doesn't compete visually. */
.ask-refining-hint {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: 6px;
  padding: 4px 8px;
  background: color-mix(in srgb, var(--claude-accent) 6%, var(--card));
  border: 1px solid color-mix(in srgb, var(--claude-accent) 18%, var(--rule));
  border-radius: var(--r-sm);
  font-size: var(--text-xs);
  color: var(--ink-2);
}
/* @applies-to button */
.ask-refining-reset {
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--claude-accent);
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  text-decoration: underline;
}
.ask-refining-reset:hover { color: var(--claude-accent-strong, var(--claude-accent)); }

/* ────────────────────────────────────────────────────────────
   Observable Plot output styling.

   Every chart rendered via site/js/charts/plot-adapter.js gets
   the `ofsted-chart` className. We theme the SVG axes / labels /
   ticks through CSS so light- and dark-mode flow from the
   tokens — no JS recolouring on theme toggle for axis chrome.

   The grid / data-mark colours come from the Plot spec (which
   reads tokens() at render time) so they ARE recoloured on
   theme toggle — see plot-adapter.js's onThemeChange + the
   chart renderer's re-render in response.

   Plot's tip mark sits on top of the chart SVG; z-index lifts
   it above the sticky tab bar (z:20) when a tooltip overlaps.
   ──────────────────────────────────────────────────────────── */

.ofsted-chart text {
  font-family: inherit;
  fill: var(--ink-2);
}
.ofsted-chart [aria-label="x-axis tick"] line,
.ofsted-chart [aria-label="y-axis tick"] line {
  stroke: var(--rule);
}
.ofsted-chart [aria-label$="-axis label"] {
  fill: var(--ink-3);
}
.ofsted-chart [aria-label="x-axis tick label"],
.ofsted-chart [aria-label="y-axis tick label"] {
  fill: var(--ink-3);
}
.ofsted-chart [aria-label="frame"] {
  stroke: var(--rule);
}
.ofsted-chart [aria-label$="grid"] line {
  stroke: var(--rule-2);
}
.ofsted-chart [aria-label="tip"] {
  z-index: 25;
}
