/* Reserve scrollbar gutter so pages whose content fits in the
   viewport render at the same width as pages that overflow —
   otherwise the layout shifts horizontally when a scrollbar
   appears (notably on macOS with "Always show scrollbars"). */
html { scrollbar-gutter: stable; font-size: 14px; }
:root {
  /* Spacing & rhythm */
  --pico-spacing: 0.75rem;
  --pico-form-element-spacing-vertical: 0.5rem;
  --pico-form-element-spacing-horizontal: 0.75rem;
  --pico-line-height: 1.4;
  --pico-typography-spacing-vertical: 0.5rem;
  --pico-block-spacing-vertical: 0.75rem;
  --pico-block-spacing-horizontal: 1rem;


  /* Flat geometry — no elevation, minimal radius */
  --pico-border-radius: 2px;
  --pico-box-shadow: none;
  --pico-card-box-shadow: none;

  /* Primary = quiet institutional blue (info / link / opening) */
  --pico-primary: #1a6496;
  --pico-primary-background: #1a6496;
  --pico-primary-hover: #145380;
  --pico-primary-focus: rgba(26, 100, 150, 0.18);
  --pico-primary-inverse: #ffffff;

  /* Neutral palette */
  --pico-color: #1e2530;
  --pico-muted-color: #5f6b7a;
  --pico-muted-border-color: #dde1e7;

  /* Semantic accents — one each for warning and success */
  --color-warning: #b45309;
  --color-success: #166534;
}
/* Heading sizes and weights — tight, not shouting. Pico hardcodes these on
   the element; custom properties have no effect so we override directly. */
h1 { font-size: 1.5rem; font-weight: 600; }
h2 { font-size: 1.25rem; font-weight: 500; }
h3 { font-size: 1.0625rem; font-weight: 500; }
h4, h5, h6 { font-size: 1rem; font-weight: 500; }
/* Pico defines --pico-card-box-shadow independently from --pico-box-shadow
   and may set it via higher-specificity selectors in its theme rules.
   Belt-and-suspenders: kill the shadow directly on the element. */
article { box-shadow: none; margin-bottom: 0; }
/* Entity list — <ul class="entity-list"> wraps every list of entity
   cards. Resets remove browser list chrome while keeping the <ul>/<li>
   in the accessibility tree so screen readers announce "list of N items".
   Each <li> contains one <article class="entity-card">. */
ul.entity-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
/* Pico v2 sets list-style-type on <li> itself, not the <ul>, so the
   parent reset doesn't cascade — mirror the rule on the child. */
ul.entity-list > li { list-style: none; padding: 0; }
/* Card chrome (background, border, padding, rounded corners,
   tinted header/footer bands) is reserved for items inside a
   possible list of items — post-list cards, the per-affiliation
   loop in `clinicians/detail.html`, etc. Those render as
   `<article class="entity-card">` and pick up the chrome from
   Pico's default `<article>` element styling automatically; no
   rule below targets them. Single-item containers (detail-page
   wrappers, auth-page forms) deliberately do NOT carry chrome —
   they use `<section class="entity-card">` / `<section class="auth-page">`,
   which Pico does not card-style. The `entity-card` /
   `entity-header` / `entity-facts` / etc. class names persist as
   DOM hooks (tests query them) but contribute no styling. */
/* Lucide icons — single source of truth for sizing AND
   spacing. `color: inherit` lets each icon pick up the
   surrounding text color (so an icon inside a `<small>` or a
   `.secondary` button matches the text next to it).
   `margin-inline-end` is applied unconditionally so the
   canonical "icon to the left of a label" pattern —
   `<button><i class="icon-x"></i>Save</button>`,
   `<span><i class="icon-x"></i>Label</span>`,
   `<dt><i class="icon-x"></i></dt><dd>Value</dd>` — works in
   any container without per-component flex `gap` or literal
   HTML whitespace. A conditional rule (e.g.
   `:not(:last-child)`) wouldn't work: CSS structural
   pseudo-classes count element siblings only, so an `<i>`
   followed by a bare text node is still `:last-child` and
   would be excluded. Icons that genuinely need no trailing
   space (icon-only buttons, etc.) should override locally. */
