/* ============================================================================
   TaxPolicy Department — site.css (B1, Bootstrap migration)
   Bootstrap-GAP components only: everything Bootstrap 5.3 has no equivalent
   for, extracted (adapted where the Bootstrap conversion changed a
   dependency — e.g. the language menu is now a Bootstrap Dropdown, so only
   the gov-bar TRIGGER button is kept here, not the old `.lang__menu` panel)
   from the now-deleted assets/css/base.css + assets/css/components.css.

   Do NOT add generic buttons/cards/forms/tables/accordion/toast/drawer CSS
   here — that is bootstrap-theme.css's job, styling Bootstrap's own classes.
   This file only ever holds markup/classes Bootstrap does not ship:
     - Skip link, government masthead strip, brand lockup
     - Dark gradient hero + gold glow, flag motifs
     - Dark site footer
     - `.prose` reading measure, `.reveal` scroll-entrance animation
     - The chart-engine's declarative chrome (assets/js/charts.js's full
       container/tooltip/legend/empty-state/bars/donut contract — every
       selector charts.js itself creates via className, verified against the
       engine's source; UNCHANGED by this migration per BOOTSTRAP_MIGRATION.md)
     - KPI tiles, the `.tag` pill label

   Load order (strict, every page): fonts.css → vendor/bootstrap/
   bootstrap.min.css → tokens.css → bootstrap-theme.css → site.css (this
   file loads LAST so it can freely use Bootstrap utility classes alongside
   its own, e.g. `<div class="hero container-xl">`).
   ============================================================================ */

/* ======================= SKIP LINK (a11y) ================================ */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -100px;
  z-index: 200;
  background: var(--color-accent-primary);
  color: var(--color-text-on-accent);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--border-radius-sm);
  font-weight: var(--font-weight-semibold);
  transition: top var(--duration-fast) var(--easing-default);
}
.skip-link:focus { top: var(--space-4); }

/* ======================= GOVERNMENT MASTHEAD ==============================
   The language switcher trigger lives here (`.gov-bar__btn`); its dropdown
   PANEL is now a real Bootstrap `.dropdown-menu` (themed in
   bootstrap-theme.css) instead of the old bespoke `.lang__menu`. */
.gov-bar {
  background: var(--gov-bar-bg);
  color: var(--gov-bar-text);
  font-size: var(--font-size-xs);
}
.gov-bar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 2.25rem;
}
.gov-bar__id { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.gov-bar__id span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gov-bar__crest {
  width: 20px; height: 20px; flex: none;
  border-radius: 50%;
  background: #fff;
  object-fit: contain;
  padding: 1px;
  box-sizing: border-box;
}
.gov-bar__id strong { font-weight: var(--font-weight-semibold); letter-spacing: 0.01em; }
.gov-bar__links { display: none; gap: var(--space-4); }
.gov-bar__links a { color: var(--gov-bar-text); opacity: 0.85; font-weight: var(--font-weight-normal); text-decoration: underline; }
.gov-bar__links a:hover { opacity: 1; text-decoration: underline; }
@media (min-width: 768px) { .gov-bar__links { display: flex; } }

.gov-bar__util { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.gov-bar__lang { position: relative; }
.gov-bar__btn {
  /* B2 fix: base.css used to carry a global native-<button>-chrome reset
     (background/border/appearance) that every plain button on the page
     inherited for free; Bootstrap's reboot only resets buttons that carry
     its OWN .btn class, so a bare <button> like this one now renders the
     browser's default gray/outset control — restated explicitly here since
     this class is (deliberately) not a Bootstrap .btn. */
  background: none; border: 0; appearance: none; -webkit-appearance: none;
  display: inline-flex; align-items: center; gap: 0.3rem;
  color: var(--gov-bar-text); opacity: 0.85;
  font-size: var(--font-size-xs); font-weight: var(--font-weight-medium);
  letter-spacing: var(--letter-spacing-wide); line-height: 1;
  padding: 0.3rem 0.2rem;
  cursor: pointer;
  transition: opacity var(--duration-fast);
}
.gov-bar__btn:hover, .gov-bar__btn[aria-expanded="true"] { opacity: 1; }
.gov-bar__btn-mark { line-height: 0; }

/* Readability text-size controls (A+/A-). */
.gov-bar__a11y { display: flex; align-items: center; gap: var(--space-1); }
.gov-bar__a11y-btn { gap: 0.05rem; font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); }
.gov-bar__a11y-btn--sm { font-size: var(--font-size-xs); }
.gov-bar__a11y-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ======================= BRAND LOCKUP ======================================
   MoFPED seal + "TaxPolicy" wordmark + subline. Reused (with local sizing
   overrides) by the nav, footer, admin sidebar and sign-in card. */
.brand { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.brand__seal { width: 38px; height: 38px; border-radius: 50%; object-fit: contain; flex: none; }
.brand__name { font-family: var(--font-family-display); font-weight: var(--font-weight-bold); font-size: var(--font-size-md); letter-spacing: -0.02em; line-height: 1.05; }
.brand__name span { display: block; font-family: var(--font-family-body); font-size: 0.62rem; font-weight: var(--font-weight-semibold); letter-spacing: var(--letter-spacing-wider); text-transform: uppercase; color: var(--color-text-secondary); }

/* Header lockup, enlarged (T5.x rebrand — ported verbatim from the retiring
   components.css `.nav`-scoped rules, rescoped to the Bootstrap navbar):
   bigger "TaxPolicy" + the "Department" subline in Capitalize case, amber.
   gold-700 (not the bright gold-400 fill accent) keeps AA on the light nav
   surface per the brief's amber-contrast rule. */
.navbar .brand__name { font-size: var(--font-size-xl); }
.navbar .brand__name span {
  font-size: var(--font-size-xs);
  letter-spacing: var(--letter-spacing-wide);
  text-transform: capitalize;
  color: var(--ug-gold-700);
}

/* ======================= HERO (dark gradient + gold glow) ==================
   Fixed dark surface, not themeable — see CLAUDE.md's "theme is light-only"
   rule. Kept intentionally minimal here (no `.hero--home` photographic
   variant, no hero-search/hero-card — those are page content, not chrome);
   the full hero pattern is ported page-by-page starting with B2. */
.hero {
  position: relative;
  background: var(--gradient-hero);
  color: var(--color-text-inverse);
  overflow: hidden;
  isolation: isolate;
}
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    radial-gradient(60% 80% at 78% 18%, rgba(249,162,29,0.20), transparent 60%),
    radial-gradient(50% 60% at 12% 0%, rgba(244,25,33,0.16), transparent 55%);
}
.hero::after {
  content: ""; position: absolute; right: -8%; top: -25%; width: 560px; height: 560px; z-index: -1;
  background: conic-gradient(from 210deg, transparent 0 58%, rgba(249,162,29,0.10) 74%, transparent 90%);
  border-radius: 50%;
}
/* ---- Hero without an overlapping float strip (B11 review) ----------------
   `.hero__grid` reserves a very deep bottom padding at >=1024px
   (`calc(var(--space-10) + var(--space-8))` = 192px) purely so the home page's
   `.float-strip` KPI card can overlap the hero's bottom edge. Any hero WITHOUT
   that strip inherits the allowance as dead space — on about.html it read as a
   large empty band between the lead paragraph and the first heading, compounded
   by `.hero__fade` dissolving the last 260px of the hero into the page canvas
   with nothing in it. This modifier restores a normal bottom and a crisp edge;
   the home hero already dropped its own fade in the 24 Jul 2026 mockup refit. */
.hero--plain .hero__grid { padding-block: var(--space-8); }
@media (min-width: 1024px) { .hero--plain .hero__grid { padding-block: var(--space-9); } }

/* bottom of hero dissolves into the page canvas */
.hero__fade {
  position: absolute; left: 0; right: 0; bottom: -40px; height: 260px; z-index: 0; pointer-events: none;
  background: linear-gradient(to bottom, transparent 0%, rgba(248,248,248,0.35) 55%, var(--color-bg-primary) 100%);
}
.hero__grid { position: relative; z-index: 1; display: grid; gap: var(--space-7); align-items: center; padding-block: var(--space-8) var(--space-10); }
@media (min-width: 1024px) { .hero__grid { grid-template-columns: 1fr 0.9fr; padding-block: var(--space-9) calc(var(--space-10) + var(--space-8)); } }
.hero :is(h1, h2, h3, h4, h5, h6) { color: #fff; }
@media (min-width: 1024px) { .hero h1 { font-size: clamp(2.25rem, 6vw, 3.125rem); } }
.hero h1 .hl { color: var(--ug-gold-400); }
.hero__lead { color: rgba(255,255,255,0.82); font-size: var(--font-size-md); max-width: 46ch; margin-top: var(--space-4); }

/* ======================= PAGE HERO (interior page header) ==================
   The light-surface counterpart to `.hero` — every interior page (rates,
   documents, policies, about, search, CMS pages) opens with one. The class was
   in the markup from the start but had NO rules anywhere in the stylesheets, so
   the <h1> rendered flush against the sticky navbar with zero breathing room.

   Deliberately quiet: a white band and a hairline, no gradient or glow, so it
   frames the page title without competing with the dark home hero. The band
   reads against both neighbours — the navbar above is translucent neutral-50
   and the page canvas below is neutral-50, while this sits at
   --color-bg-secondary (#fff), the same "lifts off the canvas" surface cards
   use. Bottom padding is lighter than top because the section that follows is
   always `.py-5`, which supplies its own leading space. */
.page-hero {
  position: relative;
  background: var(--color-bg-secondary);
  border-bottom: 1px solid var(--color-border-secondary);
  padding-block: var(--space-6) var(--space-5);
}
@media (min-width: 1024px) { .page-hero { padding-block: var(--space-7) var(--space-6); } }

/* Two steps down from the home hero's ramp (which tops out at 3.125rem) — an
   interior page title should not read as loud as the landing page. */
.page-hero h1 {
  font-size: clamp(1.75rem, 3.5vw, 2.25rem);
  max-width: 30ch;
  margin: 0;
}
/* Explicit flow spacing: reboot's heading margin is zeroed above so the gap
   below the title is one value regardless of what follows (lead, meta line,
   or the search form on the search page). Title and lead are one unit, so the
   gap between them stays tighter than the gap to the band's edge. */
.page-hero h1 + * { margin-top: var(--space-3); }
.page-hero .lead { max-width: 62ch; }
.page-hero > .container-xl > :last-child { margin-bottom: 0; }

/* ======================= FLAG MOTIFS ======================================= */
.flag-rule { height: 4px; border-radius: var(--border-radius-full); background: var(--gradient-flag); background-size: 100% 300%; }

/* ======================= FOOTER (dark surface) ============================= */
.site-footer {
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
  padding-block: var(--space-8) var(--space-6);
}
.site-footer a { color: #fff; font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.footer-grid { display: grid; gap: var(--space-6); grid-template-columns: 1fr; }
@media (min-width: 640px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1024px) { .footer-grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; } }
.footer-col h2 { color: rgba(255,255,255,0.5); font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: var(--letter-spacing-wider); margin-bottom: var(--space-3); font-family: var(--font-family-body); }
.footer-col ul { list-style: none; padding: 0; display: grid; gap: var(--space-2); }
.footer-about p { color: rgba(255,255,255,0.7); font-size: var(--font-size-sm); margin-top: var(--space-3); max-width: 32ch; }
.footer-bottom { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: space-between; align-items: center; margin-top: var(--space-7); padding-top: var(--space-5); border-top: 1px solid rgba(255,255,255,0.12); color: rgba(255,255,255,0.6); font-size: var(--font-size-xs); }
.footer-brand { display: flex; align-items: center; gap: var(--space-3); }
.footer-brand .brand__seal { width: 64px; height: 64px; }
.footer-brand .brand__name { font-size: var(--font-size-2xl); }
.footer-brand .brand__name span { font-size: var(--font-size-lg); font-weight: var(--font-weight-normal); letter-spacing: normal; text-transform: capitalize; }

/* ======================= PROSE (reading measure) ============================ */
.prose { max-width: var(--max-width-content); }
.prose p + p { margin-top: var(--space-4); }

/* ======================= REVEAL-ON-SCROLL ==================================
   Progressive enhancement; only hides content when JS adds html.js (see the
   inline script every page carries — unchanged by this migration). */
html.js .reveal { opacity: 0; transform: translateY(16px); transition: opacity var(--duration-slow) var(--easing-out), transform var(--duration-slow) var(--easing-out); }
html.js .reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { html.js .reveal { opacity: 1; transform: none; transition: none; } }

/* ======================= MISC SHARED PRIMITIVES ============================
   `.sr-only` is required verbatim: assets/js/charts.js hardcodes
   `liveRegionEl.className = "sr-only chart-live-region"` (its keyboard-nav
   aria-live announcer) — Bootstrap's equivalent is named `.visually-hidden`,
   a different class charts.js does not use, so this can't be dropped even
   though it looks like a Bootstrap-covered utility. `.dot` is a bare
   dependency of `.tag .dot` below (not a full status-dot system — see the
   retiring components.css for `.dot-live` etc. if a future page needs it). */
.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;
}
.dot { width: 8px; height: 8px; border-radius: var(--border-radius-full); display: inline-block; flex: none; }

/* Subtle, low-contrast section label pill. */
.tag {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 0.35rem 0.8rem;
  border-radius: var(--border-radius-full);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-secondary);
  box-shadow: var(--shadow-xs);
  font-family: var(--font-family-body);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-wide);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.tag .dot { background: var(--color-status-warning); }

