/* Shared search / sort / filter toolbar for every public card-grid list
   page -- articles (list + the "My articles"/"Learning paths"/"My
   learning paths"/"Saved" tabs), assessments (list + history), and
   documents (guided-template list + "My documents" history). Rendered by
   templates/includes/list_controls.html.

   Loaded unconditionally from templates/base.html (right after
   cards.css), *before* any app's own stylesheet, so an app-specific
   override still wins the cascade -- same ordering reasoning as cards.css.
   Extracted here once a third app needed a byte-for-byte copy of the
   pattern, per agent_docs/frontend-css-conventions.md's "extract once a
   third app needs it" rule -- this replaces the three app-prefixed copies
   that used to live in articles.css (.list-controls*),
   assessments.css (.assessment-list-controls*) and
   documents.css (.document-list-controls*).

   The design: the always-visible row (.list-controls__bar) holds only one
   merged search pill (.list-controls__searchbox -- an input, a clear "x",
   a magnifier submit, a divider, then the Filters icon button), capped at
   ~half the content width and centered, so it reads as one inviting thing
   to reach for rather than a control panel. Everything else -- sort,
   category, subcategory, status, date range -- lives in a left-side
   off-canvas tray (.list-controls__filters) opened by the Filters button,
   each facet its own collapsible <details> group
   (.list-controls__filter-group). An active-filter count badges the
   Filters button instead of auto-expanding the tray. Both the pill and
   the tray are ported from documents.css's .template-catalogue__* /
   .template-filters* (the /templates/?view=all catalogue) once this
   shared include needed the same look.

   static/shared/js/list_controls_ajax.js applies the tray's controls in
   place (fetch + swap, no full navigation, and keeps the tray open across
   the swap via Bootstrap's Offcanvas API) -- .lc-busy below is the brief
   dim it puts on the swapped-in results region while that fetch is in
   flight. */

/* Minimalist: no border, no background, no side padding -- the controls
   sit flush with the page gutter like the rest of the content rather
   than reading as a boxed-in widget. Vertical padding only, to keep the
   row off the elements above and below it. */
.list-controls {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}

/* Opt-in: keep the search bar (and, while it's open, the Filters tray)
   pinned as the card grid scrolls under it. Applied by the caller on the
   top-level catalogue pages where the <form> is a direct child of the
   page's content container -- so the sticky element's containing block
   spans the whole list. Also fine inside a single always-shown Bootstrap
   .tab-pane (articles:authored's Articles/Learning Paths switcher,
   authored_tabs.js) -- only one pane is ever in the render tree at a
   time, so its own containing block still spans that whole section, same
   as a standalone page. NOT for accordion sections (the "my history"
   pages), where multiple sections' forms sit
   simultaneously visible and each one's containing block is just its own
   short accordion body.

   top matches the .sticky-top #siteNavbar's rendered height so the band
   pins flush against its bottom edge -- ~75px on xl+, but ~88px below xl
   where the navbar expands into the hamburger bar, and it also tracks the
   admin nav-font setting. sticky_list_controls.js measures the live
   height into --lc-sticky-top on load + resize; the CSS var's own value
   is a no-JS fallback, refined per breakpoint. The ::before -- page-
   coloured, above the band -- backfills any sub-pixel gap so the
   scrolling cards never flash through; at rest it sits in the tabs' own
   mb-3 margin (unseen, page-on-page).

   At rest the band hugs the tabs above it (base .list-controls padding).
   `.is-pinned` -- added by sticky_list_controls.js only once the bar
   actually reaches the top -- then opens equal breathers above and below
   the search field: `padding-top` above, the out-of-flow `::after` ledge
   below. No divider lines -- just clean page-coloured space. padding-top's
   growth is offset exactly by padding-bottom + margin-block-end shrinking
   to 0, so pinning adds zero net height and the cards below never jump.

   The inline margin+padding bleeds the opaque background (and both
   pseudos) out to the card grid's own edges (a .row with g-4 pulls its
   columns 0.75rem past the container padding), so nothing peeks out
   beside the bar as it scrolls. Owns its own bottom margin -- the caller
   drops Bootstrap's .mb-* so `.is-pinned`'s `margin-block-end: 0` isn't
   fighting an !important utility. */
/* No-JS fallback for the pinned offset, refined per breakpoint (see the
   comment above). sticky_list_controls.js overrides it with the navbar's
   live measured height. */
:root {
  --lc-sticky-top: 4.6875rem;
}

@media (max-width: 1199.98px) {
  :root {
    --lc-sticky-top: 5.75rem;
  }
}