[class^="icon-"], [class*=" icon-"] {
  vertical-align: bottom;
  margin-inline-end: 0.25em;
}
/* `<ul class="glyph-list">` (rendered by the shared `icon_list`
   macro in `_shared/icon_list.html`). The Lucide glyph in each
   `<li>` is the only marker; strip the default disc bullet and
   the left padding the bullet was indented from. The `<i>`
   icon's `margin-inline-end` rule above already provides the
   icon → label gap, so this rule adds no per-item spacing.

   Pico v2 sets `list-style-type: square` on the `<li>` itself
   (not on the `<ul>`), so `list-style: none` on the parent
   alone is shadowed — the square marker still renders.
   Repeating the rule on `.glyph-list li` strips it at the level
   Pico targets.

   Class is `glyph-list` (not `icon-list`) because Lucide ships
   an `icon-list` font glyph whose `::before` character would
   render on the `<ul>` itself if we matched the name. */
.glyph-list, .glyph-list li { list-style: none; }
.glyph-list { padding-left: 0; margin: 0; }
/* Definition lists — the canonical "fact row" shape used by every
   list-card facts block and resource detail view (`_facts_block`,
   `clinicians/detail`, `programs/detail`, `users/detail`,
   `organizations/detail`). Two rules to know:

     1) **Markup contract.** Templates wrap each `<dt>/<dd>` pair
        in a `<div>` so the row is one DOM unit (easier hooks,
        easier responsive collapse). The wrapper `<div>` is
        `display: contents` so the `<dt>` and `<dd>` are still
        direct grid items of the parent `<dl>` — that's what
        makes labels align in a column across rows. Don't drop
        the wrapping `<div>` or the alignment breaks.

     2) **Mobile collapse.** On ≤640px viewports the grid
        collapses to a single column and each row's `<dt>` stacks
        above its `<dd>` (keeps labels readable on narrow
        screens; two columns would crush the value to ~half-
        screen). The wrapper `<div>` exits `display: contents`
        there so the row keeps its own block scope. */
dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.75rem;
  row-gap: 0.25rem;
  margin: 0;
}
dl > div { display: contents; }
dt { font-weight: 500; }
dd { margin: 0; }
@media (max-width: 640px) {
  dl { grid-template-columns: 1fr; row-gap: 0.5rem; }
  dl > div { display: block; }
  dt { margin-bottom: 0.125rem; }
}
/* Form-field layout — the form-side mirror of the `<dl>` grid
   above. The key trick is **subgrid**: the parent `<form>` declares
   a 2-column grid track once (`max-content 1fr`), and every
   `.form-field` row uses `grid-template-columns: subgrid` to inherit
   those tracks. That makes the label column the SAME width across
   every row in the form — including across fieldset boundaries —
   instead of each row sizing to its own label independently.

   Fieldsets use `display: contents` so their children participate
   directly in the form's grid rather than establishing a new grid
   layer. Chrome does not propagate subgrid tracks through a
   fieldset-as-grid-container, so the intermediate layer must be
   collapsed. Legends inside fieldsets get `grid-column: 1 / -1` so
   they span the full form width as visual section headers.

   Markup contract (emitted by `_shared/form_fields.html` macros):

     <label class="form-field" for="x">
       <span class="form-field-label">Label</span>
       <input id="x" ... />
       <small id="x-helper">helper</small>     {# optional #}
     </label>

   Every macro funnels through this shape — including `checkbox_field`,
   which emits a sibling `<input type="hidden">` immediately before
   the checkbox itself. The hidden input has `display: none` from the
   browser default so it doesn't take a grid slot; layout still falls
   to the label-span (col 1) and the visible checkbox (col 2).

   Mobile (≤640px): the parent's grid collapses to a single
   column; subgrids inherit that automatically and the labels
   stack above the controls. Matches the `<dl>` collapse
   threshold so the form and the facts panel agree on when the
   label-on-left layout becomes too cramped.

   Scoped to `.entity-form-page` so search forms, auth forms,
   and any other `<form>` outside the entity create/edit chrome
   keep their existing flow. */
.entity-form-page form {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.75rem;
  row-gap: var(--pico-spacing);
  align-items: baseline;
}
/* Fieldsets collapse out of the layout so their children (labels,
   legends) become direct form grid items. This lets `.form-field`
   subgrid from the form rather than from an intermediate fieldset
   layer — Chrome doesn't propagate subgrid tracks through
   fieldset-as-grid-container. */
.entity-form-page form > fieldset {
  display: contents;
}
/* Legend becomes a full-width section header in the form grid. */
.entity-form-page form > fieldset > legend {
  grid-column: 1 / -1;
  font-weight: 600;
  margin-block-start: var(--pico-spacing);
}
/* Every direct child of the form/fieldset that ISN'T a
   `.form-field` spans both columns of the parent grid: legends,
   Pico `.grid` utility wrappers, fieldset-scoped helper
   `<small>`s, the action cluster, hidden inputs, hand-rolled
   `<label>`s that haven't been macro-converted, etc. */