/* ======================= KPI / STAT TILES =================================== */
.kpi-row { display: grid; gap: var(--space-4); grid-template-columns: 1fr; }
@media (min-width: 640px) { .kpi-row { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .kpi-row { grid-template-columns: repeat(4, 1fr); } }
.kpi { padding: var(--space-5); }
.kpi__label { font-size: var(--font-size-sm); color: var(--color-text-secondary); display: flex; align-items: center; gap: var(--space-2); }
.kpi__value { font-family: var(--font-family-display); font-size: var(--font-size-3xl); font-weight: var(--font-weight-bold); letter-spacing: -0.02em; margin-top: var(--space-2); }
.kpi__delta { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); margin-top: var(--space-1); display: inline-flex; align-items: center; gap: var(--space-1); }
.kpi__delta.up { color: var(--color-status-success); }
.kpi__delta.down { color: var(--color-status-error); }
/* .kpi__meta is ALSO the chart engine's source/updated caption class (charts.js
   sets `target.className = "chart-figure__caption kpi__meta"`) — one rule
   serves both a KPI tile's meta line and every chart's caption line. */
.kpi__meta { font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin-top: var(--space-3); }

/* ============================================================================
   CHART ENGINE CHROME (assets/js/charts.js) — UNCHANGED by this migration.
   Every selector below is created verbatim by charts.js via className/
   classList; semantic/chart tokens ONLY, never raw --ug-* primitives or hex.
   See charts.js's header comment for the full declarative container/JSON API.
   ============================================================================ */
.chart-figure { width: 100%; position: relative; }
.chart-figure:focus-visible { border-radius: var(--border-radius-md); }
.chart-figure__plot { width: 100%; }
.chart-figure__svg { display: block; overflow: visible; touch-action: pan-y; }
.chart-figure__tick { font-size: var(--font-size-xs); fill: var(--chart-axis-text); }
.chart-figure__marker { r: calc(var(--chart-marker-size) / 2); stroke: var(--chart-marker-ring); stroke-width: 2px; }
.chart-figure__hit { r: 12px; }
.chart-figure__label-name { font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold); fill: var(--color-text-secondary); }
.chart-figure__label-value { font-size: var(--font-size-xs); font-variant-numeric: tabular-nums; font-weight: var(--font-weight-semibold); fill: var(--color-text-primary); }
.chart-figure__xaxis { display: flex; justify-content: space-between; margin-top: var(--space-3); font-size: var(--font-size-xs); color: var(--color-text-tertiary); }
.chart-figure__xaxis span { flex: 1; text-align: center; }
.chart-figure__legend { margin-bottom: var(--space-4); }
.chart-figure__empty { border: 1px dashed var(--color-border-primary); border-radius: var(--border-radius-lg); }

/* Missing source/updated caption — a chart config without both fields fails
   visibly by design, never silently renders no caption. */
.chart-figure__caption--missing {
  display: inline-block; padding: var(--space-2) var(--space-3);
  border: 1.5px dashed var(--color-status-warning);
  border-radius: var(--border-radius-sm);
  color: var(--color-status-warning); font-weight: var(--font-weight-semibold);
}

/* ---- Shared tooltip (one DOM node, reused by every chart on the page) --- */
.chart-tooltip {
  position: fixed; z-index: 400; pointer-events: none;
  background: var(--chart-tooltip-bg); color: var(--chart-tooltip-text);
  border-radius: var(--chart-tooltip-radius); box-shadow: var(--chart-tooltip-shadow);
  padding: var(--space-3) var(--space-4); font-size: var(--font-size-sm);
  max-width: min(88vw, 20rem);
}
.chart-tooltip__period { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); opacity: 0.7; margin-bottom: var(--space-2); }
.chart-tooltip__row { display: flex; align-items: center; gap: var(--space-2); padding-block: 2px; }
.chart-tooltip__swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.chart-tooltip__name { flex: 1; }
.chart-tooltip__value { font-variant-numeric: tabular-nums; font-weight: var(--font-weight-semibold); margin-left: var(--space-3); }
.chart-tooltip__row--total { margin-top: var(--space-1); padding-top: var(--space-2); border-top: 1px solid rgba(255,255,255,0.2); }

/* Inline share meter (breakdown table row fill) */
.meter { height: 8px; border-radius: var(--border-radius-full); background: var(--color-bg-muted); overflow: hidden; }
.meter span { display: block; height: 100%; background: var(--chart-1); border-radius: inherit; }

/* ---- Bars module (data-chart="bars", horizontal + vertical + stacked) --- */
.chart-bars { display: flex; flex-direction: column; gap: var(--chart-gap); }
.chart-bars__row {
  display: grid; grid-template-columns: 1fr; gap: var(--space-2);
  padding: var(--space-1) var(--space-2); border-radius: var(--border-radius-sm);
}
@media (min-width: 640px) {
  .chart-bars__row { grid-template-columns: 12rem 1fr 7rem; align-items: center; gap: var(--space-4); }
}
.chart-bars__row--active { background: var(--color-bg-tertiary); }
.chart-bars__label { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.chart-bars__track { height: 14px; border-radius: var(--border-radius-xs); background: var(--chart-donut-track); overflow: hidden; }
.chart-bars__fill { display: block; height: 100%; border-radius: var(--border-radius-xs); background: var(--chart-1); }
.chart-bars__value { font-size: var(--font-size-sm); font-variant-numeric: tabular-nums; color: var(--color-text-primary); font-weight: var(--font-weight-medium); }
@media (min-width: 640px) { .chart-bars__value { text-align: right; } }

/* Vertical orientation (column charts) — same .chart-bars__row wrapper,
   different internal layout; ordinal/diverging colorMode sets
   --chart-ord-N/--chart-div-* inline per row/segment (no CSS hook needed). */
.chart-bars--vertical { display: flex; flex-direction: row; align-items: flex-end; gap: var(--chart-gap); }
.chart-bars--vertical .chart-bars__row {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  flex: 1 1 0; min-width: 0; height: 100%; gap: var(--space-2);
  padding: var(--space-1); border-radius: var(--border-radius-sm);
}
.chart-bars--vertical .chart-bars__row--active { background: var(--color-bg-tertiary); }
.chart-bars__colplot { flex: 1; width: 100%; min-height: 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: var(--space-1); }
.chart-bars__fill-v { display: block; width: 100%; min-height: 2px; border-radius: var(--border-radius-xs) var(--border-radius-xs) 0 0; background: var(--chart-1); }
.chart-bars__value-v { font-size: var(--font-size-xs); font-variant-numeric: tabular-nums; font-weight: var(--font-weight-semibold); color: var(--color-text-primary); white-space: nowrap; }
.chart-bars__label-v { font-size: var(--font-size-xs); color: var(--color-text-tertiary); text-align: center; white-space: nowrap; }

/* Stacked bar module — reuses the vertical bars markup wholesale; only
   `.chart-bars__stack` (per-column series wrapper) and `.chart-bars__segment`
   (one per series) are new. */
.chart-bars__stack { width: 100%; display: flex; flex-direction: column-reverse; }
.chart-bars__segment { display: block; width: 100%; }
.chart-bars__segment:not(:first-child) { margin-top: var(--chart-gap); }

/* ---- Donut module (data-chart="donut") ---------------------------------- */
.chart-donut { display: flex; justify-content: center; }
.chart-donut__segment { transition: opacity var(--duration-fast) var(--easing-default); }
.chart-donut--hover-active .chart-donut__segment { opacity: 0.45; }
.chart-donut--hover-active .chart-donut__segment--active { opacity: 1; }
.chart-donut__leader { stroke: var(--color-border-primary); stroke-width: 1px; }
.chart-donut__center-value { font-size: var(--font-size-xl); font-weight: var(--font-weight-bold); fill: var(--color-text-primary); font-variant-numeric: tabular-nums; }
.chart-donut__center-label { font-size: var(--font-size-xs); fill: var(--color-text-secondary); text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); }

