:root {
  --edit-indicator-baseline-opacity: 0.2;
}

body {
  margin: 0;
  /* --vds-bg-primary (COMT-267 follow-up) — matches the core page
     background back to plain white, the same token `.widget` itself now
     uses (see that rule's own comment below). The intervening "design
     update, 2026-09" change to --vds-bg-secondary here was a regression:
     it silently broke the invariant `.app-shell .card`'s own COMT-101
     comment (further down this file) already documented — that a public
     page's `<body>` background is --vds-bg-primary, which is what lets
     .card's own --vds-bg-secondary read as a raised surface against it.
     With body ALSO on --vds-bg-secondary, .card (and, after COMT-267,
     .widget) blended into the page instead of standing out from it. */
  background: var(--vds-bg-primary);
  /* --agency-primary-* (COMT-159), not Secondary (usability follow-up
     pass): this is the ambient text color for every plain heading,
     paragraph, and widget value on the page — the app's own default
     reading color, not a themed accent. Using Secondary here (the
     original COMT-159 value) meant every unthemed widget rendered in
     whatever accent color an agency picked for their *secondary* brand
     slot (e.g. Aarondale's Secondary Dark is a saturated royal blue),
     which reads like the entire page is a wall of links. `.themed-*`
     (below) is still how an admin opts a specific Section/Block into
     Secondary/Tertiary coloring on purpose; this is only the passive,
     unthemed default every other widget falls back to. */
  color: var(--agency-primary-fg, var(--vds-fg-primary));
  /* A real fallback stack, not just var()'s fallback arg (COMT-96 fix) —
     var(--x, fallback) only applies when --x is undefined, not when the
     font it names fails to load. --vds-font-family-body resolves to
     "Inter" (static/vendor/vds/tokens.light.css), which is vendored and
     loaded in templates/base.html; this stack is what protects against
     it ever silently degrading to the browser's serif default again. */
  font-family: var(--vds-font-family-body), system-ui, -apple-system, sans-serif;
  /* COMT-194: sticky footer. body is the flex column [header, .app-main,
     .app-footer] for every public page (templates/base.html's default
     body_layout block); min-height: 100vh + .app-main's flex: 1 0 auto
     below is what pins the footer to the bottom of the viewport on short
     pages while still letting it flow after the content normally (not
     position: fixed) on tall ones. Scoped with the body > .app-main
     child combinator below rather than a bare .app-main rule — /admin/*
     (templates/agency/base.html) overrides body_layout entirely with its
     own position: fixed; inset: 0 .app-shell, so it never has a footer
     and its own nested .app-main (deep inside .app-shell, not a direct
     child of body) must keep its existing .app-shell .app-main scroll
     behavior (see below) untouched. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--vds-font-family-heading), system-ui, -apple-system, sans-serif;
}

/* No global anchor color existed before this (usability sanity-check
   pass) — every plain <a> fell back to the browser's own default blue/
   purple, which happened to be legible against most light-mode surfaces
   but was unreadable against dark, near-black surfaces (e.g. the
   Developer preview card on templates/portal/index.html, .card's own
   --vds-bg-secondary in dark mode). --vds-fg-brand-on-low is VDS's own
   token for a link/accent color meant to sit on a low-emphasis surface,
   and is already defined for both light and dark in
   static/vendor/vds/tokens.{light,dark}.css — just never wired up.
   .app-header a below (branded, on the Primary color) still wins on
   specificity and is untouched. */
a {
  color: var(--vds-fg-brand-on-low);
  /* No default underline (design update, 2026-09) — links read as plain
     colored text by default now; hover below is the only visual
     affordance distinguishing them further. Specific components that
     already set their own text-decoration (e.g. .widget-flip-card__back-
     trigger's deliberate underline) are untouched — this only changes
     the platform default every other plain <a> falls back to. */
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* wrap + gap (usability sanity-check pass): with no wrap, the logo,
     header_extra content (agency-preview switcher, language switcher —
     templates/portal/hub_page.html etc.) and .app-header__actions (dark
     toggle, login/user-menu) were forced into one non-wrapping row and
     visibly overflowed/collided at phone widths (~390px) — this was the
     only responsive-layout gap in the whole public header. */
  flex-wrap: wrap;
  gap: var(--vds-spacing-8) var(--vds-spacing-16);
  padding: var(--vds-spacing-16);
  /* --agency-primary-* (COMT-159): declared per-agency, per light/dark
     mode, by templates/portal/_branding_style.html — falls back to the
     platform default whenever an agency has left Primary blank. */
  background: var(--agency-primary-bg, var(--vds-bg-secondary));
  color: var(--agency-primary-fg, inherit);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  /* Bugfix: on a public page (body's own [header, .app-main, .app-footer]
     flex column — see body's own comment above), the header used to be
     a plain in-flow flex item like any other, so it scrolled away with
     the rest of the page on anything taller than one screen, and the
     footer (also plain in-flow, COMT-194) only ever came into view once
     a visitor had scrolled all the way past every Section/Block on the
     page. Pinning both to the viewport (position: sticky; top/bottom: 0)
     keeps them visible the whole time you're scrolling a topic page's
     content, matching how /admin/*'s own fixed-position shell already
     keeps its sidebar/topbar in place while only .app-shell__main
     scrolls beneath them. sticky (not fixed) still lets the header
     naturally push .app-main down on initial layout instead of needing
     a manual top-padding offset on body/.app-main to avoid it
     overlapping the first thing on the page. */
  position: sticky;
  top: 0;
  z-index: 20;
}

/* Public pages have their own agency header; don't leave an empty bar above it. */
body > .app-header:not(:has(> .app-header__start > *, > .app-header__end > *)) {
  display: none;
}

/* .app-header__start/.app-header__end (editorial-toolbar-layout
   follow-up): `.app-header` used to be `justify-content: space-between`
   across every one of header_extra's/.publish-controls'/
   .app-header__actions' own *direct* children individually — with 3
   top-level children (agency-preview switcher, login-user-menu widget,
   .publish-controls), space-between put the user-menu dead center
   instead of the opposite corner from the agency switcher, and once
   .publish-controls' own buttons grew too wide to fit one line, its
   flex-wrap pushed just that one item to a second row while the
   still-2-items-wide first row re-centered the user-menu at the far
   right, away from the Edit/Preview toggle beside it. Wrapping
   header_extra in `.app-header__start` and .publish-controls/
   header_actions together in `.app-header__end` makes `.app-header`
   itself a simple 2-item space-between row regardless of how many
   controls either side renders, and keeps the user-menu adjacent to the
   Edit/Preview toggle that precedes it (both direct children of
   `.app-header__end`) instead of splitting across the row. */
.app-header__start,
.app-header__end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8) var(--vds-spacing-16);
}

/* :not(.chrome-nav-dropdown__link):not(.app-user-menu__link) — these two
   chrome-widget dropdown panels (nav-dropdown-links/language-selector,
   login-user-menu) render inside `.app-header` (global header) and/or a
   branded `.content-section--hero` Section (page-header-section chrome
   widgets), but each panel is its own always-light `--vds-bg-primary`
   card floating below the branded/dark row its trigger sits in, not
   part of that branded surface itself. Without this exclusion, this
   rule's branded on-dark-background link color cascaded down into those
   panels' plain `<a>`s too, rendering (e.g.) Aarondale's white link
   color as white-on-white/light-grey inside the panel — unreadable. */
.app-header a:not(.chrome-nav-dropdown__link):not(.app-user-menu__link) {
  color: var(--agency-primary-fg, inherit);
}

.app-header__brand {
  font-weight: var(--vds-font-weight-semibold);
  font-size: var(--vds-font-size-heading-sm);
}

.app-header__actions {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}


.app-main {
  padding: var(--vds-spacing-16);
  /* content-layout-formatting follow-up (full-width Sections): query
     container for `.content-section--hero`'s own full-bleed breakout
     below — `cqw` units resolve against *this* element's content-box
     width rather than the viewport, so the breakout reaches exactly
     this page's own visible edges in both layouts that reuse this same
     class: templates/base.html's `.app-main` spans the full viewport
     (no sidebar), while templates/agency/base.html's nests it inside
     `.app-shell__main` (already excludes the fixed-width sidebar) — a
     `100vw`-based breakout would tunnel a hero Section under that
     sidebar in the agency version-preview views instead of stopping at
     the actual visible content column, which `cqw` avoids without any
     layout-specific branching. */
  container-type: inline-size;
  /* Named so page-chrome responsiveness (site header, breadcrumb, title) can
     query the page's own width instead of the viewport — the editor's
     tablet/mobile preview just narrows this element, and must collapse the
     chrome exactly like a real narrow screen. Its content box is the
     viewport width minus 2 * 16px padding, so the 767px viewport breakpoint
     is 735px here and the 1200px one is 1168px. */
  container-name: page;
  /* Safety net for the same breakout: `.content-section--hero`
     deliberately renders wider than its own non-contained ancestors
     (`.page-width`, `#page-sections-always-visible`, etc.) between here
     and there — clipped here (not just relying on nobody scrolling)
     so that overflow can never introduce a horizontal scrollbar. */
  overflow-x: hidden;
}

/* COMT-194: sticky footer — body is now itself a flex column with
   min-height: 100vh (see body { display: flex; ... } above), so
   .app-main growing to fill the remaining space is what pushes
   .app-footer to the bottom of the viewport on short pages. Scoped with
   the child combinator (not a bare .app-main rule) so it only matches
   templates/base.html's own default body_layout — /admin/*
   (templates/agency/base.html) nests .app-main deep inside its own
   .app-shell, not as a direct child of body, so this never touches it;
   .app-shell .app-main below keeps its own, unrelated flex/overflow
   rules for that shell's internal scroll region. */
body > .app-main {
  flex: 1 0 auto;
  /* `clip`, not the base rule's `hidden`: `hidden` makes this a scroll
     container, so descendant `position: sticky` (the page tab strip) would
     stick to this non-scrolling box instead of the viewport. */
  overflow-x: clip;
  /* Bugfix (COMT-199 follow-up, same pass as .app-header/.app-footer's
     own position: sticky above): a sticky bottom: 0 footer sticks the
     moment its own natural position would otherwise scroll past the
     viewport's bottom edge — for the last child of a long page, that's
     as soon as you've scrolled roughly one viewport height, not only at
     the very end of the document. Without reserving space below the
     real content equal to the footer's own rendered height, the footer
     would permanently hover over (and hide) the bottom of whatever
     Section/Block happens to be there while scrolling, for the entire
     rest of the page, not just its true end. Sized close to the
     footer's own actual single-line height (COMT-194's .app-footer
     padding-block + line-height, ~2.25rem) rather than the earlier,
     over-generous 4.5rem guess — still enough slack to clear it, just
     without stranding more blank space at the end of every page than
     the footer actually needs. */
  padding-bottom: 2.25rem;
}

/* .app-shell-scoped only (COMT-99 /code-review fix) — .app-main is the
   one scrolling region inside /admin/*'s fixed-position shell;
   .app-topbar and .app-fixed-actions are flex-shrink: 0 siblings that
   stay pinned in place, matching .app-sidebar (also full-height,
   non-scrolling). */
.app-shell .app-main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* Shared /admin/ content column; previews need their full-bleed sections
   measured against this column, not the scroll area's full viewport width. */
.app-main__surface {
  box-sizing: border-box;
  max-width: 1240px;
  min-height: 100%;
  margin-inline: auto;
  padding: var(--vds-spacing-32) var(--vds-spacing-32) var(--vds-spacing-48);
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  container-type: inline-size;
}

@media (max-width: 767px) {
  .app-main__surface {
    padding: var(--vds-spacing-24) var(--vds-spacing-16);
  }
}

/* COMT-194: sticky public-page footer (templates/portal/_footer.html) —
   shown on every public content page (index, hub page, topic pages),
   never on /admin/* (its own body_layout override has no footer block)
   or /internal/ (separate template hierarchy, doesn't load this stylesheet).
   flex-shrink: 0 keeps it pinned at its natural height as body's last
   flex item, never compressed by .app-main growing above it. The bar
   itself (border-top/background) stays full-bleed; .app-footer__inner
   below (a .page-width container) is what aligns the actual copyright/
   powered-by content with the rest of the page's content column instead
   of running edge to edge.

   position: sticky; bottom: 0 (usability follow-up, same pass as
   .app-header's own position: sticky above) keeps this visible in the
   viewport while scrolling a long page instead of only appearing once
   a visitor scrolls past every Section/Block — sticky (not fixed) means
   it still only starts overlapping content once the page has actually
   scrolled that far, and still sits at its natural in-flow position
   (pushed to the bottom of a short page by .app-main's flex: 1 0 auto
   above) rather than always floating over content on a page shorter
   than the viewport. */
.app-footer {
  flex-shrink: 0;
  padding-block: var(--vds-spacing-8);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-secondary, var(--vds-fg-primary));
  font-size: var(--vds-font-size-body-sm);
  position: sticky;
  bottom: 0;
  z-index: 20;
}

.app-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8) var(--vds-spacing-16);
}

.app-footer p {
  margin: 0;
}

.app-footer__powered-by {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

/* Wordmark (text-only) at normal widths, icon-only "Circle V" below the
   same 767px mobile breakpoint used elsewhere in this file — the
   wordmark's fixed pixel width (see static/vendor/vds-components, ~123px
   at size="sm") competes with the copyright text for space next to it on
   a narrow viewport, same reasoning as .content-block's mobile override
   above. .app-footer__logo--narrow is hidden by default and only shown
   inside the media query below, matching templates/agency/base.html's
   analogous expanded/collapsed brand-mark toggle. */
.app-footer__logo--narrow {
  display: none;
}

@media (max-width: 767px) {
  .app-footer {
    position: static;
    bottom: auto;
    z-index: auto;
  }

  /* In-flow footers need no overlap reserve on mobile. */
  body > .app-main {
    padding-bottom: 0;
  }

  .app-footer__logo--wide {
    display: none;
  }

  .app-footer__logo--narrow {
    display: inline-flex;
  }
}

/* Cancel/Save panel for card-edit pages (COMT-102), replacing the buttons
   that used to live in .page-header__actions. A normal flex-column sibling
   of .app-main (see .app-shell__main below) rather than position: fixed —
   that earlier approach needed a left offset tied to --agency-sidebar-width
   and still overlapped the sidebar whenever the sidebar's own height grew
   past the viewport, since position: fixed is relative to the viewport, not
   to .app-shell__main. As a flex sibling it just occupies its own row,
   full width of the content column, no offset math needed. */
.app-fixed-actions {
  display: flex;
  flex-shrink: 0;
  justify-content: flex-end;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-16);
  background: var(--vds-bg-primary);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* Same rendered height as vds-button's size="sm" (COMT-102 follow-up) — a
   native <button> and a Lit vds-button don't share sizing unless both are
   told the same value explicitly; vds-button's shadow DOM ignores a host
   `height` set from outside, so this has to match its size="sm" token
   (40px) by number, not by shared CSS. */
.app-fixed-actions .btn {
  display: inline-flex;
  align-items: center;
  height: 2.5rem;
}

.card {
  background: var(--vds-bg-secondary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  box-shadow: var(--vds-shadow-sm);
  padding: var(--vds-spacing-16);
  max-width: 32rem;
}

/* Responsive, centered content container for pages rendering real
   authored content (Hub/Topic pages, self-service version previews) —
   replaces .card's 32rem max-width there, which is sized for a narrow
   admin/agency form, not a widget-bearing content page (content-layout-
   formatting design, 2026-08-27). .card itself is untouched; every
   admin/agency form still uses it as-is. 1140px matches the real IA Pro
   Transparency mockup's own .page-width reference CSS. */
.page-width {
  width: min(1140px, calc(100% - 3rem));
  margin-inline: auto;
}

/* Public Topic Page "back to the Transparency Hub" breadcrumb strip
   (2026-09 follow-up, see _transparency_breadcrumb.html's own comment
   for why this exists and where it renders). A plain light strip, not a
   themed/hero Section — it's page chrome (a way back), not authored
   content, so it deliberately doesn't pick up the current page's own
   theme color the way the navy Page Header Section right above it does. */
.public-breadcrumb {
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-secondary);
  /* Edge-to-edge bleed (bugfix, follow-up to the above) — this <nav>
     renders inside `#page-theme-wrapper`, which is `.page-width`-capped
     to ~1140px (see _transparency_breadcrumb.html's own comment on why
     it doesn't add a second `.page-width` itself), so without a width
     override this strip's own background rectangle stopped abruptly at
     that same ~1140px column instead of spanning the page's full visible
     width the way a chrome strip should. Same breakout math as
     `.content-section--hero`/`--full-width` above (100cqw/-50cqw,
     bled fully through `.app-main`'s own padding) — `.app-main` is this
     element's nearest `container-type: inline-size` ancestor too, so the
     same technique applies unchanged. `.public-breadcrumb__inner` below
     re-caps the actual crumb content back to that same ~1140px column so
     it still lines up with the title/content above and below it. */
  width: calc(100cqw + (2 * var(--vds-spacing-16)));
  position: relative;
  left: 50%;
  margin-left: calc(-50cqw - var(--vds-spacing-16));
}

.public-breadcrumb__inner {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding-block: var(--vds-spacing-8);
  /* Re-caps this strip's own content back to the page's ~1140px column
     (see `.public-breadcrumb`'s own comment above for why the strip
     itself is now wider than that) — same `min()` formula as
     `.content-section--hero .content-section__inner`/`.page-width`. */
  width: min(1140px, calc(100% - 3rem - (2 * var(--vds-spacing-16))));
  margin-inline: auto;
}

/* Narrow screens: show only the back link; the current page is the title
   right below, and a wrapped trail overflowed its strip. */
.public-breadcrumb__back {
  display: none;
}

@container page (max-width: 735px) {
  .public-breadcrumb__inner {
    white-space: nowrap;
  }
  .public-breadcrumb__inner > vds-icon,
  .public-breadcrumb__inner > vds-breadcrumb[active] {
    display: none;
  }
  /* The chevron marks the lone remaining crumb as a link back. */
  .public-breadcrumb__inner > .public-breadcrumb__back {
    display: inline-block;
    margin-right: calc(-1 * var(--vds-spacing-4));
  }
}

/* templates/portal/index.html's landing hero (COMT-5 follow-up) — a
   plain `.content-section` has no margin-bottom by default (see that
   class's own comment: stacked authored Sections opt into spacing via
   an explicit Spacer instead), but this hero and the "Developer
   preview" `.card` right after it aren't authored Sections at all, just
   two fixed blocks on this one template — without this they visually
   ran together with no breathing room. */
.landing-hero {
  margin-bottom: var(--vds-spacing-32);
}

/* /code-review fix (COMT-101): .card's own background must stay
   --vds-bg-secondary above — templates/portal/*.html and 404.html use
   .card too, and their <body> background is already --vds-bg-primary
   (see the top of this file), so a blanket change here made those
   pages' cards blend into the page instead of reading as a raised
   surface. Only the /admin/* shell (.app-shell__main, itself
   --vds-bg-secondary) actually needed a distinct card background to
   separate from its page. */
.app-shell .card {
  background: var(--vds-bg-primary);
}

.app-shell .app-main__surface > .card {
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
}

/* Pages with a block formset (COMT-95) need more room than a simple
   single-form card like Branding. */
.card--wide {
  max-width: 56rem;
}

/* Title/status + action-button header row (COMT-95), matching the
   CC-Product reference's detail-page layout. */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
  margin-bottom: var(--vds-spacing-16);
}

.page-header__title {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.page-header__title h1 {
  margin: 0;
  font-size: var(--vds-font-size-heading-sm);
  font-weight: var(--vds-font-weight-semibold);
}

.page-header__actions {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  flex-shrink: 0;
}

.card h2 {
  margin-top: var(--vds-spacing-24);
  margin-bottom: var(--vds-spacing-8);
  padding-top: var(--vds-spacing-16);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
  font-size: var(--vds-font-size-heading-sm, 1.125rem);
}

.card form p {
  margin: 0 0 var(--vds-spacing-16);
}

.card form label {
  display: block;
  margin-bottom: var(--vds-spacing-4);
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.card form input[type="text"],
.card form input[type="email"],
.card form input[type="password"],
.card form input[type="number"],
.card form input[type="date"],
.card form textarea,
.card form select {
  width: 100%;
  max-width: 28rem;
  box-sizing: border-box;
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.card form input[type="text"]:focus,
.card form input[type="email"]:focus,
.card form input[type="password"]:focus,
.card form input[type="number"]:focus,
.card form input[type="date"]:focus,
.card form textarea:focus,
.card form select:focus {
  outline: none;
  border-color: var(--vds-bg-brand-primary);
  box-shadow: 0 0 0 3px var(--vds-border-focus-outer, var(--vds-bg-brand-light));
}

/* type="color" is a small fixed-size swatch, not a full-width text field —
   excluded from the shared rule above. */
.card form input[type="color"] {
  width: 4rem;
  height: 2.5rem;
  padding: 0;
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  cursor: pointer;
}

/* Info icon replacing a field's visible helptext span (COMT-97 follow-up)
   — see templates/agency/branding.html. Positioning, show/hide, and
   Escape-dismissal are all handled by static/js/help-tooltip.js (shared
   with /internal/'s own help icons — see that file), not CSS: it creates and
   positions the <vds-tooltip> itself via inline styles, appended straight
   to <body>, so this class only needs to style the trigger icon itself. */
.field-help {
  display: inline-flex;
  align-items: center;
  margin-left: var(--vds-spacing-4);
  color: var(--vds-fg-tertiary);
  cursor: help;
}

/* vds-tooltip's shadow CSS has no explicit width anywhere in its content
   chain (.vds-tooltip__content is display:flex with only a max-width;
   .vds-tooltip__label is width:100% of that) — with nothing forcing a
   real size, this collapses to min-content (the width of its single
   longest word) rather than shrinking to fit a full sentence, which is
   why it rendered as an extremely tall, narrow, one-word-per-line column
   even though max-width was 256px. Overriding max-width alone (tried
   first) changed nothing, since max-width was never the actual
   constraint — confirmed via getBoundingClientRect() showing ~75px
   regardless of a 512px max-width. An explicit `width` on ::part(content)
   (the Shadow Parts hook the component exposes via `part="content"`)
   gives the box a real size instead, which the rest of the internal
   flex chain (pointer-container, label) resolves its own 100% widths
   against correctly.

   Scoped to .field-help__tooltip (opted into via help-tooltip.js's
   data-tooltip-class, not applied to every vds-tooltip by default) since
   this help text runs full sentences, unlike /internal/'s usual short
   phrases — no evidence those need a wider box too, so left alone. */
.field-help__tooltip::part(content) {
  width: 320px;
  max-width: none;
}

/* Primary/Secondary/Tertiary × light/dark color table (COMT-176),
   replacing 12 separate <p> fields on templates/agency/branding.html —
   groups each slot's 4 colors (fg/bg × light/dark) into one row so
   they're easy to compare, with a live Sample swatch per mode (updated
   by static/js/branding-preview.js on every color input) instead of only
   seeing the effect after Save. */
.branding-color-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--vds-spacing-16);
}

.branding-color-table th,
.branding-color-table td {
  padding: var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  text-align: center;
  vertical-align: middle;
}

.branding-color-table thead th {
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-secondary);
}

.branding-color-table tbody th {
  text-align: left;
  font-weight: var(--vds-font-weight-semibold);
  white-space: nowrap;
}

/* Field-level errors (contrast/hex-format) render inline below the swatch
   that raised them — .card form's own error styling (errorlist) still
   applies since these are real form fields, just laid out in a table
   instead of <p> wrappers. */
.branding-color-table .errorlist {
  margin: var(--vds-spacing-4) 0 0;
  font-size: var(--vds-font-size-body-sm);
}

/* fg-on-bg preview chip — color/background-color are set inline per-slot
   at render time (initial values) and kept live by
   static/js/branding-preview.js as the color inputs change, without a
   page reload/Save. */
.color-sample {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 4.5rem;
  height: 2.5rem;
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  font-size: var(--vds-font-size-body-sm);
}

.card fieldset {
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-16);
  margin: 0 0 var(--vds-spacing-16);
}

.card fieldset legend {
  padding: 0 var(--vds-spacing-8);
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-secondary);
}

.btn {
  font: inherit;
  cursor: pointer;
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid transparent;
}

/* Publish (page-header-section design follow-up): disabled instead of
   hidden when there's nothing new to push live, so "no pending changes"
   stays visible rather than the button just disappearing. Generic
   enough to apply to any future disabled `.btn` too, not Publish-only. */
.btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.btn--primary {
  background: var(--vds-bg-brand-primary);
  color: var(--vds-fg-white);
}

.btn--secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.25rem;
  padding-block: 0;
  border-color: var(--vds-border-base);
  background: transparent;
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
  white-space: nowrap;
  text-decoration: none;
}

.btn--secondary:hover {
  background: var(--vds-bg-secondary);
  text-decoration: none;
}

/* Agency admin controls (COMT-510 UX review). Native elements styled with
   VDS tokens, for places a VDS component can't fulfil the contract (form
   submission, ARIA state, real links). One compact scale for dense admin
   screens; see the ux-componentry skill. */
.btn--sm {
  display: inline-flex;
  min-height: 2rem;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
  padding: 0 var(--vds-spacing-12);
  border-radius: var(--vds-radius-4);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
}

.btn--sm svg,
.btn--sm vds-icon {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
}

.btn--ghost {
  border-color: transparent;
  background: transparent;
  color: var(--vds-fg-secondary);
}

.btn--ghost:hover {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

/* Pressed is a neutral state; brand colour is reserved for the primary action. */
.btn--ghost[aria-pressed="true"] {
  background: var(--vds-bg-tertiary);
  color: var(--vds-fg-primary);
}

/* Native select with a token-coloured caret and room for it. */
.select {
  min-height: 2.25rem;
  box-sizing: border-box;
  padding: 0 var(--vds-spacing-32) 0 var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background-color: var(--vds-bg-primary);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--vds-fg-tertiary) 50%),
    linear-gradient(135deg, var(--vds-fg-tertiary) 50%, transparent 50%);
  background-position: calc(100% - 1.0625rem) 55%, calc(100% - 0.8125rem) 55%;
  background-repeat: no-repeat;
  background-size: 0.25rem 0.25rem;
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  appearance: none;
  cursor: pointer;
}

.select--sm {
  min-height: 2rem;
}

.select:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

/* Dropdown menu on native <details>: the <summary> is the trigger (its
   expanded state is exposed by the browser) and items are real links or
   buttons, so it works without JS. */
.menu {
  position: relative;
  display: inline-block;
}

.menu > summary {
  list-style: none;
}

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

.menu__trigger {
  display: inline-flex;
  min-height: 2rem;
  box-sizing: border-box;
  align-items: center;
  gap: var(--vds-spacing-4);
  padding: 0 var(--vds-spacing-8);
  border-radius: var(--vds-radius-4);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
  white-space: nowrap;
  cursor: pointer;
}

.menu__trigger:hover,
.menu[open] > .menu__trigger {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

.menu__caret {
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  margin-left: var(--vds-spacing-4);
  color: var(--vds-fg-tertiary);
  transition: transform 120ms ease;
}

.menu[open] .menu__caret {
  transform: rotate(180deg);
}

.menu__panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--vds-spacing-4));
  left: 0;
  display: flex;
  min-width: 14rem;
  max-width: calc(100vw - 2 * var(--vds-spacing-16));
  box-sizing: border-box;
  flex-direction: column;
  padding: var(--vds-spacing-4);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-md, 0 8px 24px rgb(0 0 0 / 16%));
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
}

.menu__panel--end {
  right: 0;
  left: auto;
}

.menu__item {
  display: flex;
  width: 100%;
  min-height: 2rem;
  box-sizing: border-box;
  align-items: center;
  gap: var(--vds-spacing-12);
  padding: 0.375rem var(--vds-spacing-8);
  border: 0;
  border-radius: var(--vds-radius-4);
  background: transparent;
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu__item:hover {
  background: var(--vds-bg-secondary);
}

/* The current choice gets a check, not a coloured fill. */
.menu__item[aria-current] {
  font-weight: var(--vds-font-weight-medium);
}

.menu__item[aria-current]::after {
  content: "";
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  margin: 0.1875rem 0 0 auto;
  background: var(--vds-fg-primary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Icons sit in a fixed column, aligned with the item's first line. */
.menu__item {
  align-items: flex-start;
}

.menu__item > svg,
.menu__item > vds-icon {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  margin-top: 0.125rem;
  color: var(--vds-fg-secondary);
}



.menu__trigger > svg {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
}

.menu__item small {
  white-space: nowrap;
}

.status-dot {
  display: inline-block;
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--vds-fg-tertiary);
}

.status-dot--on {
  background: var(--vds-bg-success-primary);
}

.menu__item-text {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
  gap: 0.125rem;
}

.menu__item small {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}



.menu__label {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-8) var(--vds-spacing-4);
  color: var(--vds-fg-tertiary);
  font-size: 0.6875rem;
  font-weight: var(--vds-font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.menu__label--with-help {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
}

.menu__label--with-help .field-help {
  margin-left: 0;
  text-transform: none;
}

.menu__separator {
  height: var(--vds-border-width-1);
  margin: var(--vds-spacing-4) 0;
  background: var(--vds-border-base);
}

/* Form fields for admin dialogs and panels: label above, control, optional
   hint. Text inputs match .select so rows of controls line up. */
.field {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--vds-spacing-4);
}

.field__label {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
}

.field__label small,
.field__hint {
  margin: 0;
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-regular);
}

.input {
  width: 100%;
  min-height: 2.25rem;
  box-sizing: border-box;
  padding: 0 var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
}

.input::placeholder {
  color: var(--vds-fg-tertiary);
}

.input:hover,
.select:hover {
  border-color: var(--vds-fg-tertiary);
}

.input:focus-visible,
.select:focus-visible {
  border-color: var(--vds-border-focus-outer);
  outline: var(--vds-border-width-1) solid var(--vds-border-focus-outer);
  outline-offset: 0;
}

.input[aria-invalid="true"] {
  border-color: var(--vds-border-error);
}

/* Choice cards: radio/checkbox options with a title and one-line
   description. A .choice__reveal inside the item only shows while that
   item's input is checked (e.g. a field picker for "One field only"). */
.choices {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  margin: 0;
  padding: 0;
  border: 0;
}

.choices > legend {
  margin-bottom: var(--vds-spacing-8);
  padding: 0;
}

.choices--row {
  flex-direction: row;
  flex-wrap: wrap;
}

.choices--row > .choices__item {
  flex: 1 1 12rem;
}

.choices__item {
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
}

.choices__item:has(> .choice input:checked) {
  border-color: var(--vds-border-brand);
  box-shadow: inset 0 0 0 var(--vds-border-width-1) var(--vds-border-brand);
}

.choices__item:has(> .choice input:focus-visible) {
  outline: var(--vds-border-width-2) solid var(--vds-border-focus-outer);
  outline-offset: var(--vds-border-width-2);
}

.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-12);
  cursor: pointer;
}

.choice input {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  margin: 0.125rem 0 0;
  accent-color: var(--vds-bg-brand-primary);
}

.choice__text {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 0.125rem;
}

.choice__title {
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
}

.choice__desc {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.choice__reveal {
  padding: 0 var(--vds-spacing-12) var(--vds-spacing-12) calc(var(--vds-spacing-12) * 2 + 1rem);
}

.choices__item:has(> .choice input:not(:checked)) > .choice__reveal {
  display: none;
}

/* Callout: a quiet summary box; --warning when it needs attention. */
.callout {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  /* A tint of the text colour reads as "slightly raised" in both themes;
     --vds-bg-secondary is darker than the dialog surface in dark mode. */
  background: color-mix(in srgb, var(--vds-fg-primary) 5%, transparent);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.callout > * {
  margin: 0;
}

.callout__title {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.4;
}

.callout__title svg {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
}

.callout--warning {
  background: var(--vds-bg-warning-light);
}

.callout--warning .callout__title svg {
  color: var(--vds-fg-warning-on-low);
}

.callout--error {
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.callout__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.callout__list li {
  padding: 0 var(--vds-spacing-4);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

/* Disclosure: <details class="disclosure"> with our caret instead of the
   browser's default triangle marker, which renders differently per browser
   and doesn't match the menus. */
.disclosure > summary {
  display: inline-flex;
  width: fit-content;
  align-items: center;
  gap: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
  list-style: none;
  cursor: pointer;
}

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

.disclosure > summary::before {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Cpath d='M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform 120ms ease;
}

.disclosure[open] > summary::before {
  transform: rotate(90deg);
}

.disclosure > summary:hover {
  color: var(--vds-fg-primary);
}

.disclosure[open] > summary {
  margin-bottom: var(--vds-spacing-8);
}

@media (prefers-reduced-motion: reduce) {
  .disclosure > summary::before {
    transition: none;
  }
}

/* Repeating editor rows (filters, sorts): labels on the first row only,
   controls aligned on one grid. Set --row-columns per editor. */
.row-editor {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

.row-editor__row {
  display: grid;
  grid-template-columns: var(--row-columns, repeat(3, minmax(0, 1fr)) auto);
  align-items: end;
  gap: var(--vds-spacing-8);
}

.row-editor__row + .row-editor__row .field__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.icon-button {
  display: inline-flex;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--vds-radius-4);
  background: transparent;
  color: var(--vds-fg-tertiary);
  font: inherit;
  cursor: pointer;
}

.icon-button:hover {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

.icon-button svg {
  width: 1rem;
  height: 1rem;
}

.icon-button--drag {
  cursor: grab;
}

/* Dialog content rhythm: one gap between blocks, actions right-aligned with
   destructive or secondary actions pushed left. */
.dialog-body {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--vds-spacing-16);
  color: var(--vds-fg-primary);
}

.dialog-body > p {
  margin: 0;
}

.dialog-lead {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.dialog-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--vds-spacing-8);
  color: var(--vds-fg-primary);
}

.dialog-actions__start {
  margin-right: auto;
}

.dialog-actions [hidden],
.dialog-actions .btn[hidden] {
  display: none;
}

/* Truncate long data values to one line with an ellipsis. Pair with a
   title attribute (the full value, capped server-side) so it shows on hover.
   Width comes from the container, or --truncate-width when set. */
.truncate {
  display: inline-block;
  max-width: var(--truncate-width, 100%);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* Bordered list for small collections in dialogs and panels (rules, version
   history). A row is lead + main (title, meta) + aside; an optional
   <details class="list__expand"> puts its action button top-right and opens
   its panel full width under the row, so forms never squeeze into a column. */
.list {
  margin: 0;
  padding: 0;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  list-style: none;
}

.list__item {
  position: relative;
  padding: var(--vds-spacing-12);
}

.list__item + .list__item {
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.list__item--current {
  background: var(--vds-bg-secondary);
}

.list__item:has(> .list__expand) .list__row {
  padding-right: 7rem;
}

.list__row {
  display: flex;
  min-width: 0;
  align-items: baseline;
  gap: var(--vds-spacing-8);
}

.list__lead {
  flex: 0 0 auto;
  min-width: 1.75rem;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.list__main {
  display: flex;
  min-width: 0;
  flex: 1;
  flex-direction: column;
  gap: 0.125rem;
  font-size: var(--vds-font-size-body-sm);
}

.list__title {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
}

a.list__title {
  text-decoration: none;
}

a.list__title:hover {
  color: var(--vds-fg-brand-on-low);
  text-decoration: underline;
}

.list__meta {
  overflow-wrap: anywhere;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.list__aside {
  flex: 0 0 auto;
  padding: 0 var(--vds-spacing-8);
  border-radius: var(--vds-radius-full);
  background: var(--vds-bg-tertiary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.list__expand > summary {
  position: absolute;
  top: var(--vds-spacing-8);
  right: var(--vds-spacing-8);
  list-style: none;
}

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

.list__expand[open] > summary {
  background: var(--vds-bg-tertiary);
  color: var(--vds-fg-primary);
}

.list__panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-12);
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.list__panel input[type="text"] {
  min-width: 12rem;
  min-height: 2rem;
  box-sizing: border-box;
  flex: 1;
  padding: 0 var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
}

.list__empty {
  margin: 0;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  text-align: center;
}

.btn--danger-text {
  color: var(--vds-fg-error-on-low);
}

/* Folder tabs (prototype data viewer): the active tab joins the panel below. */
.folder-tabs {
  display: flex;
  align-items: flex-end;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8) var(--vds-spacing-12) 0;
  overflow-x: auto;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-secondary);
}

.folder-tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: calc(-1 * var(--vds-border-width-1));
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid transparent;
  border-bottom: 0;
  border-radius: var(--vds-radius-8) var(--vds-radius-8) 0 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
  text-decoration: none;
  white-space: nowrap;
}

.folder-tabs__tab:hover {
  color: var(--vds-fg-primary);
}

.folder-tabs__tab[aria-current] {
  border-color: var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
}

.count-pill {
  display: inline-flex;
  min-width: 1.25rem;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  padding: 0.0625rem var(--vds-spacing-4);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-tertiary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
}

/* Dark text on the amber fill: white fails contrast (2.9:1). */
.count-pill--pending {
  border-radius: var(--vds-radius-full);
  /* The darker warning shade keeps white text above 4.5:1 (#c88904 is ~2.6:1). */
  background: var(--vds-bg-warning-primary-hover);
  color: var(--vds-fg-white);
  font-weight: var(--vds-font-weight-semibold);
}
.count-pill--removed {
  border-radius: var(--vds-radius-full);
  background: var(--vds-bg-error-primary);
  color: var(--vds-fg-white);
  font-weight: var(--vds-font-weight-semibold);
}

.btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  flex-shrink: 0;
}

.control-switch {
  appearance: none;
  position: relative;
  width: 2.75rem;
  height: 1.5rem;
  margin: 0;
  border: 0;
  border-radius: var(--vds-radius-full);
  background: var(--vds-bg-tertiary);
  cursor: pointer;
  flex-shrink: 0;
}

.control-switch::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--vds-bg-primary);
}

.control-switch:checked {
  background: var(--vds-bg-brand-primary);
}

.control-switch:checked::before {
  transform: translateX(1.25rem);
}

.control-switch:disabled {
  opacity: 0.65;
  cursor: not-allowed;
}

/* Delete action in the in-place edit panel (COMT-152) — visually
   distinct from Save/Cancel, using the same --vds-*-error-* tokens as
   every other error/destructive state in this app (form validation,
   etc.), not a one-off hardcoded red. */
.btn--danger {
  background: var(--vds-bg-error-primary);
  color: var(--vds-fg-white);
}

.btn--danger:hover {
  background: var(--vds-bg-error-primary-hover);
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: transparent;
  border: none;
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-8);
  color: inherit;
}

/* Same faint hover highlight as .chrome-nav-dropdown__trigger (below) —
   the header row's other menu-style controls (Services/Dashboards/...,
   the language selector) already get this treatment; the theme toggle
   should read as one more item in that same row, not a separately
   outlined control. */
.theme-toggle:hover {
  background: rgb(255 255 255 / 12%);
}

.theme-toggle__icon--to-light {
  display: none;
}

html[data-theme="dark"] .theme-toggle__icon--to-dark {
  display: none;
}

html[data-theme="dark"] .theme-toggle__icon--to-light {
  display: inline-flex;
}

.agency-switcher {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  color: inherit;
  font-size: var(--vds-font-size-body-sm);
}

.agency-switcher select {
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

/* Persistent /admin/* shell (COMT-93) — sidebar + top bar + user menu.
   Desktop-only for this first pass; no responsive collapse behavior yet.
   --agency-sidebar-width drives the sidebar's own width so collapsing it
   via agency-shell.js (toggling app-shell--sidebar-collapsed) resizes it
   without any JS layout math.

   height (not min-height) + overflow: hidden (COMT-99 follow-up) — the
   shell itself never scrolls; only .app-main does. With min-height: 100vh,
   a tall form grew .app-shell taller than the viewport, so .app-sidebar
   (a stretched flex item) grew with it and scrolled away with the page
   instead of staying put.

   position: fixed; inset: 0 (not an in-flow height: 100vh box) — small
   defect fix: on a form long enough to make .app-main scroll internally
   (e.g. branding.html's full list of per-swatch color fields), the
   *document* (<html>) became independently scrollable too, dragging the
   supposedly-pinned sidebar/topbar/fixed_actions bar along with it, even
   though .app-shell's own overflow: hidden correctly clipped its own
   rendering and .app-main correctly scrolled its own content in
   isolation. Confirmed via getBoundingClientRect()/scrollHeight probing
   that documentElement's own scrolling-area computation still counts
   descendant boxes clipped by an in-flow ancestor's overflow: hidden —
   overflow: hidden on an in-flow box stops *that box* from scrolling and
   clips its own paint, but does not stop its overflowing descendants'
   geometry from inflating <html>'s scrollable area the way one might
   expect. A position: fixed box's descendants are excluded from that
   computation entirely (same reason static/js/help-tooltip.js's
   fixed-position tooltips don't inflate it either, once actually
   positioned) — inset: 0 replaces the old height: 100vh to size it to the
   viewport the same way. */
.app-shell {
  display: flex;
  flex-direction: column;
  position: fixed;
  inset: 0;
  overflow: hidden;
  --agency-sidebar-width: 240px;
}

.app-shell__workspace {
  display: flex;
  flex: 1;
  min-height: 0;
}

.platform-agency-context {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
  flex-shrink: 0;
  padding: var(--vds-spacing-4) var(--vds-spacing-16);
  background: var(--vds-fg-secondary);
  color: var(--vds-fg-white);
  font-size: var(--vds-font-size-body-sm);
}

html[data-theme="dark"] .platform-agency-context {
  background: var(--vds-bg-tertiary);
}

.platform-agency-context svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.platform-agency-context__switcher {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--vds-spacing-4);
}

.platform-agency-context select,
.platform-agency-context__action {
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-secondary);
  border-radius: var(--vds-radius-4);
  background: transparent;
  color: inherit;
  font: inherit;
}

.platform-agency-context select {
  max-width: min(250px, 65vw);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
}

.platform-agency-context__action {
  cursor: pointer;
  font-weight: var(--vds-font-weight-semibold);
}

.platform-agency-context__action:hover {
  background: var(--vds-bg-on-primary-high);
  color: var(--vds-fg-primary);
}

.platform-agency-context :is(select, button):focus-visible {
  outline: 2px solid var(--vds-fg-white);
  outline-offset: 2px;
}

.platform-agency-context__exit {
  margin-left: auto;
}

.app-shell--sidebar-collapsed {
  --agency-sidebar-width: 56px;
}

/* Phones: agency-shell.js starts the sidebar collapsed; when expanded it
   overlays the content instead of squeezing it, and the topbar drops the
   user's name so the breadcrumb keeps its room. */
@media (max-width: 767px) {
  .app-shell .app-sidebar {
    position: absolute;
    z-index: 25;
    height: 100%;
  }

  .app-shell__main {
    margin-left: 56px;
  }

  .app-user-menu__name {
    display: none;
  }

  .app-shell:not(.app-shell--sidebar-collapsed) .app-sidebar {
    box-shadow: var(--vds-shadow-lg);
  }

  .app-shell:not(.app-shell--sidebar-collapsed) .app-sidebar__close {
    display: inline-grid;
  }
}

.app-sidebar__close {
  display: none;
  place-items: center;
  width: 2rem;
  height: 2rem;
  margin-left: auto;
  padding: 0;
  border: 0;
  border-radius: var(--vds-radius-8);
  background: transparent;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.app-sidebar__close:hover {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

.app-sidebar__close:focus-visible {
  outline: 2px solid var(--vds-border-focus, var(--vds-fg-brand-on-low));
  outline-offset: 2px;
}


.app-sidebar {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-16);
  width: var(--agency-sidebar-width);
  flex-shrink: 0;
  padding: var(--vds-spacing-16);
  background: var(--vds-bg-primary);
  border-right: var(--vds-border-width-1) solid var(--vds-border-base);
  overflow-x: hidden;
  overflow-y: auto;
}

.app-shell--sidebar-collapsed .app-sidebar {
  box-sizing: border-box;
  padding: var(--vds-spacing-8);
}

.app-sidebar__brand {
  display: flex;
  align-items: center;
}

.app-sidebar__brand-mark--expanded {
  display: block;
  width: auto;
  height: 24px;
}

html[data-theme="dark"] .app-sidebar__brand img {
  filter: brightness(0) invert(1);
}

.app-shell--sidebar-collapsed .app-sidebar__brand {
  justify-content: center;
}

.app-shell--sidebar-collapsed .app-sidebar__brand-mark--expanded {
  display: none;
}

.app-sidebar__brand-mark--collapsed {
  display: none;
}

.app-shell--sidebar-collapsed .app-sidebar__brand-mark--collapsed {
  display: block;
  width: 30px;
  height: 30px;
  object-fit: contain;
}

/* The tenant switcher is a labelled <select> — there's no sensible
   icon-only form of it, so it hides along with the nav labels rather
   than trying to squeeze into the compact rail (COMT-99). */
.app-shell--sidebar-collapsed .app-sidebar__switcher {
  display: none;
}

.app-sidebar__switcher {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
}

/* Reads as the agency section's header: label matches the group titles.
   The native <select> stays (real POST + no-JS fallback); it is restyled
   into a bordered row with the agency's mark and a VDS chevron overlaid.
   Only rendered when there is more than one agency to switch between. */
.app-sidebar__switcher label {
  margin: 0;
  padding-inline: var(--vds-spacing-8);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.app-sidebar__switcher-control {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}

.app-sidebar__switcher-mark {
  position: absolute;
  left: var(--vds-spacing-8);
  z-index: 1;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1;
  pointer-events: none;
}

.app-sidebar__switcher-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.app-sidebar__switcher-chevron {
  position: absolute;
  right: var(--vds-spacing-8);
  z-index: 1;
  width: 16px;
  height: 16px;
  color: var(--vds-fg-tertiary);
  pointer-events: none;
}

.app-sidebar__switcher select {
  appearance: none;
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  height: 40px;
  padding: 0 calc(var(--vds-spacing-8) + 24px) 0 calc(var(--vds-spacing-8) * 2 + 24px);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.app-sidebar__switcher-control:not(:has(.app-sidebar__switcher-mark)) select {
  padding-left: var(--vds-spacing-8);
}

.app-sidebar__switcher select:hover {
  border-color: var(--vds-border-secondary);
  background: var(--vds-bg-secondary);
}

.app-sidebar__switcher select:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 1px;
}

.app-sidebar__agency {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-16);
}

/* Labelled hairline ("──── Platform admin ────") separating platform-wide
   nav from the selected agency's nav; platform staff only. */
.app-sidebar__rule {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin: 0;
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
  white-space: nowrap;
}

.app-sidebar__rule::before,
.app-sidebar__rule::after {
  content: "";
  flex: 1;
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.app-shell--sidebar-collapsed .app-sidebar__rule {
  gap: 0;
}

.app-shell--sidebar-collapsed .app-sidebar__rule span {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.app-sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-16);
}

.app-shell--sidebar-collapsed .app-sidebar__nav {
  align-items: center;
  gap: var(--vds-spacing-8);
}

.app-sidebar__group {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
}

.app-shell--sidebar-collapsed .app-sidebar__group {
  align-items: center;
  gap: var(--vds-spacing-8);
}

.app-sidebar__group-title {
  margin: 0 0 var(--vds-spacing-4);
  padding-inline: var(--vds-spacing-8);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.app-shell--sidebar-collapsed .app-sidebar__group-title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.app-sidebar__item {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-tertiary);
  text-decoration: none;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
}

.app-sidebar__item svg,
.app-sidebar__item vds-icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.app-shell--sidebar-collapsed .app-sidebar__item {
  box-sizing: border-box;
  width: 40px;
  height: 40px;
  padding: 0;
  justify-content: center;
}

.app-shell--sidebar-collapsed .app-sidebar__item svg,
.app-shell--sidebar-collapsed .app-sidebar__item vds-icon {
  width: 20px;
  height: 20px;
}

/* Labels stay in the DOM (title attribute still gives collapsed users a
   hover tooltip) — only visually hidden, so nothing about the link's
   accessible name changes when collapsing (COMT-99). */
.app-shell--sidebar-collapsed .app-sidebar__item span {
  display: none;
}

.app-sidebar__item:hover {
  background: var(--vds-border-base);
  color: var(--vds-fg-secondary);
}

.app-sidebar__item--active,
.app-sidebar__item--active:hover {
  background: var(--vds-bg-inverse);
  color: var(--vds-fg-inverse);
}

.tenant-create {
  max-width: 1200px;
  margin: 0 auto;
  color: var(--vds-fg-primary);
}

.tenant-create__header h1 {
  margin: 0;
  font-size: 2rem;
}

.tenant-create__header p {
  margin: var(--vds-spacing-8) 0 var(--vds-spacing-24);
  color: var(--vds-fg-secondary);
}

.tenant-create__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--vds-spacing-16);
  padding: 0;
  margin: 0 0 var(--vds-spacing-24);
  list-style: none;
}

.tenant-create__steps li {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding-bottom: var(--vds-spacing-8);
  border-bottom: 4px solid var(--vds-border-base);
  color: var(--vds-fg-secondary);
}

.tenant-create__steps li span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--vds-border-base);
  border-radius: 50%;
}

.tenant-create__steps .tenant-create__step--current,
.tenant-create__steps .tenant-create__step--complete {
  border-color: var(--vds-bg-brand-primary);
}

.tenant-create__steps .tenant-create__step--current {
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-primary);
}

.tenant-create__steps .tenant-create__step--current span,
.tenant-create__steps .tenant-create__step--complete span {
  color: var(--vds-fg-brand-on-high);
  background: var(--vds-bg-brand-primary);
  border-color: var(--vds-bg-brand-primary);
}

.tenant-create__panel {
  min-height: 320px;
  padding: var(--vds-spacing-24);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.tenant-create__panel h2 {
  margin: 0 0 var(--vds-spacing-24);
}

.tenant-create__grid,
.tenant-create__review {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--vds-spacing-24) var(--vds-spacing-16);
}

.tenant-create__field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
}

.tenant-create__field input,
.tenant-create__field select,
.tenant-create__readonly {
  box-sizing: border-box;
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.tenant-create__readonly {
  background: var(--vds-bg-secondary);
}

.tenant-create__review {
  gap: 0;
  margin: 0;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  overflow: hidden;
}

.tenant-create__review div {
  padding: var(--vds-spacing-16);
  border-bottom: 1px solid var(--vds-border-base);
}

.tenant-create__review div:nth-child(odd) {
  border-right: 1px solid var(--vds-border-base);
}

.tenant-create__review dt {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.tenant-create__review dd {
  margin: var(--vds-spacing-8) 0 0;
}

.tenant-product-list {
  overflow-x: auto;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.tenant-product-list table,
.product-detail__table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}

.tenant-product-list th,
.tenant-product-list td,
.product-detail__table th,
.product-detail__table td {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border-bottom: 1px solid var(--vds-border-base);
  vertical-align: middle;
}

.tenant-product-list th,
.product-detail__table th {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.tenant-product-list td small,
.product-detail__table td small {
  display: block;
  margin-top: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-family: var(--vds-font-family-body);
}

.tenant-product-list__status,
.product-detail__badge {
  display: inline-flex;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border: 1px solid var(--vds-border-base);
  border-radius: 999px;
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-xs);
}

.tenant-product-list__validation {
  display: inline-flex;
  width: 1.25rem;
  height: 1.25rem;
  margin-left: var(--vds-spacing-8);
  align-items: center;
  justify-content: center;
  vertical-align: middle;
  border: 1px solid currentColor;
  border-radius: 50%;
}

.tenant-product-list__validation svg {
  width: 0.875rem;
  height: 0.875rem;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.tenant-product-list__validation--passed {
  border-color: var(--vds-border-success);
  background: var(--vds-bg-success-light);
  color: var(--vds-fg-success-on-low);
}

.tenant-product-list__validation--failed {
  border-color: var(--vds-border-error);
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.tenant-product-list__validation--not-validated {
  border-color: var(--vds-border-base);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
}

.product-detail__back {
  display: inline-block;
  margin-bottom: var(--vds-spacing-16);
  color: var(--vds-fg-primary);
  text-decoration: none;
}

.product-detail__heading,
.product-detail__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
}

.product-detail__heading {
  margin-bottom: var(--vds-spacing-24);
}

.product-detail__heading h2 {
  margin: 0;
}

.product-detail__heading p {
  margin: var(--vds-spacing-8) 0 0;
  color: var(--vds-fg-secondary);
}

.product-detail__extract {
  display: grid;
  gap: var(--vds-spacing-8);
  justify-items: end;
}

.product-detail__extract .tenant-create__button:disabled {
  opacity: .6;
  cursor: not-allowed;
}

.product-detail__extract .product-detail__readonly-note {
  max-width: 19rem;
  margin: 0;
  text-align: right;
}

.product-detail__readonly,
.product-detail__readonly-note {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.product-detail__tabs {
  display: flex;
  overflow-x: auto;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8) var(--vds-radius-8) 0 0;
  white-space: nowrap;
}

.product-detail__tabs a {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border-bottom: 2px solid transparent;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  text-decoration: none;
}

.product-detail__tabs a[aria-current="page"] {
  border-color: var(--vds-bg-brand-primary);
  color: var(--vds-fg-primary);
}

.product-detail__panel {
  min-height: 300px;
  padding: var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-top: 0;
  border-radius: 0 0 var(--vds-radius-8) var(--vds-radius-8);
}

.product-detail__summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--vds-spacing-12);
}

.product-detail__summary article,
.product-detail__validation {
  padding: var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.product-detail__summary article > span,
.product-detail__validation > span {
  display: block;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.product-detail__summary article strong {
  display: block;
  margin-top: var(--vds-spacing-8);
}

.product-detail__summary article p,
.product-detail__validation p {
  margin-bottom: 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.product-detail__notice {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
  margin: var(--vds-spacing-16) 0;
  padding: var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.product-detail__connection-test {
  display: grid;
  justify-items: start;
  gap: var(--vds-spacing-12);
  margin-top: var(--vds-spacing-16);
  scroll-margin-top: var(--vds-spacing-24);
}

.product-detail__connection-feedback {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.product-detail__connection-feedback--success {
  border-color: var(--vds-border-success);
  background: var(--vds-bg-success-light);
  color: var(--vds-fg-success-on-low);
}

.product-detail__connection-feedback--error {
  border-color: var(--vds-border-error);
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.product-detail__connection-feedback--warning {
  border-color: var(--vds-border-warning);
  background: var(--vds-bg-warning-light);
  color: var(--vds-fg-warning-on-low);
}

.product-detail__details {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  overflow: hidden;
}

.product-detail__details div {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border-bottom: 1px solid var(--vds-border-base);
}

.product-detail__details div:nth-child(odd) {
  border-right: 1px solid var(--vds-border-base);
}

.product-detail__details dt {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.product-detail__details dd {
  margin: var(--vds-spacing-8) 0 0;
}

.product-detail__table-wrap {
  overflow-x: auto;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.product-detail__table caption {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  color: var(--vds-fg-secondary);
  text-align: left;
}

.product-detail__incoming-controls,
.product-detail__incoming-controls form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.product-detail__incoming-controls {
  margin-bottom: var(--vds-spacing-16);
}

.product-detail__sample {
  max-height: 520px;
  overflow: auto;
}

.product-detail__sample .product-detail__table {
  width: max-content;
  min-width: 100%;
}

.product-detail__sample td {
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.product-detail__sample td[colspan] {
  max-width: none;
  white-space: normal;
}

.product-detail__seed-edit {
  min-width: 16rem;
  margin-top: var(--vds-spacing-8);
}

.product-detail__seed-edit > summary {
  color: var(--vds-fg-brand-on-low);
  cursor: pointer;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
}

.product-detail__enabled-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.product-detail__enabled-control input {
  width: 1rem;
  height: 1rem;
  margin: 0;
  accent-color: var(--vds-bg-brand-primary);
}

.product-detail__seed-fields {
  display: grid;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-8);
  padding: var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.product-detail__seed-fields label {
  display: grid;
  gap: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.product-detail__input {
  min-width: 0;
  max-width: 100%;
  padding: var(--vds-spacing-8);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.product-detail__empty {
  display: flex;
  min-height: 230px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--vds-spacing-24);
  border: 1px dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  text-align: center;
}

.product-detail__empty p {
  margin-bottom: 0;
  color: var(--vds-fg-secondary);
}

.product-detail__validation {
  margin-top: var(--vds-spacing-16);
}

.product-detail__validation strong {
  display: block;
  margin-top: var(--vds-spacing-8);
}

.initial-seed-panel {
  position: fixed;
  z-index: 50;
  inset: 0 0 0 auto;
  display: flex;
  width: min(440px, 100vw);
  flex-direction: column;
  border-left: 1px solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-lg);
  color: var(--vds-fg-primary);
}

.initial-seed-panel__header,
.initial-seed-panel__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--vds-spacing-16);
  border-bottom: 1px solid var(--vds-border-base);
}

.initial-seed-panel__header small {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.initial-seed-panel__header h2 {
  margin: var(--vds-spacing-4) 0 0;
}

.initial-seed-panel__header a {
  padding: var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  text-decoration: none;
}

.initial-seed-panel__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--vds-spacing-16);
}

.initial-seed-panel__status {
  display: block;
  padding: var(--vds-spacing-4);
  border: 1px solid var(--vds-bg-brand-primary);
  border-radius: 999px;
  color: var(--vds-fg-brand-on-low);
  text-align: center;
}

.initial-seed-panel__body > p,
.initial-seed-panel__dataset p {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.initial-seed-panel__dataset,
.initial-seed-panel__warning {
  margin-top: var(--vds-spacing-8);
  padding: var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.initial-seed-panel__dataset p {
  margin: var(--vds-spacing-8) 0;
}

.initial-seed-panel__dataset small {
  color: var(--vds-fg-secondary);
}

.initial-seed-panel__warning {
  margin-top: var(--vds-spacing-16);
  border-color: var(--vds-border-error);
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.initial-seed-panel__warning p {
  margin-bottom: 0;
}

.initial-seed-panel__footer {
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
  border-top: 1px solid var(--vds-border-base);
  border-bottom: 0;
}

.initial-seed-panel__footer .initial-seed-panel__cancel {
  border-color: var(--vds-bg-error-primary);
  background: var(--vds-bg-error-primary);
  color: var(--vds-fg-error-on-high);
}

.initial-seed-panel__footer .initial-seed-panel__cancel:hover {
  background: var(--vds-bg-error-primary-hover);
}

.initial-seed-panel__footer .initial-seed-panel__cancel:disabled {
  opacity: .6;
  cursor: not-allowed;
}

@media (max-width: 900px) {
  .product-detail__summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 600px) {
  .product-detail__heading,
  .product-detail__actions {
    align-items: flex-start;
    flex-direction: column;
  }

  .product-detail__extract {
    justify-items: start;
  }

  .product-detail__extract .product-detail__readonly-note {
    text-align: left;
  }

  .product-detail__summary,
  .product-detail__details {
    grid-template-columns: 1fr;
  }

  .product-detail__details div:nth-child(odd) {
    border-right: 0;
  }
}

.tenant-create__review small {
  display: block;
  color: var(--vds-fg-secondary);
}

.tenant-create__actions {
  display: flex;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-24);
  padding-top: var(--vds-spacing-16);
  border-top: 1px solid var(--vds-border-base);
}

.tenant-create__button {
  display: inline-block;
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

.tenant-create__button--primary {
  margin-left: auto;
  background: var(--vds-bg-brand-primary);
  color: var(--vds-fg-brand-on-high);
  border-color: var(--vds-bg-brand-primary);
}

.tenant-list {
  max-width: 1200px;
  margin: 0 auto;
}

.platform-users {
  max-width: 1400px;
  margin: 0 auto;
}

.platform-users__roles,
.platform-users__fields {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--vds-spacing-16);
}

.platform-users__roles {
  margin-bottom: var(--vds-spacing-24);
}

.platform-users__roles h2 {
  margin: 0;
  font-size: var(--vds-font-size-body-md);
}

.platform-users__panel {
  margin-bottom: var(--vds-spacing-24);
}

.platform-users__panel-heading {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--vds-spacing-16);
}

.platform-users__panel-heading h2 {
  margin: 0;
}

.platform-users__fields {
  margin: var(--vds-spacing-16) 0;
}

.platform-users__fields .tenant-create__field input[type="checkbox"] {
  width: auto;
}

.platform-users__fields .tenant-create__field > div:has(input[type="checkbox"]) {
  max-height: 180px;
  overflow-y: auto;
  padding: var(--vds-spacing-8);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.tenant-list__heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
  margin-bottom: var(--vds-spacing-24);
}

.tenant-list__heading h1 {
  margin: 0;
}

.tenant-list__heading p {
  margin: var(--vds-spacing-8) 0 0;
  color: var(--vds-fg-secondary);
}

.tenant-list__search {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
  margin-bottom: var(--vds-spacing-16);
  padding: var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.tenant-list__search-field {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
  max-width: 500px;
}

.tenant-list__search-field label {
  flex-basis: 100%;
}

.tenant-list__search-field input {
  min-width: 240px;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.tenant-list__table {
  overflow-x: auto;
}

.tenant-list__table vds-table {
  min-width: 850px;
}

.tenant-list__identifier {
  display: block;
  color: var(--vds-fg-secondary);
}

.tenant-list__empty {
  padding: var(--vds-spacing-24);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.tenant-list__pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-16);
  margin-top: var(--vds-spacing-16);
}

.tenant-record {
  max-width: 1400px;
  margin: 0 auto;
  color: var(--vds-fg-primary);
}

.tenant-record__header,
.tenant-record__section-heading {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--vds-spacing-16);
}

.tenant-record__header {
  margin-bottom: var(--vds-spacing-32);
}

.tenant-record__header h1 {
  margin: 0;
}

.tenant-record__header p,
.tenant-record section > p,
.tenant-record__section-heading p,
.tenant-record__card > p,
.tenant-record__empty p {
  color: var(--vds-fg-secondary);
}

.tenant-record__eyebrow {
  margin: 0 0 var(--vds-spacing-8);
  text-transform: uppercase;
  font-weight: var(--vds-font-weight-semibold);
  letter-spacing: .08em;
}

.tenant-record__tabs {
  display: flex;
  gap: var(--vds-spacing-8);
  border-bottom: 1px solid var(--vds-border-base);
  margin-bottom: var(--vds-spacing-32);
}

.tenant-record__tabs a {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border-bottom: 3px solid transparent;
  color: var(--vds-fg-secondary);
  text-decoration: none;
}

.tenant-record__tabs a[aria-current="page"] {
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
  border-color: var(--vds-bg-brand-primary);
}

.tenant-record__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--vds-spacing-16);
  margin-top: var(--vds-spacing-24);
}

.tenant-record__card {
  padding: var(--vds-spacing-24);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  margin: var(--vds-spacing-16) 0;
}

.tenant-record__card h3 {
  margin: 0 0 var(--vds-spacing-12);
}

.tenant-record__stats .tenant-record__card {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-24);
  margin: 0;
}

.tenant-record__stats .tenant-record__card strong {
  font-size: 1.5rem;
}

.tenant-record__empty {
  text-align: center;
  padding: var(--vds-spacing-64) var(--vds-spacing-24);
  border: 1px dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  margin-top: var(--vds-spacing-24);
}

.tenant-record__details {
  display: grid;
  grid-template-columns: minmax(130px, 230px) minmax(0, 1fr);
  gap: var(--vds-spacing-16);
}

.tenant-record__details dt {
  color: var(--vds-fg-secondary);
}

.tenant-record__details dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.tenant-record__lifecycle {
  border-color: var(--vds-border-error);
}

.source-setup {
  max-width: 920px;
}

.source-setup__notice {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.source-setup__steps {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: var(--vds-spacing-24);
}

.source-setup .tenant-create__field {
  margin-top: var(--vds-spacing-24);
}

.source-setup .tenant-create__field input[type="radio"],
.source-setup .tenant-create__field input[type="checkbox"] {
  width: auto;
  margin-right: var(--vds-spacing-8);
}

@media (max-width: 720px) {
  .platform-users__roles,
  .platform-users__fields {
    grid-template-columns: 1fr;
  }
  .platform-users__panel-heading {
    flex-wrap: wrap;
  }
  .tenant-create__grid,
  .tenant-create__review {
    grid-template-columns: 1fr;
  }
  .tenant-create__review div:nth-child(odd) {
    border-right: 0;
  }
  .tenant-create__steps li {
    font-size: var(--vds-font-size-body-xs);
  }
  .source-setup__steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .tenant-list__heading {
    flex-wrap: wrap;
  }
  .tenant-record__header,
  .tenant-record__section-heading {
    flex-wrap: wrap;
  }
  .tenant-record__stats {
    grid-template-columns: 1fr;
  }
  .tenant-record__details {
    grid-template-columns: 1fr;
    gap: var(--vds-spacing-8);
  }
}

/* Column of [fixed topbar, scrolling .app-main, fixed .app-fixed-actions]
   (COMT-99). Bound to the workspace below the optional platform strip. */
.app-shell__main {
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--vds-bg-secondary);
}

.app-topbar {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-16);
  padding: var(--vds-spacing-4) var(--vds-spacing-16);
  background: var(--vds-bg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  flex-shrink: 0;
}

.app-topbar__sidebar-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  padding: var(--vds-spacing-4);
  border-radius: var(--vds-radius-8);
  flex-shrink: 0;
}

.app-topbar__sidebar-toggle:hover {
  background: var(--vds-border-base);
}

.app-topbar__env {
  color: var(--vds-fg-error-on-low);
  font-weight: var(--vds-font-weight-semibold);
  font-size: var(--vds-font-size-body-sm);
}

.app-topbar__breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  flex: 1;
}

.app-user-menu {
  position: relative;
}

.app-user-menu__trigger {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  font: inherit;
  padding: var(--vds-spacing-4);
  border-radius: var(--vds-radius-8);
}

.app-user-menu__trigger:hover {
  background: var(--vds-border-base);
}

.app-user-menu__name {
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
}

/* Logged-out state's own control, rendered instead of `.app-user-menu`
   itself (see templates/base.html and widgets/chrome/_login_user_menu.
   html) — styled to read as the same kind of header-row menu item as
   its logged-in counterpart (`.app-user-menu__name`'s own font-size/
   weight) and the row's other controls (nav-dropdown-links,
   theme-toggle, language selector), not a `.btn` or a bare, browser-
   default underlined link. Deliberately not `.btn`: `.content-section
   --hero a:not(.btn)`/`.app-header a` both already skip `.btn` so a
   real primary/danger button never gets its color clobbered — this
   link *wants* that inherited/branded color, same as every other label
   in this row. */
.app-user-menu__login-link {
  display: inline-flex;
  align-items: center;
  color: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
  text-decoration: none;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
}

/* Same faint hover highlight as .theme-toggle/.chrome-nav-dropdown__trigger
   above — this row's other menu-style controls. */
.app-user-menu__login-link:hover {
  background: rgb(255 255 255 / 12%);
}

.app-user-menu__panel {
  position: absolute;
  top: calc(100% + var(--vds-spacing-8));
  right: 0;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8);
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  box-shadow: var(--vds-shadow-md);
  z-index: 10;
}

/* Toggled by agency-shell.js (COMT-93/95 /code-review fix) — a dedicated
   modifier class rather than the `hidden` attribute + a `:not([hidden])`
   override, so there's no specificity trap if this file's base rule ever
   grows its own `display` value. Server-rendered without this class so
   sign-out stays reachable if the JS never runs. */
.app-user-menu__panel--collapsed {
  display: none;
}

.app-user-menu__section-label {
  margin: 0;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: uppercase;
}

.app-user-menu__theme-option,
.app-user-menu__signout,
.app-user-menu__link {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  width: 100%;
  box-sizing: border-box;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  text-decoration: none;
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
}

.app-user-menu__theme-option:hover,
.app-user-menu__signout:hover,
.app-user-menu__link:hover {
  background: var(--vds-border-base);
}

/* Specificity fix (same root cause/fix as .chrome-nav-dropdown__panel
   .chrome-nav-dropdown__link above): `.app-user-menu__signout`/
   `.app-user-menu__theme-option` are plain `<button>`s, so the shared
   rule's own `color: var(--vds-fg-primary)` above already wins cleanly
   and needs no help — only `.app-user-menu__link` ("Manage this
   agency") is a real `<a>`, which `.app-header a` (one class + one
   element = higher specificity than the shared rule's one class) was
   overriding, rendering it in the header's own on-dark-background link
   color despite sitting inside this panel's always-light
   `--vds-bg-primary` card. Scoping to `.app-user-menu__panel
   .app-user-menu__link` (two classes) outranks `.app-header a`
   regardless of source order. */
.app-user-menu__panel .app-user-menu__link {
  color: var(--vds-fg-primary);
}

.app-user-menu__theme-option[aria-pressed="true"] {
  background: var(--vds-bg-inverse);
  color: var(--vds-fg-inverse);
}

.app-user-menu__divider {
  margin: var(--vds-spacing-4) 0;
  border: none;
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* templates/portal/transparency_page_detail.html's now-sr-only <h1>
   plus its edit-trigger (COMT-196's "Explore Transparency Data" switcher
   trigger that used to sit here was dropped along with the visible
   heading, 2026-09 two-header-dedup follow-up — see that template's own
   comment). */
.topic-page-heading {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

/* templates/portal/hub_page.html's own now-sr-only <h1> (COMT-231) —
   same inline flex row as .topic-page-heading above, just without a
   switcher trigger alongside it. */
.page-heading {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

/* No-default-title bugfix follow-up (2026-09): `<h1 id="page-title-
   heading">` is always rendered — even blank — so the page-settings
   panel's OOB swap (see templates/portal/_page_title_heading.html) has
   a stable id to swap into. `.sr-only` (two-header-dedup follow-up,
   2026-09) already keeps it out of the visual layout regardless of
   content, so this `:empty` rule no longer does any visible work, but
   stays harmless/unnecessary-but-correct rather than being ripped out
   for its own sake. */
#page-title-heading:empty {
  display: none;
}

/* The page-settings edit-trigger (COMT-231) sits inline next to the
   sr-only <h1>, not in a corner overlay — .edit-trigger's own default
   `position: absolute` (for the Section/Block corner treatment) doesn't
   apply here, same override .content-actions/.tab-strip__item already
   need for their own non-corner placements. */
.page-heading .edit-trigger,
.topic-page-heading .edit-trigger {
  position: static;
  top: auto;
  right: auto;
}

.language-switcher {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-16);
  font-size: var(--vds-font-size-body-sm);
}

.language-switcher select {
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.version-selector {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-16);
  font-size: var(--vds-font-size-body-sm);
}

.version-selector select {
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

/* .publish-controls (COMT-149 follow-up): the same Publish/Unpublish/
   version-history controls templates/agency/hub_page_edit.html's
   .page-header__actions and .version-selector already have, now also
   surfaced in templates/base.html's own sticky header — for a manager
   viewing their agency's live draft via the public in-place editor —
   so publishing changes made there doesn't require leaving for the
   older full-page editor. Sits inside `.app-header__end` alongside
   header_actions (theme-toggle/language-switcher/user-menu), rather
   than in header_extra (agency-preview switcher), since it's
   conditionally rendered per-page (only when `publish_controls` is in
   context) rather than a global, always-present control like the theme
   toggle/user menu — see `.app-header__start`/`.app-header__end`'s own
   comment for why these two are grouped separately from header_extra. */
.publish-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.publish-controls__form {
  display: inline-flex;
  margin: 0;
}

/* Editor bar: draft/live status line and the "more actions" overflow menu
   (a native <details>, so it works without JS; inline-editor.js only adds
   close-on-outside-click). */
.editor-bar-status {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* Autosave status icon: a fixed-size slot that is always present, so the
   status text never moves — only the slot's contents change (spinner while
   saving, check mark once saved, empty when idle). */
.autosave-status__text {
  display: inline-grid;
}

.autosave-status__variant {
  grid-area: 1 / 1;
  visibility: hidden;
}

.editor-bar-status[data-autosave-status="idle"] .autosave-status__variant[data-state="idle"],
.editor-bar-status[data-autosave-status="saving"] .autosave-status__variant[data-state="saving"],
.editor-bar-status[data-autosave-status="saved"] .autosave-status__variant[data-state="saved"] {
  visibility: visible;
}

.editor-bar-status[data-autosave-status="saved"] .autosave-status__variant[data-state="saved"] {
  color: var(--vds-fg-success, var(--vds-fg-secondary));
}

.autosave-status__icon {
  position: relative;
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
}

.autosave-status__icon::before {
  content: "";
  position: absolute;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.editor-bar-status[data-autosave-status="saving"] .autosave-status__icon::before {
  inset: 0;
  opacity: 1;
  border: 2px solid var(--vds-border-secondary);
  border-top-color: var(--vds-fg-secondary);
  border-radius: 50%;
  animation: autosave-spin 0.7s linear infinite;
}

.editor-bar-status[data-autosave-status="saved"] .autosave-status__icon::before {
  left: 0.3125rem;
  top: 0.0625rem;
  width: 0.25rem;
  height: 0.5rem;
  opacity: 1;
  border: solid var(--vds-fg-success, var(--vds-fg-secondary));
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.autosave-history {
  display: inline-flex;
  gap: var(--vds-spacing-2);
}

/* Selectors are deliberately as specific as the editor bar's own `.btn`
   rules (padding-inline: 16px would otherwise squeeze the 16px icon out of
   a 32px-wide button, leaving a blank box). */
.app-header.app-header--editor-bar .publish-controls .btn.autosave-history__button {
  width: 2rem;
  min-width: 2rem;
  min-height: 2rem;
  height: 2rem;
  padding: 0;
  background: transparent;
  border-color: transparent;
  color: var(--vds-fg-secondary);
}

.autosave-history__button svg {
  flex: none;
  display: block;
}

.app-header.app-header--editor-bar .publish-controls .btn.autosave-history__button:hover:not(:disabled) {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

.app-header.app-header--editor-bar .publish-controls .btn.autosave-history__button:disabled {
  opacity: 0.4;
  cursor: default;
}

@keyframes autosave-spin {
  to {
    transform: rotate(360deg);
  }
}

.editor-publish-notice {
  position: sticky;
  top: var(--editor-bar-offset, 0px);
  z-index: 20;
  display: flex;
  align-items: flex-start;
  flex-shrink: 0;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-warning);
  background: var(--vds-bg-warning-light);
  color: var(--vds-fg-warning-on-low);
  font-size: 0.8125rem;
  line-height: 1.5;
}

.editor-publish-notice vds-icon {
  flex-shrink: 0;
}

.editor-publish-notice p {
  min-width: 0;
  margin: 0;
}

.editor-bar-menu {
  position: relative;
}

/* The docked settings panel is z-index 100; an open menu must sit above it. */
.app-header.app-header--editor-bar:has(.editor-bar-menu[open]) {
  z-index: 110;
}

.editor-bar-menu__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
  list-style: none;
}

.editor-bar-menu__divider {
  margin: var(--vds-spacing-4) 0;
  border: 0;
  border-top: var(--vds-border-width-1) solid var(--vds-border-secondary);
}

.editor-bar-menu__trigger::-webkit-details-marker {
  display: none;
}

.editor-bar-menu__panel {
  position: absolute;
  top: calc(100% + var(--vds-spacing-4));
  right: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  min-width: 17rem;
  padding: var(--vds-spacing-4);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.16);
}

.editor-bar-menu__form {
  display: flex;
  margin: 0;
}

.editor-bar-menu__item {
  display: block;
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 0;
  border-radius: var(--vds-radius-4, 4px);
  background: transparent;
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: 0.8125rem;
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.editor-bar-menu__item:hover:not(:disabled) {
  background: var(--vds-bg-secondary);
}

.editor-bar-menu__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.editor-bar-menu__item--danger {
  color: var(--vds-fg-error-primary, #b42318);
}

.editor-bar-menu__heading {
  margin: var(--vds-spacing-8) 0 var(--vds-spacing-4);
  padding-inline: var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
  font-weight: var(--vds-font-weight-medium);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.editor-bar-menu__versions {
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
}

/* The Preview bar is black; the status text and menu follow it. */
body:not(.is-editing) .app-header.app-header--editor-bar .editor-bar-status {
  color: #fff;
}

/* ---------------------------------------------------------------------
   Shared /admin/ screen primitives (header, empty state, form fields).
   VDS has no page-header, empty-state or form-associated field component,
   so these are the one portal composition every agency screen reuses
   instead of each screen styling its own.
   --------------------------------------------------------------------- */
.agency-page-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--vds-spacing-16);
  margin-bottom: var(--vds-spacing-24);
}

.agency-page-header__intro {
  flex: 1 1 20rem;
  min-width: 0;
}

.agency-page-header h1 {
  margin: 0 0 var(--vds-spacing-8);
  font-size: var(--vds-font-size-heading-sm);
  font-weight: var(--vds-font-weight-semibold);
}

.agency-page-header p {
  margin-block: 0;
  color: var(--vds-fg-tertiary);
}

.agency-page-header p {
  max-width: 44rem;
}

.agency-page-header__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.agency-empty-state {
  padding: var(--vds-spacing-32) var(--vds-spacing-24);
  color: var(--vds-fg-secondary);
  text-align: center;
}

.agency-empty-state--bordered {
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.agency-empty-state h2 {
  margin: 0 0 var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-lg);
  font-weight: var(--vds-font-weight-semibold);
}

.agency-empty-state p {
  max-width: 36rem;
  margin: 0 auto var(--vds-spacing-16);
}

.agency-empty-state p:last-child {
  margin-bottom: 0;
}

.agency-form-section {
  min-width: 0; /* fieldsets default to min-content; let wide children scroll */
  margin: 0 0 var(--vds-spacing-24);
  padding: 0;
  border: 0;
}

.agency-form-section > legend {
  margin-bottom: var(--vds-spacing-8);
  padding: 0;
  font-size: var(--vds-font-size-body-lg);
  font-weight: var(--vds-font-weight-semibold);
}


.agency-form-section .agency-form-field:last-child {
  margin-bottom: 0;
}

.agency-form-field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin-bottom: var(--vds-spacing-16);
}

.agency-form-field__label {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
}

.agency-form-field__label > label {
  font-weight: var(--vds-font-weight-semibold);
}

.agency-form-field__label .field-help {
  margin-left: 0;
}

/* Matches VDS's own required marker (.vds-input-field-label-required). */
.agency-form-field__required {
  margin-left: 2px;
  color: var(--vds-fg-error-on-low);
  font-weight: var(--vds-font-weight-semibold);
}

.agency-form-required-note {
  margin: 0 0 var(--vds-spacing-16);
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-sm);
}

.agency-form-field :is(input:not([type="file"], [type="checkbox"], [type="radio"]), textarea) {
  box-sizing: border-box;
  width: 100%;
  min-height: 2.25rem;
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.agency-form-field textarea {
  padding-block: var(--vds-spacing-8);
  resize: vertical;
}

.agency-form-field input[type="file"] {
  max-width: 100%;
  color: var(--vds-fg-secondary);
  font: inherit;
}

/* Native selects with a VDS-style chevron and room for it (the UA arrow
   sat flush against the border). */
.agency-form-field select {
  width: 100%;
}

.agency-form-field select,
select.agency-select {
  box-sizing: border-box;
  min-height: 2.25rem;
  padding: var(--vds-spacing-4) var(--vds-spacing-40) var(--vds-spacing-4) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background-color: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='m4 6 4 4 4-4' stroke='%23737987' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--vds-spacing-12) center;
  background-size: 16px;
  cursor: pointer;
}

/* File drop zone. Without JS (no --enhanced) the native picker shows. */
.agency-file-drop {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
}

.agency-file-drop__prompt,
.agency-file-drop__name {
  display: none;
}

.agency-file-drop--enhanced {
  align-items: center;
  padding: var(--vds-spacing-24) var(--vds-spacing-16);
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  text-align: center;
}

.agency-file-drop--enhanced input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
}

.agency-file-drop--enhanced .agency-file-drop__prompt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--vds-spacing-8);
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.agency-file-drop__prompt svg {
  width: 28px;
  height: 28px;
  color: var(--vds-fg-tertiary);
}

.agency-file-drop--enhanced .agency-file-drop__name {
  display: block;
  max-width: 100%;
  overflow: hidden;
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.agency-file-drop__choose {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  padding: 0 var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
}

.agency-file-drop--enhanced:hover .agency-file-drop__choose {
  background: var(--vds-bg-secondary);
}

.agency-file-drop--has-file {
  border-style: solid;
}

.agency-file-drop--has-file .agency-file-drop__name {
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
}

.agency-file-drop--dragover {
  border-color: var(--vds-fg-brand-on-low);
  background: var(--vds-bg-brand-light);
}

.agency-file-drop--enhanced:has(input:focus-visible) {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.agency-form-field--error .agency-file-drop--enhanced {
  border-color: var(--vds-border-error);
}

.agency-form-field--error :is(input, select, textarea) {
  border-color: var(--vds-border-error);
}

.agency-form-field .helptext,
.agency-form-section > .helptext {
  display: block;
  margin: 0;
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-sm);
}

.agency-form-section > .helptext {
  margin-bottom: var(--vds-spacing-12);
}

.agency-form-field ul.errorlist,
.agency-form-errors ul.errorlist {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--vds-fg-error-on-low);
  font-size: var(--vds-font-size-body-sm);
}

.agency-form-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--vds-spacing-8);
}

.agency-form-actions--start {
  justify-content: flex-start;
}

/* vds-modal's internal <dialog> keeps the UA `color: CanvasText` (black
   without a dark color-scheme), and slotted content inherits from it, so
   dark mode rendered titles/labels near-black. Colour the slotted children. */
vds-modal > * {
  color: var(--vds-fg-primary);
}


/* .version-preview-banner (COMT-219, a COMT-218 follow-up): shown
   instead of .publish-controls in templates/base.html's own header
   when a manager has picked an old version from the version-history
   dropdown — rendered by public_hub_page_version_preview/
   public_transparency_page_version_preview via
   templates/portal/_version_preview_banner.html, mirroring
   .publish-controls' own layout so the header doesn't visibly jump
   between the two states. */
.version-preview-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.version-preview-banner__meta {
  margin: 0;
  color: #fff;
  font-size: 0.8125rem;
}

.version-preview-banner__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.version-preview-banner__form {
  display: inline-flex;
  margin: 0;
}

.version-preview__meta {
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-sm);
}

/* Agency version preview header: back link, title and meta stack in one
   column; actions wrap under it instead of overflowing on narrow screens. */
.version-preview__header {
  flex-wrap: wrap;
  margin-bottom: var(--vds-spacing-24);
}

.version-preview__header .page-header__title {
  flex: 1 1 16rem;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--vds-spacing-4);
  min-width: 0;
}

.version-preview__header .page-header__title p {
  margin: 0;
}

.version-preview__header .page-header__actions {
  flex-wrap: wrap;
  flex-shrink: 1;
}

.version-preview__header form {
  display: inline-flex;
  margin: 0;
}

.version-preview__back {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  margin-bottom: var(--vds-spacing-8);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  text-decoration: none;
}

.version-preview__back:hover {
  color: var(--vds-fg-primary);
  text-decoration: underline;
}

.version-preview__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8) var(--vds-spacing-16);
  padding-bottom: var(--vds-spacing-16);
  margin-bottom: var(--vds-spacing-24);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.version-preview__toolbar .language-switcher,
.version-preview__toolbar p {
  margin: 0;
}

/* .icon-select-widget (COMT-220): the JS-enhanced replacement UI
   static/js/icon-select.js builds next to (and keeps in sync with) each
   hidden `<select class="icon-select">` (block-editor "icon" field) —
   see _icon_select_widget in portal/forms.py for why a plain <select>
   can't show these icons itself. Same relative-positioned
   trigger-button + absolutely-positioned listbox shape as
   .app-user-menu above. */
.icon-select-widget {
  position: relative;
  display: inline-block;
}

.icon-select-widget__trigger {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  min-width: 12rem;
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  font: inherit;
  cursor: pointer;
}

.icon-select-widget__trigger:hover {
  background: var(--vds-border-base);
}

.icon-select-widget__label {
  flex: 1;
  text-align: left;
}

.icon-select-widget__listbox {
  position: absolute;
  top: calc(100% + var(--vds-spacing-4));
  left: 0;
  z-index: 10;
  min-width: 100%;
  max-height: 16rem;
  overflow-y: auto;
  margin: 0;
  padding: var(--vds-spacing-4);
  list-style: none;
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  box-shadow: var(--vds-shadow-md);
}

.icon-select-widget__option {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  cursor: pointer;
}

.icon-select-widget__option:hover,
.icon-select-widget__option--active {
  background: var(--vds-border-base);
}

.icon-select-widget__option--selected {
  font-weight: var(--vds-font-weight-semibold);
}

.icon-select-widget__swatch--none {
  display: inline-block;
  width: 1rem;
  height: 1rem;
}

.form-error {
  color: var(--vds-fg-error-on-low);
  font-weight: var(--vds-font-weight-semibold);
}

.messagelist {
  color: var(--vds-fg-success-on-low, var(--vds-fg-primary));
  font-weight: var(--vds-font-weight-semibold);
}

.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;
}

/* Widget Library (COMT-2/71) — mechanism-proving styles only; real visual
   treatment is deferred to COMT-61. */
.widget {
  /* COMT-267: matches the core page background (`body`'s own
     --vds-bg-primary, now that body no longer regresses to secondary —
     see body's own comment above) rather than the secondary/card token
     every other "card" surface (linked-card, admin panels, etc.) uses —
     widgets are meant to sit flush with the page. */
  background: var(--vds-bg-primary);
  /* Border temporarily disabled site-wide (COMT-267 follow-up) — with
     `.widget`'s background now matching the page background above,
     there's no design decision yet on whether/when a widget should show
     a border at all (vs. a shadow, a divider, nothing) now that it's no
     longer the only thing distinguishing a widget from its page. The
     per-block `bordered` field/toggle below stays wired end-to-end
     (model, publish freeze, admin, fixture) so flipping this back on
     — unconditionally or via that per-block flag — is a one-line change
     once that's decided, not a re-plumbing job. */
  border: none;
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-16);
  /* Bugfix (COMT-199 follow-up): six of the eight widget types
     (line-trend, bar-trend, pie-breakdown, ranked-bar, sequential-bar,
     stacked-bar) render as a real <figure> element, which every browser
     ships with a default `margin: 1em 40px` UA stylesheet rule. Nothing
     here previously overrode that, so those widgets rendered ~40px
     narrower on each side than a same-row/same-column_width kpi-card
     (a <div>, which has no such UA default) — visibly not reaching the
     same left/right edges as the row of cards above them despite both
     being column_width: full. Zeroing it here (not per-widget) fixes it
     for every current and future figure-based widget in one place.
  */
  margin: 0;
  /* Lets kpi-card's value scale off the CARD's own width (COMT-199) via
     cqw, not the full page viewport (vw) — a 1/4-width card and a
     full-width one shouldn't read the same size off a vw-based clamp,
     and the community-trust ux-prototype's own 4vw was almost
     certainly scaled against its own narrower dashboard column, not a
     bare browser viewport (where it just pins at its clamp max). */
  container-type: inline-size;
}

/* On the live page a chart widget is white on the white page, so its 16px side
   padding only insets the chart from the section's heading and divider. Drop it
   there so charts span the full section width; the editor canvas and themed
   (hero) sections keep the padded card look. */
body:not(.is-editing) .content-section:not(.content-section--hero) :is(
  .widget--line-trend,
  .widget--bar,
  .widget--pie-breakdown,
  .widget--heatmap-grid
) {
  padding-inline: 0;
}

/* COMT-267: per-block "bordered" toggle — set on the wrapper
   (`.content-block__widget`, in `_content_block.html`) rather than the
   `.widget` element itself, since it's a block-instance decision
   (`ContentBlockBase.bordered`), not a widget-type default. Currently a
   no-op (`.widget` has no border to remove, see its own comment above)
   until the border-disable above is reverted — kept in place, not
   deleted, so the field/wiring stays exercised and ready. Only un-sets
   the border/radius; background and padding stay so a borderless widget
   still reads as a distinct block, just without the visible outline.
*/
.content-block__widget--no-border .widget {
  border: none;
  border-radius: 0;
}

/* Bold title for every figure-based chart widget (line-trend, bar-trend,
   pie-breakdown, ranked-bar, sequential-bar, stacked-bar) — matches
   kpi-card's own `.widget-kpi-card__label` bold treatment (COMT-199) so
   a widget's title reads consistently regardless of which widget type
   it is. `.widget > figcaption` (not a bare `figcaption` selector) scopes
   this to a widget's own top-level caption, not any figcaption nested
   inside widget content (there is none today, but this is the safer
   selector). */
.widget > figcaption {
  font-weight: var(--vds-font-weight-semibold);
  margin-bottom: var(--vds-spacing-8);
}

/* Visible subtitle line (COMT-199 follow-up) under a figure-based
   widget's bold title — see templates/widgets/_widget_caption.html for
   why this reuses the same help_text value as the title's own (?)
   tooltip instead of a dedicated field. Explicitly un-bolds and resizes
   down from the figcaption's own bold/default size, matching the
   community-trust ux-prototype's own lighter-weight description line
   under a chart's title. Rich HTML blocks also drop their own margins to
   keep the line tight under the title. */
.widget__subtitle p,
.widget-flip-card__definition p {
  margin: 0;
}

.widget__subtitle {
  margin: var(--vds-spacing-4) 0 0;
  font-weight: var(--vds-font-weight-regular, normal);
  font-size: var(--vds-font-size-body-sm);
  color: var(--vds-fg-secondary, var(--vds-fg-primary));
}

.widget--invalid {
  /* Explicit border (not just border-color) — COMT-267's border-disable
     above zeroed `.widget`'s own border entirely, so a bare border-color
     override here would no longer render anything. An invalid widget
     still needs a visible error affordance regardless of that
     undecided, ordinary-widget-border question — this is a distinct,
     always-on error state, not the same "optional" border. */
  border: var(--vds-border-width-1) solid var(--vds-border-error);
  color: var(--vds-fg-error-on-low);
}

/* Flip-card layout (COMT-198) — an alternate presentation for a widget's
   optional help_text (COMT-126): a 3D flip to a "Metric definition" back
   face instead of a hover tooltip, matching the community-trust
   ux-prototype's KPI card. `.widget--flippable` is only added by the
   widget's own template when help_text is present, so this is a no-op
   addition to `.widget`'s existing box model otherwise — not a second
   nested box. Front/back share the same CSS-grid cell (`grid-area: 1/1`)
   rather than a `position: absolute` overlay, so the card's height is
   whichever face is taller, without either face needing a fixed/guessed
   height.
*/
.widget--flippable {
  perspective: 900px;
}

.widget-flip-card__inner {
  display: grid;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.3s ease;
}

.widget--flippable.is-flipped .widget-flip-card__inner {
  transform: rotateY(180deg);
}

.widget-flip-card__face {
  grid-area: 1 / 1;
  min-width: 0;
  backface-visibility: hidden;
}

.widget-flip-card__face--back {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  transform: rotateY(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .widget-flip-card__inner {
    transition: none;
  }
}

.widget-flip-card__trigger,
.widget-flip-card__back-trigger {
  border: 0;
  background: none;
  color: var(--vds-fg-secondary, currentColor);
  cursor: pointer;
  font: inherit;
  padding: 0;
}

.widget-flip-card__trigger {
  display: inline-flex;
  margin-inline-start: var(--vds-spacing-8);
  vertical-align: middle;
}

.widget-flip-card__back-label {
  color: var(--vds-fg-secondary, currentColor);
  font-size: var(--vds-font-size-body-sm, 0.875rem);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.widget-flip-card__definition {
  flex: 1 0 auto;
  margin: 0;
  font-size: var(--vds-font-size-body-sm, 0.875rem);
}

.widget-flip-card__back-trigger {
  align-self: flex-start;
  font-size: var(--vds-font-size-body-sm, 0.875rem);
  text-decoration: underline;
}

/* KPI card chrome — evolves toward the community-trust ux-prototype's KPI
   card: an outlined, softly-rounded card whose padding lives on the body
   and footer rather than on the card itself, so the footer band can run
   edge to edge. Scoped to `.widget--kpi-card`
   rather than `.widget` — the site-wide widget border stays disabled
   (COMT-267 follow-up) for every other widget type. */
.widget--kpi-card {
  padding: 0;
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-12);
  box-shadow: var(--vds-shadow-xs);
}

.widget--kpi-card .widget-flip-card__face--front {
  display: flex;
  flex-direction: column;
}

.widget--kpi-card .widget-flip-card__face--back {
  padding: var(--vds-spacing-20, 1.25rem);
}

.widget-kpi-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: var(--vds-spacing-20, 1.25rem) var(--vds-spacing-20, 1.25rem) var(--vds-spacing-16);
}

.widget-kpi-card__value {
  /* Scales with the card's own width (cqw, COMT-199), not the page's
     viewport (vw) — see .widget's container-type above for why. */
  font-size: clamp(2rem, 14cqw, 2.625rem);
  font-weight: var(--vds-font-weight-bold, 700);
  letter-spacing: -0.04em;
  line-height: 1;
  margin: 0;
}

/* Zeroes the browser's default <p> margin (COMT-199 bugfix) — left
   unset, that ~1em top margin stacked on top of the body's own padding,
   visibly doubling the gap above the label compared to every other
   widget. The label row reserves two lines (min-height) so values line
   up across a row of cards even when one label wraps. */
.widget-kpi-card__label {
  display: flex;
  align-items: flex-start;
  gap: var(--vds-spacing-4);
  min-height: 2.7em;
  margin: 0 0 var(--vds-spacing-8);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.35;
}

.widget-kpi-card__label .widget-flip-card__trigger {
  flex: none;
  margin-inline-start: 0;
  margin-top: 0.1em;
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
}

.widget-kpi-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--vds-spacing-4) var(--vds-spacing-8);
  min-height: 2.75rem;
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-20, 1.25rem);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-secondary);
  font-size: var(--vds-font-size-body-sm, 0.875rem);
}

.widget-kpi-card__change {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  font-weight: var(--vds-font-weight-semibold);
}

.widget-kpi-card__arrow {
  font-size: 0.9em;
}

.widget-kpi-card__baseline {
  color: var(--vds-fg-secondary);
  white-space: nowrap;
}

/* ECharts reads its container's computed size at init time (COMT-122's
   gallery bug report applies equally here) — without an explicit height
   a bare <div> renders at 0x0, silently showing nothing. Sized to the
   card's own content width (COMT-199 follow-up bugfix — an earlier pass
   bled this all the way to the card's outer edges via a negative
   margin, matching kpi-card's own sparkline treatment, but that read as
   an overcorrection compared to the community-trust ux-prototype's own
   reference, which keeps a modest left/right pad around its chart, not
   a full edge-to-edge bleed). `_trend_grid()` (portal/widget_catalogue/
   _shared.py) is what actually controls how close the plotted lines/
   bars get to this container's own edges. Taller than before
   (22rem -> 26rem) for more vertical breathing room in the card. */
.widget-line-trend__chart {
  width: 100%;
  height: 26rem;
}

.widget-pie-breakdown__chart {
  width: 100%;
  height: 18rem;
}

/* The one bar chart (templates/widgets/bar.html). prepareBarOption
   (static/js/echarts-widget.js) sets the real height from the card's width
   and row count; this is only the size before it runs. */
.widget-bar__chart {
  width: 100%;
  height: 14rem;
}

/* HTML legend above the plot: wraps with a narrow card instead of
   overlapping the bars, left-aligned, in stack order with "Other" last. */
.widget-bar__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-4) var(--vds-spacing-16);
  margin: var(--vds-spacing-8) 0 0;
  padding: 0;
  list-style: none;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm, 0.875rem);
}

.widget-bar__legend[hidden] {
  display: none;
}

.widget-bar__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
}

.widget-bar__swatch {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 2px;
  background: currentColor;
}

/* ECharts needs an explicit height on the heatmap container just like
   the other chart widgets; without it the bare div collapses to 0px and
   the valid weekday/hour payload renders invisibly. */
.widget-heatmap-grid__chart {
  width: 100%;
  height: 20rem;
}

.widget-heatmap-grid__legend {
  display: flex;
  align-items: center;
  gap: 3px;
  margin-top: 16px;
  color: var(--vds-fg-secondary);
  font-size: 12px;
}

.widget-heatmap-grid__legend[hidden] {
  display: none;
}

.widget-heatmap-grid__legend span:first-child {
  margin-right: 6px;
}

.widget-heatmap-grid__legend span:nth-last-child(2) {
  margin-left: 6px;
}

.widget-heatmap-grid__legend i {
  width: 24px;
  height: 7px;
}

/* Phone-width charts: a chart widget on a single-column page loses the
   page's 1.5rem gutter plus the widget's own padding on each side, which
   leaves too little room for the chart itself. Let just the chart
   (not the caption/toggle) bleed out to the main content edge. */
@container page (max-width: 480px) {
  .widget[data-chart-table-widget] [data-widget-chart] {
    width: auto;
    margin-inline: calc(-1 * (var(--vds-spacing-16) + 1.5rem));
  }
}

/* Flight card (COMT-2/183) real visual treatment — was still on the
   COMT-71 mechanism-proving `<ul>`/`<dl>` defaults (a plain stacked list,
   one full-width card per row) until this pass. `auto-fill`/`minmax`
   (same pattern as `.topic-card-grid` above) is what lets two or more
   cards sit side by side on a wide viewport instead of always stacking
   one-per-row — the `.widget--flight-card` itself already sits inside a
   `.content-block`, so this is the *inner* flow of multiple flights
   within one widget, independent of that Block's own `column_width`
   (static/css/app.css's `.content-block-row`/`--width-*`), which
   controls this whole widget's placement alongside *other* Blocks. */
.widget-flight-card__label {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  margin: 0;
  font-size: inherit;
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.35;
}

.widget-flight-card__count {
  margin: var(--vds-spacing-4) 0 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.widget-flight-card__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: var(--vds-spacing-24);
  list-style: none;
  margin: var(--vds-spacing-16) 0 0;
  padding: 0;
}

.widget-flight-card__card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-12);
  box-shadow: var(--vds-shadow-sm);
}

.widget-flight-card__media {
  position: relative;
  line-height: 0;
}

.widget-flight-card__map {
  display: block;
  width: 100%;
  height: auto;
}

/* Same footprint as a map image, so a withheld or missing map keeps the
   card's shape and the type badge (top-left) never overlaps the text. */
.widget-flight-card__map-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 505 / 290;
  margin: 0;
  padding: var(--vds-spacing-16);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  line-height: 1.4;
  text-align: center;
}

/* Not one of COMT-184's four suppressible fields, so no "withheld"
   placeholder is needed here — see this template's own top comment. */
.widget-flight-card__badge {
  position: absolute;
  top: var(--vds-spacing-12);
  left: var(--vds-spacing-12);
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
  /* The media wrapper's `line-height: 0` (no gap under the image) would
     otherwise collapse this pill around its text. */
  line-height: 1.5;
  border-radius: var(--vds-radius-full);
  box-shadow: var(--vds-shadow-xs);
}

.widget-flight-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-16);
}

.widget-flight-card__timestamp {
  margin: 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* Explicit color here (and on .widget-flight-card__fields dd below)
   rather than inheriting body's ambient agency-branded text color
   (COMT-159's `body { color: var(--agency-primary-fg, ...) }`) — a
   card built to present raw flight data reads better as neutral,
   VDS-toned text than tinted by whatever color an agency happened to
   pick for their own brand accent, Primary or otherwise. */
.widget-flight-card__reference {
  margin: 0 0 var(--vds-spacing-8);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-heading-xs);
  font-weight: var(--vds-font-weight-semibold);
}

.widget-flight-card__fields {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--vds-spacing-12) var(--vds-spacing-16);
  margin: 0;
  padding-top: var(--vds-spacing-12);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.widget-flight-card__field {
  min-width: 0;
}

.widget-flight-card__field--full {
  grid-column: 1 / -1;
}

.widget-flight-card__fields dt {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-medium);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.widget-flight-card__fields dd {
  margin: 0;
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  overflow-wrap: anywhere;
}

/* Prev/Next are `.btn--secondary.btn--sm` (native htmx buttons); this only
   lays them out around the page status. */
.widget-flight-card__pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-12);
  margin-top: var(--vds-spacing-24);
  padding-top: var(--vds-spacing-16);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.widget-flight-card__page-status {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-variant-numeric: tabular-nums;
}

/* Date range filter (COMT-186) real visual treatment — was still on the
   COMT-71 mechanism-proving default (a bare unstyled <form>, its own
   Start/End/Update controls stacked full-width one per line) until this
   pass. A real `.widget` surface (COMT-2's shared card look, already
   used by every other widget on this page) instead of the plain
   `<form>` edge-to-edge, with its three controls in one row on wide
   viewports — wrapping only when the two date fields plus the button
   can't fit side by side (narrow/mobile). */
.widget--date-range-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--vds-spacing-16);
}

/* Section header: heading and description on the left, the section's filters
   as pill controls on the right, then a divider above the content (UX
   prototype `.rms-dashboard-section__heading` / `.rms-dashboard-controls`).
   Hero and themed sections keep their own heading look; only plain sections
   get the compact heading scale and the divider. */
.content-section__header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-16) var(--vds-spacing-24);
}

.content-section__heading {
  flex: 1 1 24rem;
  min-width: 0;
}

/* With filters, sit them on the description's row (just above the divider)
   rather than floating level with the title. */
.content-section__header--with-filters {
  align-items: flex-end;
}

/* A plain section sits right under the tab strip rule (or the previous
   section's divider): give its heading room to breathe. */
.content-section:not(.content-section--hero):not(.content-section--site-header) .content-section__header {
  padding-top: var(--vds-spacing-24);
}

.content-section:not(.content-section--hero) .content-section__heading h2 {
  margin: 0;
  font-size: var(--vds-font-size-heading-xs, 1.25rem);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.25;
}

.content-section:not(.content-section--hero) .content-section__heading p:not(.content-section__eyebrow) {
  margin: 2px 0 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--vds-fg-secondary);
}

.content-section:not(.content-section--hero) .content-section__heading .content-section__eyebrow {
  margin-bottom: 2px;
}

/* Block titles/descriptions in plain sections follow the same scale as the
   section heading above. Direct children only, so rich text authored inside
   a body keeps its own headings. */
.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > h2 {
  margin: 0;
  font-size: var(--vds-font-size-heading-xs, 1.25rem);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.25;
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text {
  margin-top: 2px;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--vds-fg-secondary);
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text > :first-child {
  margin-top: 0;
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text > :last-child {
  margin-bottom: 0;
}

/* A hoisted filter is just the pill; its block title/description are editor labels. */
.content-section__filters .content-block__body > h2,
.content-section__filters .content-block__body > .content-block__text {
  display: none;
}

.content-section__filters {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-left: auto;
  margin-bottom: 0;
}

.content-section__filters .content-block {
  width: auto;
  flex: none;
  margin: 0;
  padding: 0;
  background: none;
}

.content-section__filters .move-trigger {
  display: none;
}

.content-section__divider {
  margin: var(--vds-spacing-16) 0 var(--vds-spacing-24);
  border: 0;
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* Section filters (UX prototype `.rms-dashboard-controls`): one toolbar of pills,
   right-aligned beside the heading on wide screens. A pill reads "Label: value" and
   turns brand-coloured once its value differs from the default (`is-active`). */
.section-filters {
  --section-filter-mobile: 0;
  box-sizing: border-box;
}

.section-filters__body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--vds-spacing-8);
}

.section-filters__toggle,
.section-filters__apply,
.section-filters__retry {
  display: none;
}

.section-filters__status {
  font-size: var(--vds-font-size-body-sm);
  color: var(--vds-fg-secondary);
}

.section-filters > .section-filters__status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.has-section-filter-sheet {
  overflow: hidden;
}

body.has-section-filter-sheet .app-shell .app-main {
  overflow-y: hidden;
}

.section-filter-sheet,
.tab-list-sheet {
  box-sizing: border-box;
  width: var(--section-filter-sheet-width, min(100% - 2rem, 32rem));
  max-width: calc(100% - 2rem);
  max-height: calc(100dvh - 2rem);
  margin: auto auto 0;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8) var(--vds-radius-8) 0 0;
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--vds-shadow-md);
  overflow-y: auto;
}

.section-filter-sheet::backdrop,
.tab-list-sheet::backdrop {
  background: rgb(0 0 0 / 50%);
}

.section-filter-sheet__header,
.tab-list-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-12);
  margin-bottom: var(--vds-spacing-16);
}

.section-filter-sheet__header h2,
.tab-list-sheet__header h2 {
  margin: 0;
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.section-filter-sheet__close,
.tab-list-sheet__close {
  min-height: 44px;
}

/* "All tabs" (tab-strip.js): one native button per tab, the current one marked. */
.tab-list-sheet__list {
  display: flex;
  flex-direction: column;
}

.tab-list-sheet__item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 46px;
  padding: 0 var(--vds-spacing-4);
  border: 0;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: none;
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  text-align: left;
  cursor: pointer;
}

.tab-list-sheet__item[aria-current="true"] {
  color: var(--agency-accent, var(--vds-fg-brand-on-low));
  font-weight: var(--vds-font-weight-semibold);
}

.tab-list-sheet__item[aria-current="true"]::after {
  content: "✓";
  margin-left: auto;
}

.tab-list-sheet__item:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: -2px;
}

.mobile-section-summary[hidden] {
  display: none !important;
}

.section-filters__reset,
.section-filter__clear {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: 0;
  background: transparent;
  color: var(--vds-fg-brand-on-low, var(--vds-bg-brand-primary));
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
  text-decoration: none;
}

.section-filters__reset:hover,
.section-filter__clear:hover {
  text-decoration: underline;
}

.section-filters__reset--default {
  display: none;
}

.section-filters__reset:focus-visible,
.section-filter__clear:focus-visible,
.section-filters__toggle:focus-visible {
  outline: 2px solid var(--vds-bg-brand-primary);
  outline-offset: 2px;
}

.content-section__content[aria-busy="true"] {
  opacity: 0.55;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

/* A filter pill: "Label: value" plus a chevron, opening a popover menu. */
.section-filter {
  position: relative;
  display: block;
  padding: 0;
  background: none;
  container-type: normal;
}

.section-filter__details {
  position: relative;
}

.section-filter__summary,
.section-filter--select {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  box-sizing: border-box;
  min-height: 36px;
  padding: 0 var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: 7px;
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-xs);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}

.section-filter__summary::-webkit-details-marker {
  display: none;
}

.section-filter__summary--static {
  cursor: default;
}

.section-filter__summary:hover,
.section-filter--select:hover {
  border-color: var(--vds-fg-secondary);
}

.section-filter__details[open] > .section-filter__summary,
.section-filter.is-active > .section-filter__details > .section-filter__summary,
.section-filter--select.is-active {
  border-color: var(--vds-bg-brand-primary);
  background: var(--vds-bg-brand-light);
}

.section-filter__summary:focus-visible,
.section-filter--select:focus-within {
  outline: none;
  box-shadow: 0 0 0 3px var(--vds-bg-brand-light), 0 0 0 1px var(--vds-bg-brand-primary);
}

.section-filter__label {
  color: var(--vds-fg-secondary);
}

.section-filter__value {
  font-weight: var(--vds-font-weight-semibold);
}

.section-filter__select {
  field-sizing: content;
  min-height: 34px;
  max-width: 100%;
  padding: 0 var(--vds-spacing-24) 0 0;
  appearance: none;
  border: 0;
  background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='m4 6 4 4 4-4' stroke='%23536172' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 0 center;
  color: var(--vds-fg-primary);
  font: inherit;
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.section-filter__select:focus-visible {
  outline: none;
}

.section-filter__select:disabled {
  color: var(--vds-fg-secondary);
  cursor: not-allowed;
}

.section-filter__count {
  display: inline-grid;
  min-width: 18px;
  height: 18px;
  place-items: center;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--vds-bg-brand-primary);
  color: var(--vds-fg-white, #fff);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1;
}

.section-filter__menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + var(--vds-spacing-4));
  right: 0;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-12);
  box-sizing: border-box;
  min-width: 16rem;
  max-width: min(20rem, calc(100vw - 24px));
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-md);
}

.section-filter__menu--list {
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
}

.section-filter__options {
  display: flex;
  flex-direction: column;
  max-height: 280px;
  overflow-y: auto;
}

.section-filter__option {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  min-height: 36px;
  padding: 6px 10px;
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

.section-filter__option:hover {
  background: var(--vds-bg-brand-light);
}

.section-filter__option input {
  accent-color: var(--vds-bg-brand-primary);
}

.section-filter__menu-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding: 0 var(--vds-spacing-4);
}

@media (pointer: coarse) {
  .section-filter__summary,
  .section-filter--select,
  .section-filter__option,
  .section-filters__reset,
  .section-filter__clear {
    min-height: 44px;
  }

  .section-filter__select {
    min-height: 42px;
  }
}

/* Tablet and phone: the heading stacks above its filters, which wrap from the left. */
@container page (max-width: 688px) {
  .content-section__header {
    flex-direction: column;
  }

  .content-section__heading {
    flex: 0 0 auto;
  }

  .content-section__filters,
  .section-filters__body {
    width: 100%;
    margin-left: 0;
    justify-content: flex-start;
  }

  .section-filter__menu {
    right: auto;
    left: 0;
  }
}

/* One real form: progressive enhancement moves its body into a native modal
   on narrow pages/previews. Without JS the controls and submit remain visible. */
@container page (max-width: 735px) {
  .content-section__header:has(> .section-filters--collapsible) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--vds-spacing-12);
  }

  .content-section__header:has(> .section-filters--collapsible) .content-section__heading {
    flex: 0 0 auto;
  }

  .content-section__header:has(> .section-filters--collapsible) .content-section__heading:not(:has(> *)) {
    display: none;
  }

  .section-filters--collapsible {
    --section-filter-mobile: 1;
  }

  .section-filters--collapsible.section-filters--enhanced {
    display: contents;
  }

  .section-filters--enhanced .section-filters__toggle {
    display: inline-flex;
    flex: 0 0 auto;
    align-self: flex-end;
    align-items: center;
    gap: var(--vds-spacing-8);
    box-sizing: border-box;
    min-height: 44px;
    padding: 0 var(--vds-spacing-12);
    border: var(--vds-border-width-1) solid var(--vds-border-base);
    border-radius: 7px;
    background: var(--vds-bg-primary);
    box-shadow: var(--vds-shadow-xs);
    color: var(--vds-fg-primary);
    font: inherit;
    font-size: var(--vds-font-size-body-sm);
    font-weight: var(--vds-font-weight-semibold);
    cursor: pointer;
  }

  .section-filters--collapsible.section-filters--active .section-filters__toggle,
  .section-filters__toggle[aria-expanded="true"] {
    border-color: var(--vds-bg-brand-primary);
    background: var(--vds-bg-brand-light);
  }

  .section-filters--enhanced > .section-filters__body {
    display: none;
  }

  .section-filters--collapsible .section-filters__body {
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
    gap: var(--vds-spacing-12);
  }

  .section-filters--collapsible .section-filter {
    width: 100%;
  }

  .section-filters--collapsible .section-filter__summary,
  .section-filters--collapsible .section-filter--select {
    display: flex;
    min-height: 44px;
    width: 100%;
    box-sizing: border-box;
    white-space: normal;
  }

  .section-filters--collapsible .section-filter__summary vds-icon {
    margin-left: auto;
  }

  .section-filters--collapsible .section-filter__select {
    flex: 1;
    min-width: 0;
    min-height: 44px;
    font-size: 1rem;
  }

  .section-filters--collapsible .section-filter__menu {
    position: static;
    min-width: 0;
    max-width: none;
    margin-top: var(--vds-spacing-4);
    box-shadow: none;
  }

  .section-filters--collapsible .section-filters__apply {
    display: inline-flex;
    justify-content: center;
  }

  .section-filters--enhanced .section-filters__apply,
  .section-filters--enhanced .widget-date-range-filter__submit {
    display: none;
  }

  .section-filters--collapsible .section-filter__option,
  .section-filters--collapsible .section-filter__clear,
  .section-filters--collapsible .section-filters__reset {
    min-height: 44px;
  }

  .section-filters--collapsible .section-filters__reset--default {
    display: inline-flex;
  }

  .section-filters--collapsible .section-filters__retry:not([hidden]) {
    display: inline-flex;
    justify-content: center;
    min-height: 44px;
  }

  .section-filters--collapsible input[type="date"] {
    min-height: 44px;
    font-size: 1rem;
  }
}

/* Phone section strip (section-filters.js): pinned under the tab row, full
   bleed like it, naming the current section (nothing when it is unnamed),
   its applied filters, and a filter trigger. */
.mobile-section-summary {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  position: fixed;
  z-index: 16;
  box-sizing: border-box;
  min-height: 48px;
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  box-shadow: var(--vds-shadow-xs);
}

.mobile-section-summary__trigger {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
  margin-left: auto;
  min-width: 44px;
  height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  cursor: pointer;
}

/* 44px-tall touch target around the visual control. */
.mobile-section-summary__trigger::after {
  content: "";
  position: absolute;
  inset: -4px 0;
}

.mobile-section-summary__trigger.is-active {
  border-color: var(--agency-accent-bg, var(--vds-bg-brand-primary));
  background: var(--vds-bg-brand-light);
}

.mobile-section-summary__trigger:focus-visible {
  outline: 2px solid var(--agency-accent-bg, var(--vds-bg-brand-primary));
  outline-offset: 2px;
}

.mobile-section-summary__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  box-sizing: border-box;
  border-radius: 9px;
  background: var(--agency-accent-bg, var(--vds-bg-brand-primary));
  color: var(--agency-accent-fg, var(--vds-fg-white, #fff));
  font-size: 0.75rem;
  font-weight: var(--vds-font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.mobile-section-summary__title {
  flex: 0 1 auto;
  min-width: 0;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mobile-section-summary:has(vds-tag) .mobile-section-summary__title {
  max-width: 40%;
}

.mobile-section-summary__tags {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  mask-image: linear-gradient(to right, #000 calc(100% - var(--vds-spacing-8)), transparent);
}

.mobile-section-summary__tags:empty {
  display: none;
}

.mobile-section-summary__tags vds-tag {
  --vds-spacing-8: var(--vds-spacing-4);
  --vds-font-size-body-xs: 0.75rem;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Section sidecar: the editable list of a Section's filters. */
.section-filters-editor__list {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  margin: 0 0 var(--vds-spacing-8);
  padding: 0;
  list-style: none;
}

.section-filter-row {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
}

.section-filter-row__main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--vds-spacing-8);
}

.section-filter-row__main > .section-filter-row__field:nth-of-type(2) {
  grid-column: 1 / -1;
  grid-row: 2;
}

.section-filter-row__main > .section-filter-row__actions {
  grid-column: 2;
  grid-row: 1;
}

.section-filter-row [hidden] {
  display: none;
}

.section-filter-row__field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  font-size: var(--vds-font-size-body-sm);
}

.section-filter-row__field > span {
  color: var(--vds-fg-secondary);
}

.section-filter-row__field select,
.section-filter-row__field input {
  box-sizing: border-box;
  width: 100%;
  min-height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.section-filter-row__actions {
  display: flex;
  gap: 2px;
}

.section-filter-row__icon {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 36px;
  border: 0;
  border-radius: var(--vds-radius-4, 4px);
  background: transparent;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.section-filter-row__icon:hover:not(:disabled) {
  background: var(--vds-bg-brand-light);
  color: var(--vds-fg-primary);
}

.section-filter-row__icon:disabled {
  opacity: 0.35;
  cursor: default;
}

.section-filter-row__hint,
.section-filters-editor__empty {
  margin: 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.widget-date-range-filter__field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  flex: 1 1 12rem;
}

/* In a filter popover the fields stack in a column, so the row-oriented 12rem basis would become a height. */
.section-filter__menu .widget-date-range-filter__field {
  flex: 0 0 auto;
}

.widget-date-range-filter__field label {
  font-weight: var(--vds-font-weight-semibold);
  font-size: var(--vds-font-size-body-sm);
  color: var(--vds-fg-primary);
}

.widget-date-range-filter__field input[type="date"] {
  box-sizing: border-box;
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.widget-date-range-filter__field input[type="date"]:focus {
  outline: none;
  border-color: var(--vds-bg-brand-primary);
  box-shadow: 0 0 0 3px var(--vds-bg-brand-light);
}

/* Reuses the app's existing .btn/.btn--primary convention (static/css/app.css,
   COMT-152's edit-panel Save button etc.) rather than a one-off color, so
   this widget's primary action matches every other primary button in the
   app instead of introducing a second "primary button" look. */
.widget-date-range-filter__submit {
  flex-shrink: 0;
}

.content-section:not(.content-section--hero) .content-section__heading p:not(.content-section__eyebrow) {
  margin: 2px 0 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--vds-fg-secondary);
}

.content-section:not(.content-section--hero) .content-section__heading .content-section__eyebrow {
  margin-bottom: 2px;
}

/* Block titles/descriptions in plain sections follow the same scale as the
   section heading above. Direct children only, so rich text authored inside
   a body keeps its own headings. */
.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > h2 {
  margin: 0;
  font-size: var(--vds-font-size-heading-xs, 1.25rem);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1.25;
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text {
  margin-top: 2px;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--vds-fg-secondary);
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text > :first-child {
  margin-top: 0;
}

.content-section:not(.content-section--hero):not(.content-section--site-header) .content-block__body > .content-block__text > :last-child {
  margin-bottom: 0;
}

/* A hoisted filter is just the pill; its block title/description are editor labels. */
.content-section__filters .content-block__body > h2,
.content-section__filters .content-block__body > .content-block__text {
  display: none;
}

.content-section__filters {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-left: auto;
  margin-bottom: 0;
}

.content-section__filters .content-block {
  width: auto;
  flex: none;
  margin: 0;
  padding: 0;
  background: none;
}

.content-section__filters .move-trigger {
  display: none;
}

.content-section__divider {
  margin: var(--vds-spacing-16) 0 var(--vds-spacing-24);
  border: 0;
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* Section filters (UX prototype `.rms-dashboard-controls`): one toolbar of pills,
   right-aligned beside the heading on wide screens. A pill reads "Label: value" and
   turns brand-coloured once its value differs from the default (`is-active`). */
.section-filters {
  --section-filter-mobile: 0;
  box-sizing: border-box;
}

.section-filters__body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--vds-spacing-8);
}

.section-filters__toggle,
.section-filters__apply,
.section-filters__retry {
  display: none;
}

.section-filters__status {
  font-size: var(--vds-font-size-body-sm);
  color: var(--vds-fg-secondary);
}

.section-filters > .section-filters__status {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.has-section-filter-sheet {
  overflow: hidden;
}

body.has-section-filter-sheet .app-shell .app-main {
  overflow-y: hidden;
}

.section-filter-sheet,
.tab-list-sheet {
  box-sizing: border-box;
  width: var(--section-filter-sheet-width, min(100% - 2rem, 32rem));
  max-width: calc(100% - 2rem);
  max-height: calc(100dvh - 2rem);
  margin: auto auto 0;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8) var(--vds-radius-8) 0 0;
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--vds-shadow-md);
  overflow-y: auto;
}

.section-filter-sheet::backdrop,
.tab-list-sheet::backdrop {
  background: rgb(0 0 0 / 50%);
}

.section-filter-sheet__header,
.tab-list-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-12);
  margin-bottom: var(--vds-spacing-16);
}

.section-filter-sheet__header h2,
.tab-list-sheet__header h2 {
  margin: 0;
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.section-filter-sheet__close,
.tab-list-sheet__close {
  min-height: 44px;
}

/* "All tabs" (tab-strip.js): one native button per tab, the current one marked. */
.tab-list-sheet__list {
  display: flex;
  flex-direction: column;
}

.tab-list-sheet__item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 46px;
  padding: 0 var(--vds-spacing-4);
  border: 0;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: none;
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  text-align: left;
  cursor: pointer;
}

.tab-list-sheet__item[aria-current="true"] {
  color: var(--agency-accent, var(--vds-fg-brand-on-low));
  font-weight: var(--vds-font-weight-semibold);
}

.tab-list-sheet__item[aria-current="true"]::after {
  content: "✓";
  margin-left: auto;
}

.tab-list-sheet__item:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: -2px;
}

.mobile-section-summary[hidden] {
  display: none !important;
}

.section-filters__reset,
.section-filter__clear {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: 0;
  background: transparent;
  color: var(--vds-fg-brand-on-low, var(--vds-bg-brand-primary));
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
  text-decoration: none;
}

.section-filters__reset:hover,
.section-filter__clear:hover {
  text-decoration: underline;
}

.section-filters__reset--default {
  display: none;
}

.section-filters__reset:focus-visible,
.section-filter__clear:focus-visible,
.section-filters__toggle:focus-visible {
  outline: 2px solid var(--vds-bg-brand-primary);
  outline-offset: 2px;
}

.content-section__content[aria-busy="true"] {
  opacity: 0.55;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

/* A filter pill: "Label: value" plus a chevron, opening a popover menu. */
.section-filter {
  position: relative;
  display: block;
  padding: 0;
  background: none;
  container-type: normal;
}

.section-filter__details {
  position: relative;
}

.section-filter__summary,
.section-filter--select {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  box-sizing: border-box;
  min-height: 36px;
  padding: 0 var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: 7px;
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-xs);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}

.section-filter__summary::-webkit-details-marker {
  display: none;
}

.section-filter__summary--static {
  cursor: default;
}

.section-filter__summary:hover,
.section-filter--select:hover {
  border-color: var(--vds-fg-secondary);
}

.section-filter__details[open] > .section-filter__summary,
.section-filter.is-active > .section-filter__details > .section-filter__summary,
.section-filter--select.is-active {
  border-color: var(--vds-bg-brand-primary);
  background: var(--vds-bg-brand-light);
}

.section-filter__summary:focus-visible,
.section-filter--select:focus-within {
  outline: none;
  box-shadow: 0 0 0 3px var(--vds-bg-brand-light), 0 0 0 1px var(--vds-bg-brand-primary);
}

.section-filter__label {
  color: var(--vds-fg-secondary);
}

.section-filter__value {
  font-weight: var(--vds-font-weight-semibold);
}

.section-filter__select {
  field-sizing: content;
  min-height: 34px;
  max-width: 100%;
  padding: 0 var(--vds-spacing-24) 0 0;
  appearance: none;
  border: 0;
  background: transparent url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='m4 6 4 4 4-4' stroke='%23536172' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 0 center;
  color: var(--vds-fg-primary);
  font: inherit;
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.section-filter__select:focus-visible {
  outline: none;
}

.section-filter__select:disabled {
  color: var(--vds-fg-secondary);
  cursor: not-allowed;
}

.section-filter__count {
  display: inline-grid;
  min-width: 18px;
  height: 18px;
  place-items: center;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--vds-bg-brand-primary);
  color: var(--vds-fg-white, #fff);
  font-size: var(--vds-font-size-body-xs);
  font-weight: var(--vds-font-weight-semibold);
  line-height: 1;
}

.section-filter__menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + var(--vds-spacing-4));
  right: 0;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-12);
  box-sizing: border-box;
  min-width: 16rem;
  max-width: min(20rem, calc(100vw - 24px));
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--vds-shadow-md);
}

.section-filter__menu--list {
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
}

.section-filter__options {
  display: flex;
  flex-direction: column;
  max-height: 280px;
  overflow-y: auto;
}

.section-filter__option {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  min-height: 36px;
  padding: 6px 10px;
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

.section-filter__option:hover {
  background: var(--vds-bg-brand-light);
}

.section-filter__option input {
  accent-color: var(--vds-bg-brand-primary);
}

.section-filter__menu-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding: 0 var(--vds-spacing-4);
}

@media (pointer: coarse) {
  .section-filter__summary,
  .section-filter--select,
  .section-filter__option,
  .section-filters__reset,
  .section-filter__clear {
    min-height: 44px;
  }

  .section-filter__select {
    min-height: 42px;
  }
}

/* Tablet and phone: the heading stacks above its filters, which wrap from the left. */
@container page (max-width: 688px) {
  .content-section__header {
    flex-direction: column;
  }

  .content-section__heading {
    flex: 0 0 auto;
  }

  .content-section__filters,
  .section-filters__body {
    width: 100%;
    margin-left: 0;
    justify-content: flex-start;
  }

  .section-filter__menu {
    right: auto;
    left: 0;
  }
}

/* One real form: progressive enhancement moves its body into a native modal
   on narrow pages/previews. Without JS the controls and submit remain visible. */
@container page (max-width: 735px) {
  .content-section__header:has(> .section-filters--collapsible) {
    flex-direction: column;
    align-items: stretch;
    gap: var(--vds-spacing-12);
  }

  .content-section__header:has(> .section-filters--collapsible) .content-section__heading {
    flex: 0 0 auto;
  }

  .content-section__header:has(> .section-filters--collapsible) .content-section__heading:not(:has(> *)) {
    display: none;
  }

  .section-filters--collapsible {
    --section-filter-mobile: 1;
  }

  .section-filters--collapsible.section-filters--enhanced {
    display: contents;
  }

  .section-filters--enhanced .section-filters__toggle {
    display: inline-flex;
    flex: 0 0 auto;
    align-self: flex-end;
    align-items: center;
    gap: var(--vds-spacing-8);
    box-sizing: border-box;
    min-height: 44px;
    padding: 0 var(--vds-spacing-12);
    border: var(--vds-border-width-1) solid var(--vds-border-base);
    border-radius: 7px;
    background: var(--vds-bg-primary);
    box-shadow: var(--vds-shadow-xs);
    color: var(--vds-fg-primary);
    font: inherit;
    font-size: var(--vds-font-size-body-sm);
    font-weight: var(--vds-font-weight-semibold);
    cursor: pointer;
  }

  .section-filters--collapsible.section-filters--active .section-filters__toggle,
  .section-filters__toggle[aria-expanded="true"] {
    border-color: var(--vds-bg-brand-primary);
    background: var(--vds-bg-brand-light);
  }

  .section-filters--enhanced > .section-filters__body {
    display: none;
  }

  .section-filters--collapsible .section-filters__body {
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
    gap: var(--vds-spacing-12);
  }

  .section-filters--collapsible .section-filter {
    width: 100%;
  }

  .section-filters--collapsible .section-filter__summary,
  .section-filters--collapsible .section-filter--select {
    display: flex;
    min-height: 44px;
    width: 100%;
    box-sizing: border-box;
    white-space: normal;
  }

  .section-filters--collapsible .section-filter__summary vds-icon {
    margin-left: auto;
  }

  .section-filters--collapsible .section-filter__select {
    flex: 1;
    min-width: 0;
    min-height: 44px;
    font-size: 1rem;
  }

  .section-filters--collapsible .section-filter__menu {
    position: static;
    min-width: 0;
    max-width: none;
    margin-top: var(--vds-spacing-4);
    box-shadow: none;
  }

  .section-filters--collapsible .section-filters__apply {
    display: inline-flex;
    justify-content: center;
  }

  .section-filters--enhanced .section-filters__apply,
  .section-filters--enhanced .widget-date-range-filter__submit {
    display: none;
  }

  .section-filters--collapsible .section-filter__option,
  .section-filters--collapsible .section-filter__clear,
  .section-filters--collapsible .section-filters__reset {
    min-height: 44px;
  }

  .section-filters--collapsible .section-filters__reset--default {
    display: inline-flex;
  }

  .section-filters--collapsible .section-filters__retry:not([hidden]) {
    display: inline-flex;
    justify-content: center;
    min-height: 44px;
  }

  .section-filters--collapsible input[type="date"] {
    min-height: 44px;
    font-size: 1rem;
  }
}

/* Phone section strip (section-filters.js): pinned under the tab row, full
   bleed like it, naming the current section (nothing when it is unnamed),
   its applied filters, and a filter trigger. */
.mobile-section-summary {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  position: fixed;
  z-index: 16;
  box-sizing: border-box;
  min-height: 48px;
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  box-shadow: var(--vds-shadow-xs);
}

.mobile-section-summary__trigger {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
  margin-left: auto;
  min-width: 44px;
  height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  cursor: pointer;
}

/* 44px-tall touch target around the visual control. */
.mobile-section-summary__trigger::after {
  content: "";
  position: absolute;
  inset: -4px 0;
}

.mobile-section-summary__trigger.is-active {
  border-color: var(--agency-accent-bg, var(--vds-bg-brand-primary));
  background: var(--vds-bg-brand-light);
}

.mobile-section-summary__trigger:focus-visible {
  outline: 2px solid var(--agency-accent-bg, var(--vds-bg-brand-primary));
  outline-offset: 2px;
}

.mobile-section-summary__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  box-sizing: border-box;
  border-radius: 9px;
  background: var(--agency-accent-bg, var(--vds-bg-brand-primary));
  color: var(--agency-accent-fg, var(--vds-fg-white, #fff));
  font-size: 0.75rem;
  font-weight: var(--vds-font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.mobile-section-summary__title {
  flex: 0 1 auto;
  min-width: 0;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mobile-section-summary:has(vds-tag) .mobile-section-summary__title {
  max-width: 40%;
}

.mobile-section-summary__tags {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  mask-image: linear-gradient(to right, #000 calc(100% - var(--vds-spacing-8)), transparent);
}

.mobile-section-summary__tags:empty {
  display: none;
}

.mobile-section-summary__tags vds-tag {
  --vds-spacing-8: var(--vds-spacing-4);
  --vds-font-size-body-xs: 0.75rem;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Section sidecar: the editable list of a Section's filters. */
.section-filters-editor__list {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  margin: 0 0 var(--vds-spacing-8);
  padding: 0;
  list-style: none;
}

.section-filter-row {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
}

.section-filter-row__main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--vds-spacing-8);
}

.section-filter-row__main > .section-filter-row__field:nth-of-type(2) {
  grid-column: 1 / -1;
  grid-row: 2;
}

.section-filter-row__main > .section-filter-row__actions {
  grid-column: 2;
  grid-row: 1;
}

.section-filter-row [hidden] {
  display: none;
}

.section-filter-row__field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  font-size: var(--vds-font-size-body-sm);
}

.section-filter-row__field > span {
  color: var(--vds-fg-secondary);
}

.section-filter-row__field select,
.section-filter-row__field input {
  box-sizing: border-box;
  width: 100%;
  min-height: 36px;
  padding: 0 var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.section-filter-row__actions {
  display: flex;
  gap: 2px;
}

.section-filter-row__icon {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 36px;
  border: 0;
  border-radius: var(--vds-radius-4, 4px);
  background: transparent;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.section-filter-row__icon:hover:not(:disabled) {
  background: var(--vds-bg-brand-light);
  color: var(--vds-fg-primary);
}

.section-filter-row__icon:disabled {
  opacity: 0.35;
  cursor: default;
}

.section-filter-row__hint,
.section-filters-editor__empty {
  margin: 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.widget-date-range-filter__field {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  flex: 1 1 12rem;
}

/* In a filter popover the fields stack in a column, so the row-oriented 12rem basis would become a height. */
.section-filter__menu .widget-date-range-filter__field {
  flex: 0 0 auto;
}

.widget-date-range-filter__field label {
  font-weight: var(--vds-font-weight-semibold);
  font-size: var(--vds-font-size-body-sm);
  color: var(--vds-fg-primary);
}

.widget-date-range-filter__field input[type="date"] {
  box-sizing: border-box;
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.widget-date-range-filter__field input[type="date"]:focus {
  outline: none;
  border-color: var(--vds-bg-brand-primary);
  box-shadow: 0 0 0 3px var(--vds-bg-brand-light);
}

/* Reuses the app's existing .btn/.btn--primary convention (static/css/app.css,
   COMT-152's edit-panel Save button etc.) rather than a one-off color, so
   this widget's primary action matches every other primary button in the
   app instead of introducing a second "primary button" look. */
.widget-date-range-filter__submit {
  flex-shrink: 0;
}

/* Public Hub/Topic page Section containers (COMT-112/118) — hero-style
   full-width background behind foreground content when the section has
   one; a plain, unstyled wrapper otherwise (a section with no background
   reads no differently than the flat blocks that came before this
   ticket). */
.content-section {
  position: relative;
  z-index: 0;
  border-radius: var(--vds-radius-8);
  /* No default margin-bottom between stacked Sections (2026-09 follow-up
     — previously `var(--vds-spacing-24)`): a themed full-width header
     Section (nav band) reading as visually "cut off" from the very next
     Section by an unconditional 24px gap looked like a bug, not
     deliberate spacing, and the same flat 24px was applied uniformly
     regardless of whether either Section actually wanted breathing room.
     An agency that *does* want space between two specific Sections now
     has an explicit, deliberate way to add it — the Spacer section —
     instead of getting it for free (and unremovably) between every
     pair. */
  overflow: hidden;
}

/* A section with filters lets their menus extend past its bottom edge. */
.content-section:has(.section-filters) {
  overflow: visible;
}

.content-section:has(.section-filters) > .content-section__background {
  border-radius: inherit;
}

.content-section--hero,
.content-section--full-width {
  /* content-layout-formatting follow-up (full-width Sections): break
     out of `.page-width`'s ~1140px column (and any Tab-panel/themed
     wrapper nested inside it) to span the full width of `.app-main`
     (see its own `container-type: inline-size` comment above) — the
     classic `left: 50%; margin-left: -50%` centering-breakout trick,
     using `cqw` (container query width) in place of the usual `vw` so
     it resolves against `.app-main`'s own box instead of the viewport.
     Shared by `--hero` (a themed/backgrounded section) and
     `--full-width` (SectionBase.width = "full", page-header-section
     design follow-up) — the two are independent: a plain, unthemed
     Section can opt into full width, and a themed hero Section isn't
     forced into it. `.content-section__inner` below re-applies the
     same ~1140px column by default so the actual heading/blocks still
     line up with the rest of the page unless `--full-width` is also
     present (see its own inner-width override below), per the design's
     "side channels" reference. */
  width: 100cqw;
  position: relative;
  left: 50%;
  margin-left: -50cqw;
  /* Rounded corners read as a floating card at the old column width;
     a full-bleed band flush with the page's own edges shouldn't have
     them. */
  border-radius: 0;
}

/* True edge-to-edge bleed (2026-09 follow-up): `100cqw`/`-50cqw` above
   only reaches `.app-main`'s content-box edge — cqw resolves against
   the query container's *content* box, i.e. inside its own
   `padding: var(--vds-spacing-16)` (see `.app-main` above) — so a
   "full width" Section still floated inside that padding on every
   side instead of actually touching the page's real edges. Bleeding
   fully through that padding here (rather than removing `.app-main`'s
   own padding, which every other, non-Section page in this shell
   — 404.html, login, etc. — still relies on for its only margin)
   keeps this scoped to Sections that actually asked for it.
   Horizontal: every hero/full-width Section, at any position in the
   page. Vertical: only when this Section is the very first thing on
   the page — pulling every hero/full-width Section's top margin
   through the padding regardless of position would wrongly cancel
   spacing between two such Sections stacked back-to-back further down
   the page. */
.content-section--hero,
.content-section--full-width {
  width: calc(100cqw + (2 * var(--vds-spacing-16)));
  margin-left: calc(-50cqw - var(--vds-spacing-16));
}

.content-section--hero:first-child,
.content-section--full-width:first-child {
  margin-top: calc(-1 * var(--vds-spacing-16));
}

.content-section--hero {
  /* --agency-primary-* (COMT-159): declared per-agency, per light/dark
     mode, by templates/portal/_branding_style.html — falls back to the
     platform default whenever an agency has left Primary blank. */
  color: var(--agency-primary-fg, var(--vds-fg-white));
  background-color: var(--agency-primary-bg, var(--vds-bg-brand-primary));
}

.content-section--hero,
.content-section--full-width {
  /* Chrome-widget dropdowns (user-menu, nav-dropdown-links, the
     language switcher's own listbox) are absolutely positioned relative
     to this Section and open *below* their trigger — i.e. below this
     Section's own bottom edge. `.content-section`'s base `overflow:
     hidden` (kept for the plain/hero-with-radius case above) clipped
     those open panels off at the Section boundary, so they visually
     looked like they were rendering "under" the next Section instead
     of on top of it.

     `overflow-x: clip` + `overflow-y: visible` rather than a blanket
     `overflow: visible` (2026-09 follow-up, regression caught right
     after the first fix landed): any non-`visible` overflow value on
     *either* axis still establishes this Section as its own block
     formatting context, which is what stops its last child's own
     bottom margin from collapsing through the Section's box and
     escaping as visible empty space *below* the Section (revealing the
     page's own background through the gap) — a blanket `overflow:
     visible` on both axes loses that containment entirely. Splitting
     the axes keeps the BFC (via `overflow-x`) so margins stay contained
     and dropdown panels still don't trigger a horizontal scrollbar,
     while `overflow-y: visible` is the only axis that actually needs to
     stop clipping a dropdown opening downward. Safe to do at all only
     because `--hero`/`--full-width` already zero out `border-radius`
     just above — there's no rounded-corner clipping need to preserve
     for these two variants. */
  overflow-x: clip;
  overflow-y: visible;
}

/* Follow-up to the overflow fix above: escaping the clip wasn't enough
   on its own — every `.content-section` (this one and the next sibling
   Section right after it in the page) is `position: relative; z-index:
   0`, i.e. its own stacking context, and stacking contexts compare by
   DOM order once their `z-index` ties, regardless of what a descendant
   panel's own `z-index` is set to. A later, plain Section (still
   `z-index: 0`) therefore painted on top of — not below — an earlier
   Section's open dropdown panel, even though the panel itself has
   `z-index: 10` and had escaped its own clipping. Lifting *this*
   Section's stacking context above its siblings only while one of its
   own chrome-menu panels is actually open (`:has()`, not an
   unconditional `z-index: 1`) keeps every closed Section's normal paint
   order untouched. */
.content-section--hero:has(.chrome-menu-panel--open),
.content-section--full-width:has(.chrome-menu-panel--open) {
  z-index: 1;
}

/* Section.padding (page-header-section design follow-up, `SectionPadding`)
   — `padding-block` now lives entirely on these modifier classes rather
   than hardcoded on `.content-section--hero` above, so a themed Section
   can dial it down (a tight nav-widget strip) or up (a spacious hero
   banner) instead of every hero Section getting the same fixed value.
   `--padding-normal` uses the exact same `--vds-spacing-24` value
   `.content-section--hero` itself used to hardcode, so every existing
   themed Section (default `padding=normal`) renders identically to
   before this field existed. */
.content-section--padding-compact { padding-block: 0; }
.content-section--padding-normal  { padding-block: var(--vds-spacing-24); }
.content-section--padding-comfy   { padding-block: var(--vds-spacing-32); }
.content-section--padding-wide    { padding-block: var(--vds-spacing-48); }

/* Wraps everything but the hero's own full-bleed background
   (`.content-section__background` below, and this element's own
   `background-color`) — `position: relative` makes it (not the
   full-bleed `.content-section--hero` itself) the positioning context
   for the corner `.content-actions` cluster, so those buttons land
   near the visible content column instead of the page's outer edge.
   Unstyled (no width) for a plain, non-hero Section — same "no visible
   change" behavior as before this pass. */
.content-section__inner {
  position: relative;
}

/* Re-establishes `.page-width`'s own ~1140px column inside a full-bleed
   hero band, so its heading/blocks still align with the rest of the
   page instead of stretching edge-to-edge themselves.

   The extra `2 * var(--vds-spacing-16)` subtracted here (2026-09
   bugfix, on top of the plain `calc(100% - 3rem)` this used to be) is
   *not* the usual page-gutter margin — it compensates for
   `.content-section--hero`/`--full-width`'s own breakout above, which
   bleeds this Section all the way through `.app-main`'s 16px padding on
   each side (see that rule's own "True edge-to-edge bleed" comment) so
   this element's `100%` is the bare, unpadded viewport-equivalent
   width. Ordinary page content (`.page-width`, used by
   `#page-theme-wrapper` and this breadcrumb's own container) never
   bleeds through that padding, so its `100%` is 32px narrower — right
   there, `calc(100% - 3rem)` alone recaps this hero/full-width content
   to the *same nominal* ~1140px width, but auto-centered on a
   32px-wider reference box, landing it 16px further left/right than
   `.page-width`'s own centered column right below it (reported: the
   Page Header's title/breadcrumb strip below it didn't line up).
   Subtracting the same 32px here brings this element's own reference
   width back down to `.page-width`'s, so both auto-center to the exact
   same physical column. */
.content-section--hero .content-section__inner {
  width: min(1140px, calc(100% - 3rem - (2 * var(--vds-spacing-16))));
  margin-inline: auto;
}

/* Section.width = "full" (page-header-section design follow-up),
   recapped at ~1140px (2026-09 follow-up, same `min()` formula as
   `.page-width`/`.content-section--hero .content-section__inner` just
   above) — the only current Section this modifier applies to is the
   Page Header Section (`portal.sample_data._add_page_header_section`,
   its one caller), whose title/nav-dropdown-links row needs to line up
   with the ordinary page content underneath it (the breadcrumb strip,
   any Section's own heading, etc.), all of which stay capped at 1140px.
   Reported against Crime Map: the title read flush against the
   viewport's real edge while the breadcrumb right below it sat
   noticeably indented on a wide screen, because this rule used to let
   the header row's foreground content stretch all the way out to
   `calc(100% - 3rem)` (the full breakout width) while everything below
   it stayed capped. Only the *background* still needs the full
   edge-to-edge bleed (`.content-section--full-width` itself, above) —
   the foreground content column re-caps here exactly like a themed hero
   Section's already does. Declared after the hero rule above so it wins
   when both classes are present (equal specificity, later source
   order), and applies just as well to a plain, unthemed full-width
   section (no `.content-section--hero` at all) since it doesn't depend
   on that class. */
.content-section--full-width .content-section__inner {
  width: min(1140px, calc(100% - 3rem - (2 * var(--vds-spacing-16))));
  margin-inline: auto;
}

/* Same chrome-widget-panel exclusion as `.app-header a` above — a
   themed hero Section can carry its own nav-dropdown-links/
   language-selector/login-user-menu chrome widgets (page-header-section
   design), whose dropdown panels are their own light `--vds-bg-primary`
   cards, not part of the hero's own branded surface. Also excludes
   `.linked_card` (COMT-239 follow-up): a linked_card block dropped into a
   hero section carries its own independent `content_theme` color via
   `.linked-card--content-*`, which must not be clobbered by the hero's
   own `--agency-primary-fg`, same reasoning as the `.themed-*` exclusion
   above. */
.content-section--hero a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card) {
  color: var(--agency-primary-fg, inherit);
}

/* --agency-tertiary-* (COMT-159): the hero's own call-to-action is the
   one real surface Tertiary is exercised on — falls back to the same
   brand-primary token .btn--primary uses everywhere else whenever an
   agency has left Tertiary blank. */
.content-section--hero .btn--primary {
  background: var(--agency-tertiary-bg, var(--vds-bg-brand-primary));
  color: var(--agency-tertiary-fg, var(--vds-fg-white));
}

/* Optional short label above a Section's own heading (COMT-208) — e.g.
   "Community Trust Hub" above a hero's main headline. Small, uppercase,
   letter-spaced treatment distinguishes it from the heading/description
   below without needing its own heading level. */
.content-section__eyebrow {
  margin: 0 0 var(--vds-spacing-8);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.85;
}

/* "Call-to-action button" block layout (COMT-208) — e.g. a hero's
   "Most requested" quick-action row. Several such blocks lay out in one
   flex row via `.content-block-row` (static/css/app.css above); `flex:
   0 0 auto` keeps each button sized to its own label rather than
   stretching to a `column_width` share of the row. `:first-child`
   promotes the first button in the row to the same primary/tertiary
   treatment as `.btn--primary` above — the rest render as a plain
   secondary/outline button — following this file's existing convention
   of resolving row-position styling in CSS rather than in the template
   (see _content_blocks.html's own comment on boundary Move buttons for
   the same pattern). */
.content-block--cta_button {
  flex: 0 0 auto;
}

/* COMT-236 bugfix: a `linked_card` block must NOT stretch to match a
   taller sibling in the same `.content-block-row` (the row's own
   `align-items` is left unset — plain flexbox stretch-to-tallest-child
   default) — that's exactly what silently inflated the card's own
   height and, since `.linked-card`'s image no longer grows to soak up
   the extra space (see `.linked-card__media` below), would otherwise
   just show up as dead air under the card's own description instead.
   `align-self: flex-start` sizes this block to its own title+image+
   caption content height, same as the user's own ask: a card with no
   image chosen yet renders shorter than one with a photo, not padded
   out to match. */
.content-block--linked_card {
  align-self: flex-start;
}

.cta-button {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid currentColor;
  text-decoration: none;
  font-weight: 600;
}

.content-block--cta_button:first-child .cta-button {
  border-color: transparent;
  background: var(--agency-tertiary-bg, var(--vds-bg-brand-primary));
  color: var(--agency-tertiary-fg, var(--vds-fg-white));
}

/* COMT-172: a Page/Tab/Section/Block's own `theme` choice (COMT-171/173)
   resolved to one of the three branding-profile slots' CSS custom
   properties (declared per-agency, per light/dark mode, by
   templates/portal/_branding_style.html — COMT-159). Generic/reusable
   across every renderable type rather than one-off per element, since
   the color pairing itself (bg+fg, with the same platform-default
   fallback pattern already used above) is identical no matter what's
   wrapped — only the surrounding padding/radius (below) differs per
   element kind.

   `.themed-primary` intentionally duplicates `.content-section--hero`'s
   own Primary colors above (same fallback values, too) rather than the
   hero rule referencing this class — `.content-section--hero` predates
   theme selection entirely (COMT-159) and stays the *default* look for
   an untouched Section (`background_color` set, theme left as Inherit),
   whereas `.themed-*` is the explicit, later-in-source override for any
   element whose theme is deliberately set to a slot (COMT-171 precedence:
   theme wins over `background_color` when set — see the templates that
   apply these classes for how that precedence is actually enforced: they
   omit the element's own inline `background-color` style whenever a
   theme is chosen, so there's nothing left for these classes to lose to). */
.themed-primary {
  color: var(--agency-primary-fg, var(--vds-fg-white));
  background-color: var(--agency-primary-bg, var(--vds-bg-brand-primary));
  /* COMT-238 follow-up: exposed as custom properties too (in addition to
     painting this element's own background/color directly, above) so a
     descendant that renders its *own* separate colored surface — like
     `.linked-card__title` below, which `.content-block--linked_card.
     content-block--themed` deliberately keeps this wrapper from ever
     painting itself, to avoid double-boxing — can still pick up
     whichever theme slot was chosen instead of showing no color change
     at all. */
  --content-block-accent-bg: var(--agency-primary-bg, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-primary-fg, var(--vds-fg-white));
}

.themed-secondary {
  color: var(--agency-secondary-fg, var(--vds-fg-white));
  background-color: var(--agency-secondary-bg, var(--vds-bg-brand-primary));
  --content-block-accent-bg: var(--agency-secondary-bg, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-secondary-fg, var(--vds-fg-white));
}

.themed-tertiary {
  color: var(--agency-tertiary-fg, var(--vds-fg-white));
  background-color: var(--agency-tertiary-bg, var(--vds-bg-brand-primary));
  --content-block-accent-bg: var(--agency-tertiary-bg, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-tertiary-fg, var(--vds-fg-white));
}

/* COMT-177: the "(Dark)"-pinned theme choices — always render a slot's
   dark-mode colors via the mode-invariant `--agency-{slot}-{bg,fg}-dark`
   custom properties (declared unconditionally on `:root`, never
   re-declared under `[data-theme="dark"]` — see
   templates/portal/_branding_style.html), regardless of whichever
   `[data-theme]` is currently active. Lets an admin pin one element to
   always look dark for intentional contrast against the rest of a
   (possibly light-mode) page. */
.themed-primary-dark {
  color: var(--agency-primary-fg-dark, var(--vds-fg-white));
  background-color: var(--agency-primary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-bg: var(--agency-primary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-primary-fg-dark, var(--vds-fg-white));
}

.themed-secondary-dark {
  color: var(--agency-secondary-fg-dark, var(--vds-fg-white));
  background-color: var(--agency-secondary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-bg: var(--agency-secondary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-secondary-fg-dark, var(--vds-fg-white));
}

.themed-tertiary-dark {
  color: var(--agency-tertiary-fg-dark, var(--vds-fg-white));
  background-color: var(--agency-tertiary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-bg: var(--agency-tertiary-bg-dark, var(--vds-bg-brand-primary));
  --content-block-accent-fg: var(--agency-tertiary-fg-dark, var(--vds-fg-white));
}

/* :not(.chrome-nav-dropdown__link):not(.app-user-menu__link) — same
   chrome-widget-panel exclusion as `.app-header a`/`.content-section
   --hero a` above: a themed block/tab-panel/page wrapper can carry
   nav-dropdown-links/language-selector/login-user-menu chrome widgets
   too, and each widget's dropdown panel is its own light
   `--vds-bg-primary` card, not part of the themed surface its trigger
   sits on. `:not(.linked-card)` (COMT-239 bugfix) excludes a `linked_card`
   block's own card for the same reason — `.linked-card` already carries
   its own independent color logic (`.linked-card--content-*` below,
   COMT-239), and this rule's `color: inherit` (two-class specificity)
   was silently winning over that single-class rule, always painting a
   linked_card's content-area text with its *title bar*'s theme color
   (inherited from this `.themed-*` wrapper) instead of its own
   `content_theme`'s — a real, hard-to-notice contrast bug (a light
   `content_theme` fg color painted on top of a light `content_theme` bg,
   or vice versa, whenever the title bar's own `theme` differed). */
.themed-primary a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card),
.themed-secondary a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card),
.themed-tertiary a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card),
.themed-primary-dark a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card),
.themed-secondary-dark a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card),
.themed-tertiary-dark a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card) {
  color: inherit;
}

/* Block/Tab-panel/Page wrapper padding+radius for a themed background —
   `.content-section--hero` above already carries this for Section; these
   are the equivalent "make it look like a colored surface, not just a
   color" treatment for the three element kinds that had no background
   concept at all before COMT-172. */
.content-block--themed,
.tab-strip__panel--themed,
.page-width--themed {
  padding: var(--vds-spacing-24);
  border-radius: var(--vds-radius-8);
}

/* `linked_card` opts out of the generic themed-surface padding+
   background above (COMT-236 bugfix — previously showed as a "bunch of
   padding" wrapped around the card the moment its own Theme was set to
   a color slot instead of Inherit): `.linked-card` already renders its
   own fully opaque, bordered card surface (see below), so wrapping
   *that* in another padded colored box double-boxes it instead of
   giving it a look. `:not(.content-block--themed)`'s own selector
   isn't needed here — plain descending-specificity source order is
   enough since this rule only ever un-sets what the rule above set,
   never on a themed block that isn't also a linked_card.

   This still isn't a no-op visually (COMT-238 follow-up on the COMT-236
   bugfix, which briefly *was* — the wrapper's own color was fully
   suppressed here with nowhere else picking it up): `--content-block-
   accent-bg`/`-fg`, set on this same wrapper by whichever `.themed-*`
   class applies (or, theme left as Inherit, straight from the block's
   own `background_color` inline style below), still cascade down to
   `.linked-card__title`'s own background/color (see that rule) even
   though this wrapper's own painted background is suppressed — so
   picking a Theme or a `background_color` still visibly recolors the
   card's title bar, just not this outer wrapper's own surface.

   `background-color: transparent !important` (COMT-238 follow-up) is
   required specifically for this declaration, unlike `padding`/
   `border-radius` above: a Block's own `background_color` (COMT-238)
   renders as an *inline* `style="background-color: ...;"` on the same
   element (see templates/portal/_content_block.html), and an inline
   style always wins over any stylesheet rule regardless of selector
   specificity or source order — without `!important` here, a
   linked_card with its own `background_color` set would still show the
   exact same double-boxed look this rule exists to prevent. */
.content-block--linked_card.content-block--themed {
  padding: 0;
  border-radius: 0;
  background-color: transparent !important;
}

/* Block-level `background_image` (COMT-45/49 follow-up) — same "photo
   behind foreground content" pairing as `.content-section--hero`/
   `.content-section__background` above, scaled down to a single block
   instead of a full-bleed row: no width/left/margin breakout (a Block
   already only ever spans its own `column_width` share of a row, never
   the full viewport), just the padding/radius/overlay/text-color
   treatment so the photo reads as a background, not a plain foreground
   image sitting behind other content by accident. */
.content-block--has-background-image {
  position: relative;
  z-index: 0;
  overflow: hidden;
  padding: var(--vds-spacing-24);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-white);
}

.content-block__background {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  z-index: -1;
}

/* Readability over an arbitrary photo — same overlay treatment as
   `.content-section--hero .content-section__background::after` above. */
.content-block--has-background-image .content-block__background::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
}

/* Same chrome-widget-panel exclusion as the Section/themed-surface rules
   above — a content block hosting a chrome widget (nav-dropdown-links,
   language-selector, login-user-menu — `.content-block__chrome-widget`)
   can also have a background image; its dropdown panel is still its own
   light `--vds-bg-primary` card, not part of the photo behind it. Also
   excludes `.linked-card` (COMT-239 follow-up), same reasoning as the
   `.themed-*`/`.content-section--hero` exclusions above — a linked_card
   block can carry its own generic wrapper `background_image` alongside
   its independent `content_theme` color, which must not be clobbered. */
.content-block--has-background-image a:not(.btn):not(.chrome-nav-dropdown__link):not(.app-user-menu__link):not(.linked-card) {
  color: inherit;
}

.content-section__background {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  z-index: -1;
}

/* Readability over an arbitrary photo (COMT-112's own design
   reference) — a plain background-color section has no image to darken. */
.content-section--hero .content-section__background::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
}

.content-section__content {
  position: relative;
}

/* Spacer Section (page-header-section design follow-up) — renders as
   plain empty vertical space instead of a header/blocks, so a page can
   insert a gap between two Sections without an otherwise-empty
   Section's own padding/background getting in the way. In manage mode it
   is selected by clicking it, like any other Section. */
.content-section__spacer {
  width: 100%;
}

/* Spacer Block (page-header-section design follow-up) — same "empty
   vertical space" concept as `.content-section__spacer` above, scoped to
   a single Block instead of a whole Section, so a spacer can sit between
   two Blocks in the same row/Section without its own Add-Block button. */
.content-block__spacer {
  width: 100%;
}

/* Lets multiple blocks sit side-by-side in a row via column_width
   (content-layout-formatting design, 2026-08-27) — automatic flex-wrap,
   no explicit row-grouping concept. Each existing _content_blocks.html
   call site (unsectioned blocks, or one Section's blocks) becomes its
   own independent row. */
.content-block-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-24);
  /* Usability follow-up pass: without this, an unsectioned row (e.g. the
     hub page's own topic-pages-list block) butted directly up against
     whatever Section followed it with zero visual buffer, especially
     obvious when that Section renders as a full-bleed `.content-section
     --hero` block. */
  margin-bottom: var(--vds-spacing-24);
}

/* Bugfix (2026-09 follow-up): a Section-scoped row (rendered inside
   _section.html's own `.content-section__content`) picking up that same
   bottom margin was NOT harmless after all, contrary to this rule's own
   former claim — a themed/hero Section with only one row and nothing
   else after it (any Section in Preview mode, where the manager-only
   "+ Add Block" trigger below doesn't render) has that row as its own
   *last* in-flow child, so its bottom margin collapses straight through
   `.content-section__content`/`.content-section__inner` (neither has its
   own padding/border to stop it) and escapes past the Section's own
   box entirely — visible as a real gap of empty page background between
   this Section and the next one, even though neither Section itself has
   any margin/padding of its own left to explain it (COMT-235 follow-up:
   reported as an unexplained gap under Aarondale's Compact-padding page
   header). A Section already has its own dedicated `padding` control
   (`SectionPadding`, above) for exactly this "how much space at the
   bottom of my own content" concern — this margin was never meant to
   double as that, so it's zeroed out entirely once a row is inside a
   Section's own content wrapper, leaving the unsectioned-row case above
   untouched. */
.content-section__content > .content-block-row {
  margin-bottom: 0;
}

/* Section -> Row -> Widget: each Row wraps its own flex `.content-block-row`,
   so a Row keeps the same wrapping layout a Section's blocks always had.
   Rows stack with the same gap blocks use, and a row's own block list sheds
   the bottom margin for the same reason as the rule above. */
.content-row > .content-block-row {
  margin-bottom: 0;
}

.content-row + .content-row {
  margin-top: var(--vds-spacing-24);
}

.content-row__actions {
  display: flex;
  align-items: stretch;
  gap: var(--vds-spacing-8);
  opacity: 0.55;
  transition: opacity 0.15s ease;
}

.content-row:hover > .content-row__actions,
.content-row__actions:focus-within {
  opacity: 1;
}

.content-row__actions .add-trigger {
  flex: 1 1 auto;
  min-height: 2.25rem;
}

/* A Row with a column count lays its widgets out as an equal-column grid.
   Responsiveness is driven by the Row's own width (container queries), not
   the viewport, so the editor's tablet/mobile preview — which just narrows the
   page — behaves exactly like a real narrow screen. Tablet and mobile column
   counts are resolved server-side (an unset tablet count caps at 2, mobile
   at 1), so a 4-column row never stays 4 columns on a phone. */
.content-row {
  container-type: inline-size;
}

/* A gutter around each Row in edit mode, so there's empty space to click to
   select the Row itself (everything inside is a widget or an add-slot). The
   default desktop row still uses the flex layout; responsive column settings
   override it only when a row explicitly opts into a fixed column count. */
body.is-editing .content-row[data-editable-hover-target] {
  margin-inline: calc(-1 * var(--vds-spacing-8));
  padding: var(--vds-spacing-8);
  border-radius: 12px;
}

.content-block-row--grid {
  display: grid;
  grid-template-columns: var(--row-template, repeat(var(--row-cols), minmax(0, 1fr)));
}

.content-block-row--grid > .content-block {
  flex-basis: auto;
  min-width: 0;
}

@container (max-width: 760px) {
  .content-block-row--grid {
    grid-template-columns: repeat(var(--row-cols-tablet), minmax(0, 1fr));
  }
}

@container (max-width: 480px) {
  .content-block-row--grid {
    grid-template-columns: repeat(var(--row-cols-mobile), minmax(0, 1fr));
  }

  .content-block-row:not(.content-block-row--grid) > .content-block:not(.content-block--width-tight):not(.content-block--cta_button) {
    flex-basis: 100% !important;
  }
}

/* Per-screen-size visibility of a widget. The sizes match the preview toggle:
   desktop above 760px, tablet 481-760px, mobile 480px and below, measured on the
   Row. Visitors don't get the widget at the hidden size; in edit mode it stays,
   faded and badged, so it can still be selected and edited. */
.content-block__hidden-badge {
  display: none;
  position: absolute;
  top: var(--vds-spacing-8);
  right: var(--vds-spacing-8);
  z-index: 3;
  padding: 2px var(--vds-spacing-8);
  border-radius: 999px;
  background: #262626;
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 600;
  pointer-events: none;
}

@container (min-width: 761px) {
  body:not(.is-editing) .content-block--hidden-desktop {
    display: none;
  }

  body.is-editing .content-block--hidden-desktop {
    opacity: 0.45;
  }

  body.is-editing .content-block__hidden-badge--desktop {
    display: block;
  }
}

@container (min-width: 481px) and (max-width: 760px) {
  body:not(.is-editing) .content-block--hidden-tablet {
    display: none;
  }

  body.is-editing .content-block--hidden-tablet {
    opacity: 0.45;
  }

  body.is-editing .content-block__hidden-badge--tablet {
    display: block;
  }
}

@container (max-width: 480px) {
  body:not(.is-editing) .content-block--hidden-mobile {
    display: none;
  }

  body.is-editing .content-block--hidden-mobile {
    opacity: 0.45;
  }

  body.is-editing .content-block__hidden-badge--mobile {
    display: block;
  }
}

/* Empty widget slots in a grid row (created by inline-editor.js): one dashed
   "Add widget" cell per unfilled desktop column. Narrow sizes keep only the
   first one — the slots are the only way to add a widget to a grid row, so
   they can't all disappear. An empty row keeps one per column shown. */
.row-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-8);
  min-height: 7rem;
  padding: var(--vds-spacing-16);
  border: 2px dashed var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: rgb(37 99 235 / 3%);
  color: var(--vds-fg-secondary);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.row-slot:hover,
.row-slot:focus-visible {
  border-color: #60a5fa;
  background: rgb(37 99 235 / 8%);
  color: #2563eb;
}

@container (max-width: 760px) {
  .content-block-row--grid > .row-slot ~ .row-slot {
    display: none;
  }

  .content-block-row--grid:not(:has(> .content-block)) > .row-slot {
    display: flex;
  }
}

/* An empty row keeps one slot per column of the size being shown. The
   column count can't be compared in CSS, so each count has its own class. */
@container (max-width: 760px) {
  .content-block-row--tablet-1:not(:has(> .content-block)) > .row-slot:nth-child(n + 2),
  .content-block-row--tablet-2:not(:has(> .content-block)) > .row-slot:nth-child(n + 3),
  .content-block-row--tablet-3:not(:has(> .content-block)) > .row-slot:nth-child(n + 4) {
    display: none;
  }
}

@container (max-width: 480px) {
  .content-block-row--mobile-1:not(:has(> .content-block)) > .row-slot:nth-child(n + 2),
  .content-block-row--mobile-2:not(:has(> .content-block)) > .row-slot:nth-child(n + 3),
  .content-block-row--mobile-3:not(:has(> .content-block)) > .row-slot:nth-child(n + 4) {
    display: none;
  }
}

/* "Add column" only makes sense once every column is filled; the slots
   (created by inline-editor.js) are the sign that one is still free. */
.row-add-column {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-8);
  width: 100%;
  margin-top: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
  border: 2px dashed var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: rgb(37 99 235 / 3%);
  color: var(--vds-fg-secondary);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.row-add-column:hover,
.row-add-column:focus-visible {
  border-color: #60a5fa;
  background: rgb(37 99 235 / 8%);
  color: #2563eb;
}

body.is-editing .content-block-row--grid:not(:has(> .row-slot)) ~ .row-add-column {
  display: flex;
}

/* A grid row's slots replace its "Add widget" bar; the bar's button stays in
   the DOM because the slots delegate to it. Hub rows suppress slots, so their
   own action bar must stay visible. */
.content-row:has(> .content-block-row--grid) > .content-row__actions:not(.content-row__actions--hub) {
  display: none;
}

/* The Row's layout trigger is reachable by keyboard and screen readers, but
   selecting a row by clicking it is the sighted route. */
.content-row .row-edit-trigger.edit-trigger {
  position: absolute;
  top: 0;
  right: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  opacity: 0;
}

.content-row .row-edit-trigger.edit-trigger:focus-visible {
  width: auto;
  height: auto;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  clip-path: none;
  opacity: 1;
}

.add-trigger--row {
  margin-top: var(--vds-spacing-16);
}

/* Whitespace bugfix (2026-09): an *empty* unsectioned-blocks row (no
   page-level blocks at all — increasingly common now that the page-
   header-section design puts everything into Sections instead) still
   picked up the margin-bottom above, showing up as a stray gap between
   the (now-collapsed, see `#page-title-heading:empty` above) title area
   and the first real Section. `:empty` only matches once
   _content_blocks.html's own `{% for %}` loop leaves no whitespace text
   node behind for an empty `blocks` list — see that template's own
   single-line opening-tag-to-`{% for %}` comment for why the markup is
   shaped that way.  */
.content-block-row:empty {
  margin-bottom: 0;
}

/* Section-level vertical alignment (page-header-section design
   follow-up, 2026-09, `SectionVerticalAlign`) — lets a row of
   differently-tall blocks (e.g. a header's title next to shorter
   nav-dropdown/theme-toggle/language-selector chrome widgets) align
   against each other on the cross axis instead of every block's own
   container silently stretching to the row's full height (the unset
   `align-items` default above). Only rendered as a modifier class when
   a Section actually opts out of that default — see
   `_content_blocks.html`'s own `vertical_align` comment. */
.content-block-row--valign-top    { align-items: flex-start; }
.content-block-row--valign-center { align-items: center; }
.content-block-row--valign-bottom { align-items: flex-end; }

.content-block {
  position: relative;
  flex-grow: 0;
  flex-shrink: 1;
  /* Bugfix: `.content-block--themed` (below) adds its own padding for a
     themed background (any slot — Primary/Secondary/Tertiary or their
     "(Dark)" pins). Without `border-box`, that padding is added on top
     of the `flex-basis` width declared by each `.content-block--width-*`
     rule (content-box is the browser default), pushing a themed block
     wider than its declared share of the row and forcing it to wrap onto
     its own line — visible as, e.g., two themed 1/2-width blocks
     stacking instead of sitting side by side, even though the identical
     Inherit-themed blocks fit fine. `border-box` keeps the padding
     inside the declared width instead of adding to it, matching every
     un-themed block's actual rendered width. */
  box-sizing: border-box;
}

.content-block--width-full           { flex-basis: 100%; }
.content-block--width-three_quarters { flex-basis: calc(75% - var(--vds-spacing-24) * 0.25); }
.content-block--width-two_thirds     { flex-basis: calc(66.666% - var(--vds-spacing-24) * 0.333); }
.content-block--width-half           { flex-basis: calc(50% - var(--vds-spacing-24) * 0.5); }
.content-block--width-one_third      { flex-basis: calc(33.333% - var(--vds-spacing-24) * 0.667); }
.content-block--width-one_quarter    { flex-basis: calc(25% - var(--vds-spacing-24) * 0.75); }
/* `tight` (page-header-section design) is the odd one out: every other
   width above claims a fixed share of the row regardless of its actual
   content (a themed header's title/nav/controls, by contrast, should
   only ever take up as much space as they need, with normal gap/padding
   in between) — `flex-basis: auto` + `flex-grow: 0` (already the
   `.content-block` default) does exactly that. */
.content-block--width-tight          { flex-basis: auto; }

/* Page Header title heading reset (2026-09 header-height follow-up):
   `column_width: tight` is only ever used for this row's title block
   plus its nav-dropdown-links/theme-toggle/language-selector chrome-
   widget siblings (`portal.sample_data._add_page_header_section`) —
   never for an ordinary reading-content heading elsewhere on the page —
   so it's a safe, narrow selector for undoing the title's own unstyled
   browser-default `<h2>` (large font-size, ~0.83em top/bottom margin).
   That default margin/font made the title's own block much taller than
   its plain, small chrome-widget siblings, which in turn (via the row's
   `align-items`) inflated the whole header strip's height well past the
   page's own grey editorial strip just above it and pushed the other
   blocks' centered inner content down away from the title's own
   top-heavy text — reported against the Crime Map page. Sized to
   roughly match a compact chrome-widget row instead of a full reading
   heading. */
.content-block--width-tight h2 {
  margin: 0;
  font-size: var(--vds-font-size-heading-sm, 1.5rem);
  line-height: 1.2;
}

/* Page Header band layout — any Section holding nav-dropdown chrome widgets
   (portal.sample_data._add_page_header_section). Title on its own row, then
   nav, theme toggle and language selector together on a second row, instead
   of whatever wrapping the title's natural width happened to produce. */
.content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-section__inner {
  padding-block: var(--vds-spacing-16);
}

.content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block-row {
  align-items: center;
  gap: var(--vds-spacing-4) var(--vds-spacing-16);
}

.content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown)
  .content-block--width-tight:not(:has(.content-block__chrome-widget)) {
  flex-basis: 100%;
}

.content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block--width-tight h2 {
  font-size: var(--vds-font-size-heading-md, 1.75rem);
  font-weight: var(--vds-font-weight-semibold);
}

/* Nav triggers carry their own horizontal padding; pull each block back so
   the first trigger's text lines up with the title above it. */
.content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block--width-tight:has(.content-block__chrome-widget) {
  margin-inline: calc(-1 * var(--vds-spacing-8)) 0;
}

/* Wide screens: title on the left, nav and controls on the right in a
   single row (the first nav block carries `align-right`), as authored. */
@container page (min-width: 1168px) {
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown)
    .content-block--width-tight:not(:has(.content-block__chrome-widget)) {
    flex-basis: auto;
  }
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block--width-tight h2 {
    font-size: var(--vds-font-size-heading-sm, 1.5rem);
  }
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block--width-tight:has(.content-block__chrome-widget) {
    margin-inline: 0;
  }
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block--align-right,
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown)
    .content-block--width-tight:not(:has(.content-block__chrome-widget)) + .content-block {
    margin-inline-start: auto;
  }
}

@container page (max-width: 735px) {
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown) .content-block-row {
    gap: var(--vds-spacing-4) var(--vds-spacing-8);
  }
  /* Panels open full-width under the whole nav row, anchored to the row
     rather than each trigger, so none can overflow the viewport. */
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown):not(:has(.edit-trigger)) .content-block-row {
    position: relative;
  }
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown):not(:has(.edit-trigger)) .content-block:has(.chrome-nav-dropdown),
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown):not(:has(.edit-trigger)) .chrome-nav-dropdown {
    position: static;
  }
  .content-section--hero:not(.content-section--site-header):has(.chrome-nav-dropdown):not(:has(.edit-trigger)) .chrome-nav-dropdown__panel {
    left: 0;
    right: 0;
    top: calc(100% + var(--vds-spacing-8));
  }
}

/* Global site header (templates/portal/_site_identity.html): identity on the
   left, nav, theme toggle and language selector on the right. Below 820px
   (phones and portrait tablets) the nav collapses behind the menu toggle
   (static/js/site-header-menu.js). */
.content-section--site-header .content-section__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8) var(--vds-spacing-24);
  padding-block: var(--vds-spacing-12);
}

.content-section--site-header .content-section__content {
  flex: 1 1 auto;
  min-width: 0;
}

.content-section--site-header .chrome-nav-dropdown__trigger {
  font-size: 0.875rem;
}

.content-section--site-header .content-block-row {
  justify-content: flex-end;
  align-items: center;
  gap: var(--vds-spacing-4) var(--vds-spacing-16);
}

/* The page's own name lives in the content below the breadcrumb. */
.content-section--site-header .content-block--width-tight:not(:has(.content-block__chrome-widget)) {
  display: none;
}

.site-identity {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
}

.site-identity__home,
.site-identity__home:hover,
.app-header .site-identity__home {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-16);
  color: inherit;
  text-decoration: none;
}

.site-identity__logo {
  display: block;
  height: 2.5rem;
  width: auto;
  max-width: 4rem;
  object-fit: contain;
}

.site-identity__text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}

.site-identity__title {
  font-family: var(--vds-font-family-heading), system-ui, sans-serif;
  font-size: 1.125rem;
  font-weight: 800;
}

.site-identity__agency {
  font-size: 0.8125rem;
}

@container page (max-width: 480px) {
  .site-identity__home {
    min-width: 0;
  }
  .site-identity__text {
    min-width: 0;
  }
  .site-identity__title {
    display: none;
  }
  .site-identity__agency {
    overflow: hidden;
    font-size: 1rem;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

.site-identity__menu-toggle {
  flex-shrink: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--vds-spacing-8);
  background: none;
  border: var(--vds-border-width-1) solid currentcolor;
  border-radius: var(--vds-radius-8);
  color: inherit;
  cursor: pointer;
}

/* Applies in the editor too, so the tablet and mobile previews collapse like a
   real device: the menu toggle opens the nav there as well. Wider than that,
   static/js/site-header-menu.js sets `data-nav-compact` when an agency's menus
   don't fit beside its identity, and the same collapsed layout applies. */
.content-section--site-header[data-nav-compact] .site-identity {
  width: 100%;
}
.content-section--site-header[data-nav-compact] .site-identity__menu-toggle {
  display: inline-flex;
}
.content-section--site-header[data-nav-compact]:not([data-nav-open]) .content-section__content {
  display: none;
}
.content-section--site-header[data-nav-compact] .content-section__content {
  flex-basis: 100%;
}
.content-section--site-header[data-nav-compact] .content-block-row {
  flex-direction: column;
  align-items: flex-start;
}
.content-section--site-header[data-nav-compact] .content-block,
.content-section--site-header[data-nav-compact] .chrome-nav-dropdown {
  width: 100%;
}
.content-section--site-header[data-nav-compact] .chrome-nav-dropdown__panel {
  position: static;
  box-shadow: none;
  margin-top: var(--vds-spacing-4);
}
.content-section--site-header[data-nav-compact] .chrome-nav-dropdown__trigger {
  width: 100%;
  justify-content: space-between;
}

@container page (max-width: 820px) {
  .content-section--site-header .site-identity {
    width: 100%;
  }
  .content-section--site-header .site-identity__menu-toggle {
    display: inline-flex;
  }
  .content-section--site-header:not([data-nav-open]) .content-section__content {
    display: none;
  }
  .content-section--site-header .content-section__content {
    flex-basis: 100%;
  }
  .content-section--site-header .content-block-row {
    flex-direction: column;
    align-items: flex-start;
  }
  .content-section--site-header .content-block,
  .content-section--site-header .chrome-nav-dropdown {
    width: 100%;
  }
  .content-section--site-header .chrome-nav-dropdown__panel {
    position: static;
    box-shadow: none;
    margin-top: var(--vds-spacing-4);
  }
  .content-section--site-header .chrome-nav-dropdown__trigger {
    width: 100%;
    justify-content: space-between;
  }
}

/* Page title block: name, description and data-freshness line, shown in the
   content under the breadcrumb (templates/portal/_page_title_block.html).
   Mobile typography scales with the page container, including editor previews. */
.public-page-title-block {
  display: flex;
  flex-direction: column;
  padding-block: var(--vds-spacing-32);
}

.public-page-title-row {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.public-page-title {
  margin: 0;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.public-page-title-row .edit-trigger {
  position: static;
}

.public-page-description {
  max-width: 45rem;
  margin: 10px 0 0;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--vds-fg-primary);
}

.public-page-update-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 14px 0 0;
  font-size: var(--vds-font-size-body-sm);
  line-height: 1.4;
  color: var(--vds-fg-secondary);
}

.public-page-update-line svg {
  width: 1.125rem;
  height: 1.125rem;
}

.public-page-update-line__cadence,
.public-page-update-line__timestamp {
  display: contents;
}

.public-page-update-line__cadence svg {
  display: none;
}

.public-page-update-line__timestamp svg {
  order: -1;
}

@container page (max-width: 735px) {
  .public-page-title {
    font-size: 1.625rem;
  }
}

/* Two stacked lines on phones; the dot only separates them when they share a line. */
@container page (max-width: 480px) {
  .public-page-title-block {
    padding-bottom: var(--vds-spacing-24);
  }
  .public-page-update-line {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--vds-spacing-8);
    margin-top: var(--vds-spacing-20);
  }
  .public-page-description {
    color: var(--vds-fg-secondary);
  }
  .public-page-update-line__cadence,
  .public-page-update-line__timestamp {
    display: flex;
    align-items: flex-start;
    gap: var(--vds-spacing-8);
  }
  .public-page-update-line svg {
    display: block;
    flex-shrink: 0;
    width: 1em;
    height: 1em;
    margin-top: 0.2em;
    order: initial;
  }
  .public-page-update-line__sep {
    display: none;
  }
}

/* Alignment (page-header-section design) — orthogonal to the width
   rules above. Only `right` needs real CSS: `margin-left: auto` on a
   flex row pushes it (and every plain, un-aligned block after it, which
   has no leftover space of its own to consume) flush against the row's
   right edge — e.g. a header row's title stays put on the left while
   its nav/controls cluster starts wherever the first `--align-right`
   block is placed. `left` is normal flow already (no rule needed) but
   still gets a class for explicitness/future use. `center` only makes
   sense paired with a width narrower than 100% (e.g. `tight`) — a
   full-width block has no leftover space on either side to center
   within. */
.content-block--align-right  { margin-left: auto; }
.content-block--align-center { margin-left: auto; margin-right: auto; }

/* Per-block vertical alignment override (`ContentBlockVerticalAlign`) —
   `align-self` beats the row's own `align-items`/`.content-block-row
   --valign-*` choice above for this one block only, e.g. every other
   block in a centered header row except one that should stay pinned to
   the top. */
.content-block--valign-top     { align-self: flex-start; }
.content-block--valign-center  { align-self: center; }
.content-block--valign-bottom  { align-self: flex-end; }
.content-block--valign-stretch { align-self: stretch; }

/* Per-block text alignment (`ContentBlockTextAlign`) — `text-align` on
   the block's own content (title/body text, and the foreground `image`
   above, a plain inline replaced element), independent of
   `.content-block--align-*` above, which positions the whole block
   within its row and does nothing once that block already fills 100%
   of the row (the common case for a hero-style block). */
.content-block--text-left   { text-align: left; }
.content-block--text-center { text-align: center; }
.content-block--text-right  { text-align: right; }

/* Bugfix: `image_left`/`image_right`/`image_top`/`image_bottom` used to
   render identically (image always stacked above the text) because
   nothing here actually keyed off the `.content-block--image_*`
   modifier class _content_block.html already put on the outer element —
   the image and text markup was just plain block-level DOM in source
   order. `_content_block.html` now always emits the same fixed DOM shape
   for all four — `.content-block__media` (a `<figure>`, so its caption
   sits as a `<figcaption>` immediately under the image itself, per this
   layout's own contract) then `.content-block__text`, in that order,
   inside one `.content-block__media-row` flex container — and the
   `flex-direction` rules below alone decide left/right/top/bottom
   placement, keyed off the outer `.content-block--<layout>` class. This
   is deliberate, not just tidier: a fixed DOM shape + pure-CSS placement
   means switching between these four in the block-edit panel is a
   plain class swap, letting `static/js/inline-editor.js`'s existing
   `LIVE_CLASS_FIELDS` preview it live (no save round-trip needed),
   exactly like `column_width`/`alignment`/etc. already do — see that
   file's own `LAYOUT_ARRANGEMENT_CLASSES` comment. Plain `flex-
   direction: row` (the unqualified `.content-block__media-row` default)
   already matches `image_left`'s own image-then-text DOM order, so only
   `image_right` (`row-reverse`, flips render order without touching the
   markup) and `image_top`/`image_bottom` (`column`/`column-reverse`)
   need their own rule. `align-items: flex-start` on the row layouts
   keeps a short caption from stretching to match a taller text column's
   height; the column layouts override to `stretch` so the text still
   spans the block's full width instead of shrinking to the image's. */
.content-block__media-row {
  display: flex;
  align-items: flex-start;
  gap: var(--vds-spacing-24);
}
.content-block--image_right .content-block__media-row {
  flex-direction: row-reverse;
}
.content-block--image_top .content-block__media-row {
  flex-direction: column;
  align-items: stretch;
}
.content-block--image_bottom .content-block__media-row {
  flex-direction: column-reverse;
  align-items: stretch;
}
.content-block--image_only .content-block__media-row {
  flex-direction: column;
  align-items: stretch;
}
.content-block__media {
  flex: 0 0 auto;
  margin: 0;
}
.content-block--image_left .content-block__media,
.content-block--image_right .content-block__media {
  max-width: 50%;
}
/* Live-preview-only concern (static/js/inline-editor.js): the real
   server render never emits `.content-block__media` at all for
   `text_only` (see _content_block.html's own `{% else %}` branch) — this
   rule only ever matters when a block that started as image_left/right/
   top/bottom/only already has that markup in the DOM and the still-
   unsaved layout dropdown is switched to Text only, so the client-side
   preview needs to visually match "no image" without actually removing
   the node (Cancel/re-render needs it back exactly as it was). */
.content-block--text_only .content-block__media {
  display: none;
}
/* Same live-preview-only concern, mirrored: `image_only` is `text_only`'s
   opposite (one content area, the image instead of the text) — the real
   server render already omits `.content-block__text` outright for
   `image_only` (_content_block.html), this rule only covers the
   unsaved-preview case where a block that started as one of the other
   four already has text markup rendered and the dropdown is switched to
   Image only. */
.content-block--image_only .content-block__text {
  display: none;
}
/* Bugfix: `display: block` on the image (its previous value) made it a
   block-level box that ignores its ancestor's `text-align` entirely —
   `.content-block--text-center`/`--text-left`/`--text-right` (below)
   already claim to cover "the block's own content ... and the
   foreground `image`, a plain inline replaced element", but a `display:
   block` image doesn't behave as one. `inline-block` keeps the same
   practical sizing (still a proper box for `max-width`/`height: auto`
   sizing, no text wrapping around it) while remaining subject to
   `text-align` like a real inline replaced element — letting
   `image_top`/`image_bottom`/`image_only`'s now-`stretch`-ed (full-width)
   `.content-block__media` figure actually center (or left/right-align)
   the image inside it via `text_align`, instead of it always hugging the
   figure's left edge regardless of that setting. `vertical-align: top`
   avoids the small bottom whitespace gap `inline`/`inline-block` images
   get from their default baseline alignment. */
.content-block__media .content-block__image {
  display: inline-block;
  vertical-align: top;
  max-width: 100%;
  height: auto;
}
.content-block__media-row .content-block__text {
  flex: 1 1 0%;
  min-width: 0;
}
.content-block__caption {
  margin: var(--vds-spacing-4) 0 0;
  color: var(--vds-fg-secondary, currentColor);
  font-size: 0.875em;
}

/* A side-by-side media row is illegible squeezed onto a phone-width
   column — stack image-then-text instead, same as `image_top` would
   (matches the `.content-block` mobile override just below). */
@media (max-width: 767px) {
  .content-block--image_left .content-block__media-row,
  .content-block--image_right .content-block__media-row {
    flex-direction: column;
  }
  .content-block--image_left .content-block__media,
  .content-block--image_right .content-block__media {
    max-width: 100%;
  }
}

/* Matches the existing VDS mobile breakpoint (static/vendor/vds/tokens.mobile.css) —
   a 1/4-width chart or KPI card is illegible on a phone regardless of its
   declared column_width. */
@media (max-width: 767px) {
  /* `tight` blocks (page header title/nav/controls) size to their content
     instead — forcing them to 100% stacked every nav item on its own row. */
  .content-block:not(.content-block--width-tight):not(.content-block--cta_button) { flex-basis: 100% !important; }
}

/* Optional icon badge (COMT-211) shown above a block's own title, e.g.
   a capability/services card's icon — a small square, rounded,
   brand-primary-colored badge matching the reference design's card
   icons, rather than a bare unstyled <vds-icon>. `display: inline-flex`
   (not block) so it sits naturally above the title text without
   stretching to the block's own width. */
.content-block__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  color: var(--agency-accent-fg, var(--vds-fg-white));
  background-color: var(--agency-accent-bg, var(--vds-bg-brand-primary));
}

/* Shared by transparency_pages.html's standalone list and
   _content_blocks.html's topic_pages_list block (COMT-141) — one visual
   treatment for "a link to a published Topic Page," not two. */
.topic-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--vds-spacing-16);
}

.topic-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--vds-spacing-8);
  background: var(--vds-bg-secondary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  padding: var(--vds-spacing-16);
  color: var(--vds-fg-primary);
  text-decoration: none;
  position: relative;
}

.topic-card:hover {
  border-color: var(--vds-bg-brand-primary);
}

.topic-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--vds-radius-8);
  color: var(--agency-accent-fg, var(--vds-fg-white));
  background-color: var(--agency-accent-bg, var(--vds-bg-brand-primary));
}

.topic-card__title {
  font-weight: 600;
}

.topic-card__description {
  color: var(--vds-fg-secondary);
  font-size: 0.875rem;
}

.topic-card__arrow {
  position: absolute;
  top: var(--vds-spacing-16);
  right: var(--vds-spacing-16);
  color: var(--vds-fg-secondary);
}

/* A card with a photo (a hand-picked link card's own, or the photo a
   topic-page list shows on every card) puts it edge to edge across the top
   in place of the icon; the arrow moves down beside the text. */
.topic-card--photo {
  overflow: hidden;
}

.topic-card__media {
  position: relative;
  display: block;
  flex: none;
  box-sizing: border-box;
  width: calc(100% + 2 * var(--vds-spacing-16));
  height: auto;
  margin: calc(-1 * var(--vds-spacing-16)) calc(-1 * var(--vds-spacing-16)) var(--vds-spacing-4);
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--vds-bg-tertiary, var(--vds-bg-secondary));
}

/* No photo yet: a soft "photo" illustration in the grey 16:9 slot, tinted
   from the text colour so it reads in light and dark themes. */
.topic-card__media--placeholder::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: min(36%, 6rem);
  aspect-ratio: 64 / 52;
  background: color-mix(in srgb, var(--vds-fg-primary) 16%, transparent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 52' fill='none' stroke='black' stroke-width='3.5' stroke-linejoin='round'%3E%3Cpath d='M14 13.5 39.5 5l3 8.5'/%3E%3Cpath d='M14 13.5 4 16.5l8 26'/%3E%3Crect x='14' y='15' width='46' height='33' rx='1.5'/%3E%3Ccircle cx='26' cy='25' r='4' fill='black' stroke='none'/%3E%3Cpath d='M18 44 32 32l7 6 7-9 10 15Z' fill='black' stroke='none'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 52' fill='none' stroke='black' stroke-width='3.5' stroke-linejoin='round'%3E%3Cpath d='M14 13.5 39.5 5l3 8.5'/%3E%3Cpath d='M14 13.5 4 16.5l8 26'/%3E%3Crect x='14' y='15' width='46' height='33' rx='1.5'/%3E%3Ccircle cx='26' cy='25' r='4' fill='black' stroke='none'/%3E%3Cpath d='M18 44 32 32l7 6 7-9 10 15Z' fill='black' stroke='none'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Absolutely placed, so a photo of any shape fills the 16:9 box instead of
   sizing it. */
.topic-card__image {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}

.topic-card--photo .topic-card__arrow {
  top: auto;
  bottom: var(--vds-spacing-16);
}

/* Hub link groups (portal/hub_content.py): a hand-picked link card is drawn
   as a topic card, so a group that lists topic pages automatically and one
   of hand-picked service links read as the same kind of grid. */
.topic-card__title,
.topic-card__description {
  padding-inline-end: var(--vds-spacing-24);
}

.link-card {
  box-sizing: border-box;
  height: 100%;
}

.link-card--no-link {
  cursor: default;
}

.link-card--no-link:hover {
  border-color: var(--vds-border-base);
}

.link-card__missing {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  margin-top: auto;
  color: var(--vds-fg-warning, var(--vds-fg-secondary));
  font-size: 0.8125rem;
  font-weight: 400;
}

/* An automatic topic-page list spans its group's whole grid and uses the
   group's own column counts, so its cards line up with hand-picked ones. */
.content-section:not(.content-section--hub-footer) .content-block-row--grid > .content-block--topic_pages_list {
  grid-column: 1 / -1;
}

.content-section:not(.content-section--hub-footer) .content-block-row--grid > .content-block--topic_pages_list .topic-card-grid {
  grid-template-columns: repeat(var(--row-cols), minmax(0, 1fr));
}

@container (max-width: 760px) {
  .content-section:not(.content-section--hub-footer) .content-block-row--grid > .content-block--topic_pages_list .topic-card-grid {
    grid-template-columns: repeat(var(--row-cols-tablet), minmax(0, 1fr));
  }
}

@container (max-width: 480px) {
  .content-section:not(.content-section--hub-footer) .content-block-row--grid > .content-block--topic_pages_list .topic-card-grid {
    grid-template-columns: repeat(var(--row-cols-mobile), minmax(0, 1fr));
  }
}

.hub-group-note {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-secondary);
  font-size: 0.8125rem;
}

.hub-group-note--empty {
  justify-content: center;
  padding-block: var(--vds-spacing-24);
}

/* "Linked card" block layout (COMT-210, reworked per COMT-210 follow-up
   for the "Services for your community" card grid, screenshot-matched
   design): `title` renders as a colored bar across the top, `image`
   sits directly under it at its own natural/`image_height` size (COMT-
   236 follow-up bugfix — this used to `flex: 1 1 auto` to fill
   whatever vertical space the row's stretch-to-tallest-sibling default
   left over, which just showed up as a dead gap between the photo and
   the caption below it, not a taller photo; see `.content-block--
   linked_card` below for the actual fix, opting this block out of that
   stretch instead), `body_text` is a bold caption directly under the
   photo, and `icon` (default `arrow-right`) sits pinned to the card's
   own bottom-right corner — `meta_text` is *not* rendered visibly here,
   it's the image's own `alt` text instead (see ContentBlockBase.
   meta_text's own docstring). Several such blocks lay out side by side
   already via the ordinary `.content-block-row` flex row + `column_
   width` (e.g. `one_third`) — this only styles one card's own look. */
.linked-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--vds-bg-secondary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  overflow: hidden;
  color: var(--vds-fg-primary);
  text-decoration: none;
  height: 100%;
  box-sizing: border-box;
}

.linked-card:hover {
  border-color: var(--vds-bg-brand-primary);
}

/* COMT-239: `content_theme` is a 'Linked card' block's second, independent
   theme choice — everything below `.linked-card__title` (the image and
   `.linked-card__description`), applied directly to `.linked-card` itself
   rather than via the `--content-block-accent-bg`/`-fg` indirection
   `.linked-card__title` above reads (see templates/portal/
   _content_block.html's own comment) — no double-boxing concern here
   since `.linked-card` already *is* this block's one real visible
   surface, unlike the suppressed `.content-block--themed` wrapper that
   indirection exists to work around. Source order after the plain
   `.linked-card` rule above is enough to win over its own `background`/
   `color` (same specificity, one class each) without `!important`.
   `.linked-card__title`'s own background stays exactly as set by `theme`/
   `background_color` above, completely independent of this — a card can
   freely show two different colors, one per area, same as this ticket's
   ask. Six variants, matching `.themed-*`'s own light/light/light/dark/
   dark/dark set (ContentTheme.choices) — a raw hex here would never
   track light/dark mode the way these slot-driven colors already do,
   which is exactly why `content_theme` (unlike `background_color`) has
   no equivalent free-hex option. */
.linked-card--content-primary {
  background-color: var(--agency-primary-bg, var(--vds-bg-brand-primary));
  color: var(--agency-primary-fg, var(--vds-fg-white));
}

.linked-card--content-secondary {
  background-color: var(--agency-secondary-bg, var(--vds-bg-brand-primary));
  color: var(--agency-secondary-fg, var(--vds-fg-white));
}

.linked-card--content-tertiary {
  background-color: var(--agency-tertiary-bg, var(--vds-bg-brand-primary));
  color: var(--agency-tertiary-fg, var(--vds-fg-white));
}

.linked-card--content-primary-dark {
  background-color: var(--agency-primary-bg-dark, var(--vds-bg-brand-primary));
  color: var(--agency-primary-fg-dark, var(--vds-fg-white));
}

.linked-card--content-secondary-dark {
  background-color: var(--agency-secondary-bg-dark, var(--vds-bg-brand-primary));
  color: var(--agency-secondary-fg-dark, var(--vds-fg-white));
}

.linked-card--content-tertiary-dark {
  background-color: var(--agency-tertiary-bg-dark, var(--vds-bg-brand-primary));
  color: var(--agency-tertiary-fg-dark, var(--vds-fg-white));
}

.linked-card__title {
  display: block;
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  /* COMT-238: `--content-block-accent-bg`/`-fg` are set on the ancestor
     `.content-block--themed` wrapper — either by whichever `.themed-*`
     class its `theme` resolves to, or (theme left as Inherit) directly
     inline from the block's own `background_color` hex (see
     templates/portal/_content_block.html) — and cascade down to here
     even though that wrapper's own `background-color` is forced
     transparent (see `.content-block--linked_card.content-block--themed`
     below) to avoid double-boxing the card's own separate surface. No
     theme/background_color set at all means both custom properties are
     unset, falling back to the original hardcoded brand-primary/inverse
     pairing exactly as before. */
  background: var(--content-block-accent-bg, var(--vds-bg-brand-primary));
  color: var(--content-block-accent-fg, var(--vds-fg-inverse));
  font-weight: var(--vds-font-weight-semibold);
}

.linked-card__media {
  display: block;
  flex: 0 0 auto;
}

.linked-card__image {
  display: block;
  width: 100%;
  /* No `image_height` set on the block falls back to this default —
     `object-fit: cover` still crops any source aspect ratio to it, same
     as the explicit-height case (inline `style="height: …px"` on the
     <img> itself, COMT-236, wins over this via normal CSS specificity).
     Deliberately NOT `height: 100%`/`flex: 1 1 auto` on `.linked-card__
     media` above any more (COMT-236 bugfix) — that stretched the media
     span to fill whatever leftover height the row's stretch-to-tallest-
     sibling default (`.content-block-row`'s unset `align-items`) left
     over, which rendered as a dead gap between the photo and the
     caption below it since the <img> itself still only ever painted
     its own fixed/default height inside that taller span. */
  height: 200px;
  object-fit: cover;
}

.linked-card__description {
  display: block;
  padding: var(--vds-spacing-12) var(--vds-spacing-48) var(--vds-spacing-12) var(--vds-spacing-16);
  font-weight: var(--vds-font-weight-semibold);
}

.linked-card__icon {
  position: absolute;
  right: var(--vds-spacing-16);
  bottom: var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
}

/* In-place editing (COMT-149) — corner-positioned trigger on any editable
   element (Section, Block), independent of whether that element renders a
   header row (an untitled Section renders none at all). Idle opacity is a
   faint "there's something here" hint, not a hard hover-reveal (confirmed
   via the visual companion during Plan 2 brainstorming) — full opacity on
   :hover and :focus-visible (the keyboard-equivalent; :hover alone locks
   keyboard users out, since they can't hover). */
.edit-trigger {
  position: absolute;
  top: var(--vds-spacing-8);
  right: var(--vds-spacing-8);
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  /* 10% darker than --vds-fg-primary (COMT-233 follow-up) — a bit more
     visual weight on the edit/add/move glyphs than the plain foreground
     token gives them. */
  color: color-mix(in srgb, var(--vds-fg-primary) 90%, black);
  cursor: pointer;
  opacity: var(--edit-indicator-baseline-opacity);
  transition: opacity 0.2s ease;
}

.edit-trigger:hover,
.edit-trigger:focus-visible {
  opacity: 1;
}

/* The vds-icon component reflects an empty `title=""` attribute onto its
   own host element by default. Since the icon visually fills the button,
   the mouse lands on the icon first — and a browser's native tooltip
   uses the innermost hovered element's own `title`, so that empty string
   was winning over the button's real `title` (COMT-152), suppressing our
   new descriptive tooltips entirely. `pointer-events: none` lets hover
   (and click) pass through to the button underneath instead. */
.edit-trigger vds-icon,
.move-trigger vds-icon,
.add-trigger vds-icon {
  pointer-events: none;
}

/* One cluster (COMT-151) for Section/Block: move-earlier, edit,
   move-later, in that DOM order, sitting together in the corner
   .edit-trigger used to occupy alone — rather than two separate
   corners, so "before/after the edit button" has an actual visual
   meaning, not just a DOM-order one. Overrides the two buttons' own
   `position: absolute` (each still useful standalone — see
   .tab-strip__item's static override below for the same pattern) so
   flexbox can lay them out relative to each other instead. */
.content-actions {
  position: absolute;
  top: var(--vds-spacing-8);
  right: var(--vds-spacing-8);
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
}

.content-actions .edit-trigger,
.content-actions .move-trigger {
  position: static;
  top: auto;
  right: auto;
  left: auto;
}

/* A Tab strip button is a small inline pill, not a large container — the
   corner-overlay treatment above doesn't fit it, so its trigger sits
   statically beside the label instead (still the same idle/hover opacity
   behavior via the shared .edit-trigger rule above). */
.tab-strip__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
}

.tab-strip__item .edit-trigger {
  position: static;
  width: 1.5rem;
  height: 1.5rem;
}

/* "+ Add ___" triggers (COMT-150; icon-only per COMT-200) — plain
   <button>, matching .edit-trigger's own precedent of a plain HTML
   control, but NOT for the ARIA-stateful-toggle reason CLAUDE.md
   documents for .edit-trigger's siblings: these are plain htmx GET
   buttons, not toggle/disclosure controls. They stay plain HTML under
   CLAUDE.md's more general fallback rule instead — no VDS "add to list"
   component exists in this vendor bundle. Icon-only (COMT-200) — the
   visible label moved to aria-label/title so screen readers and
   tooltips still get the full "Add a new ___ to this ___" context each
   call site sets, even though the button itself shows only a glyph
   now. Same idle-faint/hover-reveal opacity treatment as .edit-trigger
   (COMT-200 follow-up) — now that the button is icon-only rather than a
   labeled pill, full-opacity-always reads as more chrome-heavy than the
   rest of the editing affordances, so it gets the same "there's
   something here" idle hint instead. */
.add-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-8);
  width: 100%;
  min-height: 2.75rem;
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  margin-top: var(--vds-spacing-8);
  border: 2px dashed var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: transparent;
  color: var(--vds-fg-secondary);
  font: inherit;
  font-size: 0.875rem;
  font-weight: var(--vds-font-weight-medium, 500);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.add-trigger:hover,
.add-trigger:focus-visible {
  border-color: #60a5fa;
  background: rgb(37 99 235 / 6%);
  color: #2563eb;
}

/* "+ Add Tab" (COMT-200) sits inline with the tab strip's own row
   instead of on its own line below it — see .tab-strip's flex-wrap
   rule below. It doesn't want the standalone .add-trigger's
   margin-top (that spacing was for separating a list-ending trigger
   from the list above it; here it would just misalign the button
   against the tab row it now sits beside). */
.add-trigger--tab {
  width: auto;
  min-height: 0;
  margin-top: 0;
  margin-inline-start: var(--vds-spacing-8);
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  white-space: nowrap;
  /* Tab labels wrap to different heights and sit vertically centred in the
     row, so bottom-aligning the button left it hanging below their text and
     on top of the underline. Centre it against them instead. */
  align-self: center;
}

/* "Add row" is a compact centred pill; "Add section" is a wider pill with a
   dotted rule running out from both sides, so a section-level action reads
   as a divider between sections rather than another row control. */
.add-trigger--row {
  width: auto;
  min-width: 10rem;
  margin-inline: auto;
  border-radius: var(--vds-radius-full, 999px);
}

.add-section-divider {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-16);
  margin-top: var(--vds-spacing-16);
}

.add-section-divider::before,
.add-section-divider::after {
  content: "";
  flex: 1 1 0;
  border-top: 2px dotted var(--vds-border-base);
}

.add-trigger--section {
  flex: 0 1 auto;
  width: auto;
  min-width: min(16rem, 100%);
  margin-top: 0;
  border-style: dashed;
  border-radius: var(--vds-radius-full, 999px);
  white-space: nowrap;
}

/* Drag-and-drop reordering (COMT-151) — SortableJS's own default
   class-name hooks for the placeholder left behind at the drop target
   (.sortable-ghost) and the element actually being dragged
   (.sortable-drag). No layout changes needed elsewhere: SortableJS
   reorders DOM nodes in place within whatever layout (flex/grid) a
   container already uses (content-layout-formatting, COMT-127). */
.sortable-ghost {
  outline: var(--vds-border-width-2) dashed var(--vds-border-brand);
  opacity: 0.4;
}

.sortable-drag {
  opacity: 0.8;
}

/* Move up/down/left/right buttons (COMT-151) — the keyboard-operable
   alternative to dragging. For Section/Block, these sit inside the
   shared `.content-actions` cluster alongside .edit-trigger (see that
   rule for why); the `position: absolute` here is only the standalone/
   fallback layout for a `.move-trigger` used outside that cluster (the
   Tab strip case below overrides it to `static` too). Same idle/hover-
   reveal opacity treatment as .edit-trigger (a per-element affordance,
   not a deliberate list-level action like .add-trigger). Boundary items
   hide whichever direction doesn't apply via :first-child/:last-child —
   see _content_blocks.html's own comment for why that's a CSS rule and
   not a Django template conditional (this same partial renders both a
   normal list member and a standalone OOB-swapped fragment with no
   `forloop` context available). */
.move-trigger {
  position: absolute;
  top: var(--vds-spacing-8);
  left: var(--vds-spacing-8);
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
  cursor: pointer;
  opacity: var(--edit-indicator-baseline-opacity);
  transition: opacity 0.2s ease;
}

.move-trigger:hover,
.move-trigger:focus-visible {
  opacity: 1;
}

/* Scoped to the `> .content-actions` DIRECT child, not a bare descendant
   combinator (COMT-151 bugfix) — a Block's own .content-actions is a DOM
   descendant of its parent Section too, so a plain
   `.content-section:first-child .move-trigger--up` incorrectly matched
   (and hid) every Block's own up-button whenever it happened to live in
   the first Section, not just that Section's own up-button. */
.content-section:first-child > .content-actions .move-trigger--up,
.content-block:first-child > .content-actions .move-trigger--up {
  display: none;
}

.content-section:last-child > .content-actions .move-trigger--down,
.content-block:last-child > .content-actions .move-trigger--down {
  display: none;
}

.tab-strip__item .move-trigger {
  position: static;
  width: 1.5rem;
  height: 1.5rem;
}

.tab-strip__item:first-child > .move-trigger--left,
.tab-strip__item:last-child > .move-trigger--right {
  display: none;
}

/* Persistent in-place-editing panel (COMT-149) — one instance added once
   to base.html (inside the sidecar's Selection pane), reused for every
   editable element. Hidden whenever htmx swaps empty content into it (see
   inline-editor.js) rather than toggled by a CSS class, so "hidden" and "has
   no content" can never disagree. */
.inline-editor-panel {
  padding: var(--vds-spacing-16);
  box-sizing: border-box;
}

/* The editor sidecar: a permanent right-hand rail with three tabs —
   Selection (only while something is selected), Outline and page Settings.
   Fixed below the sticky editor header, full height. */
.editor-sidecar {
  position: fixed;
  top: var(--editor-header-offset, 0px);
  right: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: var(--editor-sidecar-width, 24rem);
  max-width: 90vw;
  height: calc(100vh - var(--editor-header-offset, 0px));
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  border-left: var(--vds-border-width-1) solid var(--vds-border-base);
  box-shadow: var(--vds-shadow-sm);
}

.editor-sidecar[hidden] {
  display: none;
}

.editor-sidecar__tabs {
  display: flex;
  flex: 0 0 auto;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-secondary);
}

.editor-sidecar__tab {
  position: relative;
  display: inline-flex;
  flex: 1 0 auto;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-8);
  min-height: 2.25rem;
  padding: 0 var(--vds-spacing-8);
  border: none;
  background: transparent;
  color: var(--vds-fg-secondary, var(--vds-fg-primary));
  font: inherit;
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-medium, 500);
  cursor: pointer;
}

.editor-sidecar__tab[hidden] {
  display: none;
}

.editor-sidecar__tab:hover {
  background: color-mix(in srgb, var(--vds-bg-secondary) 82%, var(--vds-fg-primary));
  color: var(--vds-fg-primary);
}

.editor-sidecar__tab[aria-selected="true"] {
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
}

.editor-sidecar__tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: -1px;
  left: 0;
  height: 2px;
  background: #2563eb;
}

.editor-sidecar__tab:focus-visible {
  outline: 2px solid var(--vds-border-focus, #2563eb);
  outline-offset: -2px;
}

.editor-sidecar__pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.editor-sidecar__pane[hidden] {
  display: none;
}

.editor-sidecar__settings {
  padding: var(--vds-spacing-16);
}

.editor-sidecar__settings > .inline-editor-panel {
  padding: 0;
}

.editor-sidecar__languages .editor-lang-menu__form {
  max-height: none;
  overflow: visible;
  padding: 0;
}

.editor-sidecar__languages .editor-lang-menu__check {
  display: flex;
  margin: 0;
}

/* Outline: the page's structure as a tree (page > tabs > sections > rows >
   widgets). Selecting an item scrolls to and selects it on the page. */
.editor-outline {
  padding: var(--vds-spacing-8);
  font-size: 0.8125rem;
}

.editor-outline__item {
  outline: none;
}

.editor-outline__row {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  min-height: 1.75rem;
  padding: 2px var(--vds-spacing-8) 2px 2px;
  border-radius: var(--vds-radius-8, 8px);
  color: var(--vds-fg-primary);
  cursor: pointer;
}

.editor-outline__row:hover {
  background: var(--vds-bg-secondary);
}

.editor-outline__item:focus-visible > .editor-outline__row {
  outline: 2px solid var(--vds-border-focus, #2563eb);
  outline-offset: -2px;
}

.editor-outline__item[aria-selected="true"] > .editor-outline__row {
  background: color-mix(in srgb, #2563eb 14%, var(--vds-bg-primary));
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-medium, 500);
}

.editor-outline__caret {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
}

.editor-outline__caret[data-outline-toggle]:hover {
  background: color-mix(in srgb, var(--vds-fg-primary) 10%, transparent);
}

.editor-outline__caret svg {
  width: 12px;
  height: 12px;
  transition: transform 0.12s ease;
}

.editor-outline__item[aria-expanded="true"] > .editor-outline__row > .editor-outline__caret svg {
  transform: rotate(90deg);
}

.editor-outline__icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  color: var(--vds-fg-secondary, var(--vds-fg-primary));
}

.editor-outline__icon--number {
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--vds-fg-primary) 12%, var(--vds-bg-primary));
  font-size: 0.6875rem;
  font-weight: var(--vds-font-weight-medium, 500);
}

.editor-outline__label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.editor-outline__info {
  flex: 0 0 auto;
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
  font-size: 0.6875rem;
}

.editor-outline__children {
  margin-left: 0.6875rem;
  padding-left: var(--vds-spacing-4);
  border-left: var(--vds-border-width-1) solid var(--vds-border-base);
}

.editor-outline__children[hidden] {
  display: none;
}

/* Reveal-on-navigate: leave room for the sticky header and editor bar. */
body.is-editing [data-editable-hover-target] {
  scroll-margin-top: calc(var(--editor-header-offset, 0px) + 5rem);
}

/* The sidecar is a desktop affordance: on narrow screens it only appears
   (as an overlay) while something is selected. */
@media (max-width: 899px) {
  .editor-sidecar:not(:has(#inline-editor-panel:not([hidden]))) {
    display: none;
  }
}

.inline-editor-panel[hidden] {
  display: none;
}

/* COMT-149 follow-up: while the inline-editor-panel is open for a given
   Block/Section/Tab/Page, outline the real on-page element it's editing
   so it's unambiguous which element the open panel actually affects —
   especially once a page has several themed/backgrounded elements
   sitting edge-to-edge. Fixed dark blue regardless of theme/dark-mode
   (an editing affordance, not themed content) — deliberately not
   `--vds-border-focus`, which already flips to a lighter blue in dark
   mode for its own contrast reasons and would blend into a dark themed
   background here. `outline` (not `border`) so it never shifts layout,
   and `outline-offset` keeps it from sitting flush on elements that
   already draw their own border/background edge. */
.inline-editor-target--active:not([data-editable-hover-target]) {
  outline: 3px solid #1e3a8a;
  outline-offset: 2px;
}

/* The old per-element pencil/arrow cluster is replaced by the floating
   toolbar below. It stays in the DOM — the toolbar drives its buttons, and
   it is still reachable by keyboard (shown while focused). */
.content-block > .content-actions,
.content-section__inner > .content-actions {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.tab-strip__item > .edit-trigger,
.tab-strip__item > .move-trigger,
.public-page-title-row .edit-trigger {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  opacity: 0;
}

.tab-strip__item > .edit-trigger:focus-visible,
.tab-strip__item > .move-trigger:focus-visible,
.public-page-title-row .edit-trigger:focus-visible {
  width: 1.5rem;
  height: 1.5rem;
  clip-path: none;
  opacity: 1;
  z-index: 40;
}

/* A tab's own text is its label, so it gets the ring but no chip. */
[data-editable-hover-target="tab"]::before {
  display: none;
}

.content-block > .content-actions:focus-within,
.content-section__inner > .content-actions:focus-within {
  width: auto;
  height: auto;
  overflow: visible;
  clip-path: none;
  z-index: 40;
}

/* Floating toolbar over the selected block/section (editor prototype). */
.editor-toolbar {
  position: fixed;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 2px;
  border-radius: 6px;
  background: #2563eb;
  color: #fff;
  box-shadow: 0 2px 6px rgb(15 23 42 / 30%);
}

.editor-toolbar[hidden] {
  display: none;
}

.editor-toolbar__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.editor-toolbar__button:hover:not(:disabled),
.editor-toolbar__button:focus-visible {
  background: rgb(255 255 255 / 20%);
}

.editor-toolbar__button:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -2px;
}

.editor-toolbar__button:disabled {
  opacity: 0.35;
  cursor: default;
}

.editor-toolbar__button--danger:hover:not(:disabled) {
  background: #dc2626;
}

.editor-toolbar__button svg {
  width: 1rem;
  height: 1rem;
  pointer-events: none;
}

/* Edit-mode canvas (editor prototype): the page is shown as a rounded card
   on a grey canvas, and the settings panel docks to the right of it instead
   of covering the page. Only for agency members editing (templates/base.html
   sets body.is-editing). The docked layout needs room, so below 900px the
   sidecar stays an overlay. */
body.is-editing {
  --editor-sidecar-width: 24rem;
  --editor-stage-gap: var(--vds-spacing-16);
  /* The viewport space left of the fixed sidecar — the track the stage is
     centred within below. A named custom property (not just inlined into
     each calc()) so the width and both margins below all divide the exact
     same leftover slack; computing it separately in each place made
     margin-right a fixed `sidecar + 2*gap` reserve instead of floating
     with the real leftover, which looked centred relative to the left
     edge but was still offset left of the sidecar's true midpoint on a
     wide viewport (bugfix, COMT-279 follow-up). */
  --editor-stage-track-width: calc(100vw - var(--editor-sidecar-width));
  background: color-mix(in srgb, var(--vds-fg-primary) 7%, var(--vds-bg-primary));
}

/* Preview top bar: a slim black strip so the manager's controls stay out of
   the way of the live page. Not applied while editing, which keeps the
   regular agency-branded header. */
body:not(.is-editing) .app-header.app-header--editor-bar {
  --editor-bar-control-bg: #1f1f1f;
  --editor-bar-control-border: #3a3a3a;
  gap: var(--vds-spacing-4) var(--vds-spacing-12);
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  background: #000;
  color: #fff;
  border-bottom-color: #2a2a2a;
}

body:not(.is-editing) .app-header.app-header--editor-bar a:not(.chrome-nav-dropdown__link):not(.app-user-menu__link) {
  color: #fff;
}

body:not(.is-editing) .app-header.app-header--editor-bar .app-header__start,
body:not(.is-editing) .app-header.app-header--editor-bar .app-header__end,
body:not(.is-editing) .app-header.app-header--editor-bar .publish-controls {
  gap: var(--vds-spacing-4) var(--vds-spacing-8);
}

body:not(.is-editing) .app-header.app-header--editor-bar select,
body:not(.is-editing) .app-header.app-header--editor-bar .btn {
  min-height: var(--editor-bar-control-h);
  padding: 0 var(--vds-spacing-8);
  border: 1px solid var(--editor-bar-control-border);
  border-radius: var(--vds-radius-8);
  background: var(--editor-bar-control-bg);
  color: #fff;
}

body:not(.is-editing) .app-header.app-header--editor-bar select {
  color-scheme: dark;
}

body:not(.is-editing) .app-header.app-header--editor-bar .btn:hover:not(:disabled) {
  background: #2c2c2c;
}

body:not(.is-editing) .app-header.app-header--editor-bar .btn--primary {
  border-color: transparent;
  background: var(--vds-bg-brand-primary);
}

body:not(.is-editing) .app-header.app-header--editor-bar .btn--primary:hover:not(:disabled) {
  background: var(--vds-bg-brand-primary);
  filter: brightness(1.1);
}

body:not(.is-editing) .app-header.app-header--editor-bar .editor-bp-toggle {
  padding: 1px;
  border-color: var(--editor-bar-control-border);
  background: var(--editor-bar-control-bg);
}

body:not(.is-editing) .app-header.app-header--editor-bar .editor-bp-toggle__button {
  color: #fff;
}

body:not(.is-editing) .app-header.app-header--editor-bar .editor-bp-toggle__button svg {
  width: 0.9375rem;
  height: 0.9375rem;
}

body:not(.is-editing) .app-header.app-header--editor-bar .editor-bp-toggle__button[aria-pressed="true"] {
  background: #fff;
  color: #000;
}

body:not(.is-editing) .app-header.app-header--editor-bar .mode-toggle {
  border-color: var(--editor-bar-control-border);
  background: var(--editor-bar-control-bg);
}

body:not(.is-editing) .app-header.app-header--editor-bar .mode-toggle__button {
  color: #fff;
}

body:not(.is-editing) .app-header.app-header--editor-bar .mode-toggle__button[aria-pressed="false"]:hover {
  background: #2c2c2c;
}

.app-header.app-header--editor-bar .app-user-menu__trigger {
  padding: 2px var(--vds-spacing-4);
}

body:not(.is-editing) .app-header.app-header--editor-bar .app-user-menu__trigger:hover {
  background: var(--editor-bar-control-bg);
}

body.is-editing > .app-header {
  background: var(--vds-bg-primary);
}

/* Slightly smaller type (13px, between the body-xs and body-sm tokens) for
   every control in the editor bar (editing and Preview). */
.app-header.app-header--editor-bar,
.app-header.app-header--editor-bar select,
.app-header.app-header--editor-bar .btn,
.app-header.app-header--editor-bar .app-user-menu__trigger,
.app-header.app-header--editor-bar .app-user-menu__name {
  font-size: 0.8125rem;
}

/* One shared control height so selects, buttons and both toggles line up;
   Preview (the live page seen as a manager) keeps the slimmer bar. */
.app-header.app-header--editor-bar {
  --editor-bar-control-h: 2.125rem;
  padding-block: var(--vds-spacing-8);
}

.app-header.app-header--editor-bar select,
.app-header.app-header--editor-bar .btn {
  min-height: var(--editor-bar-control-h);
  padding-block: 0;
}

.app-header.app-header--editor-bar .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-inline: var(--vds-spacing-16);
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-medium);
  white-space: nowrap;
}

.app-header.app-header--editor-bar .mode-toggle__button {
  min-height: calc(var(--editor-bar-control-h) - 2px);
}

.app-header.app-header--editor-bar .editor-bp-toggle {
  padding: 0;
  overflow: hidden;
}

.app-header.app-header--editor-bar .editor-bp-toggle__button {
  height: calc(var(--editor-bar-control-h) - 2px);
  border-radius: 0;
}

/* The back link's short "Back" label is only for the narrowest phones (below). */
.app-header--editor-bar .editor-bar-back__short {
  display: none;
}

/* Phone-width editor bar: one row in Preview (back link, mode toggle, avatar),
   two while editing (back link + avatar, then the mode toggle / language /
   more / Publish) instead of ~5 wrapped rows. The
   preview-size toggle and the draft-status line are dropped (a phone can't
   preview a wider frame), and the bar scrolls away rather than staying
   sticky so it doesn't cover a quarter of the screen. */
@media (max-width: 767px) {
  .app-header.app-header--editor-bar {
    position: relative;
    flex-wrap: wrap;
    gap: var(--vds-spacing-8);
  }

  .app-header--editor-bar .app-header__start {
    flex: 1 1 0;
    min-width: 0;
  }

  .app-header.app-header--editor-bar a.editor-bar-back {
    max-width: 100%;
    white-space: nowrap;
  }

  /* Below ~480px the full label no longer fits beside the toggle and
     avatar, so fall back to a plain "Back". */
  @media (max-width: 480px) {
    .app-header--editor-bar .editor-bar-back__label {
      display: none;
    }

    .app-header--editor-bar .editor-bar-back__short {
      display: inline;
    }
  }

  /* The avatar still opens the menu; the full email would crowd out the
     back link on a phone. */
  .app-header--editor-bar .app-user-menu__name {
    display: none;
  }

  .app-header--editor-bar .app-header__end {
    display: contents;
  }

  .app-header--editor-bar .publish-controls {
    order: 1;
    flex: 0 0 auto;
    gap: var(--vds-spacing-8);
  }

  .app-header--editor-bar .app-user-menu {
    order: 2;
  }

  /* Preview is just the Edit/Preview toggle, so the whole bar fits one row
     (back link, toggle, avatar). Editing adds language / more / Publish and
     keeps them on a second row. */
  body.is-editing .app-header--editor-bar .publish-controls {
    flex: 1 0 100%;
  }

  body.is-editing .app-header--editor-bar .app-user-menu {
    order: 1;
  }

  body.is-editing .app-header--editor-bar .publish-controls {
    order: 2;
  }

  .app-header.app-header--editor-bar .publish-controls .btn,
  .app-header.app-header--editor-bar .publish-controls .mode-toggle__button {
    padding-inline: var(--vds-spacing-8);
  }

  .app-header--editor-bar .publish-controls .editor-bp-toggle,
  .app-header--editor-bar .publish-controls .editor-bar-status {
    display: none;
  }

  .app-header--editor-bar .publish-controls > .publish-controls__form:last-child {
    margin-left: auto;
  }

  .app-header--editor-bar .editor-bar-menu {
    position: static;
  }

  .app-header--editor-bar .editor-bar-menu__panel {
    left: var(--vds-spacing-16);
    right: var(--vds-spacing-16);
    width: auto;
    min-width: 0;
    max-width: none;
  }
}

body.is-editing > .app-main,
body.is-editing > .app-footer {
  box-sizing: border-box;
  width: calc(100% - 2 * var(--vds-spacing-16));
  max-width: 1240px;
  margin-inline: auto;
  background: var(--vds-bg-primary);
}

body.is-editing > .app-main {
  flex: 0 0 auto;
  margin-top: var(--vds-spacing-16);
  padding: var(--vds-spacing-16);
  border-radius: var(--vds-radius-12, 12px) var(--vds-radius-12, 12px) 0 0;
}

body.is-editing > .app-footer {
  position: static;
  margin-bottom: var(--vds-spacing-16);
  border-radius: 0 0 var(--vds-radius-12, 12px) var(--vds-radius-12, 12px);
}

@media (min-width: 900px) {
  body.is-editing > .app-main,
  body.is-editing > .app-footer {
    --editor-stage-actual-width: min(var(--editor-stage-width, 1240px), calc(var(--editor-stage-track-width) - 2 * var(--editor-stage-gap)));
    width: var(--editor-stage-actual-width);
    max-width: none;
    /* Centre the stage in the track left of the sidecar rather than
       hugging the left edge — on a wide viewport, the full-width (1240px)
       stage left a large empty gap between it and the sidecar instead of
       splitting that slack evenly on both sides (bugfix, COMT-279
       follow-up). */
    margin-left: max(var(--editor-stage-gap), calc((var(--editor-stage-track-width) - var(--editor-stage-actual-width)) / 2));
    margin-right: calc(var(--editor-sidecar-width) + max(var(--editor-stage-gap), calc((var(--editor-stage-track-width) - var(--editor-stage-actual-width)) / 2)));
  }
}

/* Hover/selected feedback for agency members: a rounded ring drawn just
   outside the closest editable wrapper (never altering the element's own
   corners or background), plus a small label chip. Clicking selects it and
   opens its panel. Full-width bands (site header, full-width sections, the
   page itself) have no room outside them, so their ring sits just inside.
   Fixed blues like the rule above: editing chrome, not themed content. */
[data-editable-hover-target] {
  position: relative;
}

[data-editable-hover-target].inline-editor-target--hover::after,
[data-editable-hover-target].inline-editor-target--active::after {
  content: "";
  position: absolute;
  inset: -6px;
  z-index: 10;
  box-sizing: border-box;
  border: 1px solid #60a5fa;
  border-radius: 10px;
  background: rgb(37 99 235 / 4%);
  pointer-events: none;
}

/* Sections are a larger selection target than rows — the desktop baseline keeps
   the row in flex flow, while the column layout is the responsive override. The
   section ring therefore sits a little wider than the row so it reads as a
   deliberate parent container rather than a tight line around the row's content. */
.content-section[data-editable-hover-target].inline-editor-target--hover::after,
.content-section[data-editable-hover-target].inline-editor-target--active::after {
  inset: -12px;
  border-radius: 14px;
}

[data-editable-hover-target].inline-editor-target--active::after {
  border: 4px solid #2563eb;
  background: none;
}

.content-section--full-width.inline-editor-target--hover::after,
.content-section--full-width.inline-editor-target--active::after,
[data-editable-hover-target="page"].inline-editor-target--hover::after,
[data-editable-hover-target="page"].inline-editor-target--active::after {
  inset: 4px;
  border-radius: 8px;
}

[data-editable-hover-target].inline-editor-target--hover::before,
[data-editable-hover-target].inline-editor-target--active::before {
  content: attr(data-editable-label);
  position: absolute;
  top: -13px;
  left: 8px;
  z-index: 20;
  max-width: 60%;
  padding: 1px 6px;
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-neutral-strong, #404040);
  color: var(--vds-fg-white, #fff);
  font: 500 0.6875rem/1.4 var(--vds-font-family-body), system-ui, sans-serif;
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.content-section--full-width.inline-editor-target--hover::before,
.content-section--full-width.inline-editor-target--active::before,
[data-editable-hover-target="page"].inline-editor-target--hover::before,
[data-editable-hover-target="page"].inline-editor-target--active::before {
  top: 8px;
  left: 12px;
}

[data-editable-hover-target].inline-editor-target--active::before {
  background: #2563eb;
}

/* The site header is a fixed, global band, so it gets no move toolbar (see
   showToolbarFor in inline-editor.js). Its ring sits just inside the band and
   is thicker than the Sections' hover line; the label chip rides on the
   ring's top edge at the right. The doubled selector outranks the generic
   Section rules above. */
.content-section.content-section--site-header[data-editable-hover-target].inline-editor-target--hover::after,
.content-section.content-section--site-header[data-editable-hover-target].inline-editor-target--active::after {
  inset: 6px;
  border-width: 3px;
  border-radius: 12px;
}

.content-section.content-section--site-header[data-editable-hover-target].inline-editor-target--active::after {
  border-width: 4px;
}

.content-section.content-section--site-header[data-editable-hover-target].inline-editor-target--hover::before,
.content-section.content-section--site-header[data-editable-hover-target].inline-editor-target--active::before {
  content: none;
}

.site-header-editor-label {
  display: none;
  position: absolute;
  top: 0;
  right: var(--vds-spacing-24);
  z-index: 20;
  align-items: center;
  gap: var(--vds-spacing-4);
  padding: 1px 6px;
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-neutral-strong, #404040);
  color: var(--vds-fg-white);
  font-size: 0.6875rem;
  font-weight: 500;
  pointer-events: none;
}

.inline-editor-target--hover > .site-header-editor-label,
.inline-editor-target--active > .site-header-editor-label {
  display: inline-flex;
}

.inline-editor-target--active > .site-header-editor-label {
  background: #2563eb;
}

.site-header-editor-label svg {
  width: 0.875rem;
  height: 0.875rem;
}

.site-header-editor-icon svg {
  width: 100%;
  height: 100%;
}

/* Edit mode only: a faint grey wash on every plain Section (no theme or
   background of its own) so the page's section boundaries are visible.
   Themed/hero sections already show their own colour; the site header is
   chrome. A foreground-colour mix, not a fixed grey, so it reads in light and
   dark mode. The small gap keeps two neighbouring plain sections apart. */
body.is-editing .content-section:not(.content-section--hero):not(.content-section--site-header) {
  background: color-mix(in srgb, var(--vds-fg-primary) 2.5%, transparent);
  margin-bottom: var(--vds-spacing-12);
  /* Padding plus an equal negative inline margin: the wash sits a little wider
     than the content area, so widgets still line up with the page's own edges. */
  padding: var(--vds-spacing-12);
  margin-inline: calc(-1 * var(--vds-spacing-12));
}

body.is-editing .content-section.inline-editor-target--hover,
body.is-editing .content-section.inline-editor-target--active,
body.is-editing .content-section:has(.inline-editor-target--hover),
body.is-editing .content-section:has(.inline-editor-target--active) {
  overflow: visible;
  z-index: 2;
}

body.is-editing .content-block.inline-editor-target--hover,
body.is-editing .content-block.inline-editor-target--active {
  z-index: 2;
}

.content-block.inline-editor-target--hover > .content-actions .edit-trigger,
.content-block.inline-editor-target--hover > .content-actions .move-trigger,
.content-section.inline-editor-target--hover > .content-section__inner > .content-actions .edit-trigger,
.content-section.inline-editor-target--hover > .content-section__inner > .content-actions .move-trigger,
.page-width.inline-editor-target--hover > .page-heading .edit-trigger,
.page-width.inline-editor-target--hover > .topic-page-heading .edit-trigger {
  opacity: 0.8;
  box-sizing: border-box;
  border: var(--vds-border-width-1) solid var(--vds-border-brand);
  background: color-mix(in srgb, var(--vds-bg-secondary) 88%, var(--vds-border-brand));
  box-shadow: 0 1px 3px rgb(15 23 42 / 20%);
}

/* Stacked label-above-input, full-width fields (COMT-233 design-match
   follow-up) — mirrors `.card form`'s own treatment above (branding.html,
   the full-page editor forms), which this panel never inherited since
   it isn't itself inside a `.card`: labels/inputs were rendering with
   plain unstyled browser defaults (label and input side by side on one
   line, input auto-width from its `size` attribute) instead of a real
   label above a full-width control. `type="color"` is excluded, same as
   `.card form input[type="color"]` — it's a small fixed-size swatch, not
   a full-width text field. */
.inline-editor-panel form p,
.editor-form-fields > p,
.editor-form-fields section > p {
  margin: 0 0 var(--vds-spacing-16);
}

.inline-editor-panel form label,
.editor-form-fields label {
  margin: 0;
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* Groups a field's label with its info icon on one line, above the
   input (COMT-233 design-match follow-up) — the icon previously
   trailed the input itself (to its right); nesting it inside the
   <label> instead would put a focusable, tabindex="0" element (see
   help-tooltip.js) inside the label's own click-to-focus-the-input
   area, so it's a plain sibling `<span>` wrapper around both instead,
   not the `<label>` itself. `.field-help`'s own `margin-left` (further
   down this file, shared with branding.html's un-rowed icons) is
   zeroed out here since `gap` already places it correctly. */
.inline-editor-panel form .inline-editor-panel__field-label,
.editor-form-fields .inline-editor-panel__field-label {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  margin-bottom: var(--vds-spacing-4);
}

.inline-editor-panel form .inline-editor-panel__field-label .field-help,
.editor-form-fields .inline-editor-panel__field-label .field-help {
  margin-left: 0;
}

/* Field outlines sit one step darker than the card/section dividers
   (--vds-border-base) so inputs read as the interactive elements. */
:root {
  --editor-field-border: color-mix(in srgb, var(--vds-border-base) 45%, var(--vds-border-secondary));
}

.inline-editor-panel form input[type="text"],
.editor-form-fields input[type="text"],
.inline-editor-panel form input[type="email"],
.editor-form-fields input[type="email"],
.inline-editor-panel form input[type="password"],
.editor-form-fields input[type="password"],
.inline-editor-panel form input[type="number"],
.editor-form-fields input[type="number"],
.inline-editor-panel form input[type="date"],
.editor-form-fields input[type="date"],
.inline-editor-panel form input[type="url"],
.editor-form-fields input[type="url"],
.inline-editor-panel form textarea,
.editor-form-fields textarea,
.inline-editor-panel form select,
.editor-form-fields select {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--editor-field-border);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.inline-editor-panel form input[type="text"]:focus,
.editor-form-fields input[type="text"]:focus,
.inline-editor-panel form input[type="email"]:focus,
.editor-form-fields input[type="email"]:focus,
.inline-editor-panel form input[type="password"]:focus,
.editor-form-fields input[type="password"]:focus,
.inline-editor-panel form input[type="number"]:focus,
.editor-form-fields input[type="number"]:focus,
.inline-editor-panel form input[type="date"]:focus,
.editor-form-fields input[type="date"]:focus,
.inline-editor-panel form input[type="url"]:focus,
.editor-form-fields input[type="url"]:focus,
.inline-editor-panel form textarea:focus,
.editor-form-fields textarea:focus,
.inline-editor-panel form select:focus,
.editor-form-fields select:focus {
  outline: none;
  border-color: var(--vds-bg-brand-primary);
  box-shadow: 0 0 0 3px var(--vds-border-focus-outer, var(--vds-bg-brand-light));
}

/* Multi-line fields show the browser's corner drag grip; keep it vertical-only
   so a drag can't push the box wider than the sidecar. */
.inline-editor-panel form textarea,
.editor-form-fields textarea {
  resize: vertical;
  line-height: 1.4;
}

/* Input group: a fixed prefix joined to a native input (portal.forms.PrefixedTextInput),
   e.g. "/transparency/" + slug. The wrapper draws the one border and focus ring;
   the input inside drops its own so the two read as a single control. */
.input-group {
  display: flex;
  align-items: stretch;
  width: 100%;
  box-sizing: border-box;
  border: var(--vds-border-width-1) solid var(--editor-field-border);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  overflow: hidden;
}

.input-group:focus-within {
  border-color: var(--vds-bg-brand-primary);
  box-shadow: 0 0 0 3px var(--vds-border-focus-outer, var(--vds-bg-brand-light));
}

.input-group__prefix {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  padding: 0 var(--vds-spacing-12);
  border-right: var(--vds-border-width-1) solid var(--editor-field-border);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-tertiary);
  white-space: nowrap;
}

.editor-form-fields .input-group input[type="text"],
.inline-editor-panel form .input-group input[type="text"] {
  flex: 1;
  min-width: 0;
  width: auto;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.inline-editor-panel form input[type="color"],
.editor-form-fields input[type="color"] {
  width: 4rem;
  height: 2.5rem;
  padding: 0;
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  cursor: pointer;
}

/* Bugfix: the rule above's `display: block` on `select` beats the
   browser's own (very low-specificity, attribute-selector) `[hidden] {
   display: none }` default — without this, the real `<select
   class="icon-select">` that static/js/icon-select.js hides once it
   progressively-enhances it (`select.hidden = true`, COMT-220) reappears
   underneath its own custom listbox widget, rendering as two stacked
   icon pickers. `select[hidden]` is more specific than plain `select`
   here, so this wins regardless of source order. */
.inline-editor-panel form select[hidden],
.editor-form-fields select[hidden] {
  display: none;
}

[data-editable-hover-target="tab"]::after {
  display: none;
}

.editor-form-fields fieldset {
  margin: 0;
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

/* Each widget-editor collapsible section (Text/Media/Dates/Style/Advanced,
   widget-editor cleanup) is a `<vds-accordion>` — its own default styling
   already draws the header row/chevron/border; this just keeps this
   panel's own field-row spacing intact inside `<slot>` content and adds a
   bit of breathing room between stacked sections. */
.editor-form-fields .editor-section {
  display: block;
  margin-bottom: var(--vds-spacing-4);
}

.editor-form-fields .editor-section:last-child {
  border-bottom: none;
}

/* Marks the language-tabset inside a section that mixes localizable
   fields (e.g. Media's per-language `image`) with plain structural ones
   (`image_height`, `icon`) — a section that's entirely localizable
   (Text) never renders this, every field in it already is one. */
.editor-section__localizable-heading {
  margin: var(--vds-spacing-16) 0 0;
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  text-transform: uppercase;
  letter-spacing: var(--vds-letter-spacing-body-sm);
}

.language-editor {
  margin-top: var(--vds-spacing-16);
}

.language-editor__switcher {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-12);
}

.language-editor__switcher-label {
  font-weight: var(--vds-font-weight-semibold);
}

.language-editor__tablist {
  display: flex;
  align-items: center;
}

.language-editor__panel[hidden] {
  display: none;
}

/* Delete (if present) sits alone at the left; Cancel/Back and Save group
   together at the right, Save right-most (COMT-233 design-match
   follow-up) — matches the reference design's separation of the one
   destructive action from the two confirm/dismiss ones, rather than all
   four in a single left-aligned row. `space-between` still does the
   right thing when there's no Delete button (Add-flow panels): the empty
   `.inline-editor-panel__actions-left` just collapses to zero width and
   the button group is pushed to the row's right edge either way. */
.inline-editor-panel__actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-16);
}

.inline-editor-panel__actions-left,
.inline-editor-panel__actions-right {
  display: flex;
  gap: var(--vds-spacing-8);
}

/* Read-only branding-assets review block (COMT-231), only rendered in
   the page-settings panel (`show_branding_assets` context flag) — one
   .asset-browser__selection row per asset (COMT-224's own thumb+filename
   treatment, reused as-is since it's already exactly "here's the current
   asset"), a border to set it apart from the title/theme fields above,
   and a plain link (not another "Browse assets" htmx trigger) out to the
   full Branding page, where these assets are actually managed. */
.inline-editor-panel__branding {
  margin-top: var(--vds-spacing-16);
  padding-top: var(--vds-spacing-16);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.inline-editor-panel__branding-heading {
  margin: 0 0 var(--vds-spacing-8);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vds-fg-secondary);
}

.inline-editor-panel__branding .asset-browser__selection {
  margin-bottom: var(--vds-spacing-8);
}

/* Header row (COMT-233) — the panel's own "Editing X" heading plus a
   dismiss (×) button, both closer to the top-of-drawer convention a
   reviewer would expect (design reference: a titled panel with its own
   close control, not just the bottom-row Cancel button) — the bottom
   Save/Cancel/Delete row (.inline-editor-panel__actions) still does the
   exact same "discard and close" job Cancel always has; this is an
   additional, more familiar way to reach it, not a replacement. A
   bottom border marks it off from the fields below, the same divider
   role .inline-editor-panel__branding's own border-top already plays
   further down this same panel. */
.inline-editor-panel__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding-bottom: var(--vds-spacing-12);
  margin-bottom: var(--vds-spacing-16);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.inline-editor-panel__header-actions {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  flex-shrink: 0;
}

.inline-editor-panel__menu {
  position: relative;
}

.inline-editor-panel__menu-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  border-radius: var(--vds-radius-8);
  background: none;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.inline-editor-panel__menu-trigger:hover,
.inline-editor-panel__menu-trigger[aria-expanded="true"] {
  color: var(--vds-fg-primary);
}

.inline-editor-panel__menu-panel {
  position: absolute;
  top: calc(100% + var(--vds-spacing-8));
  right: 0;
  min-width: 10rem;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8);
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  box-shadow: var(--vds-shadow-md);
  z-index: 10;
}

.inline-editor-panel__menu-panel[hidden] {
  display: none;
}

.inline-editor-panel__menu-item {
  width: 100%;
  box-sizing: border-box;
  padding: var(--vds-spacing-8);
  border: none;
  border-radius: var(--vds-radius-8);
  background: none;
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.inline-editor-panel__menu-item:hover {
  background: var(--vds-border-base);
}

.inline-editor-panel__menu-item--danger {
  color: var(--vds-fg-error-on-low);
}

.inline-editor-panel__menu-divider {
  margin: 0;
  border: none;
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.inline-editor-panel__close {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: none;
  background: none;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.inline-editor-panel__close:hover {
  color: var(--vds-fg-primary);
}

/* Names which kind of element (and, for a Block, which layout/widget)
   the open panel is editing (COMT-149 follow-up, "which block am I
   editing?" self-service feedback) — a real heading now (COMT-233
   design-match follow-up: bold, sentence case, primary-color), not the
   small-caps/secondary-color "orientation copy" treatment it started
   as, matching the reference design's plain bold "Edit content" panel
   title. No margin of its own any more (COMT-233) — spacing now comes
   from .inline-editor-panel__header, its own flex parent. */
.inline-editor-panel__heading {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--vds-fg-primary);
}

.inline-editor-panel__identity {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-12);
  min-width: 0;
}

.inline-editor-panel__type-icon {
  flex-shrink: 0;
  color: var(--vds-fg-secondary);
}

.inline-editor-panel__identity .inline-editor-panel__heading {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.25;
}

.inline-editor-panel__identity .inline-editor-panel__subheading {
  margin: 0;
  line-height: 1.3;
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
}

/* "Show on" device toggles: native checkboxes drawn as chips. */
.toggle-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
}

.toggle-chip {
  position: relative;
  cursor: pointer;
}

.toggle-chip input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.toggle-chip span {
  display: block;
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: 999px;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.toggle-chip input:checked + span {
  border-color: var(--vds-border-brand);
  background: var(--vds-bg-brand-light);
  color: var(--vds-fg-brand-on-low);
  font-weight: 600;
}

.toggle-chip input:focus-visible + span {
  outline: 2px solid #2563eb;
  outline-offset: 1px;
}

/* Add widget modal body (#widget-picker-modal, static/js/widget-picker.js):
   a search box, then Text / Charts / Special groups of illustrated tiles. */
.widget-picker__intro {
  margin: 0 0 var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
}

.widget-picker__search {
  box-sizing: border-box;
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.widget-picker__search:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 1px;
}

.widget-picker__tabs {
  display: flex;
  gap: var(--vds-spacing-4);
  margin: var(--vds-spacing-16) 0 var(--vds-spacing-12);
  box-shadow: inset 0 calc(-1 * var(--vds-border-width-1)) 0 var(--vds-border-base);
}

.widget-picker__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--vds-fg-secondary);
  font: inherit;
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.widget-picker__tab:hover {
  color: var(--vds-fg-primary);
}

.widget-picker__tab[aria-selected="true"] {
  border-bottom-color: #2563eb;
  color: var(--vds-fg-primary);
}

.widget-picker__tab:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: -2px;
}

.widget-picker__tab.is-empty {
  opacity: 0.5;
}

.widget-picker__count {
  padding: 0 var(--vds-spacing-8);
  border-radius: 999px;
  background: color-mix(in srgb, var(--vds-fg-primary) 8%, transparent);
  font-size: var(--vds-font-size-body-sm);
  font-weight: normal;
}

.widget-picker__panel[hidden] {
  display: none;
}

.widget-picker__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
  gap: var(--vds-spacing-12);
}

.widget-picker__empty {
  margin: var(--vds-spacing-24) 0 0;
  color: var(--vds-fg-secondary);
  text-align: center;
}

.widget-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.widget-tile[hidden] {
  display: none;
}

.widget-tile:hover,
.widget-tile:focus-visible {
  border-color: #93c5fd;
  box-shadow: inset 0 0 0 1px #60a5fa;
  outline: none;
}

.widget-tile__preview {
  display: block;
  width: 100%;
  margin-bottom: var(--vds-spacing-4);
  border-radius: var(--vds-radius-8);
  background: color-mix(in srgb, var(--vds-fg-primary) 4%, transparent);
}

.widget-tile__title {
  font-weight: var(--vds-font-weight-semibold);
}

.widget-tile__kind {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.widget-tile__description {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* Greyscale widget illustration (templates/widgets/_preview.html) — the
   picker tile and the empty state of a widget with nothing to show. Shapes
   are currentColor, so the grey is set once here. */
.widget-preview {
  display: block;
  width: 100%;
  height: auto;
  max-height: 7.5rem;
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
}

.widget-preview__knockout {
  fill: var(--vds-bg-primary);
  fill-opacity: 0.85;
}

.widget-preview__knockout-stroke {
  stroke: var(--vds-bg-primary);
  stroke-opacity: 0.85;
}

.widget--fallback .widget-preview {
  max-width: 14rem;
  margin: 0 auto var(--vds-spacing-8);
  opacity: 0.7;
}

/* Editor-only hint in a block with nothing in it yet, so a fresh widget is a
   readable box rather than a zero-height one. Never rendered for visitors. */
.content-block__placeholder {
  margin: 0;
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
  font-style: italic;
  opacity: 0.8;
}

/* Public Hub/Topic page tab strip (COMT-112/118) — switches which tab's
   sections are visible, static/js/tab-strip.js. Native <button
   role="tab">, not vds-button (CLAUDE.md's ARIA-stateful-controls
   exception, same reasoning as the sidebar's own toggle/user-menu
   triggers, COMT-93). */
.tab-strip__list {
  display: flex;
  gap: 36px;
}

@media (max-width: 767px) {
  .tab-strip__list {
    gap: var(--vds-spacing-24);
  }
}

/* One scrolling-tab-row treatment shared by every tab set (page tab strip,
   editor sidecar, widget picker, language switcher, admin record/product
   tabs, dataset table tabs). Tabs never wrap or squash their labels: each
   keeps its natural width on a single line and the row scrolls sideways when
   it runs out of room — at any viewport width, in editor and public views
   alike. The scrollbar is hidden (touch/trackpad/shift-wheel/keyboard focus
   still scroll it) and, where the browser supports scroll-driven animations,
   the clipped edge fades so it's clear there's more. tab-strip.js scrolls the
   selected page tab into view. */
.tab-strip__list,
.editor-sidecar__tabs,
.widget-picker__tabs,
.language-editor__tablist,
.product-detail__tabs,
.tenant-record__tabs,
.dataset-browser__table-tab {
  min-width: 0;
  max-width: 100%;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.tab-strip__list::-webkit-scrollbar,
.editor-sidecar__tabs::-webkit-scrollbar,
.widget-picker__tabs::-webkit-scrollbar,
.language-editor__tablist::-webkit-scrollbar,
.product-detail__tabs::-webkit-scrollbar,
.tenant-record__tabs::-webkit-scrollbar,
.dataset-browser__table-tab::-webkit-scrollbar {
  display: none;
}

.tab-strip__list > *,
.editor-sidecar__tabs > *,
.widget-picker__tabs > *,
.language-editor__tablist > *,
.product-detail__tabs > *,
.tenant-record__tabs > *,
.dataset-browser__table-tab > * {
  flex-shrink: 0;
  white-space: nowrap;
}

/* The page tab strip shares its row with the pinned "Add tab" button: the list
   hugs its tabs and only shrinks (then scrolls) when the row runs out of room. */
.tab-strip__list {
  flex: 0 1 auto;
}

@media (prefers-reduced-motion: no-preference) {
  .tab-strip__list,
  .editor-sidecar__tabs,
  .widget-picker__tabs,
  .language-editor__tablist {
    scroll-behavior: smooth;
  }
}

@property --tab-fade-start {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@property --tab-fade-end {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@keyframes tab-fade-start {
  from { --tab-fade-start: 0px; }
  to { --tab-fade-start: 1.75rem; }
}

@keyframes tab-fade-end {
  from { --tab-fade-end: 1.75rem; }
  to { --tab-fade-end: 0px; }
}

@supports (animation-timeline: scroll(self x)) {
  .tab-strip__list,
  .editor-sidecar__tabs,
  .widget-picker__tabs,
  .language-editor__tablist,
  .product-detail__tabs,
  .tenant-record__tabs,
  .dataset-browser__table-tab {
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 var(--tab-fade-start),
      #000 calc(100% - var(--tab-fade-end)),
      transparent 100%
    );
    animation: tab-fade-start linear both, tab-fade-end linear both;
    animation-timeline: scroll(self x);
    animation-range: 0% 1.75rem, calc(100% - 1.75rem) 100%;
  }
}

/* Header row (COMT-200): "+ Add Tab" sits inline with the tab strip's
   own row instead of on its own line below it, sharing the row's
   underline (moved here from .tab-strip__list, which no longer spans
   the row's full width now that the add-trigger sits beside it) —
   align-items: flex-end keeps the icon-only add button's bottom edge
   flush with the tab buttons' own bottom edge/underline rather than
   vertically centered against their taller focus/hover box. */
.tab-strip__row {
  display: flex;
  align-items: flex-end;
  gap: var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  margin-bottom: var(--vds-spacing-32);
}

/* Topic data surfaces are neutral layout chrome, not an agency colour slot.
   Explicit page themes opt out; authored child surfaces keep their own styles. */
.topic-page:not(.page-width--themed) {
  --topic-data-canvas: var(--vds-bg-secondary);
  --topic-data-card: var(--vds-bg-primary);
}

.topic-page:not(.page-width--themed) .topic-data-surface:has(.tab-strip__panel, .content-section, .content-block) {
  position: relative;
  isolation: isolate;
  padding-block: var(--vds-spacing-32);
  color: var(--vds-fg-primary);
}

/* Paint to the app-main container edges without changing content widths or
   clipping sticky headers, menus, and the agency preview's sidebar. */
.topic-page:not(.page-width--themed) .topic-data-surface:has(.tab-strip__panel, .content-section, .content-block)::before,
.topic-page:not(.page-width--themed) > .tab-strip > .tab-strip__row::before {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 50%;
  width: calc(100cqw + (2 * var(--vds-spacing-16)));
  transform: translateX(-50%);
  z-index: -1;
  box-sizing: border-box;
  background: var(--topic-data-canvas);
  pointer-events: none;
}

.topic-page:not(.page-width--themed) > .tab-strip > .tab-strip__row {
  position: relative;
  isolation: isolate;
  margin-bottom: 0;
  /* The full-bleed ::before draws the rule; a row border would stack a
     second line under it across the content width only. */
  border-bottom-color: transparent;
}

.topic-page:not(.page-width--themed) > .tab-strip > .tab-strip__row::before {
  background: var(--vds-bg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

body .topic-page:not(.page-width--themed) .topic-data-surface .content-block__widget > .widget:not(:where(
  .widget--circle-map-page,
  .content-section--hero *,
  .tab-strip__panel--themed *,
  .content-block--themed *
)) {
  background: var(--topic-data-card);
  color: var(--vds-fg-primary);
  padding-inline: var(--vds-spacing-16);
}

.topic-page:not(.page-width--themed) .topic-data-surface .content-block__widget:not(.content-block__widget--no-border) > .widget:not(:where(
  .widget--circle-map-page,
  .content-section--hero *,
  .tab-strip__panel--themed *,
  .content-block--themed *
)) {
  border: var(--vds-border-width-1) solid var(--vds-border-base);
}

body:not(.is-editing) > .app-main .topic-page:not(.page-width--themed) .tab-strip__panel .content-section:not(.content-section--hero) .content-section__header--with-filters:not(:where(.tab-strip__panel--themed *)) {
  background: var(--topic-data-canvas);
}

/* The row's own margin now provides the space under the tab rule, so a
   tab's first section header (which otherwise adds its own top padding)
   doesn't stack a second gap on top of it. */
.tab-strip__panel .content-section:first-child:not(.content-section--hero):not(.content-section--site-header) .content-section__header:not(.content-section__header--with-filters) {
  padding-top: 0;
}

/* On the real (non-editing) page the tab row pins under the sticky
   .app-header while the active tab's content scrolls beneath it.
   --app-header-height is kept current by tab-strip.js since the header
   wraps taller on narrow screens. The row stays inside .tab-strip, so it
   unpins at the end of the last panel. */
body:not(.is-editing) > .app-main .tab-strip:not(.crime-map-sidebar__tabs) > .tab-strip__row {
  position: sticky;
  top: var(--sticky-tabs-top, var(--app-header-height, 4rem));
  z-index: 15;
  background: var(--vds-bg-primary);
}

/* A section's header (title, description, filters) pins under the tab row
   while that section scrolls, then hands over to the next section's header.
   --sticky-tabs-height is kept current by tab-strip.js. */
body:not(.is-editing) > .app-main .tab-strip__panel .content-section__header--with-filters {
  position: sticky;
  top: calc(var(--sticky-tabs-top, var(--app-header-height, 4rem)) + var(--sticky-tabs-height, 0px));
  z-index: 14;
  padding-block: var(--vds-spacing-12);
  background: var(--vds-bg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* The header's own bottom border replaces the divider rule on the live page, so
   the line stays attached to the header while it is pinned. */
body:not(.is-editing) > .app-main .tab-strip__panel .content-section__header--with-filters + .content-section__divider {
  display: none;
}

body:not(.is-editing) > .app-main .tab-strip__panel .content-section__header--with-filters {
  margin-bottom: var(--vds-spacing-24);
}

/* Once pinned (tab-strip.js adds --stuck), a topic page's section header
   leaves the grey data canvas and joins the white tab row as one full-bleed
   header: same surface, one rule along the bottom of the pair. */
body:not(.is-editing) > .app-main .topic-page:not(.page-width--themed) .tab-strip__panel .content-section:not(.content-section--hero) .content-section__header--with-filters.content-section__header--stuck:not(:where(.tab-strip__panel--themed *)) {
  isolation: isolate;
  background: transparent;
  border-bottom-color: transparent;
}

body:not(.is-editing) > .app-main .topic-page:not(.page-width--themed) .tab-strip__panel .content-section:not(.content-section--hero) .content-section__header--stuck:not(:where(.tab-strip__panel--themed *))::before {
  content: "";
  position: absolute;
  /* -1px tucks under the tab row (z-index 15) so a fractional row height
     can't leave a hairline of canvas between the two. */
  inset-block: -1px 0;
  left: 50%;
  width: calc(100cqw + (2 * var(--vds-spacing-16)));
  transform: translateX(-50%);
  z-index: -1;
  box-sizing: border-box;
  background: var(--vds-bg-primary);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  pointer-events: none;
}

body:not(.is-editing) > .app-main .topic-page:not(.page-width--themed) > .tab-strip:has(.tab-strip__panel:not([hidden]) .content-section:not(.content-section--hero) .content-section__header--stuck:not(:where(.tab-strip__panel--themed *))) > .tab-strip__row,
body:not(.is-editing) > .app-main .topic-page:not(.page-width--themed) > .tab-strip:has(.tab-strip__panel:not([hidden]) .content-section:not(.content-section--hero) .content-section__header--stuck:not(:where(.tab-strip__panel--themed *))) > .tab-strip__row::before {
  border-bottom-color: transparent;
}

@container page (max-width: 735px) {
  body:not(.is-editing) > .app-main .tab-strip__panel .content-section__header--with-filters {
    position: relative;
    top: auto;
    z-index: auto;
    padding-block: 0;
    border-bottom: 0;
  }

  body:not(.is-editing) > .app-main .tab-strip__panel .content-section__header--with-filters + .content-section__divider {
    display: block;
  }
}

/* Phone and small tablet: the top bar scrolls away so the tabs pin flush to
   the top of the screen rather than at a measured offset below it. On a
   public page tab-strip.js then sets --sticky-tabs-top to the agency header's
   height while that header is showing (below). */
@media (max-width: 48rem) {
  body:not(.is-editing):has(.tab-strip__row) {
    --sticky-tabs-top: 0px;
  }

  body:not(.is-editing):has(.tab-strip__row) > .app-header {
    position: static;
  }
}

/* The tab row scrolls sideways at every width. "All tabs" sits at its end
   on phones and narrow previews only (tab-strip.js reads --tab-strip-mobile). */
.tab-strip {
  --tab-strip-mobile: 0;
}

.tab-strip__all-tabs {
  display: inline-flex;
  flex: 0 0 auto;
  align-self: center;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-inline-start: var(--vds-spacing-8);
  padding: 0;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  cursor: pointer;
}

.tab-strip__all-tabs:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.tab-strip__all-tabs[hidden] {
  display: none;
}

/* Topic pages' (disabled, for now) data export, pinned to the tab row's far end. */
.tab-strip__export {
  display: flex;
  flex: 0 0 auto;
  align-self: center;
  margin-inline-start: auto;
  padding-inline-start: var(--vds-spacing-16);
}

@container page (max-width: 820px) {
  .tab-strip__export {
    display: none;
  }
}

@container page (max-width: 735px) {
  .public-page .tab-strip {
    --tab-strip-mobile: 1;
  }

  .public-page > .tab-strip > .tab-strip__row {
    align-items: stretch;
    gap: 0;
  }

  .public-page > .tab-strip .tab-strip__list {
    flex: 1 1 auto;
    gap: var(--vds-spacing-20);
  }

  .public-page > .tab-strip .tab-strip__button {
    padding: 12px 0;
    font-size: var(--vds-font-size-body-sm);
  }

  .public-page > .tab-strip .tab-strip__button--active {
    font-weight: var(--vds-font-weight-semibold);
  }

  .public-page > .tab-strip .tab-strip__button--active::after {
    height: 3px;
  }

  /* The agency header pins above the tabs; tab-strip.js slides it away while
     scrolling down and back on the way up. */
  body:not(.is-editing) > .app-main .public-page > .page-site-header {
    position: sticky;
    top: 0;
    z-index: 17;
  }

  /* An open menu taller than the screen scrolls within its own list, so the
     full-bleed header background isn't clipped. */
  body:not(.is-editing) > .app-main .public-page > .page-site-header .content-section--site-header[data-nav-open] .content-section__content {
    max-height: calc(100dvh - 5rem);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  body:not(.is-editing) > .app-main .public-page > .page-site-header--away {
    transform: translateY(-100%);
  }
}

@media (prefers-reduced-motion: no-preference) {
  body:not(.is-editing) > .app-main .public-page > .page-site-header {
    transition: transform 0.2s ease;
  }

  body:not(.is-editing) > .app-main .tab-strip:not(.crime-map-sidebar__tabs) > .tab-strip__row,
  .mobile-section-summary {
    transition: top 0.2s ease;
  }
}


/* A manager-only empty shell (zero Tabs from a fresh page, COMT-150, or
   all Tabs removed via Delete-Tab, COMT-152) otherwise renders as just a
   bare border line above "+ Add Tab" with nothing next to it — easy to
   mistake for a rendering glitch rather than an intentional empty state.
   `.tab-strip--empty` is toggled by tab-strip.js's syncTabStrip, not
   rendered server-side, since the same shell is reached both at initial
   page load and after an htmx swap deletes the last remaining Tab. */
.tab-strip--empty .tab-strip__list::after {
  content: "No tabs yet.";
  padding: var(--vds-spacing-8) var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-family: var(--vds-font-family-body);
  font-size: var(--vds-font-size-body-sm);
  font-style: italic;
}

.tab-strip__button {
  appearance: none;
  position: relative;
  background: none;
  border: none;
  padding: 10px 0 14px;
  font-family: var(--vds-font-family-body);
  font-size: 0.9375rem;
  font-weight: var(--vds-font-weight-regular, 400);
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.tab-strip__button:hover {
  color: var(--vds-fg-primary);
}

.tab-strip__button:focus-visible {
  outline: 2px solid var(--vds-border-focus, #2563eb);
  outline-offset: -2px;
  border-radius: var(--vds-radius-4, 4px);
}

.tab-strip__button--active {
  color: var(--agency-accent, var(--vds-fg-brand-on-low));
  font-weight: var(--vds-font-weight-medium);
}

/* Active tab: a 4px bar along the bottom edge, above the row's border. */
.tab-strip__button--active::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 4px;
  background: var(--agency-accent, var(--vds-bg-brand-primary));
}

/* ECharts reads its container's computed size at init time (COMT-122) —
   without an explicit height a bare <div> renders at 0x0, silently
   showing nothing. */
.widget-catalog__chart {
  width: 100%;
  height: 400px;
}

/* Agency Asset Library browser panel (COMT-222/225) — same "persistent
   fixed panel, hidden exactly when htmx swaps empty content into it"
   pattern as .inline-editor-panel above (see asset-browser.js), but
   usable from both the public in-place-editing surface and the
   /admin/* self-service area (see templates/base.html's comment on why
   this one is gated on plain authentication, not can_manage_agency). */
.asset-browser-panel {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: 28rem;
  max-width: 90vw;
  overflow-y: auto;
  background: var(--vds-bg-primary);
  border-left: var(--vds-border-width-1) solid var(--vds-border-base);
  box-shadow: var(--vds-shadow-sm);
  padding: var(--vds-spacing-20, 1.25rem);
  box-sizing: border-box;
  z-index: 100;
}

.asset-browser-panel[hidden] {
  display: none;
}

.asset-browser {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-16);
}

.asset-browser__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
}

.asset-browser__title {
  margin: 0;
  font-size: var(--vds-font-size-heading-xs, 1.25rem);
  font-weight: var(--vds-font-weight-semibold, 600);
  line-height: 1.3;
}

.asset-browser__subtitle {
  margin: var(--vds-spacing-4) 0 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* Upload: the whole dashed box is the (invisible) file input, so clicking
   or dropping a file anywhere on it works natively; picking one uploads. */
.asset-browser__upload {
  margin: 0;
}

.asset-browser__dropzone {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
  cursor: pointer;
}

.asset-browser__dropzone:hover,
.asset-browser__dropzone:focus-within {
  border-color: var(--vds-bg-brand-primary);
  background: color-mix(in srgb, var(--vds-bg-brand-primary) 6%, var(--vds-bg-secondary));
}

.asset-browser__dropzone:focus-within {
  outline: 2px solid var(--vds-border-focus, var(--vds-bg-brand-primary));
  outline-offset: 2px;
}

.asset-browser__dropzone-icon {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: var(--vds-bg-primary);
  color: var(--vds-bg-brand-primary);
}

.asset-browser__dropzone-icon svg {
  width: 1.25rem;
  height: 1.25rem;
}

.asset-browser__dropzone-text {
  display: grid;
  gap: 2px;
  font-size: var(--vds-font-size-body-sm);
}

.asset-browser__dropzone-text > span {
  color: var(--vds-fg-secondary);
}

.asset-browser__dropzone-busy,
.htmx-request .asset-browser__dropzone-idle {
  display: none;
}

.htmx-request .asset-browser__dropzone-busy {
  display: inline;
}

.asset-browser__file {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

#asset-upload-status:empty {
  display: none;
}

.asset-browser__upload-error,
.asset-browser__notice {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  font-size: var(--vds-font-size-body-sm);
}

.asset-browser__upload-error {
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.asset-browser__notice,
.asset-browser__upload-success {
  background: var(--vds-bg-success-light);
  color: var(--vds-fg-success-on-low, var(--vds-fg-primary));
}

.asset-browser__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--vds-spacing-12);
}

.asset-browser__empty {
  grid-column: 1 / -1;
  margin: 0;
  padding: var(--vds-spacing-24) var(--vds-spacing-16);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  text-align: center;
}

.asset-browser__item {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
}

/* Every image gets the same 4:3 frame, shown whole (letterboxed) rather
   than cropped, so wide logos and photos are both recognisable. */
.asset-browser__media {
  display: block;
  aspect-ratio: 4 / 3;
  padding: var(--vds-spacing-8);
  box-sizing: border-box;
  background: var(--vds-bg-secondary);
}

.asset-browser__thumb {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.asset-browser__item-text {
  display: grid;
  gap: 2px;
  min-width: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12) 0;
}

.asset-browser__item-title {
  overflow: hidden;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium, 500);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.asset-browser__item-meta {
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
}

.asset-browser__item-meta--missing {
  color: var(--vds-fg-warning-on-low, var(--vds-fg-secondary));
}

.asset-browser__item-actions {
  display: flex;
  gap: var(--vds-spacing-4);
  margin-top: auto;
  padding: var(--vds-spacing-8) var(--vds-spacing-12) var(--vds-spacing-12);
}

.asset-browser__item-actions .btn {
  flex: 1 1 0;
  min-width: 0;
}

/* Details view */
.asset-details__back {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  padding-inline: var(--vds-spacing-8) var(--vds-spacing-12);
}

.asset-details__notice {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-success-light);
  color: var(--vds-fg-success-on-low, var(--vds-fg-primary));
  font-size: var(--vds-font-size-body-sm);
}

.asset-details__preview {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: var(--vds-spacing-12);
  aspect-ratio: 4 / 3;
  box-sizing: border-box;
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.asset-details__preview img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.asset-details__facts {
  display: grid;
  gap: 2px;
  margin: calc(-1 * var(--vds-spacing-8)) 0 0;
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
}

.asset-details__filename {
  overflow: hidden;
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium, 500);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.asset-details__form {
  display: grid;
  gap: var(--vds-spacing-16);
}

.asset-details__field {
  display: grid;
  gap: var(--vds-spacing-4);
}

.asset-details__field label {
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold, 600);
}

.asset-details__field input,
.asset-details__field textarea {
  width: 100%;
  box-sizing: border-box;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.asset-details__field textarea {
  resize: vertical;
}

.asset-details__help,
.asset-details__error {
  margin: 0;
  font-size: 0.75rem;
}

.asset-details__help {
  color: var(--vds-fg-secondary);
}

.asset-details__error {
  color: var(--vds-fg-error-on-low);
}

.asset-details__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding-top: var(--vds-spacing-16);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.asset-details__actions-end {
  display: flex;
  gap: var(--vds-spacing-8);
  margin-left: auto;
}

.asset-details__actions .btn--danger-text {
  border: 0;
  background: none;
  padding-inline: var(--vds-spacing-8);
}

.asset-details__actions .btn--danger-text:hover {
  background: var(--vds-bg-error-light);
}

.asset-browser__selection {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.asset-browser__selection-thumb {
  width: 3rem;
  height: 3rem;
  object-fit: cover;
  border-radius: var(--vds-radius-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
}

/* Chrome widgets (page-header-section design, 2026-09) — the nav
   dropdown-of-links, theme-toggle, language-selector, and login/user
   menu widgets when placed inside a themed Section, rather than only
   ever appearing once, hardcoded, in the global site header. Sized to
   sit comfortably in a `content-block--width-tight` block. */
.content-block__chrome-widget {
  display: flex;
  align-items: center;
}


.chrome-nav-dropdown {
  position: relative;
}

.chrome-nav-dropdown__trigger {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  font: inherit;
  font-weight: var(--vds-font-weight-medium);
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  white-space: nowrap;
}

.chrome-nav-dropdown__trigger:hover {
  background: rgb(255 255 255 / 12%);
}

.chrome-nav-dropdown__panel {
  position: absolute;
  top: calc(100% + var(--vds-spacing-8));
  left: 0;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8);
  background: var(--vds-bg-primary);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  box-shadow: var(--vds-shadow-md);
  z-index: 10;
}

.chrome-nav-dropdown__link {
  display: block;
  box-sizing: border-box;
  width: 100%;
  padding: var(--vds-spacing-8);
  background: none;
  border: none;
  border-radius: var(--vds-radius-8);
  color: inherit;
  cursor: pointer;
  font: inherit;
  text-decoration: none;
  text-align: left;
}

/* Specificity fix: `.app-header a` (color: var(--agency-primary-fg,
   inherit) — this header row's own branded, on-dark-background link
   color) outranks a bare `.chrome-nav-dropdown__link` (one class vs. one
   class + one element = higher specificity), so it was winning here too
   even though this panel is its own always-light `--vds-bg-primary` card
   floating below the dark header row, not part of the header's own
   coloring — Aarondale's branded white-on-navy link color rendered as
   white text on this panel's white/light-grey background, unreadable.
   Scoping the color rule to `.chrome-nav-dropdown__panel
   .chrome-nav-dropdown__link` (two classes) outranks `.app-header a`
   regardless of which stylesheet rule comes first in source order, so
   the panel's own contents always read against its own background, not
   whatever background the trigger happens to sit on. */
.chrome-nav-dropdown__panel .chrome-nav-dropdown__link {
  color: var(--vds-fg-primary);
}

.chrome-nav-dropdown__link:hover {
  background: var(--vds-border-base);
}

/* Marks the language selector's own current-language option inside its
   dropdown panel (same "highlighted background" treatment other
   current-item indicators in this file use) — the trigger button
   already shows the current language as its label, this just repeats
   which option that is once the panel's open. */
.chrome-nav-dropdown__link--current {
  background: var(--vds-bg-secondary);
  font-weight: var(--vds-font-weight-medium);
}

/* Bugfix (same root cause/fix as `select[hidden]` above): `<vds-icon>`'s
   own vendored shadow-DOM stylesheet sets `:host { display: inline-flex }`
   with no `:host([hidden]) { display: none }` override, and that
   shadow-DOM author rule outranks the browser's own (very low-specificity,
   UA-stylesheet) `[hidden] { display: none }` default. So toggling the
   language switcher's check icon via `check.hidden = !isCurrent` (both the
   server-rendered initial state and static/js/chrome-widgets.js's
   `syncPublicLanguageSwitchers`) never actually hid it — every language
   option rendered with a checkmark, not just the current one.
   `vds-icon[hidden]` is more specific than the vendor bundle's bare
   `:host` rule, so this wins regardless of source order. */
.chrome-nav-dropdown__link vds-icon[hidden] {
  display: none;
}

/* Shared collapse state for every chrome-widget disclosure panel
   (nav-dropdown-links, login-user-menu when placed as a chrome widget) —
   toggled by static/js/chrome-widgets.js's generic
   `[data-chrome-menu-trigger]`/`[data-chrome-menu-panel]` behavior, same
   "server-rendered without this class so the panel's own content (e.g.
   sign-out) stays reachable if the JS never runs" reasoning as
   `.app-user-menu__panel--collapsed` above. */
.chrome-menu--collapsed {
  display: none;
}

/* Crime Map (COMT-281 onward; UX prototype parity COMT-502) —
   templates/widgets/circle_map.html and its _circle_map_* partials.

   One bordered card: the filter panel on the left, the map column (date
   toolbar + histogram over the canvas) on the right. Every colour resolves
   to a VDS token or the agency accent; the map-scoped custom properties
   below are also read by static/js/crime-map.js (markers, cluster rings,
   hotspots, the token-coloured basemap), so CSS stays the single source.
   Interactive controls are native elements styled with tokens — VDS web
   components don't submit form data or announce aria-pressed/expanded. */
.crime-map-layout {
  --crime-map-accent: var(--agency-accent, var(--vds-bg-brand-primary));
  --crime-map-violent: var(--vds-bg-error-primary);
  --crime-map-property: var(--vds-bg-brand-primary);
  --crime-map-other: var(--vds-fg-quarternary);
  --crime-map-heat-low: var(--vds-bg-success-primary);
  --crime-map-heat-mid: var(--vds-bg-warning-primary);
  --crime-map-heat-high: var(--vds-bg-error-primary);
  --crime-map-cluster-surface: var(--vds-bg-primary);
  --crime-map-cluster-text: var(--vds-fg-title);
  --crime-map-cluster-track: var(--vds-border-base);
  --crime-map-histogram-bar: var(--vds-fg-quarternary);
  --crime-map-panel-width: 20rem;
  --crime-map-overlay-shadow: var(--vds-shadow-md);
  position: relative;
  display: flex;
  align-items: stretch;
  box-sizing: border-box;
  min-height: 0;
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-family: var(--vds-font-family-body);
  /* The dedicated Crime Map page replaces this with the measured viewport
     space (static/js/crime-map-layout.js); embedded cards use the rule below. */
  height: calc(100vh - 14rem);
}

/* Basemap palette, resolved per theme by a hidden probe element that
   crime-map.js gives the map's own data-theme (so "Dark map" can apply the
   dark tokens on a light page). Water and greenery are drawn translucent. */
.crime-map-basemap-probe {
  --crime-map-basemap-land: var(--vds-bg-secondary);
  --crime-map-basemap-water: var(--vds-bg-brand-primary);
  --crime-map-basemap-green: var(--vds-bg-success-primary);
  --crime-map-basemap-building: var(--vds-bg-tertiary);
  --crime-map-basemap-boundary: var(--vds-border-secondary);
  --crime-map-basemap-road: var(--vds-bg-primary);
  --crime-map-basemap-road-major: var(--vds-bg-tertiary);
  --crime-map-basemap-road-motorway: var(--vds-border-secondary);
  --crime-map-basemap-label: var(--vds-fg-tertiary);
  --crime-map-basemap-label-minor: var(--vds-fg-quarternary);
  --crime-map-basemap-halo: var(--vds-bg-secondary);
}

/* Closed popovers render nothing, so their absolutely positioned panels
   never add scrollable overflow to the card. */
.crime-map-about:not([open]) > .crime-map-about__popover,
.crime-map-export:not([open]) > .crime-map-export__panel,
.crime-map-drawer:not([open]) > .crime-map-drawer__panel,
.crime-map-legend:not([open]) > .crime-map-legend__panel {
  display: none;
}

/* Progressive enhancement switches: crime-map-filters.js marks <html>. */
html:not(.crime-map-filters--enhanced) .crime-map-js-only,
html.crime-map-filters--enhanced .crime-map-no-js-only {
  display: none !important;
}

.widget--circle-map-page {
  padding: 0;
}

.widget--circle-map-embedded .crime-map-layout {
  height: min(49rem, calc(100vh - 7rem));
  min-height: 34rem;
}

/* The dedicated Crime Map page (not a tab): the map spans the full Section
   width and its own Section header/caption are hidden, since the card's
   About popover already carries that copy. The reserved footer padding is
   dropped because the card is sized to end at the footer's top edge. */
body:has(.widget--circle-map-page:not(.widget--circle-map-embedded)) > .app-main {
  padding-bottom: 0;
}

.content-section--full-width:has(.widget--circle-map-page:not(.widget--circle-map-embedded)) .content-section__inner {
  width: 100%;
  margin-inline: 0;
}

.content-section--full-width:has(.widget--circle-map-page:not(.widget--circle-map-embedded)) .content-section__header {
  display: none;
}

.widget--circle-map-page:not(.widget--circle-map-embedded) .crime-map-layout {
  border-inline: 0;
  border-radius: 0;
}

.crime-map-layout__map > figcaption {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Full screen: the Section's content wrapper goes full screen (the Fullscreen
   API's :fullscreen, or the fixed overlay crime-map-layout.js adds where the
   API is unavailable), so it survives filter swaps; the card fills it. */
.content-section__content:fullscreen,
.crime-map-layout:fullscreen,
.crime-map-fullscreen-host--fallback {
  overflow: auto;
  background: var(--vds-bg-primary);
}

.crime-map-fullscreen-host--fallback {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

:fullscreen .crime-map-layout,
.crime-map-layout:fullscreen,
.crime-map-fullscreen-host--fallback .crime-map-layout {
  height: 100vh !important;
  width: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
}

html.crime-map-fullscreen-open {
  overflow: hidden;
}

/* Shared icon button (panel toolbar, About trigger). */
.crime-map-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-secondary);
  font: inherit;
  cursor: pointer;
  list-style: none;
}

.crime-map-icon-button::-webkit-details-marker {
  display: none;
}

.crime-map-icon-button:hover {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-primary);
}

.crime-map-icon-button:focus-visible,
.crime-map-pill:focus-visible,
.crime-map-date-presets__option:focus-visible,
.crime-map-categories__caret:focus-visible,
.crime-map-panel__reset:focus-visible,
.crime-map-export__trigger:focus-visible,
.crime-map-area-search__input:focus-visible,
.crime-map-filter__clear:focus-visible,
.crime-map-date-strip__apply:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.crime-map-icon-button svg {
  width: 1rem;
  height: 1rem;
}

.crime-map-icon-button[aria-pressed="true"] {
  border-color: var(--crime-map-accent);
  color: var(--crime-map-accent);
}

.crime-map-icon-button__icon {
  display: inline-flex;
}

.crime-map-icon-button[aria-pressed="true"] .crime-map-icon-button__icon--off,
.crime-map-icon-button:not([aria-pressed="true"]) .crime-map-icon-button__icon--on {
  display: none;
}

/* The map-only dark toggle can only apply the dark tokens on a light page. */
html[data-theme="dark"] [data-crime-map-basemap-theme] {
  display: none;
}

/* Filter panel (left) */
.crime-map-panel {
  flex: 0 0 var(--crime-map-panel-width);
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
}

.crime-map-panel__toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-16) var(--vds-spacing-20, 1.25rem) var(--vds-spacing-8);
}

.crime-map-panel__collapse {
  margin-right: auto;
}

.crime-map-panel__toolbar-actions {
  display: inline-flex;
  gap: var(--vds-spacing-8);
}

.crime-map-panel__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--vds-spacing-20, 1.25rem) var(--vds-spacing-16);
}

.crime-map-panel__body[hidden] {
  display: none;
}

.crime-map-panel__heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12) 0 var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-panel__title {
  margin: 0;
  color: var(--vds-fg-title);
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-panel__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12) var(--vds-spacing-20, 1.25rem);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-panel__footer:empty {
  display: none;
}

.crime-map-panel__reset,
.crime-map-filter__clear {
  padding: 0;
  border: 0;
  background: none;
  color: var(--vds-fg-brand-on-low);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.crime-map-panel__reset:hover,
.crime-map-filter__clear:hover {
  text-decoration: underline;
}

.crime-map-panel__selected-areas {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--vds-spacing-4) var(--vds-spacing-8);
  margin-top: var(--vds-spacing-12);
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-panel__selected-label {
  color: var(--vds-fg-secondary);
}

.crime-map-panel__selected-names {
  flex: 1 1 auto;
  font-weight: var(--vds-font-weight-medium);
}

.crime-map-panel__selected-names span + span::before {
  content: ", ";
}

/* Collapsed (desktop): like the prototype, the panel shrinks to a rail of
   its toolbar buttons and the date toolbar folds away too, leaving the map
   the whole card. Expanding brings both back. */
@media (min-width: 901px) {
  .crime-map-layout--panel-collapsed .crime-map-panel {
    flex-basis: 3rem;
  }

  .crime-map-layout--panel-collapsed .crime-map-panel__toolbar {
    flex-direction: column;
    padding: var(--vds-spacing-12) var(--vds-spacing-8);
  }

  .crime-map-layout--panel-collapsed .crime-map-panel__collapse {
    margin: 0;
  }

  .crime-map-layout--panel-collapsed .crime-map-panel__collapse vds-icon {
    transform: rotate(180deg);
  }

  .crime-map-layout--panel-collapsed .crime-map-panel__toolbar-actions {
    flex-direction: column;
  }

  .crime-map-layout--panel-collapsed .crime-map-panel__footer,
  .crime-map-layout--panel-collapsed .crime-map-date-strip {
    display: none;
  }
}

/* Small-screen-only controls: the Filters pill, the sheet's close button
   and its "Show N incidents" button. */
.crime-map-canvas-controls .crime-map-filters-button,
.crime-map-panel__toolbar .crime-map-panel__close,
.crime-map-panel__footer .crime-map-panel__show {
  display: none;
}

.crime-map-panel__show {
  padding: var(--vds-spacing-8) var(--vds-spacing-16);
  border: 0;
  border-radius: var(--vds-radius-4, 4px);
  background: var(--crime-map-accent);
  color: var(--vds-fg-white);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.crime-map-panel__show:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

/* Crime categories */
.crime-map-categories__group {
  margin: 0;
  padding: var(--vds-spacing-8) 0;
  border: 0;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  min-width: 0;
}

.crime-map-categories__group:last-child {
  border-bottom: none;
}

.crime-map-categories__group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
}

.crime-map-categories__caret {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-4) 0;
  border: 0;
  background: none;
  color: var(--vds-fg-title);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  cursor: pointer;
}

.crime-map-categories__caret vds-icon {
  color: var(--vds-fg-secondary);
  transition: transform 120ms ease;
}

.crime-map-categories__caret[aria-expanded="false"] vds-icon {
  transform: rotate(-90deg);
}

.crime-map-categories__group-name--violent {
  color: var(--crime-map-violent);
}

.crime-map-categories__group-name--property {
  color: var(--crime-map-property);
}

.crime-map-categories__list {
  list-style: none;
  margin: 0;
  padding: var(--vds-spacing-4) 0 0;
}

.crime-map-categories__list[hidden] {
  display: none;
}

.crime-map-categories__label {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-12);
  min-width: 0;
  padding: var(--vds-spacing-8) 0;
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

.crime-map-categories__label input {
  width: 1rem;
  height: 1rem;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--crime-map-accent);
}

.crime-map-categories__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.crime-map-categories__count {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  font-variant-numeric: tabular-nums;
}

.crime-map-categories__count--zero {
  color: var(--vds-fg-quarternary);
}

.crime-map-categories__empty {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* COMT-308: category badge — the same Phosphor icon, on the same
   group-coloured disc, as the incident badges drawn on the map. */
.crime-map-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--crime-map-other);
  color: var(--vds-fg-white);
}

.crime-map-badge svg {
  width: 0.875rem;
  height: 0.875rem;
  fill: currentColor;
}

.crime-map-badge--violent {
  background: var(--crime-map-violent);
}

.crime-map-badge--property {
  background: var(--crime-map-property);
}

.crime-map-categories__badge {
  flex: 0 0 1.5rem;
}

/* No-JavaScript fallbacks (area multi-select, Update results). */
.crime-map-filter__areas {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin-top: var(--vds-spacing-16);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-filter__areas select {
  padding: var(--vds-spacing-4);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.crime-map-filter__areas p {
  margin: 0;
  color: var(--vds-fg-secondary);
}

.crime-map-filter__submit {
  margin-top: var(--vds-spacing-16);
}

.crime-map-filter__locked-note {
  margin: var(--vds-spacing-8) 0 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-area-detail__error {
  color: var(--vds-fg-error-on-low);
}

/* About this data popover */
.crime-map-about {
  position: relative;
}

.crime-map-about__trigger {
  width: 1.75rem;
  height: 1.75rem;
  border-color: transparent;
}

.crime-map-about__popover {
  position: absolute;
  z-index: 6;
  top: calc(100% + var(--vds-spacing-4));
  right: 0;
  box-sizing: border-box;
  /* The panel's own content width, so it never spills past the card. */
  width: min(calc(var(--crime-map-panel-width) - 2.5rem), 80vw);
  max-height: 24rem;
  overflow: auto;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-about__title {
  margin: 0 0 var(--vds-spacing-8);
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-about__body,
.crime-map-about__empty {
  color: var(--vds-fg-secondary);
}

.crime-map-about__subsection {
  margin-top: var(--vds-spacing-12);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-about__subsection-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--vds-spacing-8) 0;
  border: 0;
  background: none;
  color: var(--vds-fg-primary);
  font: inherit;
  font-weight: var(--vds-font-weight-medium);
  text-align: left;
  cursor: pointer;
}

.crime-map-about__subsection-header[aria-expanded="false"] vds-icon {
  transform: rotate(-90deg);
}

.widget-circle-map__areas {
  width: 100%;
  border-collapse: collapse;
}

.widget-circle-map__areas th,
.widget-circle-map__areas td {
  text-align: left;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.widget-circle-map__areas th {
  color: var(--vds-fg-secondary);
  font-weight: var(--vds-font-weight-medium);
}

/* Export: in the panel footer, or the page tab row (crime-map-layout.js). */
.crime-map-export {
  position: relative;
  margin-left: auto;
}

.crime-map-export[hidden] {
  display: none;
}

.crime-map-export__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
  list-style: none;
}

.crime-map-export__trigger::-webkit-details-marker {
  display: none;
}

.crime-map-export__trigger:hover {
  background: var(--vds-bg-secondary);
}

.crime-map-export__panel {
  position: absolute;
  z-index: 20;
  right: 0;
  bottom: calc(100% + var(--vds-spacing-8));
  box-sizing: border-box;
  width: min(22rem, 90vw);
  max-height: min(32rem, 70vh);
  overflow: auto;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  text-align: left;
}

.crime-map-export--in-tab-row .crime-map-export__panel {
  top: calc(100% + var(--vds-spacing-4));
  bottom: auto;
}

.crime-map-export--in-tab-row .crime-map-export__trigger {
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  font-size: var(--vds-font-size-body-md);
}

.crime-map-export__title {
  margin: 0 0 var(--vds-spacing-8);
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.tab-strip__actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  margin-left: auto;
  padding-left: var(--vds-spacing-16);
}

/* crime-map-layout.js adds this container once and hides its controls on
   other tabs; empty, it would still split the row's free space with
   .tab-strip__export and pull that button in off the right edge. */
.tab-strip__actions:not(:has(> :not([hidden]))) {
  display: none;
}

/* The active tab's own working Export (the crime map's) replaces the
   disabled page-level placeholder rather than sitting beside it. */
.tab-strip__export:has(~ .tab-strip__actions > :not([hidden])) {
  display: none;
}

/* Export belongs to the page's top, not the pinned row (tab-strip.js). */
.tab-strip__row--stuck > .tab-strip__export,
.tab-strip__row--stuck > .tab-strip__actions {
  display: none;
}

/* With Export beside them, tabs scroll sideways (like the narrow-screen
   rule above) rather than wrapping or pushing Export off the edge. */
/* Phones: the search gets its own row above the display menu. */
@media (max-width: 600px) {
  .crime-map-area-search {
    flex-basis: 100%;
  }
}

@media (max-width: 1199px) {
  .tab-strip__row:has(> .tab-strip__actions) .tab-strip__list:not(:has(.edit-trigger)) {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .tab-strip__row:has(> .tab-strip__actions) .tab-strip__button {
    flex-shrink: 0;
    white-space: nowrap;
  }
}

.crime-map-download {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-12);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-download fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: 0;
  border: 0;
}

.crime-map-download legend {
  margin-bottom: var(--vds-spacing-4);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-download p {
  margin: 0;
  color: var(--vds-fg-secondary);
}

.crime-map-download label {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.crime-map-download input {
  accent-color: var(--crime-map-accent);
}

.crime-map-download__submit {
  align-self: flex-start;
}

/* Map column */
.crime-map-layout__map {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  margin: 0;
  background: var(--vds-bg-primary);
}

/* Occurrence Date toolbar */
.crime-map-date-strip {
  flex: 0 0 auto;
  padding: var(--vds-spacing-16) var(--vds-spacing-20, 1.25rem) var(--vds-spacing-12);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-date-strip__header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--vds-spacing-8) var(--vds-spacing-16);
}

.crime-map-date-presets {
  display: inline-flex;
  flex-wrap: wrap;
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.crime-map-date-presets__option {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 0;
  border-right: var(--vds-border-width-1) solid var(--vds-border-base);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

.crime-map-date-presets__option:last-child {
  border-right: 0;
}

.crime-map-date-presets__option:hover {
  background: var(--vds-bg-secondary);
}

.crime-map-date-presets__option[aria-pressed="true"] {
  background: color-mix(in srgb, var(--crime-map-accent) 12%, var(--vds-bg-primary));
  color: var(--crime-map-accent);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-date-presets__option:focus-visible {
  outline-offset: -2px;
}

.crime-map-date-strip__headline {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  margin: 0 0 0 auto;
  text-align: right;
}

.crime-map-date-strip__headline-count {
  color: var(--vds-fg-title);
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-date-strip__headline-dates {
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-date-strip__dates {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--vds-spacing-8) var(--vds-spacing-12);
  margin-top: var(--vds-spacing-12);
}

html.crime-map-filters--enhanced .crime-map-date-strip__dates:not(.crime-map-date-strip__dates--custom) {
  display: none;
}

.crime-map-date-strip__dates label {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-date-strip__dates input {
  min-width: 0;
  box-sizing: border-box;
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  color-scheme: light;
}

html[data-theme="dark"] .crime-map-date-strip__dates input {
  color-scheme: dark;
}

.crime-map-date-strip__dates input:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.crime-map-date-strip__apply {
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--crime-map-accent);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--crime-map-accent);
  color: var(--vds-fg-white);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

/* Histogram: grey bars, the selected window tinted with the accent, and the
   two native range inputs laid over it as the start/end handles. Each input
   is shifted and sized so thumb i sits exactly on bar i's left (start) or
   right (end) edge — see _circle_map_timeline.html. */
.crime-map-timeline {
  --crime-map-thumb: 1rem;
  margin-top: var(--vds-spacing-12);
}

.crime-map-timeline__plot {
  position: relative;
  height: 4rem;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
}

.crime-map-timeline__plot--editable .crime-map-timeline__chart {
  cursor: col-resize;
  touch-action: none;
}

.crime-map-timeline__chart {
  position: absolute;
  inset: var(--vds-spacing-4) 0 0;
}

.crime-map-timeline__selection {
  position: absolute;
  top: 0;
  bottom: 0;
  border-inline: 2px solid var(--crime-map-accent);
  background: color-mix(in srgb, var(--crime-map-accent) 12%, transparent);
  pointer-events: none;
}

.crime-map-timeline__controls {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.crime-map-timeline__range {
  position: absolute;
  top: 50%;
  width: calc(100% * (var(--crime-map-buckets) - 1) / var(--crime-map-buckets) + var(--crime-map-thumb));
  height: var(--crime-map-thumb);
  margin: 0;
  padding: 0;
  transform: translateY(-50%);
  background: transparent;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}

.crime-map-timeline__range--start {
  left: calc(var(--crime-map-thumb) / -2);
}

.crime-map-timeline__range--end {
  left: calc(100% / var(--crime-map-buckets) - var(--crime-map-thumb) / 2);
}

.crime-map-timeline__range::-webkit-slider-runnable-track {
  height: var(--crime-map-thumb);
  background: transparent;
}

.crime-map-timeline__range::-moz-range-track {
  background: transparent;
}

.crime-map-timeline__range::-webkit-slider-thumb {
  width: var(--crime-map-thumb);
  height: var(--crime-map-thumb);
  border: 2px solid var(--vds-bg-primary);
  border-radius: 50%;
  background: var(--crime-map-accent);
  box-shadow: 0 0 0 1px var(--crime-map-accent);
  cursor: ew-resize;
  pointer-events: auto;
  -webkit-appearance: none;
  appearance: none;
}

.crime-map-timeline__range::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--crime-map-thumb);
  height: var(--crime-map-thumb);
  border: 2px solid var(--vds-bg-primary);
  border-radius: 50%;
  background: var(--crime-map-accent);
  box-shadow: 0 0 0 1px var(--crime-map-accent);
  cursor: ew-resize;
  pointer-events: auto;
}

.crime-map-timeline__range:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.crime-map-timeline__range:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.crime-map-timeline__edges {
  display: flex;
  justify-content: space-between;
  margin-top: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

/* Map canvas and its overlays */
.crime-map-layout__canvas {
  position: relative;
  flex: 1 1 auto;
  min-height: 18rem;
  background: var(--vds-bg-secondary);
}

.crime-map-layout__canvas--empty {
  display: grid;
  place-items: center;
  padding: var(--vds-spacing-24);
}

/* Doubled up to beat maplibre-gl.css's own `.maplibregl-map { position: relative }`. */
.crime-map-layout__canvas .widget-circle-map__map {
  position: absolute;
  inset: 0;
}

.widget-circle-map__no-points {
  margin: 0;
  color: var(--vds-fg-secondary);
}

.widget-circle-map__error {
  position: absolute;
  z-index: 4;
  top: 4rem;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-error-on-low);
  box-shadow: var(--crime-map-overlay-shadow);
}

.widget-circle-map__error[hidden] {
  display: none;
}

.widget-circle-map__boundary-error {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  color: var(--vds-fg-error-on-low);
  font-size: var(--vds-font-size-body-sm);
}

/* A skip link over the map canvas, visible only while focused. */
.widget-circle-map__list-link {
  position: absolute;
  z-index: 7;
  top: var(--vds-spacing-8);
  left: var(--vds-spacing-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-brand-on-low);
  box-shadow: var(--crime-map-overlay-shadow);
}

.widget-circle-map__list-link:not(:focus):not(:focus-visible) {
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Top overlay: slim search plus the display select, left of the zoom controls. */
.crime-map-toolbar {
  position: absolute;
  z-index: 5;
  top: var(--vds-spacing-12);
  left: var(--vds-spacing-12);
  right: 3.5rem;
  display: flex;
  gap: var(--vds-spacing-12);
  pointer-events: none;
}

.crime-map-toolbar > * {
  pointer-events: auto;
}

.crime-map-area-search {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  max-width: 40rem;
}

.crime-map-area-search__icon {
  position: absolute;
  top: 50%;
  left: var(--vds-spacing-12);
  transform: translateY(-50%);
  color: var(--vds-fg-secondary);
  pointer-events: none;
}

.crime-map-area-search__input {
  box-sizing: border-box;
  width: 100%;
  height: 2.5rem;
  padding: 0 var(--vds-spacing-12) 0 2.25rem;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  box-shadow: var(--crime-map-overlay-shadow);
}

.crime-map-area-search__input::placeholder {
  color: var(--vds-fg-quarternary);
}

.crime-map-area-search__results,
.crime-map-area-search__empty {
  position: absolute;
  z-index: 6;
  top: calc(100% + var(--vds-spacing-4));
  left: 0;
  right: 0;
  box-sizing: border-box;
  margin: 0;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-area-search__results {
  max-height: 16rem;
  overflow-y: auto;
  padding: var(--vds-spacing-4) 0;
  list-style: none;
}

.crime-map-area-search__results[hidden],
.crime-map-area-search__empty[hidden] {
  display: none;
}

.crime-map-area-search__empty {
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
}

.crime-map-area-search__option {
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  cursor: pointer;
}

.crime-map-area-search__option[aria-selected="true"],
.crime-map-area-search__option:hover {
  background: var(--vds-bg-secondary);
}

/* Display menu: a button + listbox popover, like the prototype. */
.crime-map-display {
  position: relative;
  flex: 0 0 auto;
}

.crime-map-display__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  height: 2.5rem;
  box-sizing: border-box;
  padding: 0 var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
}

.crime-map-display__trigger:hover {
  background: var(--vds-bg-secondary);
}

.crime-map-display__trigger[aria-expanded="true"] {
  border-color: var(--crime-map-accent);
}

.crime-map-display__trigger:focus-visible,
.crime-map-display__options:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.crime-map-display__icon,
.crime-map-display__option-icon {
  display: none;
  color: var(--vds-fg-secondary);
}

.crime-map-display__icon svg,
.crime-map-display__option-icon svg {
  width: 1rem;
  height: 1rem;
}

.crime-map-display__option-icon {
  display: inline-flex;
}

.crime-map-display[data-mode="incidents"] .crime-map-display__icon--incidents,
.crime-map-display[data-mode="hotspots"] .crime-map-display__icon--hotspots,
.crime-map-display[data-mode="areas"] .crime-map-display__icon--areas {
  display: inline-flex;
}

.crime-map-display__chevron {
  margin-left: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  transition: transform 120ms ease;
}

.crime-map-display__trigger[aria-expanded="true"] .crime-map-display__chevron {
  transform: rotate(180deg);
}

.crime-map-display__options {
  position: absolute;
  z-index: 7;
  top: calc(100% + var(--vds-spacing-4));
  right: 0;
  min-width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: var(--vds-spacing-4);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  list-style: none;
}

.crime-map-display__options[hidden] {
  display: none;
}

.crime-map-display__option {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium);
  white-space: nowrap;
  cursor: pointer;
}

.crime-map-display__option:hover,
.crime-map-display__option--active {
  background: var(--vds-bg-secondary);
}

.crime-map-display__options:focus-visible .crime-map-display__option--active {
  box-shadow: inset 0 0 0 2px var(--vds-border-focus);
}

.crime-map-display__option[aria-selected="true"] {
  background: color-mix(in srgb, var(--crime-map-accent) 10%, var(--vds-bg-primary));
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-display__check {
  margin-left: auto;
  padding-left: var(--vds-spacing-12);
  color: var(--crime-map-accent);
  visibility: hidden;
}

.crime-map-display__option[aria-selected="true"] .crime-map-display__check {
  visibility: visible;
}

.crime-map-display__option[aria-disabled="true"] {
  color: var(--vds-fg-quarternary);
  cursor: not-allowed;
}

/* Bottom overlay: "View incidents" drawer (left) and Legend (right). */
.crime-map-canvas-controls {
  position: absolute;
  z-index: 5;
  left: var(--vds-spacing-12);
  right: var(--vds-spacing-12);
  bottom: var(--vds-spacing-12);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--vds-spacing-12);
  pointer-events: none;
}

.crime-map-layout__canvas--empty .crime-map-canvas-controls {
  position: static;
  margin-top: var(--vds-spacing-16);
}

.crime-map-canvas-controls > * {
  pointer-events: auto;
}

.crime-map-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  min-height: 2.5rem;
  box-sizing: border-box;
  padding: 0 var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-full, 999px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}

.crime-map-pill::-webkit-details-marker {
  display: none;
}

.crime-map-pill:hover {
  background: var(--vds-bg-secondary);
}

.crime-map-pill__icon {
  display: inline-flex;
  color: var(--vds-fg-secondary);
}

.crime-map-pill__icon svg {
  width: 1rem;
  height: 1rem;
}

.crime-map-pill__count {
  min-width: 1.5rem;
  padding: 0 var(--vds-spacing-8);
  border-radius: var(--vds-radius-full, 999px);
  background: color-mix(in srgb, var(--crime-map-accent) 12%, var(--vds-bg-primary));
  color: var(--crime-map-accent);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

[open] > .crime-map-pill .crime-map-pill__chevron {
  transform: rotate(90deg);
}

.crime-map-drawer,
.crime-map-legend {
  position: relative;
  min-width: 0;
}

.crime-map-legend__panel {
  position: absolute;
  right: 0;
  bottom: calc(100% + var(--vds-spacing-8));
  box-sizing: border-box;
  width: max-content;
  max-width: min(20rem, calc(100vw - 2rem));
  max-height: min(22rem, 60vh);
  overflow: auto;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-legend__attribution {
  margin: var(--vds-spacing-12) 0 0;
  padding-top: var(--vds-spacing-8);
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
  color: var(--vds-fg-tertiary);
  font-size: var(--vds-font-size-body-xs);
}

.crime-map-legend__attribution a {
  color: inherit;
}

/* View incidents: a pill until opened, then a sheet across the bottom of
   the map headed "Hide incidents" (the prototype's incident table). */
.crime-map-drawer__handle {
  width: 1rem;
  height: 3px;
  border-radius: var(--vds-radius-full, 999px);
  background: var(--vds-fg-quarternary);
}

.crime-map-drawer__label--open,
.crime-map-drawer__short,
.crime-map-drawer[open] .crime-map-drawer__label--closed {
  display: none;
}

.crime-map-drawer[open] .crime-map-drawer__label--open {
  display: inline;
}

.crime-map-drawer[open] {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  max-height: min(26rem, 55vh);
  overflow: hidden;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
}

.crime-map-drawer[open] > .crime-map-drawer__summary {
  flex: 0 0 auto;
  justify-content: center;
  border: 0;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: 0;
  box-shadow: none;
}

.crime-map-drawer[open] .crime-map-pill__chevron {
  transform: rotate(90deg);
}

.crime-map-drawer__panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: var(--vds-spacing-12) var(--vds-spacing-16) var(--vds-spacing-16);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-layout__canvas--empty .crime-map-drawer[open] {
  position: static;
}

.crime-map-drawer__summary-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--vds-spacing-4) var(--vds-spacing-16);
}

.crime-map-drawer__title {
  margin: 0;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-drawer__split {
  margin: 0;
  color: var(--vds-fg-secondary);
}

.crime-map-drawer__show {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-8);
}

.crime-map-drawer__show select {
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
}

.widget-circle-map__details h4 {
  margin: var(--vds-spacing-8) 0 var(--vds-spacing-4);
  font-size: var(--vds-font-size-body-sm);
}

.widget-circle-map__detail:empty {
  display: none;
}

.widget-circle-map__detail {
  margin-top: var(--vds-spacing-8);
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-secondary);
}

.widget-circle-map__detail p {
  margin: 0;
}

.widget-circle-map__cluster-items {
  list-style: none;
  margin: 0;
  padding: 0;
}

.crime-map-drawer__table-wrap {
  margin-top: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
}

.crime-map-drawer__table {
  width: 100%;
  border-collapse: collapse;
}

.crime-map-drawer__table caption {
  padding: var(--vds-spacing-8) var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  color: var(--vds-fg-secondary);
  text-align: left;
}

.crime-map-drawer__table th,
.crime-map-drawer__table td {
  padding: var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  text-align: left;
  vertical-align: top;
}

.crime-map-drawer__table thead th {
  background: var(--vds-bg-secondary);
  color: var(--vds-fg-secondary);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-drawer__table tbody tr:last-child td {
  border-bottom: 0;
}

.crime-map-drawer__table tr[hidden] {
  display: none;
}

.crime-map-drawer__date {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.crime-map-drawer__time {
  color: var(--vds-fg-secondary);
}

.widget-circle-map__incident-select {
  padding: 0;
  border: 0;
  background: none;
  color: var(--vds-fg-brand-on-low);
  font: inherit;
  font-weight: var(--vds-font-weight-semibold);
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
}

.widget-circle-map__incident-select:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: 2px;
}

.widget-circle-map__list-location,
.widget-circle-map__list-categories {
  color: var(--vds-fg-secondary);
}

.widget-circle-map__list-categories {
  display: block;
  font-size: var(--vds-font-size-body-xs);
}

.widget-circle-map__load-more {
  margin-top: var(--vds-spacing-8);
}

.widget-circle-map__area-list {
  margin-top: var(--vds-spacing-16);
}

.widget-circle-map__area-list table {
  width: 100%;
  border-collapse: collapse;
}

.widget-circle-map__area-list caption {
  margin-bottom: var(--vds-spacing-4);
  font-weight: var(--vds-font-weight-semibold);
  text-align: left;
}

.widget-circle-map__area-list th,
.widget-circle-map__area-list td {
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  text-align: left;
}

.widget-circle-map__area-select {
  padding: var(--vds-spacing-4) var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  cursor: pointer;
}

.widget-circle-map__area-select[aria-pressed="true"] {
  border-color: var(--crime-map-accent);
  color: var(--crime-map-accent);
}

/* Legend content */
.widget-circle-map__legend {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
}

.widget-circle-map__legend[hidden] {
  display: none;
}

.widget-circle-map__legend > span {
  color: var(--vds-fg-secondary);
}

.widget-circle-map__legend ul {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.widget-circle-map__legend ul.widget-circle-map__legend-groups {
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--vds-spacing-4) var(--vds-spacing-12);
}

.widget-circle-map__legend li {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.widget-circle-map__swatch {
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: var(--vds-radius-4, 4px);
}

/* COMT-307: hollow ring swatches mirror the cluster ring segments. */
.widget-circle-map__swatch--ring {
  box-sizing: border-box;
  border: 3px solid var(--crime-map-other);
  border-radius: 50%;
  background: var(--crime-map-cluster-surface);
}

.widget-circle-map__swatch--ring.widget-circle-map__swatch--violent {
  border-color: var(--crime-map-violent);
}

.widget-circle-map__swatch--ring.widget-circle-map__swatch--property {
  border-color: var(--crime-map-property);
}

.widget-circle-map__swatch--area {
  border: var(--vds-border-width-1) solid var(--vds-border-secondary);
}

.widget-circle-map__heat-scale {
  height: 0.625rem;
  border-radius: var(--vds-radius-full, 999px);
  background: linear-gradient(90deg, var(--crime-map-heat-low), var(--crime-map-heat-mid), var(--crime-map-heat-high));
}

.widget-circle-map__heat-labels {
  display: flex;
  justify-content: space-between;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

/* MapLibre controls: token surfaces so they match the overlays above. */
.crime-map-layout .maplibregl-ctrl-top-right {
  top: var(--vds-spacing-4);
  right: var(--vds-spacing-4);
}

.crime-map-layout .maplibregl-ctrl-group {
  border-radius: var(--vds-radius-4, 4px);
  box-shadow: var(--crime-map-overlay-shadow);
}

.crime-map-layout .maplibregl-ctrl-bottom-right {
  bottom: 4rem;
}

/* Selected area summary (COMT-317), over the map's right edge. */
.crime-map-area-overlay {
  position: absolute;
  z-index: 5;
  top: 4.25rem;
  right: 3.5rem;
  box-sizing: border-box;
  width: min(20rem, calc(100% - 4.5rem));
  max-height: calc(100% - 9rem);
  overflow: auto;
  overflow-wrap: anywhere;
  padding: var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--crime-map-overlay-shadow);
  font-size: var(--vds-font-size-body-sm);
}

.crime-map-area-overlay[hidden] { display: none; }

.crime-map-area-overlay__header {
  display: flex;
  justify-content: space-between;
  align-items: start;
  gap: var(--vds-spacing-8);
}

.crime-map-area-overlay__header h3 {
  margin: 0;
  font-size: var(--vds-font-size-body-md);
}

.crime-map-area-overlay__close {
  flex: none;
  width: 2rem;
  height: 2rem;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4, 4px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.crime-map-area-overlay__note { color: var(--vds-fg-secondary); }
.crime-map-area-overlay__graphic { position: relative; }
.crime-map-area-overlay__chart { height: 9rem; }

.crime-map-area-overlay__center {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--vds-fg-primary);
  font-size: 1.25rem;
  font-weight: var(--vds-font-weight-semibold);
  pointer-events: none;
}

.crime-map-area-overlay__counts { width: 100%; border-collapse: collapse; }

.crime-map-area-overlay__counts th,
.crime-map-area-overlay__counts td {
  padding: var(--vds-spacing-4);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  text-align: left;
}

.crime-map-area-overlay__counts td {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.crime-map-area-overlay__swatch {
  display: inline-block;
  width: 0.65rem;
  height: 0.65rem;
  margin-right: var(--vds-spacing-8);
  border-radius: 50%;
}

.crime-map-area-overlay__swatch--violent { background: var(--crime-map-violent); }
.crime-map-area-overlay__swatch--property { background: var(--crime-map-property); }
.crime-map-area-overlay__swatch--other { background: var(--crime-map-other); }
.crime-map-area-overlay__breakdown { margin-top: var(--vds-spacing-12); }
.crime-map-area-overlay__breakdown summary { cursor: pointer; }

.crime-map-area-overlay__breakdown ul {
  margin: var(--vds-spacing-8) 0 0;
  padding: 0;
  list-style: none;
}

.crime-map-area-overlay__breakdown li {
  display: flex;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
}

/* Incident details popup (COMT-303), anchored to the map point by
   MapLibre; the card itself is plain DOM built by crime-map.js. */
.crime-map-popup .maplibregl-popup-content {
  padding: 0;
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: var(--crime-map-overlay-shadow, var(--vds-shadow-md));
  overflow: hidden;
}

.crime-map-popup.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
  border-top-color: var(--vds-bg-primary);
}

.crime-map-popup.maplibregl-popup-anchor-top .maplibregl-popup-tip {
  border-bottom-color: var(--vds-bg-primary);
}

.crime-map-popup.maplibregl-popup-anchor-left .maplibregl-popup-tip {
  border-right-color: var(--vds-bg-primary);
}

.crime-map-popup.maplibregl-popup-anchor-right .maplibregl-popup-tip {
  border-left-color: var(--vds-bg-primary);
}

.crime-map-popup__card {
  font-family: var(--vds-font-family-body);
  font-size: var(--vds-font-size-body-sm);
  min-width: 16rem;
}

.crime-map-popup__header {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-12) var(--vds-spacing-12) var(--vds-spacing-8);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-popup__titles {
  flex: 1 1 auto;
  min-width: 0;
}

.crime-map-popup__title {
  margin: 0;
  font-size: var(--vds-font-size-body-md);
  font-weight: var(--vds-font-weight-semibold);
}

.crime-map-popup__title:focus {
  outline: none;
}

.crime-map-popup__subtitle {
  margin: 0;
  color: var(--vds-fg-secondary);
}

.crime-map-popup__back,
.crime-map-popup__close {
  flex-shrink: 0;
  border: 0;
  background: none;
  color: var(--vds-fg-secondary);
  cursor: pointer;
  font: inherit;
  padding: var(--vds-spacing-4);
  border-radius: var(--vds-radius-4, 4px);
}

.crime-map-popup__close {
  width: 1.75rem;
  height: 1.75rem;
  font-size: 1.25rem;
  line-height: 1;
}

.crime-map-popup__back:hover,
.crime-map-popup__close:hover,
.crime-map-popup__item:hover {
  background: var(--vds-bg-secondary);
}

.crime-map-popup__back:focus-visible,
.crime-map-popup__close:focus-visible,
.crime-map-popup__item:focus-visible,
.crime-map-popup__more:focus-visible {
  outline: 2px solid var(--vds-border-focus);
  outline-offset: -2px;
}

.crime-map-popup__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  flex: 0 0 1.75rem;
  height: 1.75rem;
  color: var(--vds-fg-white);
  border-radius: 50%;
  background: var(--crime-map-other);
  border: 2px solid var(--vds-bg-primary);
  box-shadow: 0 0 0 1px var(--vds-border-base);
}

.crime-map-popup__badge svg {
  width: 0.875rem;
  height: 0.875rem;
  fill: currentColor;
}

.crime-map-popup__badge--violent {
  background: var(--crime-map-violent);
}

.crime-map-popup__badge--property {
  background: var(--crime-map-property);
}

.crime-map-popup__fields {
  margin: 0;
}

.crime-map-popup__field {
  display: grid;
  grid-template-columns: 7.5rem 1fr;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.crime-map-popup__field dt {
  color: var(--vds-fg-secondary);
}

.crime-map-popup__field dd {
  margin: 0;
  font-weight: var(--vds-font-weight-medium);
  overflow-wrap: anywhere;
}

.crime-map-popup__note {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.crime-map-popup__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 16rem;
  overflow-y: auto;
}

.crime-map-popup__item {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  width: 100%;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: 0;
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.crime-map-popup__item-text {
  display: flex;
  flex-direction: column;
}

.crime-map-popup__item-text span {
  color: var(--vds-fg-secondary);
}

.crime-map-popup__more {
  width: 100%;
  padding: var(--vds-spacing-8);
  border: 0;
  background: none;
  color: var(--vds-fg-brand-on-low);
  font: inherit;
  cursor: pointer;
}

.crime-map-popup__more[hidden] {
  display: none;
}

/* Small screens (COMT-502 feedback): the map is the whole card, edge to
   edge. Filters (with the date toolbar, which crime-map-layout.js moves into
   the panel) open as a sheet over the map from the Filters pill. Without
   JavaScript the panel simply stacks under the map. */
@media (max-width: 900px) {
  .crime-map-layout,
  .widget--circle-map-embedded .crime-map-layout {
    flex-direction: column-reverse;
    height: auto;
    min-height: 0;
  }

  .widget--circle-map-embedded .crime-map-layout {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    border-inline: 0;
    border-radius: 0;
  }

  /* Let the full-bleed card escape its Section's rounded, clipped box. */
  .content-section:has(.widget--circle-map-embedded) {
    overflow: visible;
  }

  .crime-map-panel {
    flex-basis: auto;
    border-right: 0;
    border-top: var(--vds-border-width-1) solid var(--vds-border-base);
  }

  .crime-map-panel__collapse {
    display: none;
  }

  .crime-map-layout__canvas {
    flex: 0 0 auto;
    height: max(26rem, min(72vh, 44rem));
  }

  /* Full screen on a small screen: the map takes all the height; filters
     stay in their overlay sheet (scrollable) instead of stacking under it. */
  :fullscreen .crime-map-layout__canvas,
  .crime-map-fullscreen-host--fallback .crime-map-layout__canvas {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
  }

  .crime-map-date-strip {
    padding: var(--vds-spacing-12);
  }

  .crime-map-date-strip__headline {
    align-items: flex-start;
    margin-left: 0;
    text-align: left;
  }

  .crime-map-date-presets {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    width: 100%;
  }

  .crime-map-date-presets__option {
    justify-content: center;
    border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  }

  .crime-map-toolbar {
    flex-wrap: wrap;
    right: 3rem;
    gap: var(--vds-spacing-8);
  }

  .crime-map-display__options {
    right: auto;
    left: 0;
  }

  .crime-map-area-overlay {
    top: auto;
    right: var(--vds-spacing-12);
    bottom: 4rem;
    width: calc(100% - 1.5rem);
    max-height: 55%;
  }

  .crime-map-canvas-controls {
    gap: var(--vds-spacing-8);
  }

  .crime-map-pill {
    padding: 0 var(--vds-spacing-12);
  }

  .crime-map-drawer__long,
  .crime-map-drawer:not([open]) .crime-map-drawer__handle {
    display: none;
  }

  .crime-map-drawer__short {
    display: inline;
  }

  /* Clears the two-row search/display toolbar above it. */
  .crime-map-drawer[open] {
    max-height: calc(max(26rem, min(72vh, 44rem)) - 10rem);
  }

  /* The incident table reflows into one card per incident. */
  .crime-map-drawer__table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .crime-map-drawer__table tbody tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--vds-spacing-4) var(--vds-spacing-8);
    padding: var(--vds-spacing-12);
    border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
  }

  .crime-map-drawer__table tbody tr[hidden] {
    display: none;
  }

  .crime-map-drawer__table td {
    display: block;
    padding: 0;
    border: 0;
  }

  .crime-map-drawer__table .crime-map-drawer__offense {
    grid-row: 1;
    grid-column: 1;
  }

  .crime-map-drawer__table .crime-map-drawer__date {
    grid-row: 1;
    grid-column: 2;
    text-align: right;
  }

  .crime-map-drawer__table .widget-circle-map__list-location {
    grid-column: 1 / -1;
  }

  /* Enhanced: the panel is a sheet over the map. */
  html.crime-map-filters--enhanced .crime-map-canvas-controls .crime-map-filters-button {
    display: inline-flex;
  }

  html.crime-map-filters--enhanced .crime-map-panel {
    position: absolute;
    z-index: 8;
    inset: var(--vds-spacing-8);
    display: none;
    flex-direction: column;
    border: var(--vds-border-width-1) solid var(--vds-border-base);
    border-radius: var(--vds-radius-8);
    box-shadow: var(--crime-map-overlay-shadow);
    overflow: hidden;
  }

  html.crime-map-filters--enhanced .crime-map-layout--sheet-open .crime-map-panel {
    display: flex;
  }

  html.crime-map-filters--enhanced .crime-map-panel__body,
  html.crime-map-filters--enhanced .crime-map-panel__body[hidden] {
    display: block;
    overflow-y: auto;
    padding-top: 0;
  }

  html.crime-map-filters--enhanced .crime-map-panel__toolbar .crime-map-panel__close {
    display: inline-flex;
    order: 1;
  }

  html.crime-map-filters--enhanced .crime-map-panel__footer .crime-map-panel__show {
    display: inline-flex;
    margin-left: auto;
  }

  .crime-map-panel__body > .crime-map-date-strip {
    margin: 0 calc(-1 * var(--vds-spacing-20)) var(--vds-spacing-4);
    padding: 0 var(--vds-spacing-20) var(--vds-spacing-16);
  }
}

/* COMT-276 phase 4: agency dataset browser */

.dataset-browser__meta {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-8);
}

.dataset-browser__version-switcher {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-16);
}

.dataset-browser__column-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  margin-right: var(--vds-spacing-16);
}

.dataset-browser__sort-row {
  display: flex;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-8);
}

.dataset-browser__active-filters {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--vds-spacing-8) 0;
}

.dataset-browser__active-filters li {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  padding: var(--vds-spacing-4) 0;
}

.dataset-browser__inline-form {
  display: inline;
}

.dataset-browser__row--suppressed {
  opacity: var(--vds-opacity-half);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
  color: var(--vds-fg-primary);
}

.data-table caption {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  text-align: left;
  font-weight: var(--vds-font-weight-semibold);
}

.data-table th,
.data-table td {
  padding: var(--vds-spacing-12) var(--vds-spacing-16);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  vertical-align: middle;
  font-size: var(--vds-font-size-body-sm);
}

.data-table thead th {
  background: var(--vds-bg-tertiary);
  color: var(--vds-fg-primary);
  font-weight: var(--vds-font-weight-semibold);
  text-transform: none;
  letter-spacing: normal;
}

.data-table tbody tr:nth-child(odd) > * {
  background: var(--vds-bg-primary);
}

.data-table tbody tr:nth-child(even) > * {
  background: var(--vds-bg-on-primary-high);
}

.data-table--unstriped tbody tr:nth-child(even) > * {
  background: var(--vds-bg-primary);
}

.data-table tbody tr:hover > * {
  background: var(--vds-bg-secondary);
}

vds-table-head vds-table-cell {
  --vds-bg-on-primary-high: var(--vds-bg-tertiary);
  --vds-font-weight-regular: var(--vds-font-weight-semibold);
}

vds-table-body vds-table-row:nth-child(odd) vds-table-cell {
  --vds-bg-on-primary-high: var(--vds-bg-primary);
  --vds-bg-brand-light: var(--vds-bg-primary);
}

vds-table-body vds-table-row:nth-child(even) vds-table-cell {
  --vds-bg-brand-light: var(--vds-bg-on-primary-high);
}

.data-table__footer {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--vds-spacing-16);
  padding: var(--vds-spacing-8) 0;
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-xs);
}

.data-table__pages {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
}

.data-table__pages a,
.data-table__page--current,
.data-table__page--disabled {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--vds-spacing-32);
  height: var(--vds-spacing-32);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-primary);
  text-decoration: none;
}

.data-table__pages a:hover,
.data-table__pages a:focus-visible {
  background: var(--vds-bg-secondary);
}

.data-table__page--current {
  background: var(--vds-bg-brand-primary);
  color: var(--vds-fg-white);
}

.data-table__page--disabled {
  color: var(--vds-fg-tertiary);
  opacity: var(--vds-opacity-half);
}

.data-table__size {
  display: flex;
  align-items: center;
  justify-self: end;
  gap: var(--vds-spacing-8);
}

.data-table__size select,
.data-table__apply {
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  cursor: pointer;
}

.data-table__size :focus-visible,
.data-table__pages :focus-visible {
  outline: var(--vds-border-width-2) solid var(--vds-border-focus-outer);
  outline-offset: var(--vds-border-width-2);
}

@media (max-width: 760px) {
  .data-table__footer {
    grid-template-columns: 1fr;
  }

  .data-table__size {
    grid-row: 2;
    justify-self: start;
  }

  .data-table__pages {
    grid-row: 3;
  }
}

.topic-faq__item {
  border-bottom: var(--vds-border-width-1) solid var(--vds-border-base);
}

.topic-faq__item summary {
  padding: var(--vds-spacing-12) var(--vds-spacing-8);
  cursor: pointer;
  font-weight: var(--vds-font-weight-semibold);
}

.topic-faq__answer {
  padding: 0 var(--vds-spacing-8) var(--vds-spacing-12);
}

.topic-glossary dl {
  display: grid;
  grid-template-columns: minmax(8rem, 1fr) minmax(0, 3fr);
  gap: var(--vds-spacing-12) var(--vds-spacing-24);
}

.topic-glossary dt {
  font-weight: var(--vds-font-weight-semibold);
}

.topic-glossary dd {
  margin: 0;
}

@media (max-width: 600px) {
  .topic-glossary dl {
    grid-template-columns: 1fr;
  }
}

/* Editor preview size (desktop / tablet / mobile): narrows the page card so
   responsive rows can be checked without leaving the editor. The page and
   its footer are centred in the space left of the sidecar — see the
   shared margin formula on body.is-editing > .app-main above, which
   applies at every breakpoint via --editor-stage-width's 1240px default. */
body.is-editing[data-editor-bp="tablet"] { --editor-stage-width: 768px; }
body.is-editing[data-editor-bp="mobile"] { --editor-stage-width: 390px; }

/* Cue on the grey canvas, just above the editable page card, saying which
   language is being edited. Lines up with the card (same width/margins as
   `body.is-editing > .app-main`) and takes over the card's top gap. */
.editor-language-note {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  box-sizing: border-box;
  margin: var(--vds-spacing-8) auto 0;
  padding-inline: var(--vds-spacing-4);
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
  line-height: 1.25;
}

.editor-language-note svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
}

body.is-editing > .editor-language-note {
  width: calc(100% - 2 * var(--vds-spacing-16));
  max-width: 1240px;
}

body.is-editing > .editor-language-note + .app-main {
  margin-top: var(--vds-spacing-4);
}

@media (min-width: 900px) {
  body.is-editing > .editor-language-note {
    --editor-stage-actual-width: min(var(--editor-stage-width, 1240px), calc(var(--editor-stage-track-width) - 2 * var(--editor-stage-gap)));
    width: var(--editor-stage-actual-width);
    max-width: none;
    /* Centre the stage in the track left of the sidecar rather than
       hugging the left edge — on a wide viewport, the full-width (1240px)
       stage left a large empty gap between it and the sidecar instead of
       splitting that slack evenly on both sides (bugfix, COMT-279
       follow-up). */
    margin-left: max(var(--editor-stage-gap), calc((var(--editor-stage-track-width) - var(--editor-stage-actual-width)) / 2));
    margin-right: calc(var(--editor-sidecar-width) + max(var(--editor-stage-gap), calc((var(--editor-stage-track-width) - var(--editor-stage-actual-width)) / 2)));
  }
}

/* Editor toolbar language selector: a pill (icon, code, chevron) opening a menu. */
.editor-lang-menu__trigger {
  gap: var(--vds-spacing-4);
  padding-inline: var(--vds-spacing-8);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-medium);
  cursor: pointer;
}

.editor-lang-menu__trigger svg {
  width: 1rem;
  height: 1rem;
}

.editor-lang-menu__trigger svg.editor-lang-menu__chevron {
  width: 0.875rem;
  height: 0.875rem;
}

.editor-lang-menu[open] .editor-lang-menu__chevron {
  transform: rotate(180deg);
}

.editor-lang-menu__panel {
  right: auto;
  left: 0;
  min-width: 15rem;
}

.editor-lang-menu__option {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-12);
}

.editor-lang-menu__option svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
}

.editor-lang-menu__option[aria-current="true"] {
  font-weight: var(--vds-font-weight-medium);
}

.editor-lang-menu__manage {
  margin-top: var(--vds-spacing-4);
  border-top: 1px solid var(--vds-border-base);
  padding-top: var(--vds-spacing-4);
}

.editor-lang-menu__manage > summary {
  cursor: pointer;
}

.editor-lang-menu__form {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  max-height: 14rem;
  overflow-y: auto;
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
}

.editor-lang-menu__check {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  font-size: 0.8125rem;
}

.editor-lang-menu__save {
  align-self: flex-start;
  margin-top: var(--vds-spacing-8);
}

.editor-lang-menu__footer {
  margin-top: var(--vds-spacing-4);
  border-top: 1px solid var(--vds-border-base);
  border-radius: 0;
  color: var(--vds-fg-brand-primary, var(--vds-fg-primary));
}

body:not(.is-editing) .app-header.app-header--editor-bar .editor-lang-menu__trigger {
  border-color: var(--editor-bar-control-border);
  background: var(--editor-bar-control-bg);
  color: #fff;
}

.app-header.app-header--editor-bar .editor-lang-menu__trigger {
  height: var(--editor-bar-control-h);
}

/* While editing, the editor toolbar's language menu is the only language
   control; the visitor-facing switcher on the canvas just shows the page. */
body.is-editing [data-public-language-switcher] {
  pointer-events: none;
}

.editor-bp-toggle {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: var(--vds-bg-primary);
}

.editor-bp-toggle__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--vds-radius-8);
  background: transparent;
  color: var(--vds-fg-primary);
  cursor: pointer;
}

.editor-bp-toggle__button svg {
  width: 1.125rem;
  height: 1.125rem;
}

.editor-bp-toggle__button[aria-pressed="true"] {
  background: var(--vds-fg-primary);
  color: var(--vds-bg-primary);
}

/* Edit / Preview segmented toggle in the editor bar. */
.mode-toggle {
  gap: 0;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: var(--vds-bg-primary);
}

.mode-toggle__button {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  min-height: 1.75rem;
  padding: 0 var(--vds-spacing-12);
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--vds-fg-primary);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-medium);
  cursor: pointer;
}

.mode-toggle__button svg {
  width: 0.9375rem;
  height: 0.9375rem;
}

.mode-toggle__button[aria-pressed="true"] {
  background: var(--vds-bg-brand-primary);
  color: #fff;
  cursor: default;
}

.mode-toggle__button[aria-pressed="false"]:hover {
  background: var(--vds-bg-secondary);
}

.app-header.app-header--editor-bar a.editor-bar-back {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  min-height: var(--editor-bar-control-h);
  padding: 0 var(--vds-spacing-8);
  border-radius: var(--vds-radius-8);
  color: var(--vds-fg-primary);
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-medium);
  text-decoration: none;
}

.app-header.app-header--editor-bar a.editor-bar-back:hover {
  background: var(--vds-bg-secondary);
}

body:not(.is-editing) .app-header.app-header--editor-bar a.editor-bar-back {
  color: #fff;
}

body:not(.is-editing) .app-header.app-header--editor-bar a.editor-bar-back:hover {
  background: var(--editor-bar-control-bg);
}

/* The signed-in name is redundant while editing: the avatar still opens the
   menu. */
body.is-editing > .app-header--editor-bar .app-user-menu__name {
  display: none;
}

/* A version preview needs the room for its own status text and actions. */
.app-header--version-preview .app-user-menu__name {
  display: none;
}

/* Add-row / add-section layout picker modal. */
.row-layout-picker {
  margin: 0;
}

.row-layout-picker__heading {
  margin: 0 0 var(--vds-spacing-16);
  color: var(--vds-fg-primary);
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium, 500);
}

.row-layout-picker__error {
  margin: 0 0 var(--vds-spacing-12);
  color: var(--vds-fg-danger, #b42318);
  font-size: var(--vds-font-size-body-sm);
}

.row-layout-picker__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--vds-spacing-8);
}

.row-layout-picker__option {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  min-width: 0;
  padding: var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-12, 12px);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}

.row-layout-picker__option:hover,
.row-layout-picker__option:focus-visible {
  border-color: #60a5fa;
  background: rgb(37 99 235 / 4%);
  box-shadow: 0 0 0 3px rgb(96 165 250 / 20%);
  outline: none;
}

.row-layout-picker__preview {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px;
  min-height: 2rem;
  padding: var(--vds-spacing-8);
  border-radius: var(--vds-radius-8, 8px);
  background: var(--vds-bg-secondary, #f5f5f5);
}

.row-layout-picker__preview i {
  display: block;
  min-width: 0;
  border-radius: 2px;
  background: rgb(38 38 38 / 16%);
}

.row-layout-picker__preview--cols-1 {
  grid-template-columns: minmax(0, 1fr);
}

.row-layout-picker__preview--cols-1 i:nth-child(n + 2),
.row-layout-picker__preview--cols-2 i:nth-child(n + 3),
.row-layout-picker__preview--cols-3 i:nth-child(n + 4),
.row-layout-picker__preview--ratio i:nth-child(n + 3) {
  display: none;
}

.row-layout-picker__preview--cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.row-layout-picker__preview--cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.row-layout-picker__preview--ratio {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.row-layout-picker__preview--ratio-21 {
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
}

.row-layout-picker__preview--ratio-12 {
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
}

.row-layout-picker__label {
  display: block;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium, 500);
  line-height: 1.3;
}

/* Row layout panel: segmented radio controls. */
.editor-form-fields .row-layout-group {
  min-width: 0;
  margin: 0 0 var(--vds-spacing-16);
  padding: 0;
  border: 0;
}

.editor-form-fields .row-layout-group legend {
  padding: 0;
  margin-bottom: var(--vds-spacing-8);
  font-weight: 600;
}

.row-layout-hint {
  margin: var(--vds-spacing-8) 0 0;
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
}

.segmented {
  display: flex;
  gap: 2px;
  padding: 2px;
  border-radius: var(--vds-radius-8);
  background: color-mix(in srgb, var(--vds-fg-primary) 7%, var(--vds-bg-primary));
}

.segmented__option {
  position: relative;
  flex: 1 1 0;
  text-align: center;
  cursor: pointer;
}

.segmented__option input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.segmented__option span {
  display: block;
  padding: var(--vds-spacing-8) var(--vds-spacing-4);
  border-radius: var(--vds-radius-4, 4px);
  color: var(--vds-fg-secondary);
  font-size: 0.875rem;
}

.segmented__option input:checked + span {
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font-weight: 600;
  box-shadow: 0 1px 2px rgb(15 23 42 / 20%);
}

.segmented__option input:focus-visible + span {
  outline: 2px solid #2563eb;
  outline-offset: 1px;
}

.segmented--split {
  margin-top: var(--vds-spacing-8);
}

.segmented__option .layout-glyph {
  display: grid;
  gap: 2px;
  height: 0.625rem;
  margin: 0 auto var(--vds-spacing-4);
  max-width: 3rem;
}

.layout-glyph--21 {
  grid-template-columns: 2fr 1fr;
}

.layout-glyph--12 {
  grid-template-columns: 1fr 2fr;
}

.layout-glyph i {
  border-radius: 2px;
  background: currentcolor;
  opacity: 0.45;
}

/* Selecting a Section or Row shows what it holds: its rows and widgets get a
   faint dashed ring with rounded corners, so the Section -> Row -> Widget
   nesting is visible without the square boxy look of the earlier outline.
   `box-shadow` keeps the sizing stable while letting the ring follow the
   same rounded treatment as the active selection and Add actions. */
.content-section.inline-editor-target--active .content-row,
.content-row.inline-editor-target--active .content-block {
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px #93c5fd;
}

.content-section.inline-editor-target--active .content-row .content-block {
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px #bfdbfe;
}

/* Keeps the Add widget modal one height across tabs and search results. */
#widget-picker-body {
  min-height: 26rem;
}

.inline-editor-panel__nav-items {
  margin-top: var(--vds-spacing-16);
}

/* Site header panel: identity fields, a reorderable list of dropdown menus
   (and, inside a menu, its links) and the header's own controls. */
.inline-editor-panel__hint {
  margin: calc(-1 * var(--vds-spacing-4)) 0 0;
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
}

.inline-editor-panel__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--vds-radius-8, 0.5rem);
  background: transparent;
  color: var(--vds-fg-secondary);
  cursor: pointer;
}

.inline-editor-panel__back:hover,
.inline-editor-panel__back:focus-visible {
  background: color-mix(in srgb, var(--vds-fg-primary) 8%, transparent);
  color: var(--vds-fg-primary);
}

.header-panel__section {
  margin-top: var(--vds-spacing-20, 1.25rem);
}

.header-panel__section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--vds-spacing-8);
  margin-bottom: var(--vds-spacing-8);
}

.header-panel__section-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--vds-fg-primary);
}

.header-panel__section-hint {
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
}

.header-panel__add {
  margin-top: var(--vds-spacing-8);
}

.header-nav-list {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.header-nav-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  min-width: 0;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8, 0.5rem);
  background: var(--vds-bg-primary);
}

.header-nav-row:hover,
.header-nav-row:focus-within {
  background: color-mix(in srgb, var(--vds-fg-primary) 4%, var(--vds-bg-primary));
}

.header-nav-row.sortable-ghost {
  opacity: 0.5;
}

.header-nav-row__handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1.75rem;
  align-self: stretch;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
  cursor: grab;
}

.header-nav-row__handle:hover,
.header-nav-row__handle:focus-visible {
  color: var(--vds-fg-primary);
}

.header-nav-row__main {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  gap: var(--vds-spacing-8);
  min-width: 0;
  padding: var(--vds-spacing-8) 0;
  border: 0;
  background: transparent;
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.header-nav-row__label {
  flex-shrink: 0;
  max-width: 55%;
  overflow: hidden;
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.header-nav-row__meta {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
  text-align: right;
  white-space: nowrap;
}

.header-nav-row__meta--truncate {
  overflow: hidden;
  text-overflow: ellipsis;
}

.header-nav-row__chevron {
  flex-shrink: 0;
  color: var(--vds-fg-secondary);
}

/* The ⋯ menu only shows while the row is hovered or has keyboard focus
   inside it (or its menu is open), so idle rows stay quiet. */
.header-nav-row__menu {
  flex-shrink: 0;
  opacity: 0;
}

.header-nav-row:hover .header-nav-row__menu,
.header-nav-row:focus-within .header-nav-row__menu,
.header-nav-row__menu:has([aria-expanded="true"]) {
  opacity: 1;
}

.header-control-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-8) 0;
}

.header-control-row + .header-control-row {
  border-top: var(--vds-border-width-1) solid var(--vds-border-base);
}

.header-control-row__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.header-control-row__label {
  font-weight: 500;
  color: var(--vds-fg-primary);
}

.header-control-row__hint {
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
}

.header-control-row__link {
  flex-shrink: 0;
  font-size: 0.875rem;
}

.inline-editor-panel__field-row--check label {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.inline-editor-panel__nav-empty {
  margin: 0 0 var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
}

.inline-editor-panel__nav-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Editor-only empty state of a chart with no data chosen yet: a greyscale
   skeleton of the chart (templates/widgets/_skeleton.html). Every bar stands in
   for text or a mark of the real widget, so the block keeps its real spacing.
   Greys are mixes of the foreground token, so they follow light/dark mode. */
.widget-skeleton {
  --skeleton-strong: color-mix(in srgb, var(--vds-fg-primary) 14%, transparent);
  --skeleton-soft: color-mix(in srgb, var(--vds-fg-primary) 8%, transparent);
  container-type: inline-size;
}

.widget-skeleton__title,
.widget-skeleton__description {
  border-radius: var(--vds-radius-sm, 0.25rem);
  cursor: text;
  outline-offset: 2px;
}

.widget-skeleton__title {
  margin: 0;
  font-size: var(--vds-font-size-body-lg, 1.125rem);
  font-weight: var(--vds-font-weight-semibold);
}

.widget-skeleton__description {
  margin: var(--vds-spacing-4) 0 var(--vds-spacing-16);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
  white-space: pre-wrap;
}

/* Grey placeholder text until the editor types something of their own. */
.widget-skeleton__title:empty::before,
.widget-skeleton__description:empty::before {
  content: attr(data-placeholder);
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
  opacity: 0.7;
}

.widget-skeleton__description:empty::before {
  font-style: italic;
}

.widget-skeleton__title:hover,
.widget-skeleton__description:hover {
  background: color-mix(in srgb, var(--vds-fg-primary) 5%, transparent);
}

.widget-skeleton__title:focus,
.widget-skeleton__description:focus {
  outline: 2px solid var(--vds-border-focus, var(--vds-fg-brand, currentColor));
}

.widget-skeleton__panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--vds-spacing-16);
  padding: var(--vds-spacing-24);
  border-radius: var(--vds-radius-lg, 1rem);
  background: color-mix(in srgb, var(--vds-fg-primary) 3%, transparent);
}

.widget-skeleton__shape {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-12, 0.75rem);
  width: 100%;
  min-height: 9rem;
}

.widget-skeleton__bar,
.widget-skeleton__columns span,
.widget-skeleton__stack span,
.widget-skeleton__cells span,
.widget-skeleton__dot,
.widget-skeleton__pie,
.widget-skeleton__clock {
  display: block;
  background: var(--skeleton-strong);
}

.widget-skeleton__bar {
  height: 0.875rem;
  border-radius: 999px;
}

.widget-skeleton__bar--tick {
  height: 0.75rem;
  background: var(--skeleton-soft);
}

.widget-skeleton__row {
  display: grid;
  grid-template-columns: minmax(3rem, 22%) 1fr;
  align-items: center;
  gap: var(--vds-spacing-16);
}

.widget-skeleton__row > .widget-skeleton__bar--tick {
  max-width: 100%;
}

.widget-skeleton__stack {
  display: flex;
  gap: 2px;
  height: 0.875rem;
  overflow: hidden;
  border-radius: 999px;
}

.widget-skeleton__stack span:last-child {
  background: var(--skeleton-soft);
}

.widget-skeleton__shape--kpi-card {
  align-items: flex-start;
  justify-content: center;
}

.widget-skeleton__bar--label { width: 55%; }
.widget-skeleton__bar--value { width: 38%; height: 2.25rem; }
.widget-skeleton__bar--note { width: 30%; height: 0.75rem; background: var(--skeleton-soft); }

.widget-skeleton__line {
  flex: 1;
  width: 100%;
  min-height: 7rem;
}

.widget-skeleton__line polyline {
  stroke: var(--skeleton-strong);
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.widget-skeleton__axis {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--skeleton-soft);
}

.widget-skeleton__columns {
  display: flex;
  align-items: flex-end;
  gap: 4%;
  height: 8rem;
}

.widget-skeleton__columns span {
  flex: 1;
  height: calc(var(--h) * 8rem);
  border-radius: var(--vds-radius-4, 4px) var(--vds-radius-4, 4px) 0 0;
}

.widget-skeleton__shape--pie-breakdown {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-24);
}

.widget-skeleton__pie {
  width: clamp(5rem, 36%, 9rem);
  aspect-ratio: 1;
  border-radius: 50%;
  background: conic-gradient(var(--skeleton-strong) 0 38%, var(--skeleton-soft) 38% 70%, var(--skeleton-strong) 70% 100%);
  mask: radial-gradient(circle, transparent 28%, #000 29%);
}

.widget-skeleton__legend {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
  width: 25%;
}

.widget-skeleton__cells {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.widget-skeleton__cells span {
  aspect-ratio: 5 / 3;
  border-radius: var(--vds-radius-4, 4px);
}

.widget-skeleton__shape--circle-map,
.widget-skeleton__shape--flight-card {
  position: relative;
  min-height: 11rem;
  border-radius: var(--vds-radius-md);
  background: var(--skeleton-soft);
}

.widget-skeleton__dot {
  --size: 2rem;
  position: absolute;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}

.widget-skeleton__shape--data-freshness {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-16);
}

.widget-skeleton__clock {
  width: 3.5rem;
  height: 3.5rem;
  flex: none;
  border-radius: 50%;
}

.widget-skeleton__lines {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--vds-spacing-8);
}

.widget-skeleton__prompt {
  margin: 0;
  color: var(--vds-fg-secondary);
  text-align: center;
}

@container (max-width: 22rem) {
  .widget-skeleton__panel {
    padding: var(--vds-spacing-16);
  }

  .widget-skeleton__row {
    grid-template-columns: minmax(2.5rem, 18%) 1fr;
    gap: var(--vds-spacing-8);
  }
}

/* An empty block (skeleton chart, or a text/FAQ/glossary/image placeholder)
   stretches its placeholder over the whole cell the row gives it, so the
   temporary state occupies the space the real content will. */
.content-block:has(> .content-block__body > .widget-skeleton, .block-skeleton, .topic-faq > .block-skeleton, .topic-glossary .block-skeleton, .content-block__media-row > .block-skeleton) {
  display: flex;
  flex-direction: column;
}

.content-block:has(.widget-skeleton, .block-skeleton) > .content-block__body {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.content-block__body > .widget-skeleton,
.topic-faq:has(> .block-skeleton),
.topic-glossary:has(.block-skeleton),
.content-block__media-row:has(> .block-skeleton) {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.widget-skeleton > .widget-skeleton__panel,
.topic-glossary:has(.block-skeleton) > dl {
  flex: 1;
}

.widget-skeleton__panel {
  justify-content: center;
}

.widget-skeleton__shape {
  flex: 1;
}

.block-skeleton {
  --skeleton-strong: color-mix(in srgb, var(--vds-fg-primary) 14%, transparent);
  --skeleton-soft: color-mix(in srgb, var(--vds-fg-primary) 8%, transparent);
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--vds-spacing-16);
  min-height: 9rem;
  width: 100%;
  box-sizing: border-box;
  padding: var(--vds-spacing-24);
  border-radius: var(--vds-radius-lg, 1rem);
  background: color-mix(in srgb, var(--vds-fg-primary) 3%, transparent);
}

.block-skeleton .content-block__placeholder {
  font-style: normal;
}

.block-skeleton__shape {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--vds-spacing-12, 0.75rem);
}

.block-skeleton__bar,
.block-skeleton__chevron {
  display: block;
  border-radius: var(--vds-radius-sm, 0.25rem);
  background: var(--skeleton-strong);
}

.block-skeleton__bar {
  height: 0.875rem;
}

.block-skeleton__bar--heading {
  width: 55%;
  height: 1.5rem;
  margin-bottom: var(--vds-spacing-4);
  background: var(--skeleton-strong);
}

.block-skeleton__bar--term {
  width: 30%;
  height: 1rem;
}

.block-skeleton__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vds-spacing-16);
  padding: var(--vds-spacing-16);
  border-radius: var(--vds-radius-md, 0.5rem);
  background: var(--skeleton-soft);
}

.block-skeleton__chevron {
  flex: none;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
}

.block-skeleton__term {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-8);
}

/* Editor-only sample copy/image for an empty heading/text or image block
   (_block_placeholder.html): same boxes as real content, muted so it reads
   as a stand-in. */
.content-block__text--placeholder {
  color: var(--vds-fg-tertiary, var(--vds-fg-secondary));
}

.content-block__text--placeholder p {
  margin: 0;
}

.content-block__text--placeholder .content-block__placeholder-heading {
  margin-bottom: var(--vds-spacing-8);
  font-size: var(--vds-font-size-heading-xs, 1.25rem);
  font-weight: var(--vds-font-weight-bold, 700);
  line-height: 1.3;
}

/* Asset field: one bordered picker — preview (or an empty tile) beside its
   actions. Remove only makes sense while an image is selected. */
.asset-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--vds-spacing-8);
  margin: 0 0 var(--vds-spacing-16);
}

.asset-field__picker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
}

.asset-field .asset-browser__selection {
  flex: 1 1 9rem;
  min-width: 0;
  gap: var(--vds-spacing-12);
}

.asset-field .asset-browser__selection-thumb {
  flex: 0 0 auto;
  width: 3.5rem;
  height: 3.5rem;
  background: var(--vds-bg-secondary);
}

.asset-field .asset-browser__selection-filename {
  min-width: 0;
  overflow: hidden;
  font-size: var(--vds-font-size-body-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.asset-field .asset-browser__selection-empty {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-12);
  color: var(--vds-fg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

/* Empty tile: a dashed square with an image glyph tinted by the theme. */
.asset-field .asset-browser__selection-empty {
  position: relative;
}

.asset-field .asset-browser__selection-empty::before {
  content: "";
  flex: 0 0 auto;
  width: 3.5rem;
  height: 3.5rem;
  box-sizing: border-box;
  border: var(--vds-border-width-1) dashed var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.asset-field .asset-browser__selection-empty::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 1.125rem;
  width: 1.25rem;
  height: 1.25rem;
  transform: translateY(-50%);
  background: var(--vds-fg-secondary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2'/%3E%3Ccircle cx='9' cy='10' r='1.75'/%3E%3Cpath d='m21 16-5-5-9 9'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2'/%3E%3Ccircle cx='9' cy='10' r='1.75'/%3E%3Cpath d='m21 16-5-5-9 9'/%3E%3C/svg%3E") center / contain no-repeat;
}

.asset-field__actions {
  display: flex;
  gap: var(--vds-spacing-4);
  margin-left: auto;
}

.asset-field:not(:has(.asset-browser__selection-thumb)) [data-asset-remove] {
  display: none;
}

.asset-field > .btn {
  justify-self: start;
}

.asset-browser__selection-filename {
  display: grid;
  gap: 2px;
}

.asset-browser__selection-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.asset-browser__selection-hint {
  justify-self: start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--vds-fg-warning-on-low, var(--vds-fg-secondary));
  font: inherit;
  font-size: 0.75rem;
  text-decoration: underline;
  cursor: pointer;
}

/* Panel footer: keep labels on one line; Back sits left as a quiet link-like
   control so Cancel/Done stay grouped on the right. */
.inline-editor-panel__actions {
  flex-wrap: wrap;
}

.inline-editor-panel__actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--vds-spacing-4);
  min-height: 2.25rem;
  box-sizing: border-box;
  padding-block: 0;
  white-space: nowrap;
}

/* A one-off panel action (e.g. a link group's "Add our topic pages") sits
   in its own quiet card: what it does, then the button. */
.inline-editor-panel__callout {
  display: grid;
  justify-items: start;
  gap: var(--vds-spacing-8);
  margin-top: var(--vds-spacing-16);
  padding: var(--vds-spacing-12);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.inline-editor-panel__callout .inline-editor-panel__hint {
  margin: 0;
}

.inline-editor-panel__callout-title {
  margin: 0;
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold, 600);
}

.inline-editor-panel__callout .btn--secondary {
  background: var(--vds-bg-primary);
}

.inline-editor-panel__actions-right {
  margin-left: auto;
}

.inline-editor-panel__back-action {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-4);
  padding-inline: var(--vds-spacing-8) var(--vds-spacing-12);
}

/* Background color: show an empty "no color" swatch (diagonal slash) once cleared. */
.clearable-color-field {
  display: inline-flex;
  align-items: center;
  gap: var(--vds-spacing-8);
}

.clearable-color-field.is-empty input[type="color"] {
  background:
    linear-gradient(
      to top right,
      transparent calc(50% - 1px),
      var(--vds-fg-error-on-low) calc(50% - 1px),
      var(--vds-fg-error-on-low) calc(50% + 1px),
      transparent calc(50% + 1px)
    ),
    var(--vds-bg-primary);
  color: transparent;
}

.clearable-color-field.is-empty input[type="color"]::-webkit-color-swatch-wrapper,
.clearable-color-field.is-empty input[type="color"]::-webkit-color-swatch {
  opacity: 0;
}

/* Query the canvas so narrow previews match phones without restyling editor controls. */
@container page (max-width: 735px) {
  .public-page {
    --public-chart-font-family: var(--vds-font-family-body);
    --public-chart-label-size: var(--vds-font-size-body-xs);
    box-sizing: border-box;
    width: 100%;
  }

  .public-page.page-width--themed {
    padding: var(--vds-spacing-16) 0;
  }

  .public-page :is(.content-section--hero, .content-section--full-width) > .content-section__inner,
  .public-page .public-breadcrumb__inner {
    width: calc(100% - (2 * var(--vds-spacing-16)));
  }

  .public-page .content-section--padding-normal { padding-block: var(--vds-spacing-16); }
  .public-page .content-section--padding-comfy { padding-block: var(--vds-spacing-24); }
  .public-page .content-section--padding-wide { padding-block: var(--vds-spacing-32); }

  .public-page .content-section--site-header .content-section__inner {
    padding-block: var(--vds-spacing-8);
  }

  .public-page .site-identity,
  .public-page .site-identity__home {
    gap: var(--vds-spacing-8);
  }

  .public-page .site-identity__agency {
    font-size: var(--vds-font-size-body-sm);
    font-weight: var(--vds-font-weight-medium);
  }

  .public-page .site-identity__menu-toggle {
    min-width: 44px;
    min-height: 44px;
  }

  .public-page .public-page-title-block {
    padding-block: var(--vds-spacing-16);
  }

  .public-page .public-page-title {
    font-size: clamp(1.625rem, 9cqi, 2.625rem);
    font-weight: var(--vds-font-weight-medium);
    line-height: 1.2;
  }

  .public-page .public-page-description {
    margin-top: var(--vds-spacing-8);
    font-size: clamp(1rem, 5cqi, 1.5rem);
    line-height: 1.45;
  }

  .public-page .public-page-update-line {
    margin-top: var(--vds-spacing-12);
    gap: var(--vds-spacing-4);
    font-size: clamp(0.875rem, 4.3cqi, 1.25rem);
    line-height: 1.5;
  }

  .public-page > .tab-strip > .tab-strip__row {
    margin-bottom: var(--vds-spacing-16);
  }

  .public-page .content-section:not(.content-section--hero):not(.content-section--site-header) .content-section__header {
    padding-top: var(--vds-spacing-16);
  }

  .public-page .content-section__heading h2,
  .public-page .content-block__body > h2 {
    font-size: 1.125rem;
    font-weight: var(--vds-font-weight-medium);
    line-height: 1.3;
  }

  .public-page .content-section__heading p:not(.content-section__eyebrow) {
    font-size: var(--vds-font-size-body-xs);
  }

  .public-page .content-section__divider {
    margin-block: var(--vds-spacing-12);
  }

  body:not(.is-editing) > .app-main .public-page .tab-strip__panel .content-section__header--with-filters {
    padding-block: var(--vds-spacing-8);
    margin-bottom: var(--vds-spacing-12);
  }

  .public-page .tab-strip__panel .content-section:first-child .content-section__header {
    padding-top: 0;
  }

  .public-page .content-block-row {
    gap: var(--vds-spacing-16);
  }

  .public-page .content-row + .content-row {
    margin-top: var(--vds-spacing-16);
  }

  .public-page .tab-strip__panel--themed,
  .public-page .content-block--themed:not(.content-block--linked_card) {
    padding: var(--vds-spacing-12);
  }

  .public-page .widget[data-chart-table-widget] {
    padding: var(--vds-spacing-12);
  }

  .public-page:not(.topic-page) .content-section:not(.content-section--hero) .widget[data-chart-table-widget] {
    padding-inline: 0;
  }

  .public-page.topic-page:not(.page-width--themed) .topic-data-surface:has(.tab-strip__panel, .content-section, .content-block) {
    padding-block: var(--vds-spacing-16);
  }

  body .public-page.topic-page:not(.page-width--themed) .topic-data-surface .content-block__widget > .widget:not(:where(
    .content-section--hero *,
    .tab-strip__panel--themed *,
    .content-block--themed *
  )) {
    padding-inline: var(--vds-spacing-12);
  }

  /* The old chart bleed compensated for gutters that are no longer present. */
  .public-page .widget[data-chart-table-widget] [data-widget-chart] {
    width: 100%;
    margin-inline: 0;
  }

  .public-page .widget > figcaption {
    font-size: var(--vds-font-size-body-md);
    font-weight: var(--vds-font-weight-medium);
    line-height: 1.3;
  }

  .public-page .widget__subtitle {
    font-size: var(--vds-font-size-body-xs);
    line-height: 1.45;
  }

  .public-page .widget--kpi-card {
    padding-inline: 0;
  }

  .public-page .widget-kpi-card__body,
  .public-page .widget--kpi-card .widget-flip-card__face--back {
    padding: var(--vds-spacing-12);
  }

  .public-page .widget-kpi-card__label,
  .public-page .widget-kpi-card__footer {
    font-size: var(--vds-font-size-body-xs);
  }

  .public-page .widget-kpi-card__value {
    font-size: 2rem;
    font-weight: var(--vds-font-weight-medium);
  }

  .public-page .widget-kpi-card__footer {
    padding-inline: var(--vds-spacing-12);
  }

  .public-page .linked-card__title {
    font-size: var(--vds-font-size-body-md);
    font-weight: var(--vds-font-weight-medium);
  }

  .public-page .linked-card__description {
    padding-inline-start: var(--vds-spacing-12);
    font-size: var(--vds-font-size-body-sm);
    font-weight: var(--vds-font-weight-regular);
  }

  .public-page input:not([type="checkbox"]):not([type="radio"]),
  .public-page select,
  .public-page textarea {
    font-size: max(1rem, var(--vds-font-size-body-md));
  }
}

/* The Hub (portal/hub_content.py): under the shared site header, an
   introduction drawn like a Topic Page's title area, then link groups with a
   clear heading over their card grid. A background photo or colour on the
   introduction turns it into a banner (the ordinary hero treatment). */
.content-section--hub-intro:not(.content-section--hero) .content-section__header {
  padding-top: var(--vds-spacing-32);
}

.content-section--hub-intro .content-section__heading h2 {
  margin: 0;
  max-width: 24ch;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.content-section--hub-intro .content-section__heading p:not(.content-section__eyebrow) {
  max-width: 45rem;
  margin: 10px 0 0;
  font-size: 1rem;
  line-height: 1.5;
}

.content-section--hub-intro:not(.content-section--hero) .content-section__heading p:not(.content-section__eyebrow) {
  color: var(--vds-fg-primary);
}

.content-section--hub-intro .content-section__divider,
.content-section--hub-links .content-section__divider {
  display: none;
}

/* The introduction's buttons sit side by side at their natural width. */
.content-section--hub-intro .content-block-row--grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--vds-spacing-12);
}

.content-section--hub-intro .content-block-row--grid > .content-block {
  flex: 0 0 auto;
}

.content-section--hub-intro .content-section__inner:has(.content-block--cta_button) > .content-section__header {
  margin-bottom: var(--vds-spacing-24);
}

.content-section--hub-intro .content-block-row > .content-block--cta_button {
  flex: 0 0 auto;
  width: fit-content;
  max-width: 100%;
  min-height: 0;
}

.content-section--hub-intro .content-row__actions .add-trigger {
  flex: 0 0 auto;
  width: fit-content;
}

.content-section--hub-links .content-section__header {
  margin-bottom: var(--vds-spacing-16);
}

.content-section--hub-links:not(.content-section--hero) .content-section__heading h2 {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.content-section--hub-links:not(.content-section--hero) .content-section__heading p:not(.content-section__eyebrow) {
  margin-top: 4px;
  font-size: 0.9375rem;
}

@container page (max-width: 480px) {
  .content-section--hub-intro .content-section__heading h2 {
    font-size: clamp(1.625rem, 9cqi, 2.625rem);
  }
}

/* Hub groups always show their add bar under the grid: a link group takes
   any number of cards, so there is no "free column" slot to wait for. */
.content-section--hub-links .content-row:has(> .content-block-row--grid) > .content-row__actions,
.content-section--hub-intro .content-row:has(> .content-block-row--grid) > .content-row__actions,
.content-section--hub-content .content-row:has(> .content-block-row--grid) > .content-row__actions {
  display: flex;
  margin-top: var(--vds-spacing-12);
}

/* Visitors never see an unfinished link card (no title or no destination
   yet), nor a link group with no finished cards; the editor keeps both, with
   a prompt, so they can be finished. */
body:not(.is-editing) .content-block:has(> .content-block__body > .link-card--unfinished),
body:not(.is-editing) .content-section--hub-links:not(:has(.topic-card:not(.link-card--unfinished), .linked-card:not(.link-card--unfinished))) {
  display: none;
}

.content-section--hub-intro .content-section__heading .content-section__eyebrow {
  margin-bottom: var(--vds-spacing-8);
}

/* The introduction over a photo: always light text on a darkened image,
   whatever the agency's colour slots are (the photo, not a slot, is the
   background here). */
.content-section--hub-intro:has(> .content-section__background) {
  color: #fff;
}

.content-section--hub-intro:has(> .content-section__background) .content-section__heading p:not(.content-section__eyebrow) {
  color: rgb(255 255 255 / 88%);
}

.content-section--hub-intro:has(> .content-section__background) .content-section__eyebrow {
  color: rgb(255 255 255 / 80%);
}

.content-section--hub-intro .content-section__background::after {
  background: linear-gradient(90deg, rgb(0 0 0 / 55%), rgb(0 0 0 / 25%));
}

.content-section--hub-intro:has(> .content-section__background) .content-row__actions .add-trigger {
  border-color: rgb(255 255 255 / 45%);
  color: rgb(255 255 255 / 85%);
}

/* The Hub's site footer (portal/hub_content.py `_add_footer`): the agency
   logo and up to three link sections (FOOTER_MAX_LINK_SECTIONS) on the
   header's colour band. The sections share the space beside the logo; on a
   tablet the logo takes its own line above them, and on a phone everything
   stacks. Topic-page cards collapse to plain text links here. */
/* The authored footer meets the powered-by bar. Only these Hub pages opt
   out of the shared sticky bar and its overlap reserve; a short page grows
   above its authored footer instead of leaving a blank band below it. */
body > .app-main:has(.hub-page > .page-site-footer) {
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
}

body > .app-main:has(.hub-page > .page-site-footer) > #public-page-content {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}

body > .app-main .hub-page:has(> .page-site-footer) {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
  padding-bottom: 0;
}

body > .app-main .hub-page > .page-site-footer {
  margin-top: auto;
}

body:has(> .app-main .hub-page > .page-site-footer) > .app-footer {
  position: static;
  bottom: auto;
  z-index: auto;
}

.page-site-footer .content-section--hub-footer {
  margin-top: var(--vds-spacing-32);
}

.content-section--hub-footer .content-block-row--grid.content-block-row--grid {
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--vds-spacing-24) var(--vds-spacing-32);
  align-items: start;
}

.content-section--hub-footer .content-block {
  min-width: 0;
  overflow-wrap: anywhere;
}

@container (max-width: 760px) {
  .content-section--hub-footer .content-block-row--grid.content-block-row--grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-flow: row;
    grid-auto-columns: auto;
    gap: var(--vds-spacing-24) var(--vds-spacing-32);
  }

  .content-section--hub-footer .content-block-row--grid > .content-block--image_only,
  .content-section--hub-footer .content-block-row--grid > .content-block:nth-child(4):last-child {
    grid-column: 1 / -1;
  }
}

@container (max-width: 480px) {
  .content-section--hub-footer .content-block-row--grid.content-block-row--grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.page-site-footer .content-section.content-section--hub-footer .content-block__body > h2 {
  margin: 0 0 var(--vds-spacing-8);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  opacity: 0.85;
}

.page-site-footer .content-section.content-section--hub-footer .content-block__body > .content-block__text,
.content-section--hub-footer .content-block__text p {
  margin: 0 0 var(--vds-spacing-4);
  font-size: 0.875rem;
  line-height: 1.5;
  color: inherit;
}

/* A link section is usually a list of links: drawn as a plain stack. */
.content-section--hub-footer .content-block__text :is(ul, ol) {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.content-section--hub-footer .content-block__text li {
  margin: 0;
  line-height: inherit;
}

.content-section--hub-footer .content-block--image_only .content-block__image {
  width: auto;
  max-width: 12rem;
  max-height: 5rem;
  object-fit: contain;
}

.content-section--hub-footer .topic-card-grid {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
}

.content-section--hub-footer .topic-card {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font-size: 0.875rem;
  line-height: 1.5;
}

.content-section--hub-footer .topic-card:hover .topic-card__title {
  text-decoration: underline;
}

.content-section--hub-footer .topic-card__title {
  font-weight: 400;
  padding: 0;
}

.content-section--hub-footer .topic-card__icon,
.content-section--hub-footer .topic-card__media,
.content-section--hub-footer .topic-card__description,
.content-section--hub-footer .topic-card__arrow,
.content-section--hub-footer .hub-group-note:not(.hub-group-note--empty) {
  display: none;
}

.content-section--hub-footer .hub-group-note--empty {
  padding: var(--vds-spacing-8);
  color: inherit;
  opacity: 0.8;
}

.content-section--hub-footer .content-row__actions .add-trigger {
  border-color: rgb(255 255 255 / 40%);
  color: inherit;
}

@container (max-width: 480px) {
  .content-section--hub-footer .content-block-row--grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Agency privacy policy table: show the shared row or the per-domain rows
   depending on the checkbox, without a server round trip (all rows stay in
   the form so unsaved edits survive toggling). */
.privacy-policy-table-scroll { max-width: 100%; overflow-x: auto; }
.privacy-policy-table { width: 100%; min-width: 52rem; border-collapse: collapse; margin-block: var(--vds-spacing-4, 1rem); }
.privacy-policy-table th,
.privacy-policy-table td { padding: var(--vds-spacing-2, 0.5rem); text-align: start; vertical-align: top; border-bottom: 1px solid var(--vds-color-border-default, currentColor); }
.privacy-policy-table fieldset { border: 0; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--vds-spacing-3, 0.75rem); }
#privacy-policy-form:has(#id_use_shared_policy:checked) .privacy-policy-row--separate,
#privacy-policy-form:has(#id_use_shared_policy:not(:checked)) .privacy-policy-row--shared { display: none; }

/* Theme section of the editor's Settings tab (agency/_theme_panel.html): colours with
   presets, Light/Dark editing, live contrast badges, and the brand logos. */
.theme-panel {
  display: grid;
  gap: var(--vds-spacing-16);
}

/* Sits right under the page's own settings, as another section of them. */
.inline-editor-panel--theme {
  padding-top: 0;
}

.theme-panel__group {
  display: grid;
  gap: var(--vds-spacing-12);
}

.theme-panel__heading {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: var(--vds-font-weight-semibold, 600);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vds-fg-secondary);
}

.theme-panel__notice,
.theme-panel__error {
  margin: 0;
  padding: var(--vds-spacing-8) var(--vds-spacing-12);
  border-radius: var(--vds-radius-8);
  font-size: var(--vds-font-size-body-sm);
}

.theme-panel__notice {
  background: var(--vds-bg-success-light);
  color: var(--vds-fg-success-on-low, var(--vds-fg-primary));
}

.theme-panel__error {
  background: var(--vds-bg-error-light);
  color: var(--vds-fg-error-on-low);
}

.theme-panel__warning {
  margin: 0;
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
}

.theme-panel__presets {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--vds-spacing-8);
}

.theme-preset {
  display: grid;
  gap: var(--vds-spacing-4);
  padding: var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.theme-preset:hover {
  background: var(--vds-bg-secondary);
}

.theme-preset:focus-visible {
  outline: 2px solid var(--vds-border-focus, var(--vds-bg-brand-primary));
  outline-offset: 2px;
}

.theme-preset[aria-pressed="true"],
.theme-preset--custom {
  border-color: var(--vds-fg-primary);
  box-shadow: inset 0 0 0 1px var(--vds-fg-primary);
}

.theme-preset--custom {
  cursor: default;
}

.theme-preset[hidden] {
  display: none;
}

.theme-preset__swatches {
  display: flex;
  height: 1.25rem;
  overflow: hidden;
  border-radius: var(--vds-radius-4);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vds-fg-primary) 12%, transparent);
}

.theme-preset__swatches > span {
  flex: 1;
}

.theme-preset--custom .theme-preset__swatches {
  background: conic-gradient(from 90deg, #e74c3c, #f1c40f, #2ecc71, #3498db, #9b59b6, #e74c3c);
}

.theme-preset__label {
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-medium, 500);
}

.theme-panel__modes {
  display: inline-flex;
  justify-self: start;
  padding: 2px;
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
}

.theme-panel__mode {
  padding: var(--vds-spacing-4) var(--vds-spacing-12);
  border: 0;
  border-radius: calc(var(--vds-radius-8) - 2px);
  background: transparent;
  color: var(--vds-fg-secondary);
  font: inherit;
  font-size: var(--vds-font-size-body-sm);
  cursor: pointer;
}

.theme-panel__mode[aria-pressed="true"] {
  background: var(--vds-bg-primary);
  color: var(--vds-fg-primary);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--vds-fg-primary) 15%, transparent);
}

.theme-panel__slots {
  display: grid;
  gap: var(--vds-spacing-8);
}

.theme-slot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "sample text inputs"
    "sample contrast inputs";
  align-items: center;
  column-gap: var(--vds-spacing-12);
  padding: var(--vds-spacing-8);
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
}

.theme-slot[hidden] {
  display: none;
}

.theme-slot--invalid {
  border-color: var(--vds-fg-error-on-low);
}

.theme-slot__sample {
  grid-area: sample;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--vds-radius-8);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vds-fg-primary) 12%, transparent);
  font-weight: var(--vds-font-weight-semibold, 600);
}

.theme-slot__text {
  grid-area: text;
  display: grid;
  min-width: 0;
}

.theme-slot__label {
  font-size: var(--vds-font-size-body-sm);
  font-weight: var(--vds-font-weight-semibold, 600);
}

.theme-slot__hint {
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
  line-height: 1.3;
}

.theme-slot__contrast {
  grid-area: contrast;
  color: var(--vds-fg-secondary);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.theme-slot__contrast--low {
  color: var(--vds-fg-error-on-low);
  font-weight: var(--vds-font-weight-semibold, 600);
}

.theme-slot__inputs {
  grid-area: inputs;
  display: flex;
  gap: var(--vds-spacing-4);
}

.theme-slot__input input[type="color"] {
  width: 2rem;
  height: 2rem;
  padding: 2px;
  border: var(--vds-border-width-1) solid var(--vds-border-base);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-primary);
  cursor: pointer;
}

/* Accent may be left empty; its compact clear control sits under the swatch. */
.theme-slot__inputs--clearable .clearable-color-field {
  display: grid;
  justify-items: center;
  gap: 2px;
}

.theme-slot__inputs--clearable .clearable-color-field .btn {
  padding: 0 var(--vds-spacing-4);
  border: 0;
  background: none;
  color: var(--vds-fg-secondary);
  font-size: 0.6875rem;
}

.theme-slot__error {
  grid-column: 1 / -1;
  margin: var(--vds-spacing-4) 0 0;
  color: var(--vds-fg-error-on-low);
  font-size: 0.75rem;
}

/* COMT-539: a chart's "Build from fields" filter values, and the read-only
   Data source panel showing which released tables and fields it reads. */
.value-checklist {
  display: flex;
  flex-direction: column;
  gap: var(--vds-spacing-4);
  max-height: 12rem;
  overflow-y: auto;
  padding: var(--vds-spacing-8);
  border: 1px solid var(--editor-field-border);
  border-radius: var(--vds-radius-4);
  background: var(--vds-bg-base);
}

.editor-form-fields .value-checklist__option {
  display: flex;
  align-items: center;
  gap: var(--vds-spacing-8);
  font-weight: var(--vds-font-weight-regular);
  color: var(--vds-fg-primary);
}

.value-checklist__empty {
  font-size: var(--vds-font-size-body-xs);
  color: var(--vds-fg-secondary);
}

.data-source-summary {
  margin-top: var(--vds-spacing-16);
  padding: var(--vds-spacing-12);
  border: 1px solid var(--vds-border-base);
  border-radius: var(--vds-radius-8);
  background: var(--vds-bg-secondary);
  font-size: var(--vds-font-size-body-sm);
}

.data-source-summary__heading {
  margin: 0 0 var(--vds-spacing-8);
  font-weight: var(--vds-font-weight-semibold);
  color: var(--vds-fg-primary);
}

.data-source-summary__list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--vds-spacing-4) var(--vds-spacing-12);
  margin: 0;
}

.data-source-summary__list dt {
  color: var(--vds-fg-secondary);
}

.data-source-summary__list dd {
  margin: 0;
  color: var(--vds-fg-primary);
  overflow-wrap: anywhere;
}

.data-source-summary__tables {
  margin: 0;
  padding: 0;
  list-style: none;
}

.data-source-summary__problems {
  margin: 0 0 var(--vds-spacing-8);
  padding-left: var(--vds-spacing-16);
  color: var(--vds-fg-error-on-low);
}