.entity-form-page form > :not(.form-field):not(fieldset),
.entity-form-page form > fieldset > :not(.form-field) {
  grid-column: 1 / -1;
}
/* Form-field row: subgrid so label sits in col 1, control col 2. */
.entity-form-page form .form-field {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  align-items: baseline;
  row-gap: 0.25rem;
  margin: 0;
  padding: 0;
  border: 0;
}
.form-field-label { font-weight: 500; }
/* "(optional)" indicator rendered by every form-field macro when
   `required=False`. Reads as muted secondary text next to the
   label so it's visually clear the field can be left blank
   without competing with the label itself. Replaces the previous
   per-form `help="Optional."` pattern (single source of truth in
   `_field_label` — see `_shared/form_fields.html`). The
   label-to-"(optional)" gap is owned here — `_field_label` emits
   no literal whitespace between the two. */
.form-field-optional {
  font-weight: 400;
  color: var(--pico-muted-color);
  margin-inline-start: 0.5em;
}
.entity-form-page form .form-field > input,
.entity-form-page form .form-field > select,
.entity-form-page form .form-field > textarea,
.entity-form-page form .form-field > .form-field-control { margin: 0; min-width: 0; }
.entity-form-page form .form-field > small {
  grid-column: 2;
  margin: 0;
  color: var(--pico-muted-color);
}
/* Error state: when an input/select/textarea inside a `.form-field`
   row carries `aria-invalid="true"` (set by the macros' `error=`
   param), the sibling `<small id="<name>-helper">` carries the
   error text instead of the helper text (Pico's canonical
   one-small-per-field pattern). The selector mirrors the muted
   rule above's specificity and adds an attribute selector so it
   wins; Pico's own `~ small` rule for invalid forms is overridden
   by the rule above, so we restate it here at matching depth. */
.entity-form-page form .form-field > [aria-invalid="true"] ~ small {
  color: var(--pico-del-color);
}
/* Form-level error banner rendered above the fields by the
   `form_banner()` macro in `_shared/form_banner.html`. Used for
   errors that don't pin to a single input — auth/login's
   "Invalid email or password" is the canonical case. Styled with
   the same del-color the per-field errors use (single visual
   vocabulary for "this didn't work") plus a left border that
   reads as a callout without the heavyweight chrome of a Pico
   <article>. Not nested under `.entity-form-page` because the
   auth pages live under `.auth-page`, not the entity-form
   chrome — the banner needs to render in both. */
.form-banner {
  margin-block-end: 1rem;
  padding: 0.75rem 1rem;
  border-inline-start: 0.25rem solid var(--pico-del-color);
  background: color-mix(in srgb, var(--pico-del-color) 8%, transparent);
  color: var(--pico-del-color);
}
/* Inline form-fields nested inside a Pico `.grid` utility (a
   responsive 2-up row) fall back to a self-contained grid since
   their parent (the `.grid` div) isn't the subgrid source. */
.entity-form-page form .grid > .form-field {
  grid-template-columns: max-content 1fr;
  grid-column: auto;
}
.entity-form-page form .grid > .form-field > small { grid-column: 2; }
/* Radio cluster: yes/no labels inline inside the right column. */
.form-field-control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}
.form-field-control > .form-field-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}
@media (max-width: 640px) {
  .entity-form-page form {
    grid-template-columns: 1fr;
  }
  .entity-form-page form .form-field > small,
  .entity-form-page form .grid > .form-field,
  .entity-form-page form .grid > .form-field > small {
    grid-column: 1;
  }
  .entity-form-page form .grid > .form-field {
    grid-template-columns: 1fr;
  }
}
/* Inline meta list — the small print under a card heading or
   page H1 that joins several short facts with " · ". Templates
   wrap each fact in its own element (`<span>{{ date }}</span>
   <span>Virtual</span>...`); this rule puts the middot between
   every pair of adjacent siblings. The advantage over a
   hardcoded `{{ items | join(' · ') }}` is locality: drop or
   hide a single child and the surrounding separators redo
   themselves; render zero or one child and nothing extra
   appears. Works on any tag (`<span>`, `<a>`, etc.) — the
   selector is purely structural. */