/* ---- Legend (shared by every chart-figure chart) ------------------------ */
.legend { display: flex; flex-wrap: wrap; gap: var(--space-4); font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.legend span { display: inline-flex; align-items: center; gap: var(--space-2); }
.legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

/* ---- Empty state (chart engine's no-data state nests .empty inside
   .chart-figure__empty; also usable standalone for any empty list/table). */
.empty { text-align: center; padding: var(--space-8) var(--space-4); color: var(--color-text-secondary); }
.empty__icon { width: 56px; height: 56px; margin: 0 auto var(--space-4); display: grid; place-items: center; border-radius: var(--border-radius-full); background: var(--color-bg-tertiary); color: var(--color-text-tertiary); }
.empty h2, .empty h3 { color: var(--color-text-primary); }  /* B11: the admin
   empty/locked-state headings became <h2> (they are the only heading in <main>
   besides the page h1 when visible); h3 kept for charts.js's own empty state. */

/* ============================================================================
   B2 ADDITIONS (public pilot, Bootstrap migration) — public/index.html-only
   chrome with no Bootstrap equivalent, ported verbatim (same tokens, same
   values) from the retiring components.css/base.css per
   BOOTSTRAP_MIGRATION.md's "extend with restraint, comment any addition".
   Later batches (B3-B6) reuse these same classes wherever their pages carry
   the same card/hero/KPI/table patterns — nothing here is index.html-scoped
   by selector, only by "first page that needed it".
   ============================================================================ */

/* ---- Icon button (nav search + mobile offcanvas toggle) ------------------
   Used on both <a> (search) and bare <button> (offcanvas toggler) — the
   background/border/appearance reset below only matters for the <button>
   case (see the matching comment on .gov-bar__btn above for why: base.css's
   global button reset is gone on converted pages, Bootstrap's reboot only
   resets its OWN .btn). */
.icon-btn {
  background: none; border: 0; appearance: none; -webkit-appearance: none;
  width: 40px; height: 40px; border-radius: var(--border-radius-sm);
  display: grid; place-items: center;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background-color var(--duration-fast), color var(--duration-fast);
}
.icon-btn:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.icon-btn svg { width: 20px; height: 20px; }

/* ---- Gov-bar language dropdown "Soon" tag (panel itself is now a real
   Bootstrap .dropdown-menu — see site.css file header) --------------------- */
.lang__soon { font-size: 0.625rem; text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); color: var(--color-text-tertiary); }

/* ---- Section heading block (tag + h2 + optional lead, above a row/grid) -- */
.section-head { max-width: 46rem; margin-bottom: var(--space-6); }
.section-head h2 { margin-top: var(--space-2); }

/* ---- Home hero — photographic treatment (mockup 2026-07-23). Scoped to
   .hero--home only; about.html's hero (ported in a later batch) keeps the
   plain dark gradient already in the base .hero rule above. */
.hero--home {
  background:
    linear-gradient(180deg,
      rgba(202, 156, 84, 0.80) 0%,
      rgba(186, 138, 70, 0.78) 20%,
      rgba(196, 152, 88, 0.75) 45%,
      rgba(180, 134, 72, 0.70) 65%,
      rgba(110, 72, 32, 0.42) 80%,
      rgba(72, 46, 20, 0.12) 92%,
      rgba(50, 30, 14, 0) 100%
    ),
    url("../img/hero-kampala-traffic.png") center bottom / cover no-repeat,
    var(--gradient-hero);
}
/* the mockup shows no red/gold glow rings on the photo — repurpose the base
   .hero::before glow layer as a left-side scrim so the headline/lead stay
   readable, and drop the corner glow (::after) entirely. */
.hero--home::before {
  background: linear-gradient(100deg, rgba(28, 17, 8, 0.55) 0%, rgba(28, 17, 8, 0.28) 40%, transparent 66%);
}
.hero--home::after { content: none; }
/* Home hero only: shorter vertically than the shared .hero__grid base rule
   above (which about.html still uses once ported). */
@media (min-width: 1024px) {
  .hero--home .hero__grid { padding-block: var(--space-9) calc(var(--space-9) + var(--space-8)); }
}
/* Citizen figure (mockup motif) — replaces a fintech stat card on the
   homepage only; mobile keeps the single-column text-only hero. */
.hero__figure { display: none; }
.hero__figure img { display: block; width: 100%; height: auto; }
@media (min-width: 1024px) {
  .hero--home .hero__figure {
    display: block; justify-self: center; align-self: end;
    width: clamp(300px, 34vw, 490px);
    max-width: 490px;
    margin-right: var(--space-5);
    margin-bottom: calc(-1 * var(--space-8));
  }
}

/* ---- Hero search pill --------------------------------------------------- */
.hero-search { margin-top: var(--space-6); max-width: 34rem; }
.search-field {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-bg-secondary);
  border-radius: var(--border-radius-full);
  padding: 0.4rem 0.4rem 0.4rem var(--space-4);
  box-shadow: var(--shadow-lg);
}
.search-field input { flex: 1; border: 0; background: transparent; min-height: 44px; font-size: var(--font-size-base); color: var(--color-text-primary); }
.search-field input:focus { outline: none; }
.search-field svg { width: 20px; height: 20px; color: var(--color-text-tertiary); flex: none; }
/* faint amber outline on the search pill, per the mockup (.search-field is
   shared elsewhere, so keep this scoped to the home hero). */
.hero--home .search-field { border: 1px solid rgba(249, 162, 29, 0.45); }

/* ---- Floating KPI strip (overlaps the hero bottom edge) ------------------ */
.float-strip {
  position: relative; z-index: 5;
  margin-top: calc(-1 * var(--space-9));
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-secondary);
  border-radius: var(--border-radius-xl);
  box-shadow: var(--shadow-float);
  padding: var(--space-2);
  display: grid; grid-template-columns: 1fr; gap: var(--space-1);
}
@media (min-width: 640px) { .float-strip { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .float-strip { grid-template-columns: repeat(4, 1fr); } }
.float-strip__item { padding: var(--space-4) var(--space-5); border-radius: var(--border-radius-lg); }
.float-strip__item + .float-strip__item { position: relative; }
@media (min-width: 1024px) {
  .float-strip__item:not(:first-child)::before {
    content: ""; position: absolute; left: 0; top: 22%; bottom: 22%; width: 1px; background: var(--color-border-secondary);
  }
}
.float-strip__label { font-size: var(--font-size-xs); color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); }
.float-strip__value { font-family: var(--font-family-display); font-size: var(--font-size-2xl); font-weight: var(--font-weight-bold); letter-spacing: -0.02em; margin-top: var(--space-1); }
.float-strip__meta { font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin-top: 2px; }

/* ---- Card-as-link affordance (quick-access service cards) ---------------- */
.card-link {
  display: block; color: inherit; position: relative;
  transition: transform var(--duration-normal) var(--easing-default), box-shadow var(--duration-normal) var(--easing-default), border-color var(--duration-fast);
}
.card-link:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); border-color: var(--color-border-primary); }
/* minimalist corner affordance — replaces repetitive "read more" links */
.card-arrow {
  position: absolute; top: var(--space-4); right: var(--space-4);
  width: 34px; height: 34px; border-radius: var(--border-radius-full);
  display: grid; place-items: center;
  border: 1px solid var(--color-border-primary);
  color: var(--color-text-tertiary);
  transition: all var(--duration-normal) var(--easing-default);
}
.card-arrow svg { width: 16px; height: 16px; }
.card-link:hover .card-arrow { background: var(--gradient-gold); border-color: transparent; color: var(--ug-ink-900); transform: rotate(-45deg); }
.feature__icon {
  width: 48px; height: 48px; border-radius: var(--border-radius-md);
  display: grid; place-items: center;
  background: var(--color-bg-tertiary); color: var(--color-text-primary);
  transition: background-color var(--duration-normal), color var(--duration-normal), transform var(--duration-normal);
}
.feature__icon svg { width: 24px; height: 24px; }
/* one accent per row: icon warms to gold only on interaction */
.card-link:hover .feature__icon { background: var(--gradient-gold); color: var(--ug-ink-900); transform: translateY(-2px); }

/* ---- Card modifiers (chart "data container" card) ------------------------ */
.card-body.card--pad-lg { padding: var(--space-6); }
.card.card--float { border-radius: var(--border-radius-xl); box-shadow: var(--shadow-float); border-color: var(--color-border-secondary); }

/* ---- Table wrap (overflow-x scroller for the chart's "view as table"
   alternative and any other wide table) ------------------------------------ */
.table-wrap { overflow-x: auto;  border: 1px solid var(--color-border-secondary); border-radius: var(--border-radius-lg); background: var(--card-bg); }