.list-controls--sticky {
  position: sticky;
  top: var(--lc-sticky-top, 4rem);
  z-index: 5;
  margin-block-end: 0.5rem;
  margin-inline: -0.75rem;
  padding-inline: 0.75rem;
  background-color: var(--site-bg-color, #fff);
  transition: padding-top 0.12s ease, padding-bottom 0.12s ease, margin-bottom 0.12s ease;
}

.list-controls--sticky::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 1rem;
  background-color: var(--site-bg-color, #fff);
}

/* Page-coloured ledge just below the bar -- an out-of-flow mirror of the
   pinned `padding-top` breather above it, so the space under the search
   field matches the space over it. Out of flow (absolute) so growing it
   on pin causes no reflow. No divider line; the only cue that content is
   passing under the bar is a soft downward shadow off this ledge, added
   in the pinned state (below). */
.list-controls--sticky::after {
  content: "";
  position: absolute;
  top: 100%;
  height: 0;
  /* Full viewport width (the form sits in a centered .container, so break
     out with 100vw + a centered left) -- so the pinned shadow reads as a
     page-spanning header edge, not a floating card. 100vw can be a hair
     wider than the content box when a scrollbar is present; the ::after is
     page-coloured and its shadow is soft + offset down, so a few px of
     overhang past the edge is imperceptible, and it can't add horizontal
     scroll (absolute, inside the container's own flow). */
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  background-color: var(--site-bg-color, #fff);
  box-shadow: 0 0 0 transparent;
  transition: height 0.12s ease, box-shadow 0.12s ease;
}

/* padding-top 0.25 -> 1 (+0.75) is exactly offset by padding-bottom
   0.25 -> 0 (-0.25) and margin-block-end 0.5 -> 0 (-0.5): pinning adds
   zero net height, so nothing below the bar jumps. The visible breather
   below the field is the ::after ledge instead. */
.list-controls--sticky.is-pinned {
  padding-top: 1rem;
  padding-bottom: 0;
  margin-block-end: 0;
}

.list-controls--sticky.is-pinned::after {
  height: 1rem;
  /* Soft, bottom-only feather onto the cards scrolling up behind the bar
     -- the negative spread keeps it from spilling past the container
     edge. Same shadow-as-elevation language as the cards' own .card--lift. */
  box-shadow: 0 8px 14px -4px rgba(0, 0, 0, 0.22);
}

/* Two-way catalogue switch row (.catalog-switch*, _catalog_switch.html):
   the flanking row that rides at the top of the shared
   .list-controls--sticky bar on a catalogue page -- a .segmented-tabs
   pill (tabs.css), centred under the centred page heading, with the
   search centred on its own line below (product owner, 2026-10-18; for
   part of that day both were left-aligned, then on one row), plus, for
   an author, a "My … ->"
   shortcut on the RIGHT set off by a hairline vertical rule. Plain
   cross-page navigation, no JS -- the current page's pill carries
   .active + aria-current, the other pill is a real link.

   Shared by both catalogue switches -- articles/_catalog_switch.html
   (Articles <-> Learning paths) and assessments/_catalog_switch.html
   (Assessments <-> Taken) -- and reused (hand-rolled) by
   articles:authored, which adds a left "<- Articles" / "<- Learning
   paths" link out to the catalogue, hence the .catalog-switch__back
   selectors below. Lived in articles.css until the assessments list
   grew the same switch.

   The row stays on ONE line at every width -- it never wraps a shortcut
   below the pill. It keeps fitting by degrading in steps as the
   .container narrows: full labels + hairline rules (lg), then a tighter
   gap / no rules / trimmed pill padding (md), then the short label forms
   (below md). `overflow-x: auto` + `safe center` is only a last-ditch
   net for a very narrow phone -- it then scrolls sideways as a unit
   rather than clip. */
.catalog-switch {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: safe center;
  gap: 1.25rem;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  /* The space under the switch, above the search (it was each partial's
     mb-2 until a one-row layout, since retired, needed to take it back). */
  margin-bottom: 0.5rem;
}

.catalog-switch::-webkit-scrollbar {
  display: none;
}

/* The pill keeps its natural width in the row (the row, not the pill,
   absorbs any overflow) -- its standalone max-width/scroll behaviour is
   still used everywhere it's not in a .catalog-switch. */
.catalog-switch .segmented-tabs {
  flex: 0 0 auto;
  max-width: none;
  overflow-x: visible;
}

/* .catalog-switch--compact: opt-in for a switch carrying more tabs than a
   single-line pill comfortably holds on a narrow viewport (documents'
   four-way Templates / My templates / Components / My components -- each
   tab also carries a count badge, so it's wider per-tab than the
   two/three-way switches this row was designed for). Wrapping onto a
   second line was tried first and looked broken (a lone pill stranded
   under the others, half the track's rounded background orphaned) --
   shrinking further instead keeps the one-line pill look intact. */
@media (max-width: 575.98px) {
  .catalog-switch--compact .segmented-tabs {
    gap: 0.125rem;
    padding: 0.1875rem;
  }

  .catalog-switch--compact .segmented-tabs__btn {
    gap: 0.2rem;
    padding: 0.4rem 0.5rem;
    font-size: 0.7rem;
  }

  .catalog-switch--compact .segmented-tabs__btn .badge {
    padding: 0.25em 0.4em;
    font-size: 0.65rem;
  }
}

.catalog-switch__sep {
  flex: 0 0 auto;
  width: 1px;
  height: 1.75rem;
  background-color: #ced4da;
}

.catalog-switch__back,
.catalog-switch__manage {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 0.95rem;
}

/* Long label by default; the short form is a second span, swapped in
   below the md breakpoint (see the two media queries). */
.catalog-switch__label-short {
  display: none;
}

/* md: the full phrases + pill still fit one line once the gap tightens,
   the hairline rules go, and the pill padding is trimmed (scoped to the
   row, so the shared .segmented-tabs is untouched elsewhere). */
@media (max-width: 991.98px) {
  .catalog-switch {
    gap: 0.625rem;
  }

  .catalog-switch__sep {
    display: none;
  }

  .catalog-switch__back,
  .catalog-switch__manage {
    font-size: 0.85rem;
  }

  .catalog-switch .segmented-tabs__btn {
    padding-left: 1rem;
    padding-right: 1rem;
  }
}

/* sm: the full phrases can't share a line with the pill -- swap each
   shortcut to its short form and shrink the row a little more so it
   still fits without clipping. */
@media (max-width: 767.98px) {
  .catalog-switch {
    gap: 0.5rem;
  }

  .catalog-switch__label-long {
    display: none;
  }

  .catalog-switch__label-short {
    display: inline;
  }

  .catalog-switch__back,
  .catalog-switch__manage {
    font-size: 0.8125rem;
  }

  .catalog-switch .segmented-tabs__btn {
    padding-left: 0.75rem;
    padding-right: 0.75rem;
    font-size: 0.8125rem;
  }
}

/* Phones (< 576px): the author's "My … ->" shortcut stays put -- it has
   no equivalent in the pill, and an author browsing the catalogue on a
   phone still needs a way to their drafts. On the narrowest phones the
   row (short labels) scrolls sideways as a unit rather than drop it
   (overflow-x on .catalog-switch). articles:authored's hand-rolled left
   "<- …" link does step aside here (redundant with its own pill's
   opposite tab). */
@media (max-width: 575.98px) {
  .catalog-switch__back,
  .catalog-switch__sep {
    display: none;
  }
}

/* The always-visible row: a centered cluster capped well short of the
   full content column so it never spans the whole page. Search + the
   Filters toggle stay on one line at every width (nowrap) -- the search
   box grows to fill the cap and shrinks to make room on a phone; the
   Filters toggle keeps its size. */
.list-controls__bar {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  max-width: 48rem;
  margin-inline: auto;
}

/* --- The search pill. Ported from documents.css's .template-catalogue__*
   (the /templates/?view=all catalogue) once a second consumer needed the
   same look: one merged, borderless-inside pill holding the input, a
   clear "x" (shown only once there's text), a magnifier submit button, a
   divider, and the Filters icon button -- rather than a boxed
   .input-group + a separate "Search" button + a separate outline
   "Filters" button. A grey hairline at rest (the site-wide field border,
   static/shared/css/fields.css -- the lighter #c4c7cd it had was under
   WCAG 1.4.11's 3:1); on hover / focus the border turns the same blue as
   a .btn-outline-secondary. No shadow. */
.list-controls__search {
  position: relative; /* anchors a caller's own suggestions listbox, if any */
  flex: 1 1 42rem;
  max-width: 42rem;
  min-width: 0;
}

.list-controls__searchbox {
  --lc-search-h: 2.85rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  min-height: var(--lc-search-h);
  padding: 0.15rem 0.5rem 0.15rem 1.05rem;
  background: var(--site-card-bg-color, #fff);
  border: 1px solid var(--site-input-border-color, #767676);
  border-radius: 999px;
  transition: border-color 0.15s ease;
}

.list-controls__searchbox:hover,
.list-controls__searchbox:focus-within {
  border-color: var(--site-btn-secondary-text-color, #0067b8);
}

.list-controls__search-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  padding: 0.45rem 0;
  font-size: 1.05rem;
  color: var(--site-text-color, #1d1d1f);
  appearance: none;
}

/* our own clear button replaces the native one */
.list-controls__search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.list-controls__search-clear {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: #5f6368;
  padding: 0.4rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
}

.list-controls__search-clear:hover {
  color: var(--site-text-color, #202124);
  background: rgba(60, 64, 67, 0.08);
}

/* shown only once the field has text (placeholder is a single space, so
   it's never actually visible -- see the include's own comment) */
.list-controls__search-input:not(:placeholder-shown) ~ .list-controls__search-clear {
  display: inline-flex;
}

/* divider between the search actions (clear / magnifier) and the Filters
   icon -- always shown */
.list-controls__search-divider {
  flex: 0 0 auto;
  align-self: stretch;
  width: 1px;
  margin: 0.4rem 0;
  background: #dadce0;
}

.list-controls__search-submit {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--site-link-color, #1a73e8);
  padding: 0.4rem 0.6rem;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 999px;
}

.list-controls__search-submit:hover {
  background: rgba(0, 103, 184, 0.08);
}

/* the Filters icon, inside the search pill next to the magnifier */
.list-controls__search-filters {
  position: relative;
}

.list-controls__search-filters.is-active {
  background: rgba(0, 103, 184, 0.1);
}

/* active-filter count -- a small badge on the Filters icon's corner */
.list-controls__filters-count {
  position: absolute;
  top: -0.15rem;
  right: -0.1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.05rem;
  height: 1.05rem;
  padding: 0 0.25rem;
  border-radius: 980px;
  background: var(--site-link-color, #0067b8);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
}

/* Title type-ahead suggestions listbox (suggest_url -- opt-in per page,
   e.g. articles:suggest / documents:suggest), anchored by
   .list-controls__search's own position:relative above. Byte-for-byte
   the same look as documents.css's .template-catalogue__suggestions* --
   that was the first consumer, this shared copy is the second; this one
   is the one to extend if a third form ever needs its own tweak. */
.list-controls__suggestions {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  right: 0;
  z-index: 20;
  margin: 0;
  padding: 0.35rem 0;
  list-style: none;
  background: var(--site-card-bg-color, #fff);
  border: 1px solid #d3d6db;
  border-radius: 1rem;
  box-shadow: 0 10px 28px rgba(16, 35, 60, 0.16);
  max-height: 20rem;
  overflow-y: auto;
}

.list-controls__suggestion {
  padding: 0.5rem 1.1rem;
  font-size: 0.97rem;
  color: var(--site-text-color, #202124);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.list-controls__suggestion:hover,
.list-controls__suggestion[aria-selected="true"] {
  background: rgba(0, 103, 184, 0.08);
}

/* "+ New X" shortcut (create_url) at the left of the bar. Hidden at rest
   and while the bar just scrolls -- revealed only once the bar is pinned
   under the navbar (.is-pinned), by which point the grid's own leading
   .add-card ghost tile has scrolled out of view. Deliberately quiet: a
   hairline dashed outline + muted text, no accent colour until :hover --
   it's a convenience, not something to draw the eye off the search box. */
.list-controls__create {
  display: none;
}

.list-controls--sticky.is-pinned .list-controls__create {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.85rem;
  border: 1px dashed #adb5bd;
  border-radius: 999px;
  color: #495057;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.list-controls--sticky.is-pinned .list-controls__create:hover,
.list-controls--sticky.is-pinned .list-controls__create:focus-visible {
  border-color: var(--site-link-color, #0067b8);
  color: var(--site-link-color, #0067b8);
  background-color: rgba(0, 103, 184, 0.05);
}

.list-controls__create-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  background-color: rgba(73, 80, 87, 0.14);
  color: #495057;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.list-controls__create-icon .bi {
  font-size: 0.8rem;
}

.list-controls__create:hover .list-controls__create-icon,
.list-controls__create:focus-visible .list-controls__create-icon {
  background-color: var(--site-link-color, #0067b8);
  color: #fff;
}

/* Phones: the search pill shrinks a touch so it plus the create
   shortcut's "+" disc still fit comfortably on the one line. */
@media (max-width: 575.98px) {
  .list-controls__searchbox {
    --lc-search-h: 2.6rem;
    padding-left: 0.85rem;
  }

  .list-controls__search-input {
    font-size: 1rem;
  }

  /* Just the "+" disc -- the label would crowd the search pill off the row. */
  .list-controls__create-label {
    display: none;
  }

  .list-controls--sticky.is-pinned .list-controls__create {
    padding: 0.5rem;
  }
}

/* --- The left-side off-canvas Filters tray. Ported from documents.css's
   .template-filters* (the /templates/?view=all catalogue) once a second
   consumer (this shared include) needed the same drawer -- per
   agent_docs/frontend-css-conventions.md, generic patterns live here once
   shared. Non-modal: no backdrop, so it starts below the navbar rather
   than tucking under it, and carries its own shadow in place of the
   missing backdrop. Results stay visible + scrollable behind it. */
.list-controls__filters.offcanvas {
  --bs-offcanvas-width: 21rem;
  --bs-offcanvas-padding-x: 1.15rem;
  --bs-offcanvas-padding-y: 1rem;
  top: var(--lc-sticky-top, 4rem);
  display: flex;
  flex-direction: column;
  box-shadow: 4px 0 24px rgba(16, 35, 60, 0.18);
}

/* Re-shown after an in-place swap (static/shared/js/list_controls_ajax.js)
   -- suppress the slide-in so the tray looks like it never closed. */
.list-controls__filters--no-anim.offcanvas {
  transition: none;
}

.list-controls__filters-header {
  align-items: center;
  border-bottom: 1px solid #e6e8eb;
}

.list-controls__filters-header-actions {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-left: auto;
}

.list-controls__filters-clear {
  font-size: 0.8125rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

.list-controls__filters-clear:hover,
.list-controls__filters-clear:focus-visible {
  text-decoration: underline;
}

.list-controls__filters-body {
  flex: 1 1 auto;
  overflow-y: auto;
}

.list-controls__filters-footer {
  flex: 0 0 auto;
  padding: 0.85rem var(--bs-offcanvas-padding-x, 1.15rem);
  border-top: 1px solid #e6e8eb;
  background: var(--site-card-bg-color, #fff);
}

/* The standalone boolean toggle (checkbox_filter_name) sits above the
   collapsible groups, not inside one -- there's only ever one. */
.list-controls__filter-switch {
  padding-bottom: 0.75rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid #e6e8eb;
}

/* --- A collapsible facet group --- */
.list-controls__filter-group {
  border-bottom: 1px solid #e6e8eb;
  padding: 0.75rem 0;
}

.list-controls__filter-group:first-of-type {
  padding-top: 0.25rem;
}

.list-controls__filter-group-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  list-style: none;
  font-weight: 600;
  font-size: 0.9rem;
  color: #16233c;
}

.list-controls__filter-group-head::-webkit-details-marker {
  display: none;
}

.list-controls__filter-group-head::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transform-origin: 60% 60%;
  opacity: 0.45;
  transition: transform 0.15s ease;
}

.list-controls__filter-group[open] > .list-controls__filter-group-head::after {
  transform: rotate(-135deg);
}

.list-controls__filter-group-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.35rem;
  border-radius: 980px;
  background: #e9f2fc;
  color: #1668c9;
  font-size: 0.7rem;
  font-weight: 700;
}

/* the <fieldset>/<div> that holds a group's option rows */
.list-controls__filter-group-body {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  border: 0;
  margin: 0;
  min-width: 0;
  padding: 0.6rem 0 0.15rem;
}

.list-controls__filter-group-body .form-check {
  margin: 0;
  min-height: 0;
  font-size: 0.875rem;
}

.list-controls__filter-group-body .form-check-label {
  cursor: pointer;
}

/* A range with two handles (list_controls.html's range_filter, the Articles catalogue's
   Reading time, 2026-11-03). Without JavaScript: two native sliders, each under its label and
   value, left as the browser draws them (accent-colour only), so the browser's own size
   stands (WCAG 2.5.8's user agent exception). list_controls_range.js adds .is-joined: both
   inputs on one track in the grid's first row, the labels and values below at each end. Each
   input there spans only the stretch its handle may travel (its own min and max), sized from
   --lc-range-low / --lc-range-high (each handle's place, 0 to 1), so both handles sit on one
   scale. The inputs take no pointer except at their handles; a press anywhere else lands on
   the track, which moves the nearer handle (WCAG 2.5.7). */
.list-controls__range {
  --lc-range-thumb: 1.5rem; /* 24px, WCAG 2.5.8's minimum target */
  --lc-range-low: 0;
  --lc-range-high: 1;
  font-size: 0.875rem;
}

.list-controls__range-field {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 0 0.5rem;
}

.list-controls__range-label {
  font-weight: 600;
}

.list-controls__range-value {
  font-variant-numeric: tabular-nums;
}

.list-controls__range-input {
  grid-column: 1 / -1;
  width: 100%;
  accent-color: #0067b8;
}

.list-controls__range.is-joined {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.15rem 1rem;
}

.list-controls__range.is-joined .list-controls__range-field {
  display: contents;
}

.list-controls__range.is-joined .list-controls__range-label {
  grid-row: 2;
}

.list-controls__range.is-joined .list-controls__range-value {
  grid-row: 3;
}

.list-controls__range.is-joined .list-controls__range-field--low .list-controls__range-label,
.list-controls__range.is-joined .list-controls__range-field--low .list-controls__range-value {
  grid-column: 1;
}

.list-controls__range.is-joined .list-controls__range-field--high .list-controls__range-label,
.list-controls__range.is-joined .list-controls__range-field--high .list-controls__range-value {
  grid-column: 2;
  text-align: right;
}

.list-controls__range-track {
  grid-row: 1;
  grid-column: 1 / -1;
  position: relative;
  height: var(--lc-range-thumb);
  cursor: pointer;
}

/* The line, from the first handle's centre to the last's. */
.list-controls__range-track::before,
.list-controls__range-fill {
  content: "";
  position: absolute;
  top: 50%;
  height: 4px;
  margin-top: -2px;
  border-radius: 2px;
}

.list-controls__range-track::before {
  left: calc(var(--lc-range-thumb) / 2);
  right: calc(var(--lc-range-thumb) / 2);
  background: var(--site-input-border-color, #767676);
}

/* The chosen stretch, between the handles. */
.list-controls__range-fill {
  left: calc(var(--lc-range-thumb) / 2 + var(--lc-range-low) * (100% - var(--lc-range-thumb)));
  right: calc(var(--lc-range-thumb) / 2 + (1 - var(--lc-range-high)) * (100% - var(--lc-range-thumb)));
  background: #0067b8;
}

.list-controls__range.is-joined .list-controls__range-input {
  grid-row: 1;
  grid-column: 1 / -1;
  justify-self: start;
  position: relative;
  z-index: 2;
  height: var(--lc-range-thumb);
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
  background: none;
  pointer-events: none;
}

.list-controls__range.is-joined .list-controls__range-input[data-lc-range-end="low"] {
  width: calc(var(--lc-range-thumb) + var(--lc-range-high) * (100% - var(--lc-range-thumb)));
}

.list-controls__range.is-joined .list-controls__range-input[data-lc-range-end="high"] {
  margin-left: calc(var(--lc-range-low) * (100% - var(--lc-range-thumb)));
  width: calc(var(--lc-range-thumb) + (1 - var(--lc-range-low)) * (100% - var(--lc-range-thumb)));
}

.list-controls__range.is-joined .list-controls__range-input::-webkit-slider-runnable-track {
  height: var(--lc-range-thumb);
  background: none;
}

.list-controls__range.is-joined .list-controls__range-input::-moz-range-track {
  height: var(--lc-range-thumb);
  background: none;
}

.list-controls__range.is-joined .list-controls__range-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: var(--lc-range-thumb);
  height: var(--lc-range-thumb);
  border: 2px solid #0067b8;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
  pointer-events: auto;
}

.list-controls__range.is-joined .list-controls__range-input::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--lc-range-thumb);
  height: var(--lc-range-thumb);
  border: 2px solid #0067b8;
  border-radius: 50%;
  background: #fff;
  cursor: pointer;
  pointer-events: auto;
}

/* The focus ring on the handle that has focus, focus.css's two tones (a white gap, then
   #0067b8), instead of round the input, which spans only part of the track. */
.list-controls__range.is-joined .list-controls__range-input:focus-visible {
  outline: none;
}

.list-controls__range.is-joined .list-controls__range-input:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px #fff, 0 0 0 4px #0067b8;
}

.list-controls__range.is-joined .list-controls__range-input:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 2px #fff, 0 0 0 4px #0067b8;
}

/* Forced colours drop the shadow ring and the fills: system colours for the line, the
   stretch and the handles, and an outline for focus. */
@media (forced-colors: active) {
  .list-controls__range-track,
  .list-controls__range.is-joined .list-controls__range-input {
    forced-color-adjust: none;
  }

  .list-controls__range-track::before {
    background: CanvasText;
  }

  .list-controls__range-fill {
    background: Highlight;
  }

  .list-controls__range.is-joined .list-controls__range-input::-webkit-slider-thumb {
    border-color: ButtonText;
    background: ButtonFace;
  }

  .list-controls__range.is-joined .list-controls__range-input::-moz-range-thumb {
    border-color: ButtonText;
    background: ButtonFace;
  }

  .list-controls__range.is-joined .list-controls__range-input:focus-visible {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
}

/* articles/_filter_sidebar.html's category -> subcategory pair defaults to
   a side-by-side row (flex: 0 1 27rem) -- too wide for the tray. Stack. */
.list-controls__filters .list-controls__filter-pair {
  flex-direction: column;
  flex: 1 1 auto;
  gap: 0.4rem;
}

.list-controls__filters .list-controls__filter-pair > *,
.list-controls__filters .list-controls__filter-pair > .position-relative {
  flex: 1 1 auto;
  width: 100%;
}

/* The date-range cluster ("<date> to <date> [Apply]") inside its group. */
.list-controls__panel-group {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

/* Category + subcategory are a dependent pair (pick a category, then its
   subcategory) -- keep them on one line at every width instead of letting
   the panel's flex-wrap split them across rows. They shrink together
   (min-width:0) rather than wrapping. */
.list-controls__filter-pair {
  display: flex;
  /* Sized so the subcategory <select>'s disabled placeholder
     ("Choose a category first") clears its dropdown arrow; don't grow
     past that on a wide panel, do shrink on a narrow one. */
  flex: 0 1 27rem;
  flex-wrap: nowrap;
  gap: 0.5rem;
  min-width: 0;
}

/* Give the subcategory <select> the larger share (~5:4) -- its disabled
   placeholder ("Choose a category first") is longer than the category
   combobox's ("Search categories…") -- rather than splitting 50/50. */
.list-controls__filter-pair > * {
  flex: 5 1 0;
  min-width: 0;
}

.list-controls__filter-pair > .position-relative {
  flex: 4 1 0;
}

.list-controls__filter-pair .article-filter-category-input,
.list-controls__filter-pair .article-filter-subcategory-input {
  width: 100%;
  min-width: 0;
}

/* The category -> subcategory combobox itself (_filter_sidebar.html +
   articles/js/article_category_picker.js). Shared by the articles and
   assessments list pages -- lived in articles.css until the assessments
   catalogue grew the same category filter. The two fixed widths below are
   the bare-control fallback; inside .list-controls__filter-pair the rule
   just above overrides them to a shared 100%. */
.article-filter-category-input {
  width: 180px;
}

.article-filter-subcategory-input {
  width: 200px;
}

/* Category picker dropdown -- a plain, fully own-styled DOM menu instead
   of a native <datalist> popup (see the comment in _filter_sidebar.html
   for why). Anchored to its .position-relative wrapper div, not the input
   directly, so its width isn't clipped to the input's own 180px. */
.article-filter-category-menu {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  width: 100%;
  min-width: 220px;
  max-height: 240px;
  overflow-y: auto;
  margin: 0.25rem 0 0;
  padding: 0.25rem 0;
  list-style: none;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 0.5rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  font-size: 0.8125rem;
}

.article-filter-category-menu.show {
  display: block;
}

.article-filter-category-menu li {
  padding: 0.375rem 0.75rem;
  cursor: pointer;
}

.article-filter-category-menu li[hidden] {
  display: none;
}

.article-filter-category-menu li:hover,
.article-filter-category-menu li.is-active {
  background-color: #f2f2f2;
}

/* The optional boolean filter (checkbox_filter_name -- e.g. "Show saved
   articles only"). Rendered as a toggle switch inside a bordered chip so
   it reads as an obvious on/off filter, distinct from the plain radio/
   checkbox rows in the collapsible groups below it. Bootstrap's
   .form-check is a block with a floated input; this flattens it into a
   row. */
/* NOTE: form-check / form-switch / list-controls__filter-switch are all on
   the *same* <div> (see list_controls.html) -- these must be compound
   selectors (no space) to actually match that element, not descendant
   selectors, or they silently match nothing. */
.list-controls__filter-switch.form-check {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 0;
  margin: 0;
  padding: 0.55rem 0.8rem;
  border: 1px solid #ced4da;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}

.list-controls__filter-switch.form-check:hover {
  border-color: var(--site-link-color, #0067b8);
}

/* Filter on -- make it unmistakable at a glance (the switch itself already
   fills with the accent colour; this lifts the whole chip too). */
.list-controls__filter-switch.form-check:has(.form-check-input:checked) {
  border-color: var(--site-link-color, #0067b8);
  background-color: rgba(0, 103, 184, 0.06);
}

.list-controls__filter-switch .form-check-input {
  float: none;
  margin: 0;
  cursor: pointer;
}

/* The switch track -- sized explicitly (a wide pill, not the ~12px square
   the plain-checkbox fallback rule would leave it). Fixed rem units, not
   em, so it doesn't ride the chip's own font-size. */
.list-controls__filter-switch.form-switch .form-check-input {
  width: 2.5rem;
  height: 1.3rem;
  border-radius: 2em;
  flex-shrink: 0;
}

.list-controls__filter-switch .form-check-label {
  font-size: 0.875rem;
  cursor: pointer;
}

/* Bottom-of-list pagination: a card grid renders the numbered pager
   ([data-full-pager]) and, when there's a next page, a hidden "Load more"
   button (includes/_load_more.html). static/shared/js/load_more.js swaps
   which one is visible; with JS off the pager stays. */
.list-more {
  display: flex;
  justify-content: center;
  margin: 0.5rem 0 1rem;
}

.list-more__btn {
  min-width: 12rem;
}

/* In-place filtering (static/shared/js/list_controls_ajax.js): while the
   swapped-in results are being fetched, dim them and lock out clicks so a
   double-tap can't fire a second request against stale markup. The
   toolbar itself stays fully interactive -- the visitor can keep
   adjusting filters. No spinner: the fetch is one small HTML response and
   a flash of a spinner reads as slower than a brief fade. */
.lc-results.lc-busy {
  opacity: 0.55;
  pointer-events: none;
  transition: opacity 0.12s ease;
}

/* The Resource Library nav (plan/_library_nav.html, ADR 0072): the space
   under it (it was the partial's mb-3, an !important utility the
   catalogues' one-row head in documents.css couldn't take back). */
.library-nav {
  margin-bottom: 1rem;
}

/* In a catalogue's sticky bar (projects:kit_list's #kitsCatalogBar) the nav
   stacks above the search like a catalogue switch, with the switch's 0.5rem
   under it, not 1rem. Same as documents.css's .template-catalogue__head. */
.list-controls--sticky > .library-nav {
  margin-bottom: 0.5rem;
}

/* The Resource Library nav (plan/_library_nav.html, ADR 0072). On a phone
   the type tabs keep their full names ("Planning kits", no short "Kits")
   at their own size, centred, not stretched across the row (product owner,
   2026-10-21). Below 400px they drop their count badges. They're trimmed
   only where the one-line pill wouldn't fit otherwise (measured from 320px
   up): less side padding between 400 and 430px, where the badges are back,
   and also 12px text below 370px. Each tab stays over 24px tall (WCAG
   2.5.8). */
@media (max-width: 399.98px) {
  .library-nav .segmented-tabs__btn .badge {
    display: none;
  }
}

@media (max-width: 369.98px) {
  .library-nav .catalog-switch .segmented-tabs__btn {
    padding-left: 0.375rem;
    padding-right: 0.375rem;
    font-size: 0.75rem;
  }
}

@media (min-width: 400px) and (max-width: 429.98px) {
  .library-nav .catalog-switch .segmented-tabs__btn {
    padding-left: 0.4375rem;
    padding-right: 0.4375rem;
  }
}

/* The ownership filters (plan/_library_filters.html, ADR 0072): All / Mine /
   Shared / Favorites as quiet text links on a page's results line, like
   GitHub's open / closed counts (product owner, 2026-10-21; they were filled
   pills under the type switch). Grey (#5c6672, 5.9:1, the .card__meta grey)
   at rest, the link colour and an underline on hover or focus, the current
   one bold and dark. They sit in a nav list, not in a run of text, so colour
   isn't the only sign they're links (WCAG 1.4.1). Each link is padded to at
   least 24px tall (WCAG 2.5.8). */
.library-filters__list {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.875rem;
}

.library-filters__item + .library-filters__item::before {
  content: "\00b7" / "";
  margin-inline: 0.5rem;
  color: #5c6672;
}

.library-filters__link {
  display: inline-block;
  padding-block: 0.25rem;
  color: #5c6672;
  text-decoration: none;
}

.library-filters__link:hover,
.library-filters__link:focus-visible {
  color: var(--site-link-color, #0067b8);
  text-decoration: underline;
}

.library-filters__link[aria-current="page"] {
  color: var(--site-text-color, #212529);
  font-weight: 600;
}

/* On its own line, centred (the templates overview, which has no results
   line), tucked under the type switch. */
.library-filters--centered {
  margin: -0.5rem 0 1rem;
}

/* A resource page's results line (.library-results): the count on the left,
   the filters centred under the search (product owner, 2026-10-21), an
   author's "+ New ..." link on the right. From md a three-column grid keeps
   the filters truly centred whether or not the count and the link are there
   (an empty Mine or Favorites has no count). On a phone the filters (on the
   left) and the link (on the right) share one line, the links at 13px with
   tighter separators, and below 400px without their visible "(2)" counts;
   the results count is visually hidden there (product owner, 2026-10-21).
   Both stay for screen readers (the count also for list_controls_ajax.js's
   announcement). Measured to fit on one line from 320px on every page. */
.library-results {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  column-gap: 1rem;
}

/* An author's "+ New ..." link reads just "+ New" on a phone (product
   owner, 2026-10-21). The full label stays as its accessible name, visually
   hidden rather than removed, and the short word is aria-hidden; "New" is
   where the name starts, so a voice user can still say what they see
   (WCAG 2.5.3). */
.library-results__new-short {
  display: none;
}

@media (max-width: 767.98px) {
  .library-results {
    flex-wrap: nowrap;
    column-gap: 0.75rem;
  }

  .library-results .library-filters__list {
    justify-content: flex-start;
    font-size: 0.8125rem;
  }

  .library-results .library-filters__item + .library-filters__item::before {
    margin-inline: 0.375rem;
  }

  .library-results__new-short {
    display: inline;
  }

  .library-results__new {
    flex-shrink: 0;
    margin-inline-start: auto;
  }

  .library-results__count,
  .library-results__new-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

@media (max-width: 399.98px) {
  .library-results .library-filters__count {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

@media (min-width: 768px) {
  .library-results {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
  }

  .library-results__count {
    grid-area: 1 / 1;
    justify-self: start;
  }

  .library-results > .library-filters {
    grid-area: 1 / 2;
  }

  .library-results__new {
    grid-area: 1 / 3;
    justify-self: end;
  }
}