.meta > * + *::before { content: " · "; }
@media (max-width: 576px) {
  .meta { display: flex; flex-direction: column; gap: 0.15em; }
  .meta > * + *::before { display: none; }
}
/* Page-scoped zone bar. Every page renders one
   `<div class="toolbar">` carrying:
     `<h1>` — the page heading (left cell).
     `.toolbar-actions` — a single cell wrapping the filter link
       (`.toolbar-filter-link`) and the action menu
       (`.toolbar-right`). Wrapping both in one cell is what
       gives the toolbar atomic reflow: at ≥641px the heading
       and actions share one row; at ≤640px the actions cell
       drops below the heading as a single unit. The cluster
       never splits mid-toolbar.
   Pages without actions or filter link render only the H1; the
   macro omits `.toolbar-actions` entirely so the grid's second
   column collapses. */
.toolbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.75rem;
}
/* Pico's default `<button>` is `display: block; width: 100%`
   (form-shaped); the toolbar overrides to natural-width inline
   buttons. Reaches every nested `<button>` / `[role="button"]`
   regardless of whether it lives in a form, menu, or `<li>`. */
.toolbar button, .toolbar [role="button"] { width: auto; margin: 0; }
.toolbar-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  justify-content: flex-end;
}
.toolbar-filter-link { min-width: 0; }
.toolbar-right { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
/* Atomic stack on narrow viewports. The actions cell drops to
   a second row in one move — the cluster doesn't split. */
@media (max-width: 640px) {
  .toolbar { grid-template-columns: 1fr; }
  .toolbar-actions { justify-content: flex-start; flex-wrap: wrap; }
}
/* `<menu>` is HTML's native "list of commands" element — used
   by the page toolbar (`menu.toolbar`, `menu.toolbar-right`)
   and any in-row action cluster (e.g. the users-table Actions
   cell). Strip its default `<ul>`-style bullets + padding so
   the container is invisible chrome; the parent's flex layout
   (when present) handles spacing, and `<li>` children carry no
   visual weight. The `.toolbar button` rule above reaches each
   action via descendant selector. */
menu { list-style: none; padding: 0; margin: 0; }
td > menu > li { margin: 0; }
td > menu > li > button,
td > menu > li > [role="button"] {
  margin: 0;
  padding-block: 0.25rem;
  padding-inline: 0.5rem;
  font-size: 0.875rem;
}
/* Breadcrumb back affordance. The macro emits a single
   `<a class="breadcrumb-back">` — a Lucide arrow-left glyph plus
   the deepest clickable parent's label — at every viewport
   width. The breadcrumb sits as a thin line above the toolbar
   row that carries the `<h1>`, so the chrome between the
   global nav and the content is one band instead of two.

   Pico's default `<a>` underline ran across both the icon and
   the label, and the icon's `vertical-align: -0.15em` made it
   sit below the underline so the chevron read as visually
   detached from the label. Strip the underline on the link as
   a whole, scope the underline to the label `<span>` on hover/
   focus, and use a flex `gap` for the chevron-to-label spacing
   (overriding the icon's universal `margin-inline-end`). */
.breadcrumb-back {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  text-decoration: none;
}
.breadcrumb-back > i { margin-inline-end: 0; }
.breadcrumb-back:hover .breadcrumb-back-label,
.breadcrumb-back:focus .breadcrumb-back-label {
  text-decoration: underline;
}
/* Entity create/edit forms — cap form width on large screens so
   short fields don't stretch across the whole container, and
   standardize the Save/Cancel/Delete action cluster. The wrapper
   `.entity-form-page` is emitted by `views/form_new.html` and
   `views/form_edit.html`; per-entity templates extend those, so
   they pick up the cap without per-template wiring. */
:root { --form-max-width: 720px; }
.entity-form-page > form,
.entity-form-page > section > form {
  max-width: var(--form-max-width);
}
/* Save / Cancel / Delete cluster — pinned to the form's full
   content width so the row aligns with the rest of the form's
   fields instead of clustering at the left edge as content-
   width flex items.

   Layout shape:
     Save + Cancel each `flex: 1 1 0` so they grow to fill the
     row (50/50 with no Delete; ~40/40 with Delete present, since
     Delete is content-width). Delete (`form-actions-destructive`)
     keeps its content width and `margin-left: auto` pushes it to
     the far right so a stray click on the wrong target stays
     unlikely.

   The cluster is a `<div class="form-actions">` rendered by the
   shared `actions` macro
   (`framework/templates/_shared/actions.html`). All entity
   create/edit forms route through that macro — no per-template
   action markup. On mobile (≤540px) the cluster wraps to a
   column and each control becomes full-width, with Delete reset
   back below the others. */
.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: stretch;
  margin-block-start: var(--pico-spacing);
}
.form-actions > button:not(.form-actions-destructive),
.form-actions > [role="button"]:not(.form-actions-destructive) {
  flex: 1 1 0;
  margin: 0;
}
.form-actions > .form-actions-destructive {
  margin: 0;
  margin-left: auto;
}
@media (max-width: 540px) {
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions > .form-actions-destructive { margin-left: 0; }
}
/* (fieldset margin-block-start removed — fieldsets use display:contents
   and ignore box-model properties; spacing is on legend instead.) */