/* ---- CTA / trust band (dark gradient banner) ------------------------------ */
.cta-band {
  background: var(--gradient-hero);
  color: #fff;
  border-radius: var(--border-radius-xl);
  padding: var(--space-7);
  display: grid; gap: var(--space-5); justify-items: start;
  position: relative; overflow: hidden;
}
.cta-band::before { content:""; position:absolute; inset:0; background: var(--glow-gold); }
.cta-band > * { position: relative; }
.cta-band :is(h1, h2, h3, h4, h5, h6) { color: #fff; }
/* ---- Split CTA band (B11 review) -----------------------------------------
   The base `.cta-band` is a single start-aligned column, which on a wide card
   leaves the whole right half empty and pushes the button far down — visible
   weight at the bottom of the page with nothing balancing it. This modifier
   puts the copy and the action side by side from tablet up, which also makes
   the card shorter. Base (stacked) behaviour is unchanged for the bands that
   want it — index.html's, which carries a stat row underneath, and
   understand.html's, whose two buttons sit inline. */
@media (min-width: 768px) {
  .cta-band--split { grid-template-columns: 1fr auto; align-items: center; column-gap: var(--space-7); }
}


.cta-band p { color: rgba(255,255,255,0.8); }
.cta-band__cta { margin-top: var(--space-1); }
.cta-band__stats { margin-top: var(--space-3); width: 100%; }
.stat-block { text-align: left; }
.stat-block__num { font-family: var(--font-family-display); font-size: var(--font-size-4xl); font-weight: var(--font-weight-bold); letter-spacing: -0.03em; line-height: 1; }
.stat-block__num .unit { color: var(--color-status-warning); }
.stat-block__label { color: var(--color-text-secondary); margin-top: var(--space-2); font-size: var(--font-size-sm); }
.cta-band .stat-block__num .unit { color: var(--ug-gold-500); }

/* ============================================================================
   B3 ADDITIONS (public batch A: about, accessibility, privacy, faq, contact,
   search, understand, understand-vat) — Bootstrap-gap chrome these 8 pages
   need that B1/B2 didn't already port, each ported verbatim (same tokens,
   same values) from the retiring base.css/components.css per
   BOOTSTRAP_MIGRATION.md's "extend with restraint, comment any addition".
   Reusable by any later batch that carries the same pattern, same as B2's
   own additions above.
   ============================================================================ */

/* ---- Eyebrow label (uppercase micro-label, dark-hero and light contexts) --
   Ported verbatim from the retiring base.css. */
.eyebrow {
  font-family: var(--font-family-body);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-wider);
  text-transform: uppercase;
  color: var(--color-text-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* ---- Section rhythm (vertical padding-block for content sections) --------
   Ported verbatim from the retiring base.css; `.home .section` override
   deliberately not ported (index.html-only, out of this batch's scope). */
.section { padding-block: var(--space-8); }
@media (min-width: 768px) { .section { padding-block: var(--space-9); } }
.section--tight { padding-block: var(--space-7); }

/* ---- Page header block (tag/eyebrow + h1 + intro paragraph, inner pages) --
   Ported verbatim from the retiring components.css. Bootstrap's own
   `.border-0`/`.p-0` utilities neutralise it where a page needs a bare
   heading block instead (accessibility.html, privacy.html use these to
   cancel the border/padding, same as their pre-Bootstrap inline-style
   override did). */
.page-head { padding-block: var(--space-7) var(--space-6); border-bottom: 1px solid var(--color-border-secondary); }
.page-head h1 { font-size: clamp(2rem, 5vw, var(--font-size-4xl)); }
.page-head p { color: var(--color-text-secondary); margin-top: var(--space-3); max-width: 60ch; }

/* ---- Divider rule (thin, token-colored, borderless) — Bootstrap's own
   `<hr>` default doesn't match this look; ported verbatim from the
   retiring base.css. */
.divider { height: 1px; background: var(--color-border-secondary); border: 0; }

/* ---- "Read more" link affordance (related-links asides) — ported verbatim
   from the retiring components.css FEATURE block. */
/* B7 fix (found live, affects every page that already uses this class):
   Bootstrap's reboot bakes `text-decoration: underline` onto the bare `a`
   selector (the same "baked, not variable-driven" gotcha this file's B3
   `a:not([class])` fix already documents) — `.feature__more` carries a
   class, so that classless-only fix doesn't reach it, and every
   already-shipped "Read more"-style link using this class (contact.html,
   laws-vat-act.html, performance-data.html, understand-vat.html, this
   task's own dashboard "Full analytics" link…) rendered underlined at rest,
   contrary to the arrow-affordance look `.card-arrow`/`.feature__icon`
   establish alongside it. Restated here once, fixing every page at the
   source rather than a per-page markup patch. */
.feature__more { color: var(--color-text-secondary); font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); text-decoration: none; display: inline-flex; align-items: center; gap: var(--space-1); transition: color var(--duration-fast), gap var(--duration-fast); }
.card-link:hover .feature__more { gap: var(--space-2); color: var(--color-status-warning); }

/* ---- Gold-fill icon variant (understand-vat.html's "in short" callout) ---
   Ported verbatim from the retiring components.css. */
.feature__icon--gold { background: var(--gradient-gold); color: var(--ug-ink-900); }

/* ---- Soft status badges (search.html result-type labels) — ported verbatim
   from the retiring base.css. Layered on top of Bootstrap's own themed
   `.badge` (bootstrap-theme.css handles shape/weight/spacing); these two
   rules only ever set background/color. */
.badge-open   { background: var(--color-success-bg); color: var(--color-status-success); }
.badge-closed { background: var(--color-bg-muted); color: var(--color-text-secondary); }
.survey-status--closed { background: var(--color-error-bg); color: var(--color-status-error); }

/* ---- Filter chips (search.html result-type filter) — ported verbatim from
   the retiring components.css; app.js's data-filter engine (unchanged)
   drives these exactly as it did the pre-Bootstrap chips — this IS the
   "restyled as themed pill buttons" treatment the migration contract calls
   for, since the pre-existing chip look already is a themed pill button. */
.filter-bar { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin-bottom: var(--space-4); }
.chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 0.4rem 0.85rem; border-radius: var(--border-radius-full);
  border: 1px solid var(--color-border-primary); background: var(--color-bg-secondary);
  font-size: var(--font-size-sm); color: var(--color-text-secondary); text-decoration: none;
  transition: all var(--duration-fast);
}
.chip:hover { border-color: var(--color-text-primary); color: var(--color-text-primary); text-decoration: none; }
.chip[aria-pressed="true"] { background: var(--color-bg-inverse); color: var(--color-text-inverse); border-color: transparent; }
/* Category/list-filter chips are anchors marked with aria-current rather than
   aria-pressed toggle buttons — same look (mirrors admin-modules.css's rule
   for the admin console's own chip-as-link filters), correct semantics. */
.chip[aria-current="page"] { background: var(--color-bg-inverse); color: var(--color-text-inverse); border-color: transparent; }
.chip[aria-current="page"] .num { color: inherit; opacity: 0.7; }
.chip .num { color: var(--color-text-tertiary); }

/* ---- Category tabs (publications browse) ---------------------------------
   A grouped segmented-style tab bar for the one place a filter *is* the
   primary way of navigating the page rather than a secondary refinement.
   Every tab is an equal flex share of the bar (uniform box size regardless
   of label length) inside a shared "track", the way a segmented control
   distributes its options — gold-accent active tab matches the site's other
   "selected" treatments (feature-icon hover, admin avatar). Real links
   (full page navigation), so — unlike .segmented above — no JS tab engine. */
.category-nav {
  display: flex; flex-wrap: wrap; gap: var(--space-1);
  padding: var(--space-1); margin-bottom: var(--space-5);
  background: var(--color-bg-tertiary);
  border: 1px solid var(--color-border-secondary);
  border-radius: var(--border-radius-lg);
}
.category-nav__item {
  flex: 1 1 8rem; min-width: 8rem; text-align: center;
  padding: var(--space-3) var(--space-4); border-radius: var(--border-radius-md);
  color: var(--color-text-secondary); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
  text-decoration: none; transition: all var(--duration-fast);
}
.category-nav__item:hover { color: var(--color-text-primary); text-decoration: none; }
.category-nav__item[aria-current="page"] {
  background: var(--gradient-gold); color: var(--ug-ink-900);
  box-shadow: var(--shadow-float); font-weight: var(--font-weight-semibold);
}

/* ---- Active primary-nav indicator (desktop navbar-nav + offcanvas) -------
   Adapted from the retiring components.css `.nav__link[aria-current="page"]`
   rule (selector renamed only: `.nav-link` is Bootstrap's class for the same
   element in both the desktop navbar-nav and the offcanvas body). Not
   exercised by B2's index.html (no primary-nav item is ever "current" on the
   homepage) — first used by B3's about.html/understand.html/
   understand-vat.html, each marking its own top-nav item aria-current. */
.nav-link[aria-current="page"] {
  color: var(--color-text-primary);
  font-weight: var(--font-weight-semibold);
}
.nav-link[aria-current="page"]::after {
  content: ""; display: block; height: 2px; margin-top: 4px; border-radius: 2px;
  background: var(--gradient-gold);
}

/* ---- [hidden] reset (found live, contact.html's demo-form success alert) -
   Ported verbatim from the retiring base.css reset (`[hidden] { display:
   none !important; }`). Bootstrap's own reboot never sets this — it relies
   on the browser's UA-stylesheet default of `display:none` for `[hidden]`,
   which has NO !important behind it. Any Bootstrap display utility applied
   to the same element (`.d-flex`, `.d-grid`, `.d-block`…) IS `!important`
   and silently wins, so an element toggled via the `hidden` attribute (e.g.
   app.js's `data-demo-form` success reveal) rendered visible from first
   paint the moment it also carried a layout utility class. Restated
   globally here, same as base.css guaranteed sitewide, so this can't
   resurface on any later batch that combines the two. */
[hidden] { display: none !important; }

/* ---- Bare-<button> chrome reset (B11-review, found live) ------------------
   The retiring base.css carried a global native-<button> reset that every
   plain button inherited for free. Bootstrap's reboot only normalises buttons
   carrying its OWN component classes (.btn, .btn-close, .accordion-button,
   .navbar-toggler…), so each of our custom button classes has had to restate
   `background/border/appearance` for itself — and any one we missed rendered
   the browser's grey/outset control. B2 caught `.gov-bar__btn` and `.icon-btn`
   that way; a B11 review of the open account menu caught three more
   (`.role-option`, `.admin-account__trigger`, `.segmented button`), all
   invisible in screenshots because they sit inside closed menus or read as
   "just a slightly boxy control".

   Restated here once, globally, instead of waiting for the next one to be
   spotted. `:where()` gives the rule ZERO specificity, so every class-based
   rule still wins with no !important arms race — Bootstrap's own
   `.btn { background-color: … }` (0,1,0) and our `.chip`/`.badge`-style
   buttons override it automatically, exactly as they did when base.css's
   reset came first in the cascade. */
:where(button) {
  background: none;
  border: 0;
  appearance: none;
  -webkit-appearance: none;
}

/* ============================================================================
   B4 ADDITIONS (public batch B: laws, laws-vat-act, rates, news, article) —
   Bootstrap-gap chrome these 5 pages need, ported verbatim (same tokens,
   same values) from the retiring components.css/base.css per
   BOOTSTRAP_MIGRATION.md's "extend with restraint, comment any addition".
   ============================================================================ */

/* ---- `.table-wrap` sticky first column (settles B1's open question) ------
   B1/B2 only ported the plain overflow-x scroller half of `.table-wrap`
   (above, in the B2 ADDITIONS block); this is the other half the migration
   plan explicitly calls out — "port `.table-wrap` verbatim from
   components.css into site.css (overflow scroll + sticky first column)".
   Ported verbatim from the retiring components.css (design-review
   Should-fix 3): pins the row-identity (first) column of any table nested
   in `.table-wrap` so identity + trailing row-actions stay reachable while
   the rest of a wide row scrolls horizontally at narrow widths. Selector is
   generic (`.table-wrap .table …`), so it applies equally whether `.table`
   is Bootstrap's own class (laws.html/rates.html's real data tables) or a
   charts.js-generated table (index.html's "View as table") — no markup
   changes needed either way. `:not([colspan])` excludes full-width detail
   cells (rates.html's `data-detail-row` explanations use a single colspan
   cell for the whole row) — that cell already spans the row's full
   scrollable width, so sticking it would freeze the entire detail row
   instead of letting it scroll with its siblings. */
.table-wrap .table th:first-child:not([colspan]),
.table-wrap .table td:first-child:not([colspan]) {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--card-bg);
  border-right: 1px solid var(--color-border-secondary);
  max-width: 11rem;
}
.table-wrap .table tbody tr:hover td:first-child:not([colspan]) { background: var(--color-bg-tertiary); }

/* ---- Row-action cell alignment (laws.html/rates.html "Open"/"Details"
   buttons) — ported verbatim from the retiring components.css. */
/* B11 review: `align-items: center` added. Making the <td> a flex container
   (as the pre-Bootstrap rule did) opts it out of table-cell vertical-align,
   so the action link floated off the row's optical centre — most visible on
   laws.html, whose title cells wrap to two lines. */
.table .row-actions { display: flex; align-items: center; gap: var(--space-1); justify-content: flex-end; }

/* ---- Gold soft badge (news.html/article.html "Featured"/"Policy" labels) -
   Ported verbatim from the retiring base.css; layered on top of Bootstrap's
   own themed `.badge` same as the B3-added `.badge-open`/`.badge-closed`
   pair — this rule only ever sets background/color. */
.badge-gold { background: var(--ug-gold-100); color: var(--ug-gold-900); }

/* ============================================================================
   B5 ADDITIONS (public batch C: performance + 13 drill-downs) — Bootstrap-gap
   chrome this batch needs, ported verbatim (same tokens, same values) from
   the retiring base.css/components.css per BOOTSTRAP_MIGRATION.md's "extend
   with restraint, comment any addition". First batch to carry a real
   chart-bound `.segmented` control and the sticky sub-nav "portal bar".
   ============================================================================ */

/* ---- Sticky portal control bar (period/view controls under the navbar) --
   Ported verbatim from the retiring components.css. `--nav-bg`/`--nav-height`
   are still live tokens (tokens.css) also used by the Bootstrap navbar itself
   (bootstrap-theme.css), so this sticks flush under it with no gap/overlap. */
.portal-bar {
  position: sticky; top: var(--nav-height); z-index: 50;
  background: var(--nav-bg);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--color-border-secondary);
}
.portal-bar__inner { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; justify-content: space-between; padding-block: var(--space-3); }

/* ---- Segmented toggle (chart-bound controls only) ------------------------
   Ported verbatim from the retiring components.css. Per BOOTSTRAP_MIGRATION.md,
   a segmented control that drives a chart (no real tab panes to show/hide)
   keeps the legacy click+arrow-key `.segmented`/`[role="tablist"]` engine in
   app.js instead of becoming a real Bootstrap Tab — the container carries
   BOTH `.segmented` (for this ruleset) and `.nav.nav-pills` (bootstrap-theme.css
   themes an identical-looking container, so the two overlap harmlessly), but
   the buttons stay plain `[role="tab"]` with no `.nav-link`/`.active` class,
   since Bootstrap's tab.js — which is what would toggle `.active` — never
   runs for these. Bootstrap only styles `.nav-pills .nav-link.active`, which
   these buttons never get, so the actual selected-pill look (background/
   shadow/weight) comes entirely from the `[aria-selected="true"]` rule below,
   driven by app.js's existing click/keydown handlers untouched by this
   migration. */
.segmented {
  display: inline-flex; padding: 4px; gap: 2px;
  background: var(--color-bg-tertiary);
  border-radius: var(--border-radius-full);
  border: 1px solid var(--color-border-secondary);
}
.segmented button {
  padding: 0.5rem 1rem; border-radius: var(--border-radius-full);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary); transition: all var(--duration-fast);
}
.segmented button[aria-selected="true"] { background: var(--color-bg-secondary); color: var(--color-text-primary); box-shadow: var(--shadow-xs); font-weight: var(--font-weight-semibold); }
/* T23 a11y pass (pre-migration): the rule above's box-shadow out-specifies
   the global :focus-visible ring (an attribute selector beats a bare
   pseudo-class), so a currently-selected segmented tab showed no focus ring
   at all when reached by keyboard — compose the ring back on top of the
   selected-tab shadow instead of replacing it. */
.segmented button[aria-selected="true"]:focus-visible { box-shadow: var(--focus-ring), var(--shadow-xs); }

/* ---- Live status dot (performance pages' "Updated …" tag pill) — ported
   verbatim from the retiring base.css. Note: `.tag .dot` (declared earlier in
   this file, itself ported from base.css) is TWO classes and so outranks this
   single-class rule on specificity wherever the dot sits inside a `.tag` —
   the same as it did pre-migration (both rules already coexisted in base.css
   there), so the tag's dot renders amber even with `.dot-live` present. Not a
   migration regression: kept verbatim for parity, and for any future use of
   `.dot-live` outside a `.tag`. */
.dot-live { background: var(--color-status-success); box-shadow: 0 0 0 3px var(--color-success-bg); }

/* ============================================================================
   B6 ADDITIONS (public batch D: consultations, consultation, forum,
   account-sign-in, account-register, account) — Bootstrap-gap chrome this
   batch needs, ported verbatim (same tokens, same values) from the retiring
   components.css per BOOTSTRAP_MIGRATION.md's "extend with restraint, comment
   any addition". This is the last public batch — no further site.css
   additions are expected until B10's cleanup pass.
   ============================================================================ */

/* ---- Consultation list card (consultations.html) — ported verbatim from the
   retiring components.css. Sits on the outer `.card.card-link` anchor (same
   as index.html's `.feature` precedent); the actual visible vertical rhythm
   between badge/h3/p/deadline comes from an inner `.card-body d-flex
   flex-column` wrapper added at conversion time (Bootstrap's bare `.card` has
   no padding), so this rule's own flex/gap is a harmless no-op on the outer
   element — kept for the `.consult-item--featured` descendant selectors
   below, which still need this class present on an ancestor of h3/.text-muted/
   .consult-item__deadline. */
.consult-item { display: flex; flex-direction: column; gap: var(--space-3); height: 100%; }
.consult-item__deadline { font-size: var(--font-size-sm); color: var(--color-status-error); font-weight: var(--font-weight-semibold); }
/* Featured card pops forward within the row (the "Closing soonest" card). */
.consult-item--featured {
  background: var(--gradient-hero); color: #fff; border-color: transparent;
  box-shadow: var(--shadow-float); position: relative; overflow: hidden;
}
.consult-item--featured::before { content:""; position:absolute; inset:0; background: var(--glow-gold); }
.consult-item--featured > * { position: relative; }
/* Heading override is level-agnostic on purpose (B11 review): Bootstrap's reboot
   paints every heading with --bs-heading-color (dark ink), so a heading on a dark
   surface never inherits the parent's `color: #fff` — this rule is what makes the
   title readable. Keying it to one tag meant B11's heading-outline fix (h3 -> h2)
   silently turned the featured card's title dark-on-dark. Same treatment applied
   to .hero and .cta-band above, which carry the identical risk. */
.consult-item--featured :is(h1, h2, h3, h4, h5, h6) { color: #fff; }
/* Bootstrap's `.text-muted` is `color: var(--bs-secondary-color) !important`,
   so a descendant colour rule can never win — B6 ported one and it silently
   lost, leaving this card's body copy dark-grey on the dark gradient (found
   in the B11 review). Re-point the variable on the card instead: no
   !important, and it covers every Bootstrap utility that reads it. */
.consult-item--featured { --bs-secondary-color: rgba(255,255,255,0.78); }
.consult-item--featured .text-muted { color: rgba(255,255,255,0.78); }
.consult-item--featured .consult-item__deadline { color: var(--ug-gold-300); }
.consult-item--featured:hover { transform: translateY(-4px); }

/* ============================================================================
   B7 ADDITIONS (admin pilot: admin/index.html canonical shell +
   admin/sign-in.html) — admin-portal Bootstrap-gap chrome, ported verbatim
   (same tokens, same values) from the now-deleted components.css per
   BOOTSTRAP_MIGRATION.md's "extend with restraint, comment any addition",
   PLUS the role-gating / editor-mode CSS every admin page depends on. This
   section covers the shell itself (sidebar, topbar, dropdowns, dashboard
   chrome); the list-toolbar, status-pill, moderation-queue and
   editor-attachment chrome arrived with the batches that first needed it
   (B8/B9 sections below).
   ============================================================================ */

/* ---- Admin shell grid (sidebar + main column) -----------------------------
   Breakpoint deliberately 992px (Bootstrap's own compiled `lg`), not the
   design's own 1024px `--breakpoint-lg` reference token used pre-migration —
   the sidebar below is now a real Bootstrap `.offcanvas-lg`, whose responsive
   cutover is BAKED into bootstrap.min.css at 992px/991.98px and can't be
   retuned via a token (verified: `.offcanvas-lg`'s two media blocks are
   `max-width:991.98px` / `min-width:992px` literal in the compiled CSS). Every
   other breakpoint in this shell (`.admin`, `.admin-toggle`) is kept in
   lockstep with it so the sidebar/toggle/grid all flip at the exact same
   width — a 992-vs-1024 gap here would leave a dead/inert hamburger button or
   a one-column layout fighting a sticky sidebar for 32px of viewport width.
   `.admin-2col` (the dashboard's OWN inner 2-column split, unrelated to the
   sidebar) keeps its original 1024px cutover unchanged below — nothing
   depends on it matching the shell's breakpoint. */
.admin { min-height: 100vh; display: grid; grid-template-columns: 1fr; }
@media (min-width: 992px) { .admin { grid-template-columns: var(--sidebar-width) 1fr; } }

/* ---- Admin sidebar (dark, custom — Bootstrap Offcanvas at mobile) ---------
   Ported from the retiring components.css `.admin-sidebar` rule, adapted to
   layer on top of Bootstrap's `.offcanvas-lg` responsive class instead of the
   old bespoke `data-open` attribute + sibling `.drawer__scrim` (markup:
   `class="admin-sidebar offcanvas-lg offcanvas-start"`, no base `.offcanvas`
   class — verified in bootstrap.min.css that the BARE `.offcanvas` selector
   carries an UNSCOPED `position:fixed;visibility:hidden` with no media query
   at all, so adding it back would keep the sidebar permanently fixed/hidden
   even at desktop widths; `.offcanvas-lg` alone already supplies the full
   below-992px fixed/hidden/transform ruleset AND reverts to a plain static
   block at/above 992px on its own):
     - Below 992px, `.offcanvas-lg` supplies `position:fixed`, `visibility:
       hidden` <-> `visible`, and Bootstrap's own backdrop/focus-trap/Escape/
       body-scroll-lock via a REAL `bootstrap.Offcanvas` instance
       (`data-bs-toggle="offcanvas"` on the topbar toggle button, verified
       against bootstrap.bundle.min.js's Offcanvas class) — this rule only
       needs to supply the actual slide transform, keyed off Bootstrap's OWN
       `.show`/`.showing` state classes (replacing the old `[data-open=
       "true"]` attribute selector; `:not(.hiding)` on `.show` mirrors
       Bootstrap's own transform-reset condition exactly, so the sidebar
       doesn't snap back mid-close-transition).
     - At/above 992px, `.offcanvas-lg` resets itself to a plain static block
       (no fixed/visibility/transform, `background-color:transparent
       !important`) — this rule's own `@media (min-width: 992px)` override
       re-asserts the sticky full-height rail exactly as pre-migration.
   `!important` on `background` is required, not decorative: Bootstrap's
   `.offcanvas-lg` bakes `background-color: transparent !important` for its
   >=992px "static panel" state, which otherwise wins over this rule's plain
   background regardless of source order (`!important` always beats
   non-important, whichever stylesheet loads last). admin.js's own "ADMIN
   SIDEBAR" block adds the two things Bootstrap's Offcanvas doesn't guarantee
   (initial focus onto the first nav item, focus-return to the toggle on
   close) — see that file for the full reasoning. */
/* Selector deliberately `.admin-sidebar.offcanvas-start` (not just
   `.admin-sidebar`): Bootstrap's own below-992px rule for `width` lives on
   the compound selector `.offcanvas-lg.offcanvas-start` (specificity 0,0,2,0)
   — a bare single-class `.admin-sidebar` rule (0,0,1,0) LOSES that
   specificity fight regardless of source order, so the sidebar rendered at
   Bootstrap's own default `--bs-offcanvas-width` (400px, clamped to 100vw by
   its `max-width:100%`) instead of this design's `min(82vw, 17rem)` — caught
   live (computed width was exactly the viewport width at 375px). Matching
   Bootstrap's own two-class specificity here (site.css loads after
   bootstrap.min.css, so the tie resolves in our favor) fixes it without an
   `!important`. `.offcanvas-start` is a permanent modifier class (not a
   toggled state), so scoping the whole rule to it loses no targeting. */
.admin-sidebar.offcanvas-start {
  background: var(--sidebar-bg) !important; color: var(--sidebar-text);
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4);
  width: min(82vw, var(--sidebar-width));
  overflow-y: auto;
  transform: translateX(-100%);
  transition: transform var(--duration-normal) var(--easing-out);
}
.admin-sidebar.show:not(.hiding), .admin-sidebar.showing { transform: none; }
@media (min-width: 992px) {
  /* Same two-class specificity as the mobile rule above — a bare
     `.admin-sidebar` (0,1,0) loses to `.admin-sidebar.offcanvas-start`
     (0,2,0) regardless of source order or media query, which left the
     sidebar translateX'd off-screen at desktop (caught on B7 review). */
  .admin-sidebar.offcanvas-start { position: sticky; top: 0; height: 100vh; transform: none; width: auto; }
}
.admin-brand { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-2) var(--space-4); }
.admin-brand .brand__seal { width: 34px; height: 34px; }
.admin-brand .brand__name { color: #fff; }
.admin-brand .brand__name span { color: rgba(255,255,255,0.5); }
.admin-nav { display: grid; gap: 2px; }
.admin-nav__label { font-size: 0.625rem; text-transform: uppercase; letter-spacing: var(--letter-spacing-wider); color: rgba(255,255,255,0.4); padding: var(--space-4) var(--space-2) var(--space-2); }
.admin-nav a {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0.6rem var(--space-3); border-radius: var(--border-radius-sm);
  color: var(--sidebar-text); font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
  transition: background-color var(--duration-fast), color var(--duration-fast);
}
.admin-nav a svg { width: 18px; height: 18px; flex: none; opacity: 0.8; }
.admin-nav a:hover { background: rgba(255,255,255,0.06); color: #fff; }
.admin-nav a[aria-current="page"] { background: var(--sidebar-item-active-bg); color: var(--sidebar-text-active); }
.admin-nav a[aria-current="page"] svg { opacity: 1; }
.admin-sidebar__footer { margin-top: auto; padding-top: var(--space-4); border-top: 1px solid rgba(255,255,255,0.1); }
.admin-userline { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2); font-size: var(--font-size-sm); }
.admin-avatar { width: 34px; height: 34px; border-radius: var(--border-radius-full); background: var(--gradient-gold); color: var(--ug-ink-900); display: grid; place-items: center; font-weight: 700; flex: none; }

/* ---- Admin main column / topbar / content --------------------------------- */
.admin-main { min-width: 0; background: var(--color-bg-primary); display: flex; flex-direction: column; }
.admin-topbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--topbar-height); display: flex; align-items: center; gap: var(--space-3);
  padding-inline: var(--space-4); background: var(--nav-bg);
  backdrop-filter: saturate(180%) blur(12px); -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--color-border-secondary);
}
/* margin-right: auto absorbs whatever space is left over once the search
   box hits its max-width cap, so the trailing cluster (FY badge, notif
   bell, account menu) sits flush at the topbar's right edge instead of
   bunching up right after the search box with dead space trailing it. */