/* Search-page multi-choice filters render as a `<fieldset>` of
   single-click checkboxes (replacing the previous native
   `<select multiple>` listbox — #583). The default layout is one
   option per line so short choice sets (Age groups, Languages)
   read top-to-bottom. Long sets opt into `.search-checkbox-grid`
   which lays the options out in 2–3 responsive columns so a
   50-state list doesn't stretch the form to 50 rows tall. */
.search-checkbox-fieldset {
  margin-block-start: var(--pico-spacing);
}
.search-checkbox-fieldset > label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-block: 0.125rem;
}
.search-checkbox-fieldset > label > input[type="checkbox"] {
  margin: 0;
  flex: 0 0 auto;
}
.search-checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  column-gap: var(--pico-spacing);
  row-gap: 0.125rem;
}
.search-checkbox-grid > legend {
  grid-column: 1 / -1;
}
@media (max-width: 540px) {
  .search-checkbox-grid {
    grid-template-columns: 1fr;
  }
}
/* Auth-page form — `/auth/login`, `/auth/register`,
   `/auth/forgot-password`, `/auth/reset-password/<token>`,
   `/auth/verify`. Each page extends `base.html` directly (not
   the entity-form grammar) and renders a single
   `<section class="auth-page">`. **No card chrome** — auth
   pages render as plain forms on the page, not as cards
   (deliberately distinct from detail-page `<section class="entity-card">`
   wrappers which DO carry chrome). The section sits inside the
   `<main class="container">` which Pico already centers and
   caps; without the rules below the form itself still stretches
   to the container's full width (~720px on tablet, ~1300px on
   desktop, #584). Cap at 28rem (~448px) — narrower than
   `--form-max-width` since the auth forms are visually dense
   (2–3 short fields). Centered via `margin-inline: auto`.
   Mobile (< 28rem) is unchanged since the cap is wider than
   the viewport. */
.auth-page {
  max-width: 28rem;
  margin-inline: auto;
}
/* Page footer (`<footer class="container">` in base.html — sibling
   of `<header>` and `<main>`). Centered chrome line, present on
   every page via the default `{% block footer %}` body. */
footer.container { text-align: center; }
fieldset { border: none; padding: 0; margin: 0 0 1.25rem; }
legend { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--pico-muted-color); font-weight: 500; padding: 0; }
/* Primary nav — brand always visible; links in a <details> so mobile
   can collapse them behind a hamburger toggle. */
#primary-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 0.5rem;
}
/* Primary nav hamburger — strip Pico's built-in chevron */
.nav-menu-toggle { list-style: none; margin-bottom: 0; }
#primary-nav > details { margin: 0; }
#primary-nav > details[open] > summary { margin-bottom: 0; }
.nav-menu-toggle::-webkit-details-marker { display: none; }
.nav-menu-toggle::marker { display: none; }
.nav-menu-toggle::after { display: none; }
/* Desktop: summary hidden, links always visible inline.
   #nav-menu is both a flex item (flex:1 fills remaining space) and
   a flex container so margin-left:auto on the <ul> right-aligns it.
   display:contents caused a Chrome bug where the <ul> got zero width. */
@media (min-width: 641px) {
  .nav-menu-toggle { display: none; }
  #nav-menu { flex: 1; display: flex; }
  #nav-menu > ul { list-style: none; padding: 0; margin: 0 0 0 auto; gap: 1rem; display: flex; }
}
/* Mobile: hamburger shows, open menu covers full screen below nav */
@media (max-width: 640px) {
  header.container { position: relative; }
  #nav-menu:not([open]) .nav-menu-close-icon { display: none; }
  #nav-menu[open] .nav-menu-open-icon { display: none; }
  #nav-menu[open] > ul {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    min-height: 100dvh;
    z-index: 10;
    background: var(--pico-background-color);
    display: flex;
    flex-direction: column;
    list-style: none;
    padding: 0;
    margin: 0;
  }
}
/* Sticky-footer layout. The body is a three-row grid
   (`<header>` / `<main>` / `<footer>`) sized to the viewport, so
   short pages pin the footer at the bottom instead of leaving it
   floating mid-screen. The landing page reuses this row scaffold
   to vertically center its hero (see `landing.html`); regular
   content pages just benefit from the pinned footer. */
body {
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}