.admin-topbar__search { flex: 1; max-width: 26rem; margin-right: auto; display: flex; align-items: center; gap: var(--space-2); background: var(--color-bg-tertiary); border-radius: var(--border-radius-full); padding: 0.4rem var(--space-3); }
.admin-topbar__search input { border: 0; background: transparent; flex: 1; min-height: 32px; }
.admin-topbar__search input:focus { outline: none; }
.admin-topbar__search svg { width: 16px; height: 16px; color: var(--color-text-tertiary); }
.admin-content { padding: var(--space-5) var(--space-4) var(--space-8); }
@media (min-width: 768px) { .admin-content { padding: var(--space-6); } }
/* Dashboard's own inner action-items/activity-feed 2-column split — kept at
   its original 1024px cutover (unrelated to the shell breakpoint above). The
   `> * { min-width: 0 }` escape hatch matches the pre-migration grid-blowout
   fix: without it, an intrinsically wide descendant collapsing this grid to
   a single track at <1024px can still force the grid item — and with it
   `<html>` — wider than the viewport at narrow widths. */
.admin-2col { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
.admin-2col > * { min-width: 0; }
@media (min-width: 1024px) { .admin-2col { grid-template-columns: 1.3fr 1fr; align-items: start; } }
.admin-toggle { display: grid; }
@media (min-width: 992px) { .admin-toggle { display: none; } }

.notif { position: relative; }
.notif__count { position: absolute; top: 4px; right: 4px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--border-radius-full); background: var(--ug-red-500); color: #fff; font-size: 10px; font-weight: 700; display: grid; place-items: center; }

/* ---- Account / quick-create dropdown (topbar) -----------------------------
   The panel itself is now a real Bootstrap `.dropdown-menu` (bg/border/
   radius/shadow/hover/active already themed sitewide in bootstrap-theme.css)
   — only the pieces Bootstrap has no equivalent for are ported: the round
   avatar trigger, the account-id header, and the "Viewing as" role-radio
   block. Plain menu links (Profile/View public site/Sign out, and every
   quick-create destination) became `.dropdown-item` outright rather than
   porting a redundant `.admin-account__item` (same precedent as the gov-bar
   language menu's own `.dropdown-item` adoption in B2). `.admin-account`
   is a one-line no-op alongside Bootstrap's own `.dropdown` (both set
   position:relative) — kept only for the class-name continuity later admin
   batches' dropdown wrappers already expect. */
.admin-account { position: relative; }
.admin-account__trigger { padding: 0; display: flex; border-radius: var(--border-radius-full); }
.admin-account__menu { min-width: 13rem; }
.admin-account__id { padding: var(--space-2) 0.7rem var(--space-3); border-bottom: 1px solid var(--color-border-secondary); margin-bottom: var(--space-1); }
.admin-account__id strong { display: block; font-size: var(--font-size-sm); color: var(--color-text-primary); }
.admin-account__id small { color: var(--color-text-tertiary); }

/* ---- Role switcher UI ("Viewing as" group inside the account dropdown) ---- */
.admin-account__roles {
  padding-bottom: var(--space-1); margin-bottom: var(--space-1);
  border-bottom: 1px solid var(--color-border-secondary);
}
.admin-account__roles-label {
  font-size: 0.625rem; text-transform: uppercase; letter-spacing: var(--letter-spacing-wider);
  color: var(--color-text-tertiary); padding: var(--space-2) 0.7rem 0.3rem;
}
.role-option {
  /* Same B2 fix as .gov-bar__btn/.icon-btn above, needed here too and missed
     until B11-review: this is a bare <button> (deliberately not a Bootstrap
     .btn — Bootstrap's dropdown arrow-key handling only targets .dropdown-item,
     see admin.js), so without the reset the retiring base.css used to provide
     it rendered the browser's native gray/outset control — three heavy bordered
     boxes inside the account menu instead of flat radio rows. Only visible with
     the dropdown open, which is why no screenshot caught it. */
  background: none; border: 0; appearance: none; -webkit-appearance: none;
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; padding: 0.45rem 0.7rem; border-radius: var(--border-radius-sm);
  font-size: var(--font-size-sm); color: var(--color-text-primary); text-align: left;
  transition: background-color var(--duration-fast);
}
.role-option:hover { background: var(--color-bg-tertiary); }
.role-option::before {
  content: ""; width: 14px; height: 14px; border-radius: var(--border-radius-full); flex: none;
  border: 1.5px solid var(--color-border-primary);
  transition: border-color var(--duration-fast), background-color var(--duration-fast);
}
.role-option[aria-checked="true"] { font-weight: var(--font-weight-semibold); }
.role-option[aria-checked="true"]::before {
  border-color: var(--color-accent-primary-active);
  background: var(--gradient-gold);
  box-shadow: inset 0 0 0 3px var(--color-bg-secondary);
}

/* ---- Role switcher: pure-CSS visibility gate ------------------------------
   Ported verbatim from the now-deleted components.css — the client-side
   permission-demo gate for the whole admin portal. Administrator sees
   everything (no rule needed). See assets/js/admin.js for the data attributes
   these key off, and remember the gate is presentation only: a real build
   enforces permissions server-side. */
html[data-role="editor"] [data-roles]:not([data-roles~="editor"]) { display: none !important; }
html[data-role="moderator"] [data-roles]:not([data-roles~="moderator"]) { display: none !important; }
/* [data-roles-only="editor"] is Editor-exclusive even from Administrator —
   currently only used by the dashboard's two Content-Editor-only KPI cards. */
html:not([data-role="editor"]) [data-roles-only="editor"] { display: none !important; }

/* ---- Editor create/edit mode (?new -> data-editor-mode on <html>) --------- */
html[data-editor-mode="create"] [data-mode="edit"] { display: none !important; }
html[data-editor-mode="edit"] [data-mode="create"] { display: none !important; }

/* ---- Action item row (dashboard) ------------------------------------------ */
.action-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) 0; }
.action-item + .action-item { border-top: 1px solid var(--color-border-secondary); }
.action-item__icon { width: 36px; height: 36px; border-radius: var(--border-radius-sm); display: grid; place-items: center; flex: none; }
.ai-amber { background: var(--color-warning-bg); color: var(--color-status-warning); }
.ai-red { background: var(--color-error-bg); color: var(--color-status-error); }
.ai-green { background: var(--color-success-bg); color: var(--color-status-success); }
.action-item__body { flex: 1; min-width: 0; font-size: var(--font-size-sm); }
.action-item__body small { color: var(--color-text-tertiary); }

/* ---- Activity feed (dashboard) --------------------------------------------- */
.feed { display: grid; gap: var(--space-4); }
/* B7 fix (found live): `.feed__item` is an anchor (each entry links to the
   page the activity happened on) that never set its own `color` pre- or
   post-migration — pre-Bootstrap this was fine (no bare `a{color}` rule
   existed for classed anchors, only `a:not([class])`), so it simply
   inherited the surrounding ink color. bootstrap-theme.css's own blanket
   `a { color: var(--color-text-link) }` override (added for classless
   in-prose links) now wins for ANY unstyled anchor, including this one —
   the whole feed row (name + activity text) rendered gold-800 instead of
   ink. Same root cause as `.card-link`'s own `color: inherit` above;
   applying the identical fix here rather than a per-page override. */
.feed__item { display: flex; gap: var(--space-3); font-size: var(--font-size-sm); color: inherit; }
.feed__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent-primary); margin-top: 5px; flex: none; box-shadow: 0 0 0 3px var(--color-warning-bg); }
.feed__item small { color: var(--color-text-tertiary); display: block; }

/* ---- Sign-in page (admin/sign-in.html) — dark portal chrome, no
   sidebar/topbar; compact card on a full-viewport dark ground reusing the
   public hero's gradient/glow treatment. Ported verbatim (same tokens) from
   the retiring components.css; only used by this one page. */
.signin {
  position: relative;
  min-height: 100vh;
  display: flex; flex-direction: column;
  background: var(--gradient-hero);
  isolation: isolate; overflow: hidden;
}
.signin::before { content: ""; position: absolute; inset: 0; z-index: -1; background: var(--glow-gold); }
.signin__stage {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-6); padding: var(--space-6) var(--space-4);
}
.signin__card { width: 100%; max-width: 26rem; }
/* B7 fix (found live): `.signin__brand` is an anchor back to the public
   site; it never set its own `color` pre- or post-migration (same "relied
   on inherited ink, no bare a{color} rule existed pre-Bootstrap" gap as
   `.feed__item` above) so bootstrap-theme.css's blanket `a { color:
   var(--color-text-link) }` now wins, rendering the "TaxPolicy" wordmark
   gold-800 instead of ink — before/after screenshot comparison caught the
   wordmark had changed color. Fixed with the same `color: inherit` pattern. */
.signin__brand { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); margin-inline: auto; color: inherit; }
.signin__brand .brand__seal { width: 64px; height: 64px; }
.signin__brand .brand__name { text-align: center; }
.signin__flag { width: 3rem; margin: var(--space-5) auto 0; }
.signin__forgot { margin-top: var(--space-4); font-size: var(--font-size-sm); }
.signin__note { font-size: var(--font-size-xs); text-align: center; margin-top: var(--space-4); }
/* weight + underline pinned — see .gov-bar__links a / a:not([class]) comments
   elsewhere in this file for the same non-color WCAG 1.4.1 cue rule. */
.signin__back { color: rgba(255,255,255,0.75); font-size: var(--font-size-sm); font-weight: var(--font-weight-normal); text-decoration: underline; }
.signin__back:hover { color: #fff; text-decoration: underline; }
/* Default :focus-visible ring is a low-alpha gold shadow tuned for light
   surfaces; on this page's near-black ground it fails 3:1 non-text contrast,
   so a link sitting directly on the dark ground (outside the light card)
   gets a full-opacity ring instead. Ported verbatim (T23). */
.signin__back:focus-visible { box-shadow: 0 0 0 3px var(--color-accent-primary); }

/* ============================================================================
   B8 ADDITIONS (admin batch A: rates, documents, news, explainers,
   consultations, surveys, users, feedback, forum, performance, analytics) —
   admin-portal Bootstrap-gap chrome these 11 list/dashboard pages need,
   ported verbatim (same tokens, same values) from the retiring
   components.css/base.css per BOOTSTRAP_MIGRATION.md's "extend with
   restraint, comment any addition". B7 already ported the admin shell itself
   (sidebar/topbar/dropdowns/role-gating) and the dashboard's own KPI/feed/
   action-item chrome — this batch adds the list-toolbar, status-pill, and
   moderation-queue chrome every one of its 11 pages needs, none of which the
   dashboard exercised.
   ============================================================================ */

/* ---- List toolbar (search pill + chip group + sort select, one row) ------
   Ported verbatim from the retiring components.css. Search inputs/selects
   inside carry Bootstrap's own .form-control/.form-select class per the
   migration's component-mapping table; this rule's own border:0/background:
   transparent on the search input wins the cascade (more specific than the
   bare .form-control class alone, site.css loads after bootstrap.min.css)
   so the pill look survives unchanged, same reasoning as B2's
   .admin-topbar__search precedent. */
.toolbar { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin-bottom: var(--space-4); }
.toolbar .filter-bar { margin-bottom: 0; }
.toolbar__search {
  flex: 1 1 16rem; min-width: 0; display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-bg-tertiary); border-radius: var(--border-radius-full); padding: 0.4rem var(--space-3);
}
.toolbar__search input { border: 0; background: transparent; flex: 1; min-width: 0; min-height: 32px; font-size: var(--font-size-sm); }
.toolbar__search input:focus { outline: none; box-shadow: none; }
.toolbar__search svg { width: 16px; height: 16px; color: var(--color-text-tertiary); flex: none; }
.toolbar__sort { flex: none; width: auto; min-width: 9rem; }
.toolbar .combo { flex: none; width: auto; }

/* Toolbar items with no visible label of their own (the Apply/Reset pair,
   the search pill) get a hidden .form-label the same markup/height as every
   labeled filter beside them, purely so their control lines up on the same
   row as the combobox triggers instead of floating centered against the
   label+control stack next to it. */
.label-spacer { visibility: hidden; }
.toolbar__actions-buttons { display: flex; gap: var(--space-2); }
.toolbar__search-wrap { flex: 1 1 16rem; min-width: 0; }

/* ---- Status pills (content lifecycle: Draft/Published/Archived) ----------
   Ported verbatim from the retiring components.css; layered on top of
   Bootstrap's own themed .badge same as B3's badge-open/badge-closed pair —
   these three rules only ever set background/color. */
.badge-draft     { background: var(--color-bg-muted); color: var(--color-text-secondary); }
.badge-published { background: var(--color-success-bg); color: var(--color-status-success); }
.badge-archived  { background: var(--color-bg-tertiary); color: var(--color-text-tertiary); }

/* ---- Two more soft badge variants (forum.html's Reported/Escalated pills,
   feedback.html's "In review" pill) — ported verbatim from the retiring
   base.css, same layering convention as the pair above. */
.badge-dark  { background: var(--color-bg-inverse); color: var(--color-text-inverse); }
.badge-alert { background: var(--color-error-bg); color: var(--color-status-error); }

/* ---- Moderation queue row (forum.html / feedback.html) -------------------
   Ported verbatim from the retiring components.css. Each row is a single
   self-contained card — no separate detail-row sibling, unlike the
   table-based lists — so admin.js's generic sortList() (which only
   re-appends a [data-detail-row] sibling) never mis-moves anything when the
   sort select reorders rows. */
.queue-row { border: 1px solid var(--color-border-secondary); border-radius: var(--border-radius-md); padding: var(--space-4); background: var(--card-bg); }
.queue-row + .queue-row { margin-top: var(--space-3); }
.queue-row__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.queue-row__meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); font-size: var(--font-size-xs); color: var(--color-text-tertiary); margin-top: var(--space-1); }
.queue-row__body { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--color-border-secondary); font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.queue-row__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.queue-row__excerpt { margin-top: var(--space-2); font-size: var(--font-size-sm); color: var(--color-text-secondary); }

/* ---- Page-local reject-reason dialog (forum.html only) -------------------
   forum.html's "Reject post" dialog is a separate, page-owned element (not
   a window.confirmDialog() call — that singleton has no hook for an inline
   required-textarea field, and admin.js is out of scope to modify for this
   task), so it keeps the SAME bespoke overlay/panel language
   window.confirmDialog()'s own LEGACY implementation still uses on
   unconverted pages, ported verbatim here (same tokens) so it keeps
   rendering now that base.css/components.css are gone. The
   textarea field inside uses Bootstrap's own .form-label/.form-control/
   .invalid-feedback/.is-invalid per the migration's forms mapping — only the
   outer dialog chrome needed a port. `.btn-danger` is NOT re-ported here:
   it's already themed for Bootstrap's own class in bootstrap-theme.css
   (confirmDialog's Bootstrap-modal implementation already relies on it). */
.dialog { position: fixed; inset: 0; z-index: 250; visibility: hidden; }
.dialog[data-open="true"] { visibility: visible; }
.dialog__scrim {
  position: absolute; inset: 0; background: var(--color-surface-overlay);
  opacity: 0; transition: opacity var(--duration-normal) var(--easing-default);
}
.dialog[data-open="true"] .dialog__scrim { opacity: 1; }
.dialog__panel {
  position: relative; z-index: 1; margin: 12vh auto 0; width: min(92vw, 26rem);
  background: var(--color-bg-secondary); border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-xl); padding: var(--space-6);
  transform: translateY(16px) scale(0.98); opacity: 0;
  transition: transform var(--duration-normal) var(--easing-out), opacity var(--duration-normal) var(--easing-out);
}
.dialog[data-open="true"] .dialog__panel { transform: none; opacity: 1; }
.dialog__title { font-size: var(--font-size-lg); }
.dialog__message { color: var(--color-text-secondary); font-size: var(--font-size-sm); margin-top: var(--space-2); }
.dialog__actions { display: flex; justify-content: flex-end; gap: var(--space-3); margin-top: var(--space-6); }

/* ============================================================================
   B9 ADDITIONS (admin batch B: rate-edit, document-edit, news-edit,
   explainer-edit, consultation-edit, performance-edit, user-edit,
   survey-detail, settings) — only TWO classes were left un-ported by B1-B8 for
   this batch: everything else these 9 pages need is either already here
   (.admin-2col, .empty, .kpi, .table-wrap, .num, .tag, .badge-*, .card--pad-lg)
   or maps onto a Bootstrap component (the whole legacy .field/.label/.input/
   .select/.textarea/.checkbox/.radio form language becomes .form-label/
   .form-control/.form-select/.form-check/.form-text/.invalid-feedback, which
   bootstrap-theme.css already themes to the same tokens — see that file's FORMS
   block). Both additions below are ported verbatim (same tokens, same values)
   from the retiring components.css.
   ============================================================================ */

/* ---- Editor attachment drop-zone (admin/*-edit.html metadata rails) -------
   Dashed affordance for the demo "Attach files" action. No real upload — each
   editor page's own script just toasts. Ported verbatim from the retiring
   components.css; used by document-edit.html, explainer-edit.html and
   consultation-edit.html. Deliberately NOT re-expressed as Bootstrap's
   .form-control[type=file] (a different control with a different affordance —
   this one has no <input type="file"> at all, by design). */
.attach-zone {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  padding: var(--space-6) var(--space-4); text-align: center;
  border: 1.5px dashed var(--color-border-primary);
  border-radius: var(--border-radius-md);
  background: var(--color-bg-tertiary);
  color: var(--color-text-tertiary);
}

/* ---- Performance entry grid (admin/performance-edit.html) ----------------
   Per-tax-head / per-sector collected-vs-target data-entry table, ported
   verbatim from the retiring components.css. Built on Bootstrap's own .table
   (borders/hover/spacing) plus .num (mono, right-aligned figure cells); this
   block adds only what neither covers: sizing a number input to fit inside a
   table cell, muting the read-only "% of target" text, and styling the totals
   row (kept live by that page's own script). The number inputs now also carry
   Bootstrap's .form-control — the `width: 8rem` here still wins, since
   `.entry-grid input[type="number"]` (0,2,1) outranks `.form-control`'s own
   `width: 100%` (0,1,0). */
.entry-grid td.entry-grid__figure { padding-block: var(--space-2); }
.entry-grid input[type="number"] { width: 8rem; text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-family-mono); }
.entry-grid td.num.entry-grid__pct { color: var(--color-text-secondary); }
.entry-grid tfoot th, .entry-grid tfoot td { font-weight: var(--font-weight-semibold); border-top: 2px solid var(--color-border-primary); background: var(--color-bg-tertiary); }


/* ---- Revenue performance chart --------------------------------------
   White card framing around the Highcharts container — the chart draws its
   own white background per the design spec, this just gives it the same
   card edge every other panel on the site has. Fixed heights (not
   aspect-ratio) because Highcharts sizes itself to its container's pixel
   height, not a ratio. */
.chart-panel {
  background: #FFFFFF;
  border: 1px solid var(--color-border-secondary);
  border-radius: var(--border-radius-lg);
  padding: var(--space-4);
}
.chart-panel__canvas { height: 480px; }
@media (max-width: 640px) { .chart-panel__canvas { height: 380px; } }

/* The performance table's values read better centered under their headers
   than at .table td.num's site-wide right alignment — scoped to this table
   only, everywhere else keeps the mono right-aligned figure convention.
   Extra .table qualifier matches bootstrap-theme.css's ".table td.num"
   specificity plus one, since that stylesheet loads after this one. */
.performance-table.table td.num { text-align: center; }

/* ---- Performance table — modernized card look ---------------------------
   Both the single-line table (chart's "Table view" toggle) and the
   comparison table share this class, so one pass of styling covers both:
   a gold accent underline on the header row (echoes the chart's own
   accent), zebra striping and a warm hover tint so a decade-plus of yearly
   rows stays scannable, and tabular-nums weight bumped so figures read as
   data rather than incidental text. The sticky first column added above
   for `.table-wrap` needs its own even-row/hover overrides here so it tints
   in step with the rest of the row instead of leaving a stray white seam
   down the frozen year column. */
.performance-table.table { margin-bottom: 0; }
.performance-table.table > thead th {
  background: var(--color-bg-tertiary);
  border-bottom: 2px solid var(--color-accent-primary);
  padding-block: var(--space-3);
}
.performance-table.table > tbody td,
.performance-table.table > tbody th {
  padding-block: var(--space-3);
  vertical-align: middle;
}
.performance-table.table > tbody tr { transition: background-color 0.15s ease; }
.performance-table.table > tbody tr:nth-child(even) { background: var(--color-bg-tertiary); }
.performance-table.table > tbody tr:hover { background: var(--color-warning-bg); }
.performance-table.table td.num { font-weight: var(--font-weight-medium); }
.performance-table.table > tbody tr:nth-child(even) td:first-child:not([colspan]) {
  background: var(--color-bg-tertiary);
}
.performance-table.table > tbody tr:hover td:first-child:not([colspan]) {
  background: var(--color-warning-bg);
}

/* ---- Comparison table's two-row grouped header ---------------------------
   Built by the renderComparisonTable() JS inline in performance/index.blade.php,
   since the number of revenue lines (and so columns) changes with the multi-select —
   each group gets an "Actual" + "Growth rate" pair under its revenue line
   name. `.performance-table__group-start` is added by that JS to the first
   cell of every group after the first, giving a divider between one line's
   pair of columns and the next's without relying on fragile nth-child column
   math (the year column's own width isn't fixed). */
.performance-table--comparison thead th.performance-table__group {
  text-align: center;
  background: var(--color-bg-secondary);
  border-bottom: 1px solid var(--color-border-secondary);
}
.performance-table--comparison thead tr:last-child th {
  border-bottom: 2px solid var(--color-accent-primary);
}
.performance-table__group-start { border-left: 1px solid var(--color-border-secondary); }

.checkbox li{
    list-style: none;
    display: inline-block;
}

.checkbox {
    margin:0;
    padding:0;
}

.alert p{
    margin:0;
    padding:0;
}

/* ---- Revenue line combobox --------------------------------------------
   Progressive enhancement for the performance filter's "Revenue line"
   select — see public/assets/js/revenue-line-combobox.js. The plain
   .combo__native-select is what actually renders (and submits the form)
   until the .js class lands on <html> (site-wide convention, set inline in
   layouts/partials/website/head.blade.php before body paints), at which
   point the styled trigger/panel takes over and the native select is kept
   only to carry the submitted value. */
.combo__native-select { display: block; }
.combo__control { display: none; }
.js .combo__native-select { display: none; }
.js .combo__control { display: block; position: relative; }

.combo__trigger {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; min-width: 12rem;
  padding: 0.5rem var(--space-4);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-primary);
  border-radius: var(--border-radius-full);
  font-size: var(--font-size-sm); color: var(--color-text-primary);
  cursor: pointer; transition: border-color var(--duration-fast) var(--easing-default), box-shadow var(--duration-fast) var(--easing-default);
}
.combo__trigger:hover { border-color: var(--ug-neutral-400); }
.combo__trigger:focus-visible, .combo--open .combo__trigger {
  outline: none; border-color: var(--color-border-focus);
  box-shadow: 0 0 0 3px rgba(201, 143, 7, 0.18);
}
.combo__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.combo__chevron { flex: none; color: var(--color-text-tertiary); transition: transform var(--duration-fast) var(--easing-default); }
.combo--open .combo__chevron { transform: rotate(180deg); }

.combo__panel {
  position: absolute; z-index: 60; top: calc(100% + var(--space-2)); left: 0;
  width: max(100%, 18rem);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-secondary);
  border-radius: var(--border-radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
}
.combo__search-wrap {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-bg-tertiary); border-radius: var(--border-radius-full);
  padding: 0.4rem var(--space-3); margin-bottom: var(--space-2);
}
.combo__search-icon { flex: none; color: var(--color-text-tertiary); }
.combo__search { border: 0; background: transparent; flex: 1; min-width: 0; font-size: var(--font-size-sm); }
.combo__search:focus { outline: none; box-shadow: none; }

.combo__list { list-style: none; margin: 0; padding: 0; max-height: 20rem; overflow-y: auto; }
.combo__group-label {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-tertiary);
}
.combo__option {
  padding: 0.45rem var(--space-3);
  border-radius: var(--border-radius-sm);
  font-size: var(--font-size-sm); color: var(--color-text-primary);
  cursor: pointer;
}
.combo__option:hover, .combo__option--active { background: var(--color-bg-tertiary); }
.combo__option[aria-selected="true"] { color: var(--color-text-link); font-weight: var(--font-weight-semibold); }
.combo__empty { padding: var(--space-3); margin: 0; text-align: center; font-size: var(--font-size-sm); color: var(--color-text-tertiary); }

/* ---- Revenue line multi-select ----------------------------------------
   Tap-to-toggle sibling of the combobox above — see
   public/assets/js/revenue-line-multiselect.js. Every option tap flips
   that option on/off and the panel stays open, so picking several values
   never needs a held Ctrl/Cmd (which touch has no equivalent for). */
.multi-combo__native-select { display: block; }
.multi-combo__control { display: none; }
.js .multi-combo__native-select { display: none; }
.js .multi-combo__control { display: block; position: relative; }

.multi-combo__trigger {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; min-width: 14rem;
  padding: 0.55rem var(--space-4);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-primary);
  border-radius: var(--border-radius-full);
  font-size: var(--font-size-sm); color: var(--color-text-primary);
  cursor: pointer; transition: border-color var(--duration-fast) var(--easing-default), box-shadow var(--duration-fast) var(--easing-default);
}
.multi-combo__trigger:hover { border-color: var(--ug-neutral-400); }
.multi-combo__trigger:focus-visible, .multi-combo--open .multi-combo__trigger {
  outline: none; border-color: var(--color-border-focus);
  box-shadow: 0 0 0 3px rgba(201, 143, 7, 0.18);
}
.multi-combo__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.multi-combo__chevron { flex: none; color: var(--color-text-tertiary); transition: transform var(--duration-fast) var(--easing-default); }
.multi-combo--open .multi-combo__chevron { transform: rotate(180deg); }

.multi-combo__panel {
  position: absolute; z-index: 60; top: calc(100% + var(--space-2)); left: 0;
  width: max(100%, 20rem);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border-secondary);
  border-radius: var(--border-radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
}
.multi-combo__search-wrap {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-bg-tertiary); border-radius: var(--border-radius-full);
  padding: 0.4rem var(--space-3); margin-bottom: var(--space-2);
}
.multi-combo__search-icon { flex: none; color: var(--color-text-tertiary); }
.multi-combo__search { border: 0; background: transparent; flex: 1; min-width: 0; font-size: var(--font-size-sm); }
.multi-combo__search:focus { outline: none; box-shadow: none; }

.multi-combo__list { list-style: none; margin: 0; padding: 0; max-height: 18rem; overflow-y: auto; }
.multi-combo__group-label {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--font-size-xs); font-weight: var(--font-weight-semibold);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-tertiary);
}
.multi-combo__option {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 0.55rem var(--space-3);
  border-radius: var(--border-radius-sm);
  font-size: var(--font-size-sm); color: var(--color-text-primary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.multi-combo__option:hover, .multi-combo__option--active { background: var(--color-bg-tertiary); }
.multi-combo__check {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem;
  border: 1.5px solid var(--color-border-primary);
  border-radius: var(--border-radius-sm);
  color: transparent;
  transition: background var(--duration-fast) var(--easing-default), border-color var(--duration-fast) var(--easing-default), color var(--duration-fast) var(--easing-default);
}
.multi-combo__option[aria-selected="true"] { color: var(--color-text-link); font-weight: var(--font-weight-semibold); }
.multi-combo__option[aria-selected="true"] .multi-combo__check {
  background: var(--color-accent-primary); border-color: var(--color-accent-primary); color: #fff;
}
.multi-combo__option-label { overflow: hidden; text-overflow: ellipsis; }
.multi-combo__empty { padding: var(--space-3); margin: 0; text-align: center; font-size: var(--font-size-sm); color: var(--color-text-tertiary); }

.multi-combo__footer {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  margin-top: var(--space-2); padding-top: var(--space-2);
  border-top: 1px solid var(--color-border-secondary);
}
.multi-combo__clear, .multi-combo__done {
  border: 0; background: none; padding: var(--space-1) var(--space-2);
  font-size: var(--font-size-sm); cursor: pointer; border-radius: var(--border-radius-sm);
}
.multi-combo__clear { color: var(--color-text-tertiary); }
.multi-combo__clear:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); }
.multi-combo__done { color: var(--color-text-link); font-weight: var(--font-weight-semibold); }
.multi-combo__done:hover { background: var(--color-bg-tertiary); }
