/* The app's own stylesheet. build-plan.md "Keeping the UI consistent"; N-25.
 *
 * Pico is the design system and its --pico-* variables are the tokens; this file is what Pico
 * does not already do, and every rule here has to justify itself. No partial carries a <style>
 * block or a style= attribute, so anything visual that is not Pico's default is a class here.
 * Dark mode is Pico's own, from prefers-color-scheme, and nothing else decides it. The top bar is
 * the VIEW's colour in both schemes, and the sidebar a tint of it that is pale in one and dark in
 * the other, so the logo follows the scheme (PR-289).
 *
 * Pico is vendored inside a cascade layer (scripts/vendor.js says how and why). A layered rule
 * loses to an unlayered one whatever its specificity, so everything in this file beats Pico by
 * being unlayered, and SurveyJS's stylesheet beats Pico's element styling inside the survey
 * without a reset — Pico styles bare <input> and <button>, and would otherwise win on
 * specificity (PR-43, surveyjs/pico-adapter.css).
 */

/* ── typography: an application, not a document ───────────────────────────────────────── */

/* Pico steps its root font up at every breakpoint, to 21px on a wide screen, which is right
 * for an article and wrong for a screen full of controls. LEGACY-2 pinned it at 16px and that
 * density is what CIYJ liked; the same here, with headings sized for a page title rather than
 * a chapter. Everything else in Pico is in rem, so this one value carries through.
 *
 * ── one scale, the mockup's (CIYJ, 2026-09-17) ──────────────────────────────────────────────
 *
 * Sizes had drifted apart — a sidebar row at 16px beside a 20px top bar, badges at 13, notes in
 * em of whatever they sat in — so they are written here as one scale and in rem, never em, so a
 * size means the same thing wherever it is used:
 *
 *   20px  1.25rem      a page's heading
 *   18px  1.125rem     the SITE's name in the top bar
 *   17px  1.0625rem    a section heading
 *   15px  0.9375rem    the app's own text: the page, the sidebar's rows, a card's heading
 *   14px  0.875rem     a table, a form control, the band at a SITE, the WARNING band, a menu
 *   13.5px 0.84375rem  the breadcrumb
 *   13px  0.8125rem    the CYCLE chip
 *   12.5px 0.78125rem  a heading row in a table, a telephone number under a name, the footer
 *   12px  0.75rem      a badge, a chart's own words
 *   11.5px 0.71875rem  a PROGRAM
 *   11px  0.6875rem    the CUSTOMER over the SITE's name
 *   10.5px 0.65625rem  a group's name in the sidebar
 *
 * Icons are 18px throughout. */
:root {
  --pico-font-size: 16px;
}

h1 {
  --pico-font-size: 1.25rem;
}
h2 {
  --pico-font-size: 1.0625rem;
}
h3 {
  --pico-font-size: 1rem;
}
h4,
h5,
h6 {
  --pico-font-size: 0.9375rem;
}

/* A card: a thin border rather than a shadow, as LEGACY-2 had it. */
article {
  border: 1px solid var(--pico-muted-border-color);
  box-shadow: none;
}

/* ── tokens of our own ────────────────────────────────────────────────────────────────── */

/* ── THE VIEW COLOURS (PR-289) ─────────────────────────────────────────────────────────────
 *
 * Change a VIEW's colour here and nowhere else. The top bar, its edge, the sidebar's tint in both
 * schemes, the current row's mark, the switcher's swatch and the band at a SITE are all worked out
 * from these five with color-mix, below. Provisional: they may change on CIYJ's word.
 *
 * White text on each top bar, measured 2026-09-17: Site 5.34:1, Coach 5.93:1, Staff 7.59:1,
 * Configuration 10.61:1. Anything under WCAG's 4.5:1 is too light for a top bar, and the
 * accessibility crawl, which scans every VIEW, is what fails it. The accent is Configuration's edge
 * and current-row mark, and carries no text. */
:root {
  --pbs-view-site: #4a6c9e;
  --pbs-view-coach: #1f6f6a;
  --pbs-view-staff: #6d4577;
  --pbs-view-config: #3a3f46;
  --pbs-view-config-accent: #ee7d33;
}

/* The page's own text colour, captured where it still is the page's. Pico redefines --pico-color
   on every <a> to the link colour, so inside one `var(--pico-color)` comes out blue — which is why
   the needs-attention chips did, their text being a link's (measured 2026-09-18). A custom property
   is substituted where it is declared, so this holds the body's value wherever it is read. */
body {
  --pbs-ink: var(--pico-color);
}

:root {
  --pbs-sidebar-width: 230px;
  --pbs-sidebar-collapsed-width: 56px;
  /* The size this WEB APP reads at. A token because the SURVEY is not drawn by this stylesheet:
     SurveyJS composes every size from a base unit, and surveyjs/pico-adapter.css sets that base
     from this, so a SURVEY reads at the size the page around it does. */
  --pbs-text-size: 0.9375rem;
  /* Light by default; the dark values are below, once for prefers-color-scheme and once for an
     explicit [data-theme], the way Pico does it. A navigation item is the sidebar's foreground
     rather than a link colour: blue on blue read as decoration (CIYJ, 2026-09-08). Measured on the
     palest and darkest of the four tints: the foreground 13.2:1 light and 13.6:1 dark, the muted
     one 5.3:1 light and 6.9:1 dark. */
  --pbs-sidebar-fg: #1b2230;
  --pbs-sidebar-muted: #566070;
  --pbs-topbar-fg: #ffffff;
  --pbs-topbar-height: 3.5rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --pbs-sidebar-fg: #eef2f7;
    --pbs-sidebar-muted: #a6afbd;
  }
}

:root[data-theme="dark"] {
  --pbs-sidebar-fg: #eef2f7;
  --pbs-sidebar-muted: #a6afbd;
}

/* Which VIEW the shell is drawing, as one variable, and everything it wears worked out from it. */
.shell-view-site,
.view-swatch-site,
.site-band-site {
  --pbs-view: var(--pbs-view-site);
}
.shell-view-coach,
.view-swatch-coach,
.site-band-coach {
  --pbs-view: var(--pbs-view-coach);
}
.shell-view-staff,
.view-swatch-staff,
.site-band-staff {
  --pbs-view: var(--pbs-view-staff);
}
.shell-view-config,
.view-swatch-config,
.site-band-config {
  --pbs-view: var(--pbs-view-config);
}

.shell {
  /* What a VIEW draws its controls in. The same colour as its chrome everywhere but CONFIGURATION,
     whose charcoal is too near the page's own ink to read as a control (PR-289). */
  --pbs-view-accent: var(--pbs-view);
  --pbs-view-edge: color-mix(in srgb, var(--pbs-view) 55%, #fff);
  --pbs-view-mark: var(--pbs-view);
  --pbs-sidebar-bg: color-mix(in srgb, var(--pbs-view) 11%, #fff);
  --pbs-sidebar-hover: color-mix(in srgb, var(--pbs-view) 9%, transparent);
  --pbs-sidebar-current: color-mix(in srgb, var(--pbs-view) 16%, transparent);
  --pbs-sidebar-rule: color-mix(in srgb, var(--pbs-view) 22%, transparent);
  --pbs-sidebar-border: color-mix(in srgb, var(--pbs-view) 18%, transparent);
}

/* Charcoal with an orange edge, so a USER can feel that a change here reaches every SITE. */
.shell-view-config {
  --pbs-view-accent: var(--pbs-view-config-accent);
  --pbs-view-edge: var(--pbs-view-config-accent);
  --pbs-view-mark: color-mix(in srgb, var(--pbs-view-config-accent) 78%, #000);
}

/* ── Pico's primary is the VIEW's own colour (CIYJ, 2026-09-18) ─────────────────────────────
 *
 * Everything a page draws in the primary — a filled button, the FAB, a link — takes the colour of
 * the console it is in, so the chrome and the page read as one thing rather than as a coloured bar
 * over a blue app. Worked out from the same one token as the top bar and the sidebar (PR-289), so a
 * VIEW's colour still changes in one place.
 *
 * White on each of the four measures 5.34:1, 5.93:1, 7.59:1 and 10.61:1 (PR-289), which is what a
 * filled button needs and, being the same pair the other way up, what the colour needs as link text
 * on the page. CONFIGURATION's is dark enough to read as body text rather than as a link, which is
 * what Pico's underline is carrying there. */
.shell {
  --pico-primary: var(--pbs-view-accent);
  --pico-primary-background: var(--pbs-view-accent);
  --pico-primary-border: var(--pbs-view-accent);
  --pico-primary-inverse: #fff;
  --pico-primary-underline: color-mix(in srgb, var(--pbs-view-accent) 45%, transparent);
  --pico-primary-hover: color-mix(in srgb, var(--pbs-view-accent) 82%, #000);
  --pico-primary-hover-background: color-mix(in srgb, var(--pbs-view-accent) 82%, #000);
  --pico-primary-hover-border: color-mix(in srgb, var(--pbs-view-accent) 82%, #000);
  --pico-primary-hover-underline: color-mix(in srgb, var(--pbs-view-accent) 70%, transparent);
  --pico-primary-focus: color-mix(in srgb, var(--pbs-view-accent) 25%, transparent);

  /* A switch is a filled control and wears the VIEW's colour like the rest of them (LOURA,
     2026-09-21). Restating it here is not the redundancy it looks: Pico declares
     `--pico-switch-checked-background-color: var(--pico-primary-background)` on `:root`, and a
     custom property's `var()` is substituted where it is *declared*, not where it is used. So that
     token resolved to Pico's own blue on `:root` and inherited down as blue, whatever `.shell`
     said about the primary afterwards. Declared again here, it resolves against this block's
     primary and comes out the VIEW's colour — which is also why it must point at the primary
     rather than at the accent, so CONFIGURATION's darkening and the dark scheme's lift both
     reach it. */
  --pico-switch-checked-background-color: var(--pico-primary-background);

  /* Pico's secondary follows the VIEW as well (CIYJ, 2026-09-19). A `.secondary` button is the act
     a screen does not want to be its default — deleting, mostly — and left at Pico's own slate it
     was the one control on the page belonging to no VIEW at all. The VIEW's colour taken down
     towards the ink rather than a second hue, so it reads as quieter and not as a different kind of
     thing; darker than the primary, so white on it passes wherever the primary does. */
  --pico-secondary: color-mix(in srgb, var(--pbs-view-accent) 60%, #333a45);
  --pico-secondary-background: color-mix(in srgb, var(--pbs-view-accent) 60%, #333a45);
  --pico-secondary-border: color-mix(in srgb, var(--pbs-view-accent) 60%, #333a45);
  --pico-secondary-inverse: #fff;
  --pico-secondary-underline: color-mix(in srgb, var(--pbs-view-accent) 45%, transparent);
  --pico-secondary-hover: color-mix(in srgb, var(--pbs-view-accent) 60%, #191d24);
  --pico-secondary-hover-background: color-mix(in srgb, var(--pbs-view-accent) 60%, #191d24);
  --pico-secondary-hover-border: color-mix(in srgb, var(--pbs-view-accent) 60%, #191d24);
  --pico-secondary-focus: color-mix(in srgb, var(--pbs-view-accent) 25%, transparent);
}

/* CONFIGURATION wears the orange, at the two weights that pass (CIYJ, 2026-09-18). #ee7d33 itself
   is 2.76:1 against white, so it can never be text or a link — which is what PR-289 meant by "the
   accent carries no text". Darkened to 70% it is 5.15:1 on the page, and left bright it is 5.60:1
   under near-black, so a filled control keeps the colour and turns its own text over. */
.shell-view-config {
  --pico-primary: color-mix(in srgb, var(--pbs-view-config-accent) 70%, #000);
  --pico-primary-inverse: #1f242c;
  --pico-primary-underline: color-mix(in srgb, var(--pbs-view-config-accent) 55%, transparent);
  --pico-primary-hover: color-mix(in srgb, var(--pbs-view-config-accent) 55%, #000);
  --pico-primary-hover-background: color-mix(in srgb, var(--pbs-view-config-accent) 82%, #000);
  --pico-primary-hover-border: color-mix(in srgb, var(--pbs-view-config-accent) 82%, #000);
}

/* In the dark scheme the four are too dark to read on the page, so the colour is lifted towards
   white for anything wearing it and the text on a filled control goes dark instead. Derived rather
   than measured: nothing has looked at these yet. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .shell {
    --pico-primary: color-mix(in srgb, var(--pbs-view) 45%, #fff);
    --pico-primary-background: color-mix(in srgb, var(--pbs-view) 55%, #fff);
    --pico-primary-border: color-mix(in srgb, var(--pbs-view) 55%, #fff);
    --pico-primary-inverse: #10141b;
    --pico-primary-underline: color-mix(in srgb, var(--pbs-view) 30%, transparent);
    --pico-primary-hover: color-mix(in srgb, var(--pbs-view) 30%, #fff);
    --pico-primary-hover-background: color-mix(in srgb, var(--pbs-view) 35%, #fff);
    --pico-primary-hover-border: color-mix(in srgb, var(--pbs-view) 35%, #fff);
    --pico-primary-hover-underline: color-mix(in srgb, var(--pbs-view) 55%, transparent);
    --pico-primary-focus: color-mix(in srgb, var(--pbs-view) 35%, transparent);

    --pico-secondary: color-mix(in srgb, var(--pbs-view) 55%, #cdd3dc);
    --pico-secondary-background: color-mix(in srgb, var(--pbs-view) 62%, #cdd3dc);
    --pico-secondary-border: color-mix(in srgb, var(--pbs-view) 62%, #cdd3dc);
    --pico-secondary-inverse: #10141b;
    --pico-secondary-underline: color-mix(in srgb, var(--pbs-view) 35%, transparent);
    --pico-secondary-hover: color-mix(in srgb, var(--pbs-view) 40%, #fff);
    --pico-secondary-hover-background: color-mix(in srgb, var(--pbs-view) 45%, #fff);
    --pico-secondary-hover-border: color-mix(in srgb, var(--pbs-view) 45%, #fff);
    --pico-secondary-focus: color-mix(in srgb, var(--pbs-view) 35%, transparent);
  }
}

:root[data-theme="dark"] .shell {
  --pico-primary: color-mix(in srgb, var(--pbs-view) 45%, #fff);
  --pico-primary-background: color-mix(in srgb, var(--pbs-view) 55%, #fff);
  --pico-primary-border: color-mix(in srgb, var(--pbs-view) 55%, #fff);
  --pico-primary-inverse: #10141b;
  --pico-primary-underline: color-mix(in srgb, var(--pbs-view) 30%, transparent);
  --pico-primary-hover: color-mix(in srgb, var(--pbs-view) 30%, #fff);
  --pico-primary-hover-background: color-mix(in srgb, var(--pbs-view) 35%, #fff);
  --pico-primary-hover-border: color-mix(in srgb, var(--pbs-view) 35%, #fff);
  --pico-primary-hover-underline: color-mix(in srgb, var(--pbs-view) 55%, transparent);
  --pico-primary-focus: color-mix(in srgb, var(--pbs-view) 35%, transparent);
  --pico-secondary: color-mix(in srgb, var(--pbs-view) 55%, #cdd3dc);
  --pico-secondary-background: color-mix(in srgb, var(--pbs-view) 62%, #cdd3dc);
  --pico-secondary-border: color-mix(in srgb, var(--pbs-view) 62%, #cdd3dc);
  --pico-secondary-inverse: #10141b;
  --pico-secondary-underline: color-mix(in srgb, var(--pbs-view) 35%, transparent);
  --pico-secondary-hover: color-mix(in srgb, var(--pbs-view) 40%, #fff);
  --pico-secondary-hover-background: color-mix(in srgb, var(--pbs-view) 45%, #fff);
  --pico-secondary-hover-border: color-mix(in srgb, var(--pbs-view) 45%, #fff);
  --pico-secondary-focus: color-mix(in srgb, var(--pbs-view) 35%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .shell {
    --pbs-view-mark: var(--pbs-view-edge);
    --pbs-sidebar-bg: color-mix(in srgb, var(--pbs-view) 20%, #10141b);
    --pbs-sidebar-hover: rgba(255, 255, 255, 0.06);
    --pbs-sidebar-current: rgba(255, 255, 255, 0.1);
    --pbs-sidebar-rule: rgba(255, 255, 255, 0.12);
    --pbs-sidebar-border: rgba(255, 255, 255, 0.08);
  }
}

:root[data-theme="dark"] .shell {
  --pbs-view-mark: var(--pbs-view-edge);
  --pbs-sidebar-bg: color-mix(in srgb, var(--pbs-view) 20%, #10141b);
  --pbs-sidebar-hover: rgba(255, 255, 255, 0.06);
  --pbs-sidebar-current: rgba(255, 255, 255, 0.1);
  --pbs-sidebar-rule: rgba(255, 255, 255, 0.12);
  --pbs-sidebar-border: rgba(255, 255, 255, 0.08);
}

/* ── htmx (the rules it would inject, with includeIndicatorStyles off) ─────────────────── */

.htmx-indicator {
  opacity: 0;
  transition: opacity 200ms ease-in;
}
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

/* ── utilities ────────────────────────────────────────────────────────────────────────── */

/* Read by assistive technology, drawn by nothing. */
/* The same thing for a box that has to stay in the flow. `visually-hidden` takes its box out of
   flow, and an out-of-flow box inside a column flow is what a browser balances columns badly around:
   the charts and the state map each carry their text equivalent as a table, and with those escaping,
   STAFF's Stats left a stretch of nothing under the short columns and then put every card but one
   into a single column (CIYJ, 2026-09-18). This clips instead of positioning, so the box is a
   pixel in the flow and nothing has left it. A wrapper rather than the table itself, because
   `display` on a table is what a screen reader reads its rows and columns from. */
.visually-hidden-flow {
  display: block;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.icon {
  flex: none;
  vertical-align: middle;
}

/* Pico scales the root font with the viewport, 100% to 137.5%, so a wide screen reads as though
   it were zoomed. Capped, on CIYJ's word 2026-09-09: this stylesheet is in rem throughout, so one
   line moves every screen together (PR-197). */
html {
  font-size: 100%;
}

/* Pico applies the size through this variable, on `:where(:root)`, and bumps it in five media
   queries; setting the variable as well as the size means nothing that reads it is left on the
   scaled value. Both are unlayered, so they beat the vendored file whatever its specificity
   (PR-124).
 *
 * The form-element spacing goes with it. Pico sizes a control for a document — 0.75rem of
 * padding above and below, at the body's own font size — and a screen of them reads as though
 * it were built for a phone. An application's controls are smaller than its prose (PR-202). */
:root {
  --pico-font-size: 100%;
  --pico-form-element-spacing-vertical: 0.4rem;
  --pico-form-element-spacing-horizontal: 0.65rem;
}

button,
input,
select,
textarea,
[role="button"] {
  font-size: 0.875rem;
}

/* A table is denser than prose too: the rows are what is being read, not the words in them. Pico
 * sets one for a document — a whole --pico-spacing either side of every cell — and the mockup is
 * tighter, which gives the row's own words the room (CIYJ, 2026-09-17). The heading row is a label
 * over its column rather than a line of the table: smaller, muted, on the card's own ground and
 * never wrapped. SurveyJS dresses its own tables, and its classes beat these element rules. */
table {
  font-size: 0.875rem;
}

th,
td {
  padding: 0.5rem 0.625rem;
  /* Pico paints every cell with the page's own background, which reads as a white block inside a
     card. A cell takes whatever it is sitting on instead. */
  background: transparent;
}

/* A heading row reads as a label over its column, not as a rule across the page (CIYJ, 2026-09-18,
   asking for the mockup's over Pico's, everywhere). Pico gives thead a 0.1875rem border — three
   pixels, heavy enough to read as the page's own divider — where the mockup has the same hairline
   the rows below it use. Everything else here the two already agreed on. */
thead th {
  font-size: 0.78125rem;
  font-weight: 600;
  color: var(--pico-muted-color);
  white-space: nowrap;
  vertical-align: bottom;
  background: var(--pico-card-sectioning-background-color);
  border-bottom: 1px solid var(--pico-table-border-color);
}

td {
  vertical-align: top;
}

/* The last row's rule is the card's own edge, so the table does not end in two lines. The row
   header goes with it: a table whose first cell is a `th scope="row"` otherwise ends in a stub of a
   line under that one column and nothing under the rest (LOURA, 2026-09-22). */
tbody tr:last-child td,
tbody tr:last-child th {
  border-bottom: none;
}

/* ── the shell ────────────────────────────────────────────────────────────────────────── */

.shell-body {
  margin: 0;
}

.shell {
  display: grid;
  font-size: var(--pbs-text-size);
  grid-template-columns: var(--pbs-sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
}

.shell-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The page, at the width its route declares (PR-285). Centred by default, at a SITE's Home's width:
 * full width read as stretched on a 1920px screen. `wide` is for a page that is a big table. The
 * breadcrumb is inside the same column, so its first item lines up with the page under it. */
.page {
  flex: 1;
  width: 100%;
  margin-inline: auto;
  padding-inline: var(--pico-spacing);
}

.page-centred {
  max-width: calc(64rem + 2 * var(--pico-spacing));
}

.page > main {
  padding-block: 0 var(--pico-block-spacing-vertical);
}

/* A page with no breadcrumb — a VIEW's home — still stands off the chrome above it. */
.page > main:first-child {
  padding-top: var(--pico-block-spacing-vertical);
}

/* ── the page shapes (PR-291) ─────────────────────────────────────────────────────────────
 *
 * Every screen is built from one of these, so a USER meets the same arrangements everywhere:
 *
 *   dashboard        cards flowing into as many columns as fit, three at most (.dashboard)
 *   list             filters in a row over a table (.filter-bar, then the table)
 *   charts           filters in a card at the side, the drawings beside them (.aside-and-main)
 *   survey           the SURVEY page, which is SurveyJS's own and shaped like nothing else (N-16)
 *   reading          one column at a comfortable measure, for a page that is read (.reading)
 *   record           a band of facts, the sections as links, one section at a time, each section
 *                    carrying its own acts (PR-291)
 *
 * The records — a SITE's settings, a CUSTOMER, a SURVEY's record, the OUTCOME MEASURE builder —
 * wear the dashboard's flow until the sweep after step 7 moves them, which is when this shape is
 * built.
 */

.reading {
  max-width: 70ch;
}

.page > nav[aria-label="breadcrumb"] {
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0 0;
  font-size: 0.84375rem;
}

/* A page-wide control at the far end of the breadcrumb's row, where LEGACY-2 put a report's Print /
   Save PDF (CIYJ, 2026-09-17). Pico starts a breadcrumb's row from the left, so the row is spaced
   out above; a page with nothing at the end looks as it did. */
.breadcrumb-end {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.breadcrumb-end button {
  width: auto;
  margin: 0;
  padding: 0.35rem 0.9rem;
  font-size: 0.875rem;
}

/* ── the page is on its way ───────────────────────────────────────────────────────────── */

/*
 * A bar across the top while htmx has a request out (LOURA, 2026-09-22). A boosted navigation keeps
 * the page it is on until the answer arrives, so a slow screen looked like a link that had not
 * been clicked.
 *
 * No script: htmx puts `htmx-request` on the link or form it is asking for, and `:has()` reads that
 * from the body. The element is the server's, drawn once per page by the layout, so this adds no
 * second renderer — and a browser that does not support `:has()` is left with what it has today.
 *
 * It waits 0.2s before showing anything, so a page that comes back at once does not flash a bar at
 * the reader. Above the sticky bar and the sidebar, under the MODALS and the save state at 3000.
 */
.page-progress {
  position: fixed;
  inset: 0 0 auto;
  z-index: 2900;
  height: 3px;
  overflow: hidden;
  pointer-events: none;
}

.page-progress::before {
  content: "";
  display: block;
  width: 35%;
  height: 100%;
  border-radius: 0 3px 3px 0;
  background: var(--pico-primary);
  opacity: 0;
  transform: translateX(-100%);
}

body:has(.htmx-request) .page-progress::before {
  animation: page-progress 1.1s cubic-bezier(0.45, 0, 0.35, 1) 0.2s infinite;
}

@keyframes page-progress {
  from {
    opacity: 1;
    transform: translateX(-100%);
  }
  to {
    opacity: 1;
    transform: translateX(340%);
  }
}

/* Motion is what the bar is made of, so for a reader who has asked for none it simply appears and
   stays: the same 0.2s wait, a full-width bar, and nothing moving (WCAG 2.3.3). A zero-length
   animation held by `forwards` is how a delay is honoured without a transition to hang it on. */
@media (prefers-reduced-motion: reduce) {
  body:has(.htmx-request) .page-progress::before {
    width: 100%;
    animation: page-progress-steady 0s linear 0.2s forwards;
  }
}

@keyframes page-progress-steady {
  to {
    opacity: 0.6;
    transform: none;
  }
}

/* A page's one act, floated in the corner rather than sitting on a row that scrolls away. On trial
   on STAFF's Sites alone (CIYJ, 2026-09-18); if it stays, PR-291's "a record is for acting" gets an
   amendment saying which screens take one and which keep the row.
   Under the MODAL it opens, which the browser puts in its own top layer, and over the page. */
/* The ground is on the box and not on the pill inside it (LOURA, 2026-09-24): a pill stands over
   whatever the page has scrolled under it, and the outlined one — Open this cycle's response,
   where a SURVEY takes one per CYCLE (S-27) — carries no background of its own, so the footer's
   rule ran straight through the words. Here it cannot be undone by anything a child is dressed
   with, and the filled pill simply covers it. */
.fab {
  position: fixed;
  right: clamp(1rem, 3vw, 2.5rem);
  bottom: clamp(1rem, 3vw, 2.5rem);
  z-index: 5;
  display: flex;
  border-radius: 2rem;
  background: var(--pico-background-color);
  box-shadow: 0 0.35rem 1.1rem rgb(0 0 0 / 28%);
}

.fab .modal-link {
  padding: 0.55rem 1.1rem;
  border-radius: 2rem;
  font-size: 0.9375rem;
}

/* The marked form of the act (A-38), for somebody who is shown it but cannot press it: the same
   pill in the same corner, dead, inside the mark's dashed box. `.gated` is built for a card and
   carries margins that would push the pill off that corner, so it gives them back here and its
   padding is only the room its badge needs. */
.fab .gated {
  margin: 0;
  padding: 0.75rem 0.55rem 0.55rem;
  background: var(--pico-background-color);
}

.fab .gated button {
  width: auto;
  margin: 0;
  padding: 0.55rem 1.1rem;
  border-radius: 2rem;
  font-size: 0.9375rem;
}

.fab .gated button > .icon {
  margin-right: 0.4rem;
  vertical-align: -0.15em;
}

/* A comparability change ticked off on the go-live check. The box is the control; the words are
   for a screen reader, the column heading carrying them for everybody else. */
.ack {
  display: inline-flex;
  margin: 0;
}

.ack-box {
  margin: 0;
}

/* **The act reads as unavailable while anything is unread** (LOURA, 2026-09-22). The `required`
   attribute is what actually refuses the submission and the handler asks again from a fresh scan;
   this is the part that says so before somebody reaches the corner of the screen. `:has()` and not
   a script, because the state is already in the document. */
body:has(.ack-box:not(:checked)) .fab {
  box-shadow: none;
}

body:has(.ack-box:not(:checked)) .fab button {
  opacity: 1;
  background: var(--pico-muted-border-color);
  border-color: var(--pico-muted-border-color);
  color: var(--pico-muted-color);
  cursor: not-allowed;
}

/* The act, when the page already knows it would be refused (LOURA, 2026-09-22). Pico dims a
   disabled control to half, which at this size reads as something still loading; here the pill
   stops looking like a control at all — flat, muted, lying on the page rather than over it — and
   the cursor says so on the way past. Why is not in here: it is words in the first card, where it
   can be read, and on the hover. A disabled button is not focusable, so nothing that only it
   carries would ever be announced. */
.fab-stopped {
  box-shadow: none;
}

.fab button:disabled {
  opacity: 1;
  background: var(--pico-muted-border-color);
  border-color: var(--pico-muted-border-color);
  color: var(--pico-muted-color);
  cursor: not-allowed;
}

/* Two acts as one control, with a rule down the middle (CIYJ, 2026-09-19): inside a SECTION both a
   RESOURCE and a SECTION can be added, and two pills side by side read as two decisions. The shadow
   is the group's, or the seam between them shows. */
.fab .modal-link:not(:last-child) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.fab .modal-link:not(:first-child) {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 1px solid color-mix(in srgb, var(--pico-primary-inverse) 45%, transparent);
}

/* The same row on a page that has the act and no trail (CIYJ, 2026-09-18): a VIEW's own list, where
   a breadcrumb would name what the switcher and the marked sidebar row already say. It keeps the
   breadcrumb's own spacing so the page below starts where it always did. */
.page-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: var(--pico-block-spacing-vertical);
}

/* A line under a field saying something about it that the label has no room for. Pico's own
   `<small>` in a form, pulled up under the control it belongs to. */
.field-note {
  display: block;
  margin: -0.5rem 0 var(--pico-spacing);
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
}

/* ── badges ───────────────────────────────────────────────────────────────────────────── */

/* A short status word with a colour. The colour is one token, --pbs-badge-fg, and the
 * background is the same colour thinned out, so each tone is one line and every tone follows
 * Pico's light and dark values. Pico has no warning colour of its own, so one is kept here in
 * both schemes, the way Pico switches: prefers-color-scheme unless data-theme says otherwise.
 * The tint is 10% and the muted tone is Pico's secondary grey, not its muted one: measured
 * against WCAG's 4.5:1, the muted grey on a 14% tint of itself sits at 4.46 in light and 3.79
 * in dark, and axe fails the page; every tone here clears 4.5 in both schemes. That grey is
 * written out below rather than taken from Pico's variable, which no longer holds it. */
/* Pico has no warning colour of its own, and its primary is not dark enough to be read on a tint
   of itself: #0172ad on 10% of #0172ad over the card measures 4.42:1, under WCAG's 4.5 (measured
   2026-09-10, when the STAFF VIEW's cards put an info badge on a crawled page for the first time
   and axe failed it). So both tones are kept here, in both schemes. Dark needs no help — Pico's
   primary measures 5.56:1 there — and is named anyway so the pair is read together. */
/* The muted tone is Pico's own secondary grey, written out here rather than read from
   `--pico-secondary` (LOURA, 2026-09-20). That variable stopped being a grey the day the VIEW's
   colour was taken through it for `.secondary` buttons, and every muted badge went with it: in
   CONFIGURATION the orange landed at 4.31:1 on its own tint and the crawl failed
   `/outcome-measures/:id`. A button wearing the VIEW is what that change was for; a status word is
   not, so the badge keeps the grey it was measured at — 4.80:1 light, and the dark value beside
   it. */
:root {
  --pbs-warning-color: #9a5400;
  --pbs-info-color: #1a5f96; /* 5.63:1 on its own tint, light */
  --pbs-badge-muted: #5d6b89; /* 4.80:1 on its own tint, light */
  /* NT-15's band wants a yellow ground rather than a tenth of the tone above, which came out
     peach (CIYJ, 2026-09-10). Measured with the tone on it: 5.15:1 light, 7.15:1 dark. */
  --pbs-warning-band-bg: #fdf3d0;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --pbs-warning-color: #f2b352;
    --pbs-info-color: #01aaff; /* 5.56:1 on its own tint, dark */
    --pbs-badge-muted: #969eaf;
    --pbs-warning-band-bg: #3a2f12;
  }
}
[data-theme="dark"] {
  --pbs-warning-color: #f2b352;
  --pbs-info-color: #01aaff;
  --pbs-badge-muted: #969eaf;
  --pbs-warning-band-bg: #3a2f12;
}

.badge {
  --pbs-badge-fg: var(--pbs-badge-muted);
  display: inline-block;
  padding: 0.05em 0.6em;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
  color: var(--pbs-badge-fg);
  /* A ring rather than a wash, for every badge and not only the ones saying who something reaches
     (CIYJ 2026-09-19 for those; LOURA 2026-09-20 for the rest). A PROGRAM is an outlined pill, so a
     filled one beside it read as a different kind of thing when it answers the same question — and
     once the outlined ones were drawn, the filled ones were the odd shape everywhere else too.
     It also takes the word off a tint and onto the page, which is a wider gap: the muted grey
     measures 4.80:1 on a tenth of itself and 5.34:1 on the card. */
  border: 1px solid currentColor;
  background: none;
}

.badge-success {
  --pbs-badge-fg: var(--pico-ins-color);
}
.badge-danger {
  --pbs-badge-fg: var(--pico-del-color);
}
.badge-warning {
  --pbs-badge-fg: var(--pbs-warning-color);
}
.badge-info {
  --pbs-badge-fg: var(--pbs-info-color);
}
/* Several in one cell sit on one line with a little air between. */
.badges .badge + .badge {
  margin-inline-start: 0.35rem;
}

/* A badge that leads with a mark rather than with its word. The icon is drawn at the badge's own
   size, so it sits on the text's baseline rather than standing over it. */
.badge > .icon {
  margin-right: 0.25rem;
  vertical-align: -0.15em;
}

/* ── unfinished work, marked where it is drawn ────────────────────────────────────────── */

/* A red dashed ring around something the screen is drawing but the app does not yet keep: a value
   hardcoded, a control disabled, a column that has no column behind it (LOURA, 2026-09-20).
   Nobody outside this repository should ever meet it — it is a note from one developer to herself
   about where the screen is ahead of the schema, and the ring is there to be seen in passing and
   removed when the work lands. Inline padding only, so it never changes what it is wrapped around. */
.unfinished {
  padding: 0 0.2rem;
  border: 1px dashed var(--pico-del-color);
  border-radius: 4px;
}

/* Yes and no as a mark in a cell, with the word beside it for anyone not reading the colour. */
.mark-yes {
  color: var(--pico-ins-color);
}

.mark-no {
  color: var(--pico-del-color);
}

/* ── a page's tools above its table ───────────────────────────────────────────────────── */

/* What a SITE is told above a SURVEY's RESPONSES (S-41). */
.instructions {
  max-width: 70ch;
}

/* The RESPONSES card's bar: whether the SURVEY is open today, and the way to begin a RESPONSE. */
.card-bar-end {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  white-space: nowrap;
}

/* ── the foot of every page ───────────────────────────────────────────────────────────── */

/* How to reach CIYJ, one line, small, with a bar between the items. */
.site-footer {
  padding: var(--pico-spacing);
  font-size: 0.78125rem;
  color: var(--pico-muted-color);
}
.site-footer ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-footer li {
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-footer li + li::before {
  content: "|";
  margin-inline: 0.6em;
  color: var(--pico-muted-border-color);
}
.shell-content > .site-footer {
  border-top: 1px solid var(--pico-muted-border-color);
}

/* ── the RESPONSE page ─────────────────────────────────────────────────────────────────── */

/* The save state, always on screen at the foot of the viewport whatever is scrolled (R-45):
 * the RESPONSE's status as the server last said it, and one of the save words, which the
 * browser only shows or hides. Above SurveyJS's own popups, which sit at 2000 and below. */
.save-toast {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 3000;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  background: var(--pico-card-background-color);
  box-shadow: var(--pico-card-box-shadow);
  font-size: 0.9375rem;
  color: var(--pico-muted-color);
}

/* `display: flex` above beats the browser's own rule for a hidden element, so the box says it
   again: it is only on screen while it has a word to say (LOURA, 2026-09-24). */
.save-toast[hidden] {
  display: none;
}

/* The RESPONSE's status at the end of the breadcrumb row, where the trail names the RESPONSE it
   belongs to. Its badges carry their own colours; this is only the room they stand in. */
#response-status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
/* "Changes saved" is the one word a USER is looking for, so it is the one that stands out:
   Pico's insertion green, weighted (CIYJ, 2026-09-15). */
.save-toast [data-save="saved"] {
  color: var(--pico-ins-color);
  font-weight: 600;
}

/* What follows a completed RESPONSE: SurveyJS says thank you, centred, and this offers the
 * next one under it. */
.end-options {
  margin-top: var(--pico-spacing);
  text-align: center;
}


/* A row that names the group the rows under it belong to, in a table that would otherwise
 * repeat its header per group. */
.table-group th {
  padding-top: 1rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
  border-bottom: 1px solid var(--pico-muted-border-color);
}

/* ── sidebar ──────────────────────────────────────────────────────────────────────────── */

/* Always wholly on screen: as tall as the viewport, stuck to its top while the page scrolls. The
 * logo and the switcher stay put and the rows scroll on their own inside if the viewport is shorter
 * than they are — the rows and not the whole sidebar, because a scrolling box clips anything that
 * reaches out of it, and the switcher's menu does (PR-283). Above the top bar in the stacking
 * order for the same reason, which is what keeps it ten past that bar's own. */
.sidebar {
  position: sticky;
  top: 0;
  z-index: 210;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  padding: 1rem 0.5rem;
  border-right: 1px solid var(--pbs-sidebar-border);
  background: var(--pbs-sidebar-bg);
  color: var(--pbs-sidebar-fg);
}

.sidebar .icon {
  width: 18px;
  height: 18px;
}

/* STAFF have enough rows to scroll, and the browser's own bar is a grey slab down the tint (CIYJ,
   2026-09-18). Thin, and in the VIEW's own colours: the thumb is the tint the current row wears and
   the trough is the sidebar itself, so it reads as part of the panel rather than as a window edge.
   `scrollbar-width` and `scrollbar-color` are the standard pair; the WebKit rules under them are for
   the browsers that have not caught up, and say the same thing. */
/* The rows scroll and the foot does not (LOURA, 2026-09-24). The whole <nav> scrolled until the
   SITE VIEW's rows were grouped, and then Switch site — the only way out of a SITE — was the last
   thing in a list that no longer fitted, so it sat below the fold on an ordinary laptop. The scroll
   belongs to the groups; the foot keeps its place at the bottom of the panel. */
.sidebar nav {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.sidebar .nav-groups {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-color: color-mix(in srgb, var(--pbs-view) 32%, transparent) transparent;
  scrollbar-width: thin;
}

.sidebar .nav-groups::-webkit-scrollbar {
  width: 6px;
}

.sidebar .nav-groups::-webkit-scrollbar-track {
  background: transparent;
}

.sidebar .nav-groups::-webkit-scrollbar-thumb {
  border-radius: 3px;
  background: color-mix(in srgb, var(--pbs-view) 32%, transparent);
}

.sidebar .nav-groups::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--pbs-view) 48%, transparent);
}

/* Pico lays a <nav> out horizontally, pulling its lists and links outward by their own
 * spacing so they sit flush. This one is vertical, so each of those is put back to zero. */
.sidebar nav ul,
.sidebar nav ul:first-of-type,
.sidebar nav ul:last-of-type {
  display: block;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sidebar .nav-foot {
  margin-top: auto;
  padding-top: 0.75rem;
  border-top: 1px solid var(--pbs-sidebar-rule);
}

.sidebar nav li {
  display: block;
  margin: 0;
  padding: 1px 0;
}

.sidebar .nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin: 0;
  padding: 0.4rem 0.75rem;
  border-radius: var(--pico-border-radius);
  color: var(--pbs-sidebar-fg);
  font-size: 0.9375rem;
  line-height: 1.3;
  text-decoration: none;
  white-space: nowrap;
}

a.nav-item:hover,
a.nav-item:focus-visible {
  background: var(--pbs-sidebar-hover);
  color: var(--pbs-sidebar-fg);
  text-decoration: none;
}

/* The row the USER is on: a tint of the VIEW, heavier, and a mark down its left edge. */
a.nav-item[aria-current="page"] {
  background: var(--pbs-sidebar-current);
  font-weight: 600;
}

a.nav-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0.35rem;
  bottom: 0.35rem;
  left: 0;
  width: 3px;
  border-radius: 2px;
  background: var(--pbs-view-mark);
}

/* A page not built yet, or not for this USER: shown, so the shape is visible, but not a link. */
.nav-soon {
  color: var(--pbs-sidebar-muted);
  cursor: default;
}

/* A group's name over its rows, in the STAFF VIEW's sidebar (PR-283). Words, not a heading: the
 * page's own headings are the outline a screen reader walks, and the list says what it is by
 * pointing at this. */
.nav-group {
  display: block;
  padding: 0.75rem 0.75rem 0.2rem;
  font-size: 0.65625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pbs-sidebar-muted);
}

.logo {
  display: block;
  margin: 0 0.75rem 1rem;
}

/* ── the switcher between VIEWS (A-36, PR-283) ────────────────────────────────────────────── */

/* Pico's dropdown, dressed as a row of the sidebar: the VIEW's swatch, "View" over its name. */
.view-switch {
  margin: 0 0 0.5rem;
}

.sidebar .view-switch > summary:not([role]) {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  height: auto;
  padding: 0.5rem 0.75rem;
  border: 0;
  border-radius: var(--pico-border-radius);
  background: var(--pbs-sidebar-current);
  color: var(--pbs-sidebar-fg);
  box-shadow: none;
  line-height: 1.2;
}

.sidebar .view-switch > summary:not([role]):hover,
.sidebar .view-switch > summary:not([role]):focus-visible {
  background: var(--pbs-sidebar-hover);
}

.sidebar .view-switch > summary:not([role])::after {
  margin-inline-start: auto;
}

.view-swatch {
  flex: none;
  width: 1rem;
  height: 1rem;
  border-radius: 4px;
  background: var(--pbs-view);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}

.view-switch-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The sidebar's foreground and not its muted grey: on the switcher's tint the grey measured 4.06:1 on
   Configuration's and 4.26:1 on Staff's. Size and capitals are what set it under the name. */
.view-switch-text small {
  font-size: 0.65625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pbs-sidebar-fg);
}

.view-switch-text strong {
  font-size: 0.96875rem;
}

/* The menu reaches past the sidebar's edge rather than being squeezed into it. */
.view-switch > ul {
  width: 17.5rem;
  max-width: calc(100vw - 5rem);
  padding: 0.35rem;
}

.view-switch li {
  padding: 0;
}

.view-switch li a {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  margin: 0;
  padding: 0.5rem 0.65rem;
  border-radius: var(--pico-border-radius);
  white-space: normal;
}

.view-switch li .view-swatch {
  margin-top: 0.2rem;
}

.view-switch-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.4rem;
  line-height: 1.35;
}

/* The current VIEW's row wears a ground of our own rather than Pico's dropdown hover. Pico derives
   that hover from the VIEW's primary, so how dark it is changes with the VIEW, and the muted grey
   on it measures 4.25:1 in STAFF and 4.39:1 in COACH — under 4.5:1, and only in two of the four,
   which is why the crawl found it on one run and not the next. At 16% of the VIEW over the menu's
   own white the same grey measures 4.96:1 in the worst of them (measured 2026-09-21). */
.view-switch li a[aria-current="true"] {
  background: var(--pbs-sidebar-current);
}

/* The sidebar's muted grey rather than Pico's, which is muted again on a tinted ground. */
.view-switch-here {
  font-size: 0.75rem;
  color: var(--pbs-sidebar-muted);
}

.view-switch-description {
  flex-basis: 100%;
  font-size: 0.8125rem;
  color: var(--pbs-sidebar-muted);
}

.logo img {
  display: block;
  width: 100%;
  height: auto;
}

/* The small mark that stands in for the logo when the sidebar is collapsed. */
.logo-mark {
  display: none;
  color: var(--pbs-sidebar-fg);
}

/* ── collapsing: a checkbox before the shell, a label in the top bar ───────────────────── */

.nav-toggle-button {
  border: none;
  background: none;
  display: inline-flex;
  align-items: center;
  padding: 0.5rem;
  margin: 0;
  border-radius: var(--pico-border-radius);
  color: var(--pbs-topbar-fg);
  cursor: pointer;
}

.nav-toggle-button:hover {
  background: rgba(255, 255, 255, 0.12);
}

.shell-collapsed {
  grid-template-columns: var(--pbs-sidebar-collapsed-width) minmax(0, 1fr);
}

/* Words that go when the sidebar is icons are clipped rather than removed, so a screen reader still
 * reads a row's name, the switcher's VIEW and a group's name. */
.shell-collapsed .logo img,
.shell-collapsed .nav-label,
.shell-collapsed .view-switch-text,
.shell-collapsed .nav-group {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

.shell-collapsed .logo-mark {
  display: flex;
  justify-content: center;
  padding: 0.5rem 0;
}

.shell-collapsed .logo {
  margin-inline: 0;
}

.shell-collapsed .nav-item {
  justify-content: center;
  padding-inline: 0;
}

.shell-collapsed .view-switch > summary:not([role]) {
  justify-content: center;
  padding-inline: 0;
}

.shell-collapsed .view-switch > summary:not([role])::after {
  display: none;
}

.shell-collapsed .view-switch .view-swatch {
  width: 1.375rem;
  height: 1.375rem;
}

/* With no group names, a rule is what says where one group ends. */
.shell-collapsed .nav-group + .nav-group-items {
  margin-top: 0.4rem;
  padding-top: 0.4rem;
  border-top: 1px solid var(--pbs-sidebar-rule);
}

/* A group whose name opens and shuts it: Settings, seven rows of work done once in a while that
   were pushing the daily rows and Switch site off the bottom (LOURA, 2026-09-24). A plain
   <details>, so it works with no script, and the server says which one is open.

   Pico claims <details> and <summary> by tag — an accordion's colours, a bottom margin, its own
   chevron — and it claims the open state with more weight than a class carries, so these match the
   shape of its own selectors rather than out-guessing them. The chevron is Pico's and is kept: it
   is what says the name can be pressed, and the view switcher above already wears it. */
.sidebar .nav-fold,
.sidebar .nav-fold[open] > summary {
  margin-bottom: 0;
}

.sidebar .nav-fold > summary,
.sidebar .nav-fold[open] > summary:not([role]):not(:focus) {
  color: var(--pbs-sidebar-muted);
}

/* ── a heading that opens, and a row that opens ─────────────────────────────────────────────
   Settings is a section heading (LOURA, 2026-09-24); Reports is a row, and wears `.nav-item` on
   its <summary> so its icon, font, padding and hover are the ones Surveys and Improvement plans
   have. Both take the same marker, at the right end, as the view switcher above them has it, so
   the three match.

   **The marker is positioned, not laid out.** Pico floats its own chevron; a float and then a
   flex item with `margin-left: auto` were both tried here and neither put it at the right end —
   the second took the whole name across the panel with it (measured 2026-09-24). Absolute against
   the right edge is out of the flow altogether, so nothing about a summary's own layout reaches
   it, and the name goes back to starting where every other name in its column does.

   Two borders on a rotated box rather than an image: nothing to fetch, it takes `currentColor`,
   and `translateY(-50%)` centres it whichever way it is turned. */
.sidebar .nav-fold > summary,
.sidebar .nav-row-fold > summary {
  position: relative;
  padding-right: 1.5rem;
  list-style: none;
  cursor: pointer;
}

.sidebar .nav-fold > summary::after,
.sidebar .nav-row-fold > summary::after {
  position: absolute;
  top: 50%;
  right: 0.7rem;
  float: none;
  width: 0.35rem;
  height: 0.35rem;
  margin: 0;
  border: 0 solid currentColor;
  border-right-width: 1.5px;
  border-bottom-width: 1.5px;
  background: none;
  content: "";
  color: var(--pbs-sidebar-muted);
  transform: translateY(-50%) rotate(-45deg);
}

/* Shut it points along the row, open it points down at what it opened. */
.sidebar .nav-fold[open] > summary::after,
.sidebar .nav-row-fold[open] > summary::after {
  transform: translateY(-50%) rotate(45deg);
}

/* Pico's margins and accordion colours, given back on the row fold as on the heading fold. */
.sidebar .nav-row-fold,
.sidebar .nav-row-fold[open] > summary {
  margin-bottom: 0;
}

.sidebar .nav-row-fold > summary,
.sidebar .nav-row-fold[open] > summary:not([role]):not(:focus) {
  color: var(--pbs-sidebar-fg);
}

.sidebar .nav-row-fold > summary:hover {
  background: var(--pbs-sidebar-hover);
}

/* What it opens onto: rows, indented under the row that opened them, and no rule or tint of their
   own — the indent is what says they belong to it. */
.sidebar .nav-sub .nav-item {
  padding-left: 1.5rem;
}

/* Collapsed to icons there is no indent to give and no chevron to draw: every row is its icon,
   centred, and the fold is held open so these are reachable at all. */
.shell-collapsed .sidebar .nav-sub .nav-item,
.shell-collapsed .sidebar .nav-row-fold > summary {
  padding-inline: 0;
}

.shell-collapsed .sidebar .nav-row-fold > summary::after {
  display: none;
}

/* A whole group of rows marked as not everybody's (A-38): Settings, which is a SITE's FULL ACCESS
   person's work, and which STAFF, a COACH and SUPER ADMIN are shown without holding it anywhere
   but a SITE in TRAINING (A-12). `.gated` is built for a group of controls inside a card, so it
   gives those margins back here; no side padding, so the rows inside keep the inset every other
   row on the sidebar has. */
.sidebar .gated {
  margin: 0.9rem 0.5rem var(--pico-spacing);
  padding: 0.85rem 0 0.25rem;
}

/* Collapsed to icons the box stays — it is what says this group is not everybody's — and its
   badge goes the way every other word here goes: clipped rather than removed, so a screen reader
   still reads it. Its own words would be wider than the whole column. */
.shell-collapsed .sidebar .gated {
  margin-inline: 0.25rem;
}

.shell-collapsed .sidebar .gated-badge {
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

@media (prefers-reduced-motion: no-preference) {
  .shell {
    transition: grid-template-columns 150ms ease-out;
  }
}

/* ── top bar ──────────────────────────────────────────────────────────────────────────── */

/* The top bar and the band at a SITE, which stay on screen together while the page scrolls.
 *
 * Well clear of the page under it (LOURA, 2026-09-22). At 1 it lost to SurveyJS, which sticks a
 * matrix's row labels at 12 and its own centre container at 15 and paints them over anything
 * lower — so scrolling a SURVEY drew the question across the bar. Below 2000, where SurveyJS's
 * popups start: a dropdown opened near the top of a page belongs over the bar rather than clipped
 * by it, and the MODALS and the save state sit at 3000 above both. */
.shell-head {
  position: sticky;
  top: 0;
  z-index: 200;
}

/* The VIEW's colour, with a paler edge of it under the bar (PR-289). */
.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: var(--pbs-topbar-height);
  padding: 0.25rem 1rem;
  background: var(--pbs-view);
  color: var(--pbs-topbar-fg);
  border-bottom: 3px solid var(--pbs-view-edge);
}

.topbar .icon {
  width: 18px;
  height: 18px;
}

.place {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

/* LEGACY-2's kicker over the facility name: small, uppercase, spaced. **Its rank comes from the
   size and the letter-spacing, never from dimming it** (LOURA, 2026-09-28). Held at 60% white it
   measured 3.03:1 on the SITE bar where text this small wants 4.5:1, and that one span failed axe
   on every page that draws the shell — fourteen browser cases from one line. Taking the colour off
   inherits the bar's own white and measures 5.34:1 there, 5.93 at a COACH, 7.59 at STAFF and 10.61
   in CONFIGURATION. The look is the one that was chosen; only the dimming is gone. */
.place-over {
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.6em;
  font-weight: 600;
  letter-spacing: 0.06em;
}

.place-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.95em;
}

.topbar-end {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-left: auto;
}

/* The CYCLE open today (PR-286): a pill on the bar, and white with the danger colour for the last
 * seven days. The danger red is fixed rather than Pico's, because the chip sits on the VIEW's
 * colour in both schemes: #a93a35 on white is 6.28:1. */
.cycle-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  padding: 0.1rem 0.65rem;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.22);
  color: var(--pbs-topbar-fg);
  font-size: 0.8125rem;
  white-space: nowrap;
}

.cycle-chip b {
  font-variant-numeric: tabular-nums;
}

.cycle-chip-soon {
  border-color: #fff;
  background: #fff;
  color: #a93a35;
  font-weight: 600;
}

/* ── the band at a SITE (PR-284) ──────────────────────────────────────────────────────────── */

/* Whose screen this is, for a CIYJ USER at a SITE, in the colour of the VIEW they came from: a
 * faint tint of it and a thick edge, and the VIEW's name on a chip of its own colour. The tint is
 * 6%: at the mockup's 9% the link colour on it measured 4.50:1 on Configuration's, too close
 * (PR-290). */
.site-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 0.4rem 1rem;
  border-bottom: 1px solid color-mix(in srgb, var(--pbs-view) 28%, var(--pico-background-color));
  border-left: 6px solid var(--pbs-view);
  background: color-mix(in srgb, var(--pbs-view) 6%, var(--pico-background-color));
  color: var(--pico-color);
  font-size: 0.875rem;
}

.site-band-view {
  padding: 0 0.6rem;
  border-radius: 999px;
  background: var(--pbs-view);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1.25rem;
}

/* The one badge that keeps a ground under it. Everywhere else a badge is a ring on the page, but
   the band is a solid VIEW colour carrying white text, and CLOSED and Viewing only are the two
   badges whose whole job is a tone — red or amber, which cannot be read off it (LOURA,
   2026-09-20). So here the word keeps a near-white pill to sit on, and the ring goes round that. */
.site-band .badge {
  background: color-mix(in srgb, var(--pbs-badge-fg) 10%, var(--pico-background-color));
}

/* ── the bell (NT-17) ─────────────────────────────────────────────────────────────────── */

/* Pico's dropdown, dressed like the USER menu beside it: no field box, no marker. */
.bell-menu {
  margin: 0;
}

.topbar .bell-menu > summary:not([role]) {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: auto;
  padding: 0.5rem;
  border: 0;
  border-radius: var(--pico-border-radius);
  background: none;
  color: var(--pbs-topbar-fg);
  box-shadow: none;
}

.topbar .bell-menu > summary:not([role]):hover,
.topbar .bell-menu > summary:not([role]):focus-visible {
  background: rgba(255, 255, 255, 0.12);
}

.topbar .bell-menu > summary:not([role])::after {
  display: none;
}

/* The count rides the bell's shoulder. Nothing unread draws none at all, so this only ever
   holds a number worth reading. */
.bell-count {
  position: absolute;
  top: 0.1rem;
  right: 0.05rem;
  min-width: 1.05rem;
  padding: 0 0.2rem;
  border-radius: 1rem;
  background: var(--pico-del-color);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.05rem;
  text-align: center;
}

.bell-menu ul {
  left: auto;
  right: 0;
  width: 22rem;
  max-width: calc(100vw - 2rem);
}

/* One line each, clipped rather than wrapped: the menu is a way in, and the whole of anything is
   read on the messages screen. */
.bell-menu li a {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The way to the rest, set apart from what it is the rest of. */
.bell-more {
  border-top: 1px solid var(--pico-muted-border-color);
}

.bell-more a {
  font-weight: 600;
}

/* ── NT-15's WARNING band ─────────────────────────────────────────────────────────────── */

/* Above the page and under the breadcrumb, on every signed-in screen (PR-224). It is the
   interruption; the message is read on the messages screen, so this is one line and a link. */
.warning-band {
  padding: 0.6rem var(--pico-block-spacing-horizontal);
  font-size: 0.875rem;
  border-bottom: 1px solid color-mix(in srgb, var(--pbs-warning-color) 45%, transparent);
  background: var(--pbs-warning-band-bg);
  color: var(--pbs-warning-color);
}

/* Each one is a line: the warning mark, then the title. The mark is drawn and never read out —
   the words say what it is (WCAG 1.4.1), and a screen reader announcing "warning" before every
   line of a two-line band is noise. */
.warning-band li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
}

.warning-band .icon {
  color: var(--pbs-warning-color);
}

.warning-band ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.warning-band li + li {
  margin-top: 0.25rem;
}

/* No underline: the band is already a coloured strip with a mark on it, and an underline made it
   read as a stray link in the chrome rather than as the warning it is (CIYJ, 2026-09-10). It
   still underlines on hover and on focus, so it is never a link you cannot tell is one. */
.warning-band a {
  color: inherit;
  font-weight: 600;
  text-decoration: none;
}

.warning-band a:hover,
.warning-band a:focus-visible {
  text-decoration: underline;
}

/* The USER's menu: Pico's dropdown, restyled for a dark bar. */
.user-menu {
  margin: 0;
}

/* Specific enough to beat Pico's own summary styling, which paints a form-field box. */
.topbar .user-menu > summary:not([role]) {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  height: auto;
  padding: 0.5rem 0.25rem;
  border: 0;
  border-radius: var(--pico-border-radius);
  background: none;
  color: var(--pbs-topbar-fg);
  font-size: 0.875rem;
  box-shadow: none;
}

.topbar .user-menu > summary:not([role]):hover,
.topbar .user-menu > summary:not([role]):focus-visible {
  background: rgba(255, 255, 255, 0.12);
}

.topbar .user-menu > summary:not([role])::after {
  display: none;
}

.user-menu ul {
  left: auto;
  right: 0;
  min-width: 10rem;
}

.user-menu li {
  padding: 0.5rem;
}

.user-menu form {
  margin: 0;
}

.user-menu form button {
  width: 100%;
  margin: 0;
  padding: 0.5rem 1rem;
}

/* ── narrow screens: the sidebar starts collapsed, the toggle expands it ───────────────── */

@media (max-width: 48rem) {
  .shell {
    grid-template-columns: var(--pbs-sidebar-collapsed-width) minmax(0, 1fr);
  }
  .shell-collapsed {
    grid-template-columns: var(--pbs-sidebar-width) minmax(0, 1fr);
  }
  .shell:not(.shell-collapsed) .logo img,
  .shell:not(.shell-collapsed) .nav-label,
  .shell:not(.shell-collapsed) .view-switch-text,
  .shell:not(.shell-collapsed) .nav-group,
  .cycle-chip-long {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
  }
  .shell-collapsed .logo img,
  .shell-collapsed .nav-label,
  .shell-collapsed .view-switch-text {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
  }
  .shell-collapsed .nav-group {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0.75rem 0.75rem 0.2rem;
    overflow: visible;
    clip: auto;
    clip-path: none;
  }
  .shell:not(.shell-collapsed) .view-switch > summary:not([role]) {
    justify-content: center;
    padding-inline: 0;
  }
  .shell:not(.shell-collapsed) .view-switch > summary:not([role])::after {
    display: none;
  }
  .shell-collapsed .view-switch > summary:not([role]) {
    justify-content: flex-start;
    padding-inline: 0.75rem;
  }
  .shell-collapsed .view-switch > summary:not([role])::after {
    display: block;
  }
  .shell-collapsed .nav-group + .nav-group-items {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  .shell:not(.shell-collapsed) .nav-group + .nav-group-items {
    margin-top: 0.4rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--pbs-sidebar-rule);
  }
  .shell:not(.shell-collapsed) .logo-mark {
    display: flex;
    justify-content: center;
    padding: 0.5rem 0;
  }
  .shell-collapsed .logo-mark {
    display: none;
  }
  .shell:not(.shell-collapsed) .nav-item {
    justify-content: center;
    padding-inline: 0;
  }
  .shell-collapsed .nav-item {
    justify-content: flex-start;
    padding-inline: 0.75rem;
  }
  .shell-collapsed .logo {
    margin-inline: 0.75rem;
  }
  .shell:not(.shell-collapsed) .logo {
    margin-inline: 0;
  }
  .topbar {
    padding-inline: 0.75rem;
  }
  .user-menu summary > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
  }
}

/* Short viewports: a smaller logo and tighter items, so the foot of the sidebar is still on
 * screen on a laptop with a crowded browser. Below this the sidebar scrolls inside itself. */
@media (max-height: 40rem) {
  .sidebar {
    padding-block: 0.5rem;
  }
  .logo {
    max-width: 175px;
    margin-inline: auto;
    margin-bottom: 0.5rem;
  }
  .sidebar .nav-item {
    padding-block: 0.3rem;
  }
  .sidebar .nav-foot {
    padding-top: 0.25rem;
  }
}

/* ── partials ─────────────────────────────────────────────────────────────────────────── */

.notice {
  padding: var(--pico-spacing);
  border-left: 0.25rem solid var(--pico-primary);
  background: var(--pico-card-background-color);
}
.notice-warning {
  border-left-color: var(--pico-mark-background-color);
}
.notice-error {
  border-left-color: var(--pico-del-color);
}

/* ── signed out ───────────────────────────────────────────────────────────────────────── */

.account {
  max-width: 28rem;
  margin-inline: auto;
}

.account picture img {
  display: block;
  margin: 0 auto var(--pico-block-spacing-vertical);
  height: auto;
}

/* A remark after a name: "(closed)" after a SITE in a list. */
.aside {
  color: var(--pico-muted-color);
}

/* A list inside a table cell: the VERSIONS of a SURVEY on the survey versions page. No
   bullets and no indent, each item on its own line. */
ul.plain {
  list-style: none;
  padding-inline-start: 0;
  margin-bottom: 0;
}
ul.plain li {
  list-style: none; /* Pico marks each li itself, so the ul's own rule is not enough */
  margin-bottom: calc(var(--pico-spacing) * 0.25);
}

/* ── the dashboard: cards that flow into as many columns as fit ───────────────────────── */
/*
 * CSS multi-column, which is the only thing that gives all three of what a dashboard wants with
 * no JavaScript: the column count follows the width, the cards pack with no holes under a short
 * one, and they stay in one sequence so the reference cards land after the work.
 *
 * A grid was tried twice and cannot do it. `auto-fit` puts cards in rows, so a short card leaves
 * a hole until the next row — the jagged edges. Fixed roles fix that but stop adapting. Grid
 * masonry is not shipped anywhere yet, and the JavaScript kind is not worth N-16.
 *
 * The cost, stated: cards read down a column and then across, so on a wide screen card two is
 * below card one rather than beside it. That is how a dashboard is read and how a newspaper is
 * set; it would be wrong for anything numbered. DOM order is unchanged, so the tab order and
 * what a screen reader hears are the source order either way (WCAG 1.3.2).
 *
 * Never more than three columns, however wide the page (PR-285), and cards 19rem across, so the
 * centred page's 64rem holds three as the mockup did; PR-169's 23rem held two (PR-290).
 */
.dashboard {
  columns: 19rem 3;
  column-gap: var(--pico-spacing);
}

/*
 * One flow, however many pages the cards came from (PR-213).
 *
 * A fetched page arrives wrapped: `.panel-part` is the box htmx swaps into, and what lands inside
 * it is that page's own `.dashboard`. Nested, that is a second column context, so the two groups
 * packed as rows — and the gap under a short card became whatever the tallest card beside it left,
 * which is the ragged spacing CIYJ saw on the SITE record (2026-09-10).
 *
 * `display: contents` takes those wrappers out of the layout without taking them out of the DOM,
 * which is exactly what they are: handles for htmx, not boxes. The cards below them pack as one
 * sequence, which is what PR-169 chose columns for in the first place.
 */
.dashboard .panel-part,
.dashboard .dashboard,
.dashboard .stacked {
  display: contents;
}

/* One column of cards, read in order (LOURA, 2026-09-24). `.dashboard` packs cards into columns,
   which suits a set of independent panels and not a record somebody reads top to bottom — a
   facility's address, then what kind of place it is, then who is named here. Block flow is all it
   takes; `.card` carries the space between them. It is a class rather than nothing so that the
   rule above can dissolve it when this same page is fetched into a panel that does pack. */
.stacked {
  display: block;
}

/* Something the whole page has to read spans the flow rather than sitting in one column. */
.dashboard .notice {
  column-span: all;
}

/* A <fieldset> used only to switch a card's fields off in one place — CIYJ reads the form a site's
   own person fills in, so the fields are drawn and dead rather than swapped for a list. It must
   leave no mark of its own: Pico gives a fieldset a bottom margin, and the last field inside
   already carries one, so without this the Save drifts a line away from the fields it sends. */
.plain-fields {
  margin-bottom: 0;
}

.card {
  /* Never split across a column boundary. */
  break-inside: avoid;
  margin-bottom: var(--pico-block-spacing-vertical);
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  padding: 0 var(--pico-spacing) var(--pico-spacing);
  /*background: var(--pico-card-sectioning-background-color);*/
}

/* The card's own bar: the heading, and an action at its right end. A <form> or a link is not
   permitted content inside a heading, so they are siblings under a <header>. */
.card > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 calc(var(--pico-spacing) * -1) var(--pico-spacing);
  padding: 0.5rem var(--pico-spacing);
  border-bottom: 1px solid var(--pico-muted-border-color);
  background: none;
}

/* The heading wraps rather than pushing the action out of the card: at 19rem a long title such as
   "Performance-based Standards contacts" no longer fits on one line beside its icon (PR-290). */
.card > header > h2 {
  min-width: 0;
  margin: 0;
  font-size: 0.9375rem;
}

.card > header > :not(h2) {
  flex: none;
}

.card > header form {
  margin: 0;
}

/* The card's own foot, mirroring its bar: what the table above it was counted over. It is the
   card's edges it runs to, which is why the side and bottom margins undo .card's padding — the
   same trick the header above uses on its sides. */
.card > footer {
  margin: var(--pico-spacing) calc(var(--pico-spacing) * -1) calc(var(--pico-spacing) * -1);
  padding: 0.5rem var(--pico-spacing);
  border-top: 1px solid var(--pico-muted-border-color);
  border-radius: 0 0 var(--pico-border-radius) var(--pico-border-radius);
  background: var(--pico-code-background-color);
  color: var(--pico-muted-color);
  font-size: 0.78125rem;
}

.card-action {
  display: inline-flex;
  align-items: center;
  color: var(--pico-secondary);
}

.card-action:hover,
.card-action:focus-visible {
  color: var(--pico-primary);
}

/* One radio or checkbox with a note under it. `.aside` is a block, so left to itself it landed
   under the control rather than under the label and the two lines overlapped. Two columns puts
   the control in the first and both lines of text in the second. */
.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
}

.choice > input {
  margin: 0;
}

.choice > .aside {
  grid-column: 2;
  margin: 0;
}

/* A card header that carries more than one: a link to the whole list, and the ⊕ that opens the
   panel for adding one. */
.card-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
}

/* An act here that writes is a form around one icon button, and the form is a flex box so that
   button is a flex item rather than an inline one (LOURA, 2026-09-22). As an inline box it sat on a
   baseline, the line box added the strut's descender underneath it, and centring the form then
   centred a box with space at the bottom — which lifted the ⊕ off the gear beside it. */
.card-actions form {
  display: flex;
}

/* A remark at the foot of a card, set apart from the content above it: where to go for the
   thing this card only reports. `.aside` indents the first line, which is right after a name in
   a list and wrong for a line of its own. */
.card-foot {
  margin-block: var(--pico-block-spacing-vertical) 0;
  margin-inline-start: 0;
}

/* The ⊕ is a button, so Pico gives it a filled box; here it is an icon like its neighbours. */
button.card-action {
  width: auto;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
}

.card table {
  margin-bottom: 0;
}

/* The name takes the room; a count never wraps. Only on a dashboard card, whose tables are a
   name and a number — a table of real content wraps like any other (PR-198). */
.dashboard .card th:not(:first-child),
.dashboard .card td:not(:first-child) {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

.card dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 1rem;
  margin: 0;
}

.card dt {
  color: var(--pico-muted-color);
}

.card dd {
  margin: 0;
}

/* ── the PBS PROGRAM, as a badge (G-6) ────────────────────────────────────────────────── */
/*
 * Six PROGRAMS, fixed and mutually exclusive, which is inside the half-dozen a reader can still
 * map to a meaning. The name is in the badge, so the colour is a shortcut and never the message
 * — WCAG 1.4.1 refuses colour as the only carrier, and a coach scanning twenty rows reads the
 * shape before the word either way. Tinted rather than saturated: these repeat down a table and
 * a wall of solid colour is worse than none.
 */
.program {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border: 1px solid currentColor;
  border-radius: 1rem;
  font-size: 0.71875rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}

.program-correction {
  color: #1d6fb8;
}
.program-detention {
  color: #7a4fb5;
}
.program-community {
  color: #14795a;
}
.program-assessment {
  color: #a15c00;
}
.program-community-supervision {
  color: #0e7490;
}
.program-r2d2 {
  color: #a13d63;
}
.program-other {
  color: var(--pico-muted-color);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .program-correction {
    color: #7db6ea;
  }
  :root:not([data-theme="light"]) .program-detention {
    color: #bfa2e8;
  }
  :root:not([data-theme="light"]) .program-community {
    color: #63c6a3;
  }
  :root:not([data-theme="light"]) .program-assessment {
    color: #d9a441;
  }
  :root:not([data-theme="light"]) .program-community-supervision {
    color: #5fbdd4;
  }
  :root:not([data-theme="light"]) .program-r2d2 {
    color: #e094b4;
  }
}

:root[data-theme="dark"] .program-correction {
  color: #7db6ea;
}
:root[data-theme="dark"] .program-detention {
  color: #bfa2e8;
}
:root[data-theme="dark"] .program-community {
  color: #63c6a3;
}
:root[data-theme="dark"] .program-assessment {
  color: #d9a441;
}
:root[data-theme="dark"] .program-community-supervision {
  color: #5fbdd4;
}
:root[data-theme="dark"] .program-r2d2 {
  color: #e094b4;
}

/* ── a JURISDICTION, folded (the Coach VIEW) ──────────────────────────────────────────── */

.fold {
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.fold:last-of-type {
  border-bottom: none;
}

/* A flex summary puts Pico's ::after marker on the baseline of the name rather than wherever
   the inline flow left it. */
.fold > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0;
  font-weight: 600;
}

.fold > summary .aside {
  font-weight: normal;
}

/* One line per SITE: the name and its PROGRAM, then what it owes, right-aligned. */
.site-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.35rem 1rem;
  padding: 0.4rem 0 0.4rem 1.25rem;
  border-top: 1px solid var(--pico-muted-border-color);
}

/* The last line of a fold, which is about the agency rather than about a SITE: one link across
   the whole row, so it reads as the group's own footer and not as another facility. */
.site-line-whole {
  justify-content: flex-start;
  font-size: 0.875em;
}

.site-line-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.tallies {
  display: flex;
  align-items: baseline;
  gap: 1.25rem;
  color: var(--pico-muted-color);
  font-size: 0.875em;
  white-space: nowrap;
}

.tally b {
  color: var(--pico-color);
}

/* The SITE chooser's own search form went with the chooser's own shape (LOURA, 2026-09-24): it is
   a filter row over cards like every other list now, and `.filter-bar` dresses it. `.switch`
   stays — every screen with a toggle in its filter row wears it. */

.switch {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0 0.75rem;
  color: var(--pico-muted-color);
  white-space: nowrap;
}

.switch input {
  margin: 0;
}

/* ── CIYJ's own work, listed until there is something to put on a dashboard ────────────── */

.work-list {
  list-style: none;
  padding: 0;
}

/* list-style on the li, not only the ul: Pico sets it on the li directly, and a declaration on
   the element beats one inherited from its parent whatever the layers say. */
.work-list li {
  list-style: none;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.work-list li:last-child {
  border-bottom: none;
}

/* The note sits under the link rather than beside it, and the rule under each item is what keeps
   the two apart from the next pair (CIYJ, 2026-09-10). It reads as a list of things to do with a
   line of explanation each, which is what it is — and the explanation is worth keeping while five
   of the nine name a page nobody has built. */
.work-list .aside {
  margin-top: 0.15rem;
  font-size: 0.875em;
  line-height: 1.35;
}

/* A page not built yet, shown in its place, as the sidebar shows one (PR-154). */
.work-soon {
  color: var(--pico-muted-color);
}

/* A note beside a name: the customers under a site, a coach's assignment count, a due date. */
.aside {
  display: block;
  color: var(--pico-muted-color);
}

td .aside,
.dropdown .aside {
  display: inline;
  margin-left: 0.5rem;
}

.you-are {
  margin-bottom: 0;
  color: var(--pico-muted-color);
  font-size: 0.875em;
}

/* The sidebar toggle is a form now (PR-164), so the button needs the label's old shape. */
.nav-toggle-form {
  display: flex;
  margin: 0;
}

.nav-toggle-form button {
  width: auto;
  margin: 0;
}

/* ── modals ───────────────────────────────────────────────────────────────────────────── */

/*
 * `showModal()` puts a dialog in the top layer and stops the page behind being clicked, but it
 * does not stop it scrolling — so an open panel had two scrollbars, its own and the list behind
 * it (CIYJ, 2026-09-10). Pico answers this with a `modal-is-open` class its own script adds; this
 * WEB APP has no such script and needs none, because `:has()` asks the question in CSS.
 *
 * `:modal` rather than `[open]`, so this is exactly the modals that are over the page. The inline
 * `<dialog open>` a browser with no script gets is a panel *in* the page's flow (N-29), and that
 * one has to go on scrolling with the page or half of it could not be reached.
 *
 * The gutter is reserved on every page, because taking a scrollbar away is what would otherwise
 * jog the whole page sideways at the moment a modal opens.
 */
html {
  scrollbar-gutter: stable;
}

html:has(dialog:modal) {
  overflow: hidden;
}

/* ── tabs in a long form (PR-272) ────────────────────────────────────────── */
/*
 * static/tabs.js builds the strip and hides all but one panel. Everything here dresses what that
 * script made, so a browser without it has no strip, no hidden panels, and the stack of headed
 * parts the server wrote — which is the form as it was before tabs (N-19).
 *
 * The strip scrolls sideways rather than wrapping, because a wrapped tab strip reads as two rows
 * of buttons and a modal is narrow.
 */
.tab-list {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.tab {
  flex: 0 0 auto;
  margin: 0;
  padding: 0.4rem 0.75rem;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--pico-muted-color);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  width: auto;
}

.tab:hover {
  background: none;
  color: var(--pico-color);
}

.tab[aria-selected="true"] {
  border-bottom-color: var(--pico-primary);
  color: var(--pico-primary);
}

/* The panel takes focus when its tab is chosen, and a focus ring around a whole panel is noise. */
.tab-panel:focus {
  outline: none;
}

/* ── a fetched fragment inside a MODAL ──────────────────────────────────── */
/*
 * These panels were the browser's own `popover` until 2026-09-10 and carried a stylesheet of
 * their own to look like a dialog. They are dialogs now (N-29, PR-209), so Pico dresses them,
 * gives them the backdrop and the scroll, and all that is left is what a *fetched* fragment
 * needs: it arrives as a whole page's article and needs no second frame in here.
 *
 * **Not a dialog's own article.** A fetched page brings its MODALS with it, and a MODAL is a
 * `<dialog><article>` — so without the exclusion this took the padding and the border off every
 * dialog in the panel and left the words hard against the edge, off centre and scrolling sideways
 * (LOURA, 2026-09-24, on LOCATIONS inside a SITE's settings). What this rule is for is the frame a
 * fragment arrives wearing, never the frame a dialog *is*.
 */
.panel-part article:not(dialog article) {
  border: none;
  padding-inline: 0;
  margin-block: var(--pico-block-spacing-vertical);
}

/* A modal that holds more than one (PR-208): CIYJ's record, then the facility's own. The second
   is a plain div rather than an article, so it takes its spacing here. A dialog is narrower than
   two dashboard columns, so the cards inside stack — which is what a panel wants. */
.panel-part + .panel-part {
  margin-top: var(--pico-block-spacing-vertical);
}

/* While a fragment is on its way. htmx puts `htmx-request` on the element making the request for
   exactly as long as it is in flight, so the fallback link — which is there for a browser that
   will never fetch anything — steps aside for a word saying what is happening. Without this the
   panel opened showing two links that vanished a moment later, which reads as a mistake rather
   than as loading (CIYJ, 2026-09-10).
 *
 * The loading line is hidden by default rather than marked `hidden`: with no script nothing is
 * ever loading, so it is never shown and never announced, and the link stands as it should. */
.panel-loading {
  display: none;
  padding-block: var(--pico-block-spacing-vertical);
  color: var(--pico-muted-color);
}

.panel-part.htmx-request > .panel-fallback {
  display: none;
}

.panel-part.htmx-request > .panel-loading {
  display: block;
}

/* The link that opens one, sitting among controls at the end of a line, so it is dressed as a
   button. It is not one and does not say it is: with no script the browser follows it and the
   server draws the modal open (N-19), which is a link's behaviour and not a button's. Pico's
   outline secondary, at the size the panel's own header button used to be. */
.modal-link {
  display: inline-block;
  padding: 0rem 0.75rem;
  border: 1px solid var(--pico-primary);
  border-radius: var(--pico-border-radius);
  color: var(--pico-primary);
  font-size: 0.875rem;
  line-height: 1.5;
  text-decoration: none;
  white-space: nowrap;
}

.modal-link:hover,
.modal-link:focus-visible {
  border-color: var(--pico-primary-hover);
  color: var(--pico-primary-hover);
}

/* The class is worn by a `<button>` wherever the act posts rather than navigates, and Pico claims a
   button by its tag: it paints one solid, this sets only a border and the same colour for the
   words, and the act came out a filled block with nothing written on it (LOURA, 2026-09-24). A
   disabled one was legible all along, the disabled rule below repainting it — so the fault showed
   only the day one of these went live. */
button.modal-link {
  background: transparent;
}

/* A mark in front of the words, on the one that carries one: New response, at the foot of a
   SURVEY's page. The icon sits on the text's baseline rather than standing over it. */
.modal-link > .icon {
  margin-right: 0.4rem;
  vertical-align: -0.15em;
}

.site-line-end {
  display: flex;
  align-items: center;
  gap: 1rem;
  white-space: nowrap;
}

.site-line-end button {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.75rem;
  font-size: 0.8125rem;
}

/* ── the USERS at a SITE, and CIYJ's own (pages/site-users.jsx, pages/ciyj-users.jsx) ───────────── */

/* **A button in a MODAL is a button and not a bar across the dialog** (LOURA, 2026-09-22). Pico
   stretches a submit to its form's width, which is right for a page's form read down a 34rem
   measure and wrong in a panel already only as wide as what is in it. One rule rather than a class
   on twenty screens' worth of dialogs: inside a `<dialog>` this is always what is wanted, and the
   ones that had been given `.row-actions` by hand were the same fix written out longhand. */
dialog button {
  width: auto;
}

/* The buttons at the foot of a form in a MODAL: save, invite, remove.
   Not the default `stretch` (LOURA, 2026-09-26): a gated save is a dashed box a good deal taller
   than the button inside it, and a stretched row made Close as tall as the box — a plain link
   wearing a button's height for no reason.
   **The *last* baseline, not the first.** A gate's own first line is the button inside it, but the
   badge naming it is `position: absolute` only in the gate that wraps a group; in the inline one it
   still counts, so `baseline` lined the badge up with the word beside it and left the button it
   marks sitting below. The last line box in each is the button's own text, which is what a reader
   compares. `flex-end` first, for anything that does not know the keyword: bottom edges, which is
   out by the gate's own padding and not by a line. */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  align-items: last baseline;
  gap: 0.5rem;
  margin-top: var(--pico-spacing);
}

.row-actions button {
  width: auto;
  margin: 0;
}

/* **In a table cell it is the row's act, not a form's foot** (LOURA, 2026-09-22). The spacing that
   stands these off a MODAL's last field pushed the act half a line down the row, so it sat under
   the line it belongs to instead of beside it — the cells are top-aligned and it alone was not. */
td > .row-actions {
  margin-top: 0;
}

/* The cancel beside a confirm's button is a link — it goes back to the question's first shape
   (N-29) — dressed as Pico's secondary outline button at the size of the button beside it, so
   the pair read as one row of choices (CIYJ, 2026-09-15). */
.row-actions .modal-link {
  padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
  border-width: var(--pico-border-width);
  font-size: 0.875rem;
  line-height: var(--pico-line-height);
}

/* The first line of a fold that names one more of what is under it: a LIVING unit's SUBUNITS
   (pages/locations.jsx, PR-249). The box takes the row and the button sits at its end. */
.site-line-add {
  flex-wrap: nowrap;
  align-items: center;
  margin: 0;
}

.site-line-add input {
  flex: 1 1 8rem;
  min-width: 0;
  margin: 0;
}

.site-line-add button {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.75rem;
  font-size: 0.8125rem;
}

/* A form at the end of a line — a SUBUNIT's Remove — is a button and no more. */
.site-line-end form {
  margin: 0;
}

/* Two fields that are one thought: a first name and a last name. */
.two-up {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
}

.two-up > * {
  flex: 1 1 10rem;
}

/* A short list of things that are each a word and a way to take it away: email domains, the
   coaches assigned to a site. Not a table — a table of one column is a list with lines round it. */
.chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 var(--pico-spacing);
  padding: 0;
  list-style: none;
}

.chip-list li {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0.3rem 0.2rem 0.7rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
}

.chip-list form {
  margin: 0;
}

.chip-list button {
  width: auto;
  margin: 0;
  padding: 0.05rem 0.5rem;
  font-size: 0.75rem;
}

/* Something in force here that this screen does not own — an EMAIL DOMAIN named by the CUSTOMER
   (O-34). It carries no Remove, so the dashed edge says why before anybody reaches for one. */
.chip-inherited {
  border-style: dashed;
  padding-inline: 0.7rem;
  color: var(--pico-muted-color);
}

/* A record page is forms, so its cards want a readable measure rather than the full width. */
.card form {
  max-width: 34rem;
  margin-bottom: 0;
}

/* The other way round: one form wrapping several cards, which is how a RESOURCE's page is built
   (CIYJ, 2026-09-19). The cards take the page's width and the fields inside them take the measure,
   so a field is the same size here as it is in a form inside a card. The header is the card's own
   and spans it.

   `.card-stack` is that shape with no single form around it, for a record changed a piece at a
   time — a SITE's settings (LOURA, 2026-09-21). Same column, same measure; what differs is where
   the act sits, which is each card's own business there. */
.card-form .card > *,
.card-stack .card > * {
  max-width: 34rem;
}

.card-form .card > header,
.card-stack .card > header {
  max-width: none;
}

/* A page fetched into a stack brings its own flow with it — `.dashboard`, as the facility record
   does. Taken out of the layout so what packs is the cards, exactly as the column flow does it
   (PR-213); nested, the fetched page would be a three-column grid inside a single column. */
.card-stack .panel-part,
.card-stack .dashboard,
.card-stack .card-stack {
  display: contents;
}

/* A table takes the card and not the measure. The measure is for fields, which are read one at a
   time down a column; a table is read across, and four columns squeezed into 34rem is the layout
   this card was moved away from (LOURA, 2026-09-21). The same exception a RESOURCE's page makes
   for the block where its markdown sits beside its preview. */
.card-stack .card > .scroller,
.card-form .card > .scroller {
  max-width: none;
}

/* A table with something after it stands off it (LOURA, 2026-09-21): the next control began
   against the last row, so the add form read as part of the table. Where the table is the last
   thing in the card, the card's own padding is already that space. */
.card > .scroller:not(:last-child) {
  margin-bottom: var(--pico-block-spacing-vertical);
}

.card-form button[type="submit"] {
  width: auto;
}

/* The kind's own fields take the page rather than the measure, because a page's markdown is shown
   beside what it comes to (CIYJ, 2026-09-19). What is a field inside it keeps the measure. */
.card-form .card > .what-fields {
  max-width: none;
}

/* The same exception for a card whose form *is* a body written beside its preview — a SURVEY's
   INSTRUCTIONS (LOURA, 2026-09-22). The un-capping is on the form and not on the grid inside it,
   because `.card form` caps the form itself and a wide child of a narrow parent is still narrow. */
.card-stack .card > .wide-form {
  max-width: none;
}

.what-fields label,
.what-fields input {
  max-width: 34rem;
}

/* One kind's fields at a time, in the stylesheet (LOURA, 2026-09-20). The server writes all three
   groups, which is the form a browser with neither script nor `:has()` gets and what PR-272 asked
   for; this shows the one the radio says and hides the other two.
   Not a round trip: swapping the block from the server redraws it from what is saved, so a page
   body typed out and then compared against a video link was gone on the way back. `:has()` keeps
   every keystroke, answers in no time at all, and needs no script — the same argument the "Who
   sees it" fieldset makes below.
   Scoped to the radios, so the edit page — which draws one kind and no radios — is untouched. */
.kind-choice:has(.kind-pick-file:checked) ~ .what-fields > :not(.what-file),
.kind-choice:has(.kind-pick-page:checked) ~ .what-fields > :not(.what-page),
.kind-choice:has(.kind-pick-video:checked) ~ .what-fields > :not(.what-video) {
  display: none;
}

.body-and-preview {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}

@media (max-width: 62rem) {
  .body-and-preview {
    grid-template-columns: minmax(0, 1fr);
  }
}

.preview {
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
}

.preview-label {
  display: block;
  margin-bottom: 0.4rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
}

.preview > .resource-page > :last-child {
  margin-bottom: 0;
}

/* The one act a form page is for, as a floating control like every list's (CIYJ, 2026-09-19). */
.fab button[type="submit"] {
  width: auto;
  margin: 0;
  padding: 0.55rem 1.4rem;
  border-radius: 2rem;
  font-size: 0.9375rem;
}

/* "Everyone" and "Restrict to…" are one question, so the list of who belongs to the second answer
   and is not there under the first (CIYJ, 2026-09-19). In the stylesheet rather than over htmx,
   which is what the kind's fields above do and for the same reason: a round trip to hide a list
   redraws the fieldset from what is saved and loses whatever had just been ticked. Everything is
   written either way, so a browser with no `:has()` gets both halves and the write path ignores
   every tick under Everyone. */
.audience:has(.audience-everyone:checked) .indented {
  display: none;
}

.card form button[type="submit"] {
  width: auto;
}


/* A switch beside a search box, or under a form field: the label is the control. */
.switch {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 var(--pico-spacing);
  color: var(--pico-muted-color);
  white-space: nowrap;
}

/* A note under a switch is spaced like a note under a field, because it is the same thing. Pico
   pulls `input + small` up over the field's own bottom margin; a switch is a <label>, so that rule
   never reached one and the note sat a whole row away from the control it explains — closer to the
   Save button under it than to the toggle it belongs to (LOURA, 2026-09-21). The same pull, so the
   two read as one answer and what follows does not move.

   It is a sibling and not inside the label because `.switch` is a flex row: a block dropped in
   became a third item on that line. `aria-describedby` is what keeps it attached to the control. */
.switch + .aside {
  margin-top: calc(var(--pico-spacing) * -0.75);
  margin-bottom: var(--pico-spacing);
}

.switch input {
  margin: 0;
}

/* A narrow column of controls beside the thing they control: filters and a table, a third and
   two thirds. Flex rather than a grid with a breakpoint, because a breakpoint is a guess about
   the viewport and this one was wrong for anybody running the browser zoomed — the flex bases
   are what decide when the pair stops fitting, at any zoom or window size. */
.aside-and-main {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 var(--pico-block-spacing-horizontal);
}

.aside-and-main > :first-child {
  flex: 1 1 14rem;
  min-width: 0;
}

.aside-and-main > :last-child {
  flex: 2 1 26rem;
  min-width: 0;
}

/* The same two columns the other way round: what the page is about first and the narrow column
   after it, so the reading order down a phone is the page's own (the SURVEY's INSTRUCTIONS, then
   the RESOURCES beside them). */
.main-and-aside {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 var(--pico-block-spacing-horizontal);
}

.main-and-aside > :first-child {
  flex: 2 1 26rem;
  min-width: 0;
}

.main-and-aside > :last-child {
  flex: 1 1 14rem;
  min-width: 0;
}

/* A form whose fields stack: each label over its control, full width, ending in the button. */
.filters label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: 0.875rem;
}

.filters input[type="search"],
.filters input[type="text"] {
  width: 100%;
  margin-bottom: var(--pico-spacing);
}

.filters .switch {
  display: flex;
  margin-bottom: var(--pico-spacing);
}

.filters .switch input {
  margin-bottom: 0;
}

.filters button[type="submit"] {
  width: 100%;
  margin: 0;
}

/* The same stacked form, opened inside a card that already holds something else. */



/* A table wider than its card scrolls inside it rather than pushing the card open. */
/* A list's table sits in a box of its own: the mockup's, exactly (CIYJ, 2026-09-18). The border and
   the rounded corners are what make the heading row read as a cap on the table rather than a grey
   stripe across the page — it is the same height as the mockup's to a third of a pixel, and looked
   taller for want of anything closing it off (measured 2026-09-18). The border goes on the box
   rather than the table so that on a narrow screen it stays put while the table scrolls inside it,
   and `overflow-x` is what rounds the header's own corners off.
   Only a list table: one inside a card is already in a box, and a chart's legend is not a table a
   reader scans. `.scroller` is what marks a list table, and marks nothing else. */
.scroller {
  overflow-x: auto;
  border: 1px solid var(--pico-table-border-color);
  border-radius: 6px;
  background: var(--pico-background-color);
}

/* A table under a card's own facts stands off them (LOURA, 2026-09-24): a LIVING unit's SUBUNITS
   sat against the last line of its `<dl>` and read as another row of it. */
.card dl + .scroller {
  margin-top: var(--pico-block-spacing-vertical);
}

/* Inside a card the box is the card's, so this is the scrolling and nothing else — which is what
   the rule above means by "one inside a card is already in a box". A second border and a second
   set of rounded corners a rem inside the first reads as a frame around a frame (LOURA,
   2026-09-21). */
.card > .scroller {
  border: none;
  border-radius: 0;
  background: none;
}

/* ── the PROGRAM donut (the Staff VIEW) ───────────────────────────────── */
/*
 * Server-rendered SVG with no library (PR-159, PR-214). The slices carry their PROGRAM's own
 * colour class and paint with `currentColor`, so this chart and every PROGRAM badge read one
 * palette and a colour changed here changes both.
 */
.donut-and-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--pico-spacing);
}

.donut {
  flex: none;
  width: 9rem;
  height: 9rem;
}

.donut-legend {
  flex: 1 1 9rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.donut-legend li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin: 0;
  padding: 0.15rem 0;
  list-style: none;
}

/* ── the state tile grid (the Staff VIEW) ─────────────────────────────── */
/*
 * Server-rendered SVG with no library and no path data at all (PR-219): the arrangement is a dozen
 * strings in ui/tile-grid.js, so Q-N2's "tens of kilobytes against N-2's connections" does not
 * arise for this chart.
 *
 * The bands are one colour thinned out, the way the badges are, so both schemes follow Pico with
 * one value to keep. They stop at 52%: the count is written on the tile and has to stay legible
 * against it in light and dark alike, and the darkest band is where that would go first.
 */
.tilemap {
  display: block;
  width: 100%;
  height: auto;
  margin-block: 0.25rem var(--pico-spacing);
}

.tile rect {
  fill: var(--pbs-tile-fill, var(--pico-muted-border-color));
}

.tile text {
  fill: var(--pico-color);
  text-anchor: middle;
  font-weight: 600;
}

.tile-code {
  font-size: 3.4px;
}

.tile-count {
  font-size: 3px;
  font-weight: 400;
}

/* A state with no SITES is drawn and not hidden: "we are not there" is an answer, and a gap in
   the grid would read as a mistake. */
/* The five bands are the VIEW's own colour and four tints of it (CIYJ, 2026-09-18), mixed into the
   page rather than written out: both ends of every mix follow the scheme, so the ramp reads the same
   way up in the dark one and there is nothing to keep in step by hand. The steps are uneven on
   purpose — 6, 18, 36, 60, 100 — because an even ramp gave four washes a reader cannot rank at a
   glance, which is the whole job of a choropleth. Only the last carries its text in the opposite
   ink: white on each of the four VIEW colours measures 5.34:1 and up (PR-289), and a tint of one is
   lighter than that, so nothing below the last band may turn its text over. */
.tile-0 rect {
  --pbs-tile-fill: color-mix(in srgb, var(--pico-primary) 6%, var(--pico-background-color));
}
.tile-0 .tile-code {
  fill: var(--pico-muted-color);
  font-weight: 400;
}

.tile-1 {
  --pbs-tile-fill: color-mix(in srgb, var(--pico-primary) 18%, var(--pico-background-color));
}
.tile-2 {
  --pbs-tile-fill: color-mix(in srgb, var(--pico-primary) 36%, var(--pico-background-color));
}
.tile-3 {
  --pbs-tile-fill: color-mix(in srgb, var(--pico-primary) 60%, var(--pico-background-color));
}
.tile-4 {
  --pbs-tile-fill: var(--pico-primary);
}

.tile-4 text {
  fill: var(--pico-primary-inverse);
}

/* ── the OUTCOME MEASURE DASHBOARD (a SITE's reports) ─────────────────── */
/*
 * LEGACY-2's page as it was (CIYJ, 2026-09-17; PR-277). The chart is ui/outcome-chart.jsx, and it
 * carries its own look as attributes on the drawing — Chart.js 4's defaults, LEGACY-2's #336699 and
 * #cc0000 — so nothing here decides a colour of it. What is here is what a stylesheet has to do: the
 * hover that darkens a column and shows its tooltip, the page around the charts, and dark mode for
 * the text and grid Chart.js drew in #666 and 10% black.
 */
.om-chart {
  display: block;
  width: 100%;
  height: auto;
}

.om-mark {
  cursor: default;
}

.om-mark:hover .om-lift,
.om-mark:hover .om-tip {
  visibility: visible;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .om-chart .om-text {
    fill: #a8adb5;
  }
  :root:not([data-theme="light"]) .om-chart g[stroke] {
    stroke: #ffffff;
  }
  :root:not([data-theme="light"]) .om-chart .om-halo {
    stroke: var(--pico-card-background-color);
  }
}
[data-theme="dark"] .om-chart .om-text {
  fill: #a8adb5;
}
[data-theme="dark"] .om-chart g[stroke] {
  stroke: #ffffff;
}
/* A line's edge is the card it sits on, so it still separates the line from a column in the dark. */
[data-theme="dark"] .om-chart .om-halo {
  stroke: var(--pico-card-background-color);
}

.om-filters .om-heading {
  display: block;
  margin: 0 0 0.5rem;
  padding: 0;
  font-weight: 600;
}

.om-filters fieldset {
  margin-bottom: var(--pico-spacing);
}

.om-filters button[type="submit"] {
  width: auto;
}

/* One multi-choice filter, as a fold (LOURA, 2026-09-24), where it was a `select multiple` eight
   rems tall. Four of those filled the column and none of them said to hold ctrl; four of these are
   four lines until one is opened. The chevron sits at the right end and turns to point at what it
   opened, as the view switcher and the sidebar's folds do — absolutely placed, so nothing about the
   summary's own layout reaches it. */
.om-choices {
  margin-bottom: var(--pico-spacing);
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.om-choices > summary {
  position: relative;
  padding: 0.4rem 1.5rem 0.4rem 0;
  list-style: none;
  font-weight: 600;
  cursor: pointer;
}

.om-choices > summary::after {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  float: none;
  width: 0.35rem;
  height: 0.35rem;
  margin: 0;
  border: 0 solid currentColor;
  border-right-width: 1.5px;
  border-bottom-width: 1.5px;
  background: none;
  content: "";
  transform: translateY(-50%) rotate(-45deg);
}

.om-choices[open] > summary::after {
  transform: translateY(-50%) rotate(45deg);
}

/* How many are ticked, so a shut fold still says what it is doing. A number and not a badge: it is
   part of the words the summary reads out. */
.om-chosen {
  color: var(--pico-muted-color);
  font-weight: 400;
}

/* A long list of CYCLES or of SITES scrolls inside the fold rather than running the panel off the
   screen. The height is about eight ticks, which is where a list stops being scannable anyway. */
.om-choices fieldset {
  max-height: 16rem;
  margin: 0 0 var(--pico-spacing);
  overflow-y: auto;
}

/* The search box is the first thing in the panel now, and the widest. */
.om-filters input[type="search"] {
  margin-bottom: var(--pico-spacing);
}

.om-count {
  color: var(--pico-muted-color);
  font-size: small;
}

.om-note {
  padding-left: 0.75rem;
  border-left: 3px solid var(--pico-del-color);
}

/* The OMNIBUS's numbers under its scatter (LOURA, 2026-09-29). Full height, with the page doing
   the scrolling: a box of its own scrolling inside a page that also scrolls is two scrollbars to
   work out, and the rows are what a reader falls back to where the chart's labels are crowded. */
.om-points {
  margin-top: 1rem;
}

.om-points table {
  margin: 0;
}

.om-points caption {
  padding-bottom: 0.5rem;
  color: var(--pico-muted-color);
  font-size: 0.875em;
  text-align: left;
}

.om-points :is(th, td):not(:first-child) {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The facilities view's own note, which is a basis and not a warning, in LEGACY-2's muted rule. */
.om-note-muted {
  border-left-color: var(--pico-muted-border-color);
}

/* "Trends over time · Compare facilities": links, the current one bold on a rule of the primary. */
.om-views ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 1.5rem;
  margin: 0;
  padding: 0;
}

.om-views li {
  padding: 0;
  list-style: none;
}

.om-views a {
  margin: 0;
  padding: 0.25rem 0;
  color: var(--pico-muted-color);
  text-decoration: none;
}

.om-views a[aria-current="page"] {
  border-bottom: 2px solid var(--pico-primary);
  color: var(--pico-primary);
  font-weight: 600;
}

/* A group of controls a SITE's people see only at some ACCESS LEVEL, marked for a CIYJ USER
   (CIYJ, 2026-09-17): the view-only yellow, dashed, with the level on a pill across its top edge.
   The pill carries its own ground, so it reads wherever the group sits. Never on paper. */
.gated {
  position: relative;
  margin: 0.9rem 0 var(--pico-spacing);
  padding: 0.85rem 0.6rem 0.35rem;
  border: 1px dashed var(--pbs-warning-color);
  border-radius: var(--pico-border-radius);
}

/* In a row of filters the mark must not move what stands beside it (CIYJ, 2026-09-18). `.gated`
   carries a margin and a bottom padding meant for a card, which lifted the control inside it off
   the line every other control sits on.
   Here the box keeps its own breathing room — the badge above, a little under the control — and
   gives back below exactly what it takes, so `.filter-bar`, which aligns on the bottom edge, puts
   the boxed control on the same line as the bare ones and the dashed border sits under the row. */
.filter-bar .gated {
  align-self: flex-end;
  margin: 0 0 -0.4rem;
  padding: 0.75rem 0.5rem 0.4rem;
}

/* The acts a list offers above itself: the anonymous-link panels over a SURVEY's RESPONSES. One to
   a line, against the table's right edge (LOURA, 2026-09-23), so they read as things that can be
   done to the list below rather than as a toolbar across the top of it. */
.acts {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
  margin-block: 0 var(--pico-block-spacing-vertical);
}

/* The same row under the table it belongs to, where its spacing is above rather than below. */
.acts-foot {
  margin-block: 0.75rem 0;
}

/* `.gated` carries a margin meant for a card; here the line above is the mark's own room. */
.acts .gated {
  margin: 0.5rem 0 0;
  padding: 0.75rem 0.5rem 0.4rem;
}

.acts button {
  width: auto;
  margin: 0;
  padding: 0 0.75rem;
  font-size: 0.875rem;
  line-height: 1.5;
}

/* The marked form of an act that is a link reads as that link, dead — not as a button (LOURA,
   2026-09-24). The live control here is `.modal-link-plain`, underlined words, so a bordered pill
   in its place says the two are different acts when the only difference is who may press. The
   mark's own dashed box is what says it cannot be pressed; the colour only has to be quiet. */
.acts .gated button {
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--pico-muted-color);
  font-weight: 600;
  text-decoration: underline;
  opacity: 1;
  cursor: not-allowed;
}

/* The rule between links is what separates them, so the first wants none: in a panel the header
   has already drawn the line above it. */
.link-lines .site-line:first-child {
  border-top: 0;
}

/* Pico gives a submit a bottom margin, for the space between the rows of a form. Inside a gate
   that margin is space *inside* the dashed box, under the one control it marks (LOURA,
   2026-09-24). Buttons only: a gate also holds a `<select>` — the COACH picker, Compare Sites —
   that is a row of a form and keeps the spacing a form row has. */
.gated :is(button, [type="submit"], [type="button"]) {
  margin-bottom: 0;
}

/* A gate around one control rather than round a group of them (LOURA, 2026-09-24): it hugs what it
   marks. A dashed box the width of the panel, holding one button, read as a section of the panel
   rather than as a mark on that button. */
/* `fit-content` as well as `inline-block`, and the two `start`s with them: a gate this small can
   land in a flex row, in a grid, or straight in a form, and only the first of those shrinks an
   inline-block on its own. It hugs what it marks in all of them. */
.gated-inline {
  display: inline-block;
  width: fit-content;
  max-width: 100%;
  align-self: start;
  justify-self: start;
  margin: 0.9rem 0 0;
  padding: 0.85rem 0.55rem 0.5rem;
}



/* **A disabled act is outlined and readable, never a filled block** (LOURA, 2026-09-24). Pico
   repaints a disabled control with `--pico-background-color: var(--pico-border-color)` and leaves
   the label the colour it had — white on mid-grey, which is a button with no words on it. So: no
   fill, the muted border, the muted text.

   Not scoped to `.gated`, because a gate is not always on the control: where a whole column of
   acts is marked the badge is in its `<th>` and the cells carry nothing but `disabled`. The FAB
   keeps its own filled form, which is more specific than this.

   `pointer-events` comes back so the cursor can say why nothing happens; `disabled` is what
   refuses the press, not the pointer. */
button:disabled,
[type="submit"]:disabled,
[type="button"]:disabled {
  opacity: 1;
  border-color: var(--pico-muted-border-color);
  background-color: transparent;
  color: var(--pico-muted-color);
  pointer-events: auto;
  cursor: not-allowed;
}

.gated-badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 0 0.45rem;
  border: 1px dashed var(--pbs-warning-color);
  border-radius: 1rem;
  background: var(--pbs-warning-band-bg);
  color: var(--pbs-warning-color);
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}

@media print {
  .gated {
    margin: 0;
    padding: 0;
    border: 0;
  }

  .gated-badge {
    display: none;
  }
}

/* A MEASURE's name and its description under it, in the card's bar. */
.om-card > header {
  display: block;
}

.om-card > header > h2 {
  font-weight: 400;
  white-space: normal;
}

.om-card > header > small {
  display: block;
  margin-top: 0.25rem;
}

.om-pager {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: 1.5rem;
  margin-top: 1rem;
}

.om-off {
  color: var(--pico-muted-color);
}

/* LEGACY-2's list of REPORTS: a link, and what it is under it. */
.report-list dt {
  color: inherit;
}

.report-list dd {
  margin: 0 0 0.75rem;
  color: var(--pico-muted-color);
}

@media print {
  .om-card {
    break-inside: avoid;
  }
}

/* The key: the same four bands, as small swatches with their range in them. */
.tilemap-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.85rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.75rem;
}

.tilemap-key li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--pico-muted-color);
}

/* The band itself, as a swatch. The word beside it sits on the page's own ground, so no band has
   to be light enough for dark ink and dark enough for light ink at once (CIYJ, 2026-09-18). */
.tilemap-key i {
  display: block;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 2px;
  background: var(--pbs-tile-fill);
}

/* ── the messages screen (NT-17) ──────────────────────────────────────────── */

/* A card for each, where the two kinds were rows in one `<ul>` ruled off from each other and an
   ANNOUNCEMENT's body sat behind a fold (LOURA, 2026-09-24). They are still the same thing to the
   person reading them, which is why NT-17 puts them in one place; what tells them apart is what is
   written in the card, never its frame.

   When, and then what kind of message it is, on a line under the heading: a card's header carries
   the name and the act, and a badge crowded in there pushed a long title onto four lines. */
.message-marks {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0 0 var(--pico-spacing);
}

/* Unread is carried by the badge, which is a word; the weight is a second signal for somebody
   scanning, never the only one (WCAG 1.4.1). */
.message-unread > header > .message-title {
  font-weight: 700;
}

.message-when {
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* The one a link came for: the bell's row, or NT-15's warning band. Nothing folds any more, so
   `open` cannot open anything — what it can still do is say which card the eye is looking for in a
   list of twenty. A band down the edge, never a colour on its own (WCAG 1.4.1): the card is also
   the only one the address names. */
.message-here {
  border-left: 4px solid var(--pico-primary);
}

/* What has already been read, folded under what has not (LOURA, 2026-09-24). A quiet rule rather
   than a card: it is the lid on the drawer, not another message. The chevron sits at the right end
   and turns to point at what it opened, which is what the view switcher and the sidebar's own
   folds do — absolutely placed, so nothing about the summary's own layout reaches it. */
.read-fold {
  margin-top: var(--pico-block-spacing-vertical);
  border-top: 1px solid var(--pico-muted-border-color);
}

.read-fold > summary {
  position: relative;
  padding: 0.6rem 1.5rem 0.6rem 0;
  list-style: none;
  color: var(--pico-muted-color);
  font-size: 0.875rem;
  cursor: pointer;
}

.read-fold > summary::after {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  float: none;
  width: 0.35rem;
  height: 0.35rem;
  margin: 0;
  border: 0 solid currentColor;
  border-right-width: 1.5px;
  border-bottom-width: 1.5px;
  background: none;
  content: "";
  transform: translateY(-50%) rotate(-45deg);
}

.read-fold[open] > summary::after {
  transform: translateY(-50%) rotate(45deg);
}

/* Open, the first card wants the same air between it and the lid as the cards have between them. */
.read-fold[open] > summary {
  margin-bottom: var(--pico-block-spacing-vertical);
}

/* ── a SITE's IMPROVEMENT PLANS (pages/improvement-plans.jsx) ────────────── */

/* The AREAS a PLAN names, run together on one line and cut off with the whole of them as a hover.
   A `max-width` of nought is what makes the column give way to the ones beside it rather than
   widening to fit — the same thing the API accounts' table does with an account's name. */
.plan-focus {
  max-width: 0;
  width: 40%;
}

/* IP-11's act, beside the word it changes (LOURA, 2026-09-26). The badge and the pencil on one
   line, the pencil far enough off not to read as part of the badge. Both cells put their icon in a
   `.row-icons`, which is where an icon on a row gets its colour — the same one the PLAN's own rows
   use, and the reason these two read as the same control in both places. */
.plan-status {
  white-space: nowrap;
}

.plan-status .row-icons a {
  margin-left: 0.45rem;
}

/* What is left in the acts column once the status pencil moved beside its badge: the history, as
   an icon. Pinned to its content, so the FOCUS column — which is built to give way — gives up the
   width rather than this one, which is what stacked two links when there were two. */
.plan-acts {
  width: 1%;
  white-space: nowrap;
}

/* A PLAN's history: what happened, newest first, each with what was said about it under it. */
.plan-history {
  margin: 0;
  padding: 0;
  list-style: none;
}

.plan-history > li + li {
  margin-top: var(--pico-spacing);
  border-top: 1px solid var(--pico-muted-border-color);
  padding-top: var(--pico-spacing);
}

.plan-event-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.plan-event-when {
  margin-left: auto;
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* What somebody said about the change, in their own words, set apart from the list of what moved. */
.plan-event-said {
  margin: 0.4rem 0 0;
  font-style: italic;
}

.plan-event-changes {
  margin: 0.25rem 0 0;
  padding-left: 1.1rem;
  font-size: 0.9375rem;
}

/* ── one PLAN's own page ─────────────────────────────────────────────────── */

/* What is written in a section: a list, ruled off between one thing and the next, because they are
   separate pieces of writing and not paragraphs of one. */
.plan-entries {
  margin: 0;
  padding: 0;
  list-style: none;
}

.plan-entry {
  border-top: 1px solid var(--pico-muted-border-color);
  padding: var(--pico-spacing) 0;
  list-style: none; /* Pico marks each li itself, so the ul's own rule is not enough */
}

.plan-entry:first-child {
  border-top: none;
  padding-top: 0;
}

/* The acts on the right of the line the writing starts on, so they do not push it down. */
.plan-entry-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  float: right;
  margin-left: var(--pico-spacing);
}

.plan-entry-head .row-form {
  margin: 0;
}

.plan-entry-head button {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.7rem;
  font-size: 0.8125rem;
}

.plan-entry-by {
  clear: both;
  margin: 0.4rem 0 0;
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
}

.plan-entry-by .badge {
  margin-left: 0.5rem;
}

/* What was said about the PLAN, with replies indented under what they answer. One step of indent:
   a thread nested deeper is unreadable on a telephone, and the old system's data goes only two
   deep. The COMMENTS are a list of their own beside the ACTION STEPS now, not a thread hanging off
   each step (LOURA, 2026-09-25). */
.plan-comment-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.plan-comment-list > li {
  list-style: none; /* Pico marks each li itself, so the ul's own rule is not enough */
}

.plan-comment-list > li + li {
  margin-top: 0.75rem;
}

.plan-replies {
  margin: 0.5rem 0 0 1rem;
  border-left: 2px solid var(--pico-muted-border-color);
  padding-left: 0.75rem;
}

/* 0.6rem between paragraphs, not 0.2: a carried-over COMMENT runs to several of them and the
   tighter gap read as one block of text (LOURA, 2026-09-26). Smaller than a step's, the writing
   being smaller. */
.plan-comment > :is(p, ul, ol) {
  margin: 0.6rem 0 0;
  font-size: 0.9375rem;
}

/* The first line sits under the name, which has its own gap. */
.plan-comment > :is(p, ul, ol):first-of-type {
  margin-top: 0.2rem;
}

.plan-comment-who {
  margin: 0;
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
}

.plan-comment .row-links {
  margin-top: 0.3rem;
  font-size: 0.8125rem;
}

.plan-comment .row-form {
  margin: 0;
}

/* ── the rail, and one part at a time ────────────────────────────────────── */

/* A PLAN reads as a rail of its parts and the one being read, which is the shape a SITE has been
   working in for years. Below the fold width the rail stacks above the part rather than squeezing
   beside it: at that size a 15rem column leaves nothing for the writing. */
/* `stretch`, not `flex-start`, so the rail is as tall as the part beside it and the line between
   them runs the whole way down rather than stopping under the last item (LOURA, 2026-09-25). */
.plan-shell {
  display: flex;
  align-items: stretch;
  gap: calc(var(--pico-spacing) * 1.5);
}

/* **The whole PLAN reads down the page, not across it** (GL-4). The row above lays out a rail and
   *one* part; asked for every part it laid out eight, each a flex item taking an equal share, so
   the screen showed eight columns a few words wide. The print rules hid that, which is why it was
   found by cancelling a print rather than by printing (LOURA, 2026-09-25). */
.plan-shell.whole {
  display: block;
}

/* The rail becomes a band above the parts, as it does on a narrow screen: a 15rem column with a
   border down its right is a column, and there is nothing beside it here. Paper drops it entirely. */
.plan-shell.whole .plan-rail {
  margin-bottom: var(--pico-block-spacing-vertical);
  border-right: none;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.plan-shell.whole .plan-rail ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.plan-shell.whole .plan-rail li {
  width: auto;
}

.plan-shell.whole .plan-part + .plan-part {
  margin-top: calc(var(--pico-block-spacing-vertical) * 2);
  padding-top: var(--pico-block-spacing-vertical);
  border-top: 1px solid var(--pico-muted-border-color);
}

/* `display: block` on the nav as well as its list: Pico lays a `nav` and its `ul` out as rows, and
   overriding only the list leaves the nav itself a flex container.
   12rem, not 15 (LOURA, 2026-09-26): the column was taking width off an ACTION STEP's writing to
   hold whitespace. Not narrower, because the longest item a PLAN can show is "Analysis of
   performance" and below this it wraps to three lines rather than two. */
.plan-rail {
  display: block;
  flex: 0 0 12rem;
  border-right: 1px solid var(--pico-muted-border-color);
  padding-right: 0;
}

.plan-rail ul,
.plan-rail li {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Air between the rows, enough that a hovered item's background stops short of its neighbours and
   leaves a gap you can see. Flush, it ran straight into the words above and below it and read as
   covering them (LOURA, 2026-09-25). */
.plan-rail li + li {
  margin-top: 0.4rem;
}

/* Links, not buttons — each is a real address the server answers (N-19). Drawn without the link's
   colour and underline, because a column of twelve underlined blue words is a list nobody can
   read at a glance. */
.plan-rail-item {
  display: block;
  width: 100%;
  border-left: 3px solid transparent;
  padding: 0.6rem 0.75rem;
  color: inherit;
  text-decoration: none;
  line-height: 1.3;
}

.plan-rail-item:hover,
.plan-rail-item:focus {
  background-color: var(--pico-muted-border-color);
  color: inherit;
}

/* The part being read. `aria-current` carries the same fact for anybody not looking at the paint.
   The band runs to the rail's own edge on both sides, so it reads as the row being on rather than
   as a pill sitting inside the column. */
.plan-rail-item.here {
  border-left-color: var(--pico-primary);
  background-color: var(--pico-muted-border-color);
  font-weight: 600;
}

.plan-part {
  flex: 1 1 auto;
  min-width: 0;
}

.plan-part-name {
  margin-bottom: 0.25rem;
}

/* The words the coaches use, carried over whole. Muted, because it is the same paragraph every
   time and the writing under it is what changes. */
.plan-part-says {
  margin-bottom: var(--pico-block-spacing-vertical);
  color: var(--pico-muted-color);
}

/* A section is a card and its question is the card's heading, with the one act that adds to it in
   the same bar — so a long list can be added to without scrolling to its foot (LOURA, 2026-09-25).
   `.card > header` already lays that out; nothing is needed here but the gap between two of them,
   which the card's own spacing gives. */

/* Writing is read at a line length and not at the window's width, which is `.reading`'s 70ch
   wherever a page sets one (LOURA, 2026-09-26). A PLAN's page is `wide`, so without this an
   ACTION STEP and a COMMENT ran the whole way across a large screen. The tables keep the width:
   a row of numbers is read across, a paragraph is not. */
.plan-part-says,
.plan-entry > :is(p, ul, ol),
.plan-comment > :is(p, ul, ol),
.plan-steps .plan-step-body {
  max-width: 70ch;
}

/* ── ACTION STEPS, as the to-do list they are ────────────────────────────── */

/* A tick, what it is, when it runs, whose it is, and the acts (LOURA, 2026-09-25). The tick column
   and the acts column are as narrow as their contents; the writing takes what is left. */
.plan-steps {
  margin: 0;
}

.plan-steps td {
  vertical-align: top;
}

.plan-steps .plan-step-tick,
.plan-steps .plan-step-acts {
  width: 1%;
  white-space: nowrap;
}

.plan-step-when {
  white-space: nowrap;
}

/* **One column, not three** (LOURA, 2026-09-26): when it runs, then the step, then whose it is.
   Three columns each wrapped — a carried-over step is a couple of hundred words and names five
   people — and the reader's eye had to cross two gutters to put one step together. Stacked, the
   dates and the names are what they read as: a line of fact above and below the writing. */
.plan-steps .plan-step-when,
.plan-steps .plan-step-who {
  margin: 0;
  color: var(--pico-muted-color);
  font-size: 0.875rem;
}

/* The three stand apart from each other, where the step's own paragraphs keep the tighter rhythm
   `* + *` gives them below (LOURA, 2026-09-26). Written as children of the cell so these beat
   that rule and the one resetting a paragraph's margins, both of which are more specific than a
   bare `.plan-step-when` and would leave it dead. */
.plan-steps .plan-step-body > .plan-step-when {
  margin-bottom: calc(var(--pico-spacing) * 1.25);
}

.plan-steps .plan-step-body > .plan-step-who {
  margin-top: calc(var(--pico-spacing) * 1.25);
}

/* Air between one step and the next, now that each is a block of three things rather than a line
   of three cells: without it two steps run together and the second reads as the first's tail. */
.plan-steps td {
  padding-block: calc(var(--pico-spacing) * 0.9);
}

.plan-steps .plan-step-body > :is(p, ul, ol) {
  margin: 0;
}

/* **A carried-over step is prose and has to read as prose** (LOURA, 2026-09-26). The screens ask
   for a short to-do item and mostly get one, but eighteen years of PLANS hold steps of several
   paragraphs, and at 0.4rem they ran together into a wall. Below the gap between the three parts
   of the cell, so the hierarchy still reads: paragraphs are nearer each other than the dates and
   the owner are to the writing. */
.plan-steps .plan-step-body > * + * {
  margin-top: 0.7rem;
}

/* A finished step is quieter, never struck through: struck-out text is read as deleted and this is
   read as achieved. */
.plan-step-done .plan-step-body,
.plan-step-done .plan-step-when {
  color: var(--pico-muted-color);
}

/* A control that is an icon and nothing else. The name it answers to is on the control
   (aria-label), never in the drawing — the icon itself stays decorative. */
.icon-button {
  display: inline-flex;
  align-items: center;
  width: auto;
  margin: 0;
  padding: 0.2rem;
  border: none;
  background: none;
  box-shadow: none;
  color: var(--pico-secondary);
  line-height: 1;
}

.icon-button:hover:not(:disabled),
.icon-button:focus-visible:not(:disabled) {
  background: none;
  color: var(--pico-primary);
}

.icon-button:disabled {
  background: none;
  opacity: 0.35;
}

.icon-button .icon {
  width: 1.15rem;
  height: 1.15rem;
}

/* The run of them at the end of a row. `row-form` is what each submit is wrapped in, so the forms
   have to lay out as a row too or each one takes a line of its own. */
.row-icons {
  display: flex;
  align-items: center;
  gap: 0.15rem;
}

.row-icons .row-form,
.row-icons .gated {
  margin: 0;
}

.row-icons a {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem;
  color: var(--pico-secondary);
}

.row-icons a:hover,
.row-icons a:focus-visible {
  color: var(--pico-primary);
}

/* **The one destructive act in the row reads as one.** Edit and delete sit side by side as two
   outlines of the same weight, so what keeps them apart under the pointer is the colour: the trash
   goes to the delete colour where everything beside it goes to the primary. Hover and focus both,
   so it is not a thing only a mouse is told. */
.row-icons a[title="Delete"]:hover,
.row-icons a[title="Delete"]:focus-visible,
.icon-button.danger:hover:not(:disabled),
.icon-button.danger:focus-visible:not(:disabled) {
  color: var(--pico-del-color);
}

.row-icons a .icon {
  width: 1.15rem;
  height: 1.15rem;
}

/* The same three in the panel: the dates read as one span side by side, the person on a line of
   their own because a name is longer than a date. */
.plan-span {
  margin-top: var(--pico-spacing);
}

.plan-span-dates {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--pico-spacing);
}

@media (max-width: 30rem) {
  .plan-span-dates {
    grid-template-columns: 1fr;
  }
}

.plan-empty {
  margin: 0;
  color: var(--pico-muted-color);
}

/* Who touched it last, under everything and quiet with it. */
.plan-last-changed {
  margin-top: calc(var(--pico-block-spacing-vertical) * 1.5);
  color: var(--pico-muted-color);
  font-size: 0.875rem;
}

/* ── the slideout ────────────────────────────────────────────────────────── */

/* The same MODAL, down the right-hand side and full height, for a panel holding a long piece of
   writing.
   **Pico's `dialog` is the overlay, not the panel**: fixed to the viewport, `min-width` and
   `min-height` 100%, and a flex box that centres the `<article>` inside it. So a slideout is not a
   narrower dialog — sizing the dialog does nothing, because `min-width` beats `width`. It is the
   same overlay with its article pinned to the end and stretched, which is what these two rules do.
   Only ever a promoted dialog: `<dialog open>` with no script is a panel in the page's flow
   (N-29), and pinning that to the right of the document would be a panel nobody could reach. */
dialog.slideout:modal {
  align-items: stretch;
  justify-content: flex-end;
}

dialog.slideout:modal > article {
  width: min(46rem, 100%);
  /* Beats Pico's 510px/700px cap on a dialog's article, which is what made this full width. */
  max-width: min(46rem, 100%);
  /* And its `calc(100vh - spacing * 2)`, which is what kept it off the top and the bottom. */
  max-height: 100%;
  margin: 0;
  border-radius: 0;
  overflow-y: auto;
}

/* On paper the rail is navigation to nowhere, and every part is already there (GL-4). */
@media print {
  .plan-rail {
    display: none;
  }

  .plan-shell {
    display: block;
  }

  /* A part starts its own page: a PLAN is read a section at a time and a heading stranded at the
     foot of a sheet is the section that gets missed. The first keeps the trail's company. */
  .plan-part + .plan-part {
    break-before: page;
  }

  .plan-part .card {
    break-inside: auto;
  }
}

/* Narrow enough that a rail at the side leaves nothing for the writing: it becomes a band above
   the part instead, its items wrapping across. */
@media (max-width: 48rem) {
  .plan-shell {
    display: block;
  }

  .plan-rail {
    margin-bottom: var(--pico-block-spacing-vertical);
    border-right: none;
    border-bottom: 1px solid var(--pico-muted-border-color);
    padding-right: 0;
  }

  .plan-rail ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
  }

  .plan-rail-item {
    border-left: none;
    border-bottom: 3px solid transparent;
    border-radius: var(--pico-border-radius) var(--pico-border-radius) 0 0;
  }

  .plan-rail-item.here {
    border-left-color: transparent;
    border-bottom-color: var(--pico-primary);
  }
}

/* Where a PLAN is aimed. Two folds, a kind each, both shut until somebody asks for one. The
   chevron sits at the right end and turns to point at what it opened, as the OM dashboard's
   filters and the sidebar's folds do. */
.focus-fold {
  margin-bottom: var(--pico-block-spacing-vertical);
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.focus-fold > summary,
.placed-fold > summary {
  position: relative;
  padding: 0.6rem 1.5rem 0.6rem 0;
  list-style: none;
  font-size: 1.0625rem;
  font-weight: 600;
  cursor: pointer;
}

.focus-fold > summary::after,
.placed-fold > summary::after {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  float: none;
  width: 0.4rem;
  height: 0.4rem;
  margin: 0;
  border: 0 solid currentColor;
  border-right-width: 1.5px;
  border-bottom-width: 1.5px;
  background: none;
  content: "";
  transform: translateY(-50%) rotate(-45deg);
}

.focus-fold[open] > summary::after,
.placed-fold[open] > summary::after {
  transform: translateY(-50%) rotate(45deg);
}

.focus-fold[open] > summary,
.placed-fold[open] > summary {
  margin-bottom: var(--pico-spacing);
}

/* RS-2's panel, folded (LOURA, 2026-09-26). On a SITE's own screens it sits in the column beside
   the work, as every other placed panel does; on the lists that span SITES there is no room beside
   seven columns, so it goes above them and is shut until somebody asks — the reading is the thing
   they came for least. A card rather than a bare fold, so it reads as the same panel either way. */
.placed-fold {
  margin-bottom: var(--pico-block-spacing-vertical);
}

/* The summary stands where the card's own header stands: a card carries no top padding, its header
   supplying that and running to both edges on a negative margin. Without the same treatment the
   words sat against the card's top edge.
   Paragraph sized (LOURA, 2026-09-26): the focus folds it borrows from are a screen's own headings
   and this is a panel's name, which the card it sits in has already framed. */
.placed-fold > summary {
  margin: 0 calc(var(--pico-spacing) * -1);
  padding: 0.5rem var(--pico-spacing);
  font-size: 1rem;
}

/* Shut, the card is the bar: the card's own bottom padding is room under something that is not
   drawn. */
.placed-fold:not([open]) {
  padding-bottom: 0;
}

.placed-fold[open] > summary {
  margin-bottom: var(--pico-spacing);
  border-bottom: 1px solid var(--pico-muted-border-color);
}

/* How many are in there, so a shut fold still says how much it holds. */
.focus-count {
  color: var(--pico-muted-color);
  font-weight: 400;
}

/* A card a group, each a table of seven columns. What a MEASURE measures is the column that gives
   way — the numbers beside it are short and fixed, and a description that widened the table would
   push the goal box off the screen. */
.focus-table {
  font-size: 0.9375rem;
}

.focus-about {
  max-width: 22rem;
  color: var(--pico-muted-color);
}

.focus-table td {
  vertical-align: top;
}

.focus-table .choice {
  margin: 0;
}

/* The goal and its unit on one line, and **the same line on every row**: the unit was a sibling
   that wrapped under the box wherever the column was tight, and rows with no unit sat a line
   higher than rows with one (LOURA, 2026-09-25). A fixed slot holds the space whether or not
   there is a unit to put in it. */
.goal-cell {
  white-space: nowrap;
}

.goal-cell > .goal-box {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25rem;
}

.goal-cell input[type="number"] {
  width: 5.5rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
}

.goal-cell .goal-unit {
  display: inline-block;
  min-width: 1ch;
  color: var(--pico-muted-color);
}

/* The way out of the aiming screen, at the bottom where a way out belongs: the act itself floats
   in the corner, and a Cancel beside it is the one pressed by mistake. The form carries nothing
   else — every control on the screen reaches it by `form=`. */
.focus-foot {
  margin: var(--pico-block-spacing-vertical) 0;
}

/* The fixed list: one line an item, not a table. A note about an item is a sentence, and a
   textarea in a cell is a grey box the eye stops at on each of twenty-eight rows. */
.focus-items {
  margin: 0;
  padding: 0;
  list-style: none;
}

.focus-items > li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
  padding: 0.4rem 0;
}

.focus-items > li:last-child {
  border-bottom: none;
}

.focus-items .choice {
  flex: 0 0 22rem;
  margin: 0;
}

/* The note takes what is left of the line, and is quiet until somebody types in it. */
.focus-items input[type="text"] {
  flex: 1 1 14rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
  border-color: transparent;
  background: var(--pico-form-element-background-color);
  font-size: 0.9375rem;
}

.focus-items input[type="text"]:focus {
  border-color: var(--pico-form-element-active-border-color);
}

/* The picker's search, on one line above the table it narrows. */
.plan-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: var(--pico-spacing);
}

.plan-search input[type="search"] {
  flex: 1 1 12rem;
  margin: 0;
}

.plan-search button {
  width: auto;
  margin: 0;
}

/* A goal and its unit on one line, in a box only as wide as a number needs. */
.goal-cell > input[type="number"] {
  display: inline-block;
  width: 5.5rem;
  margin: 0;
  padding: 0.25rem 0.5rem;
}

.goal-cell > .aside {
  margin-left: 0.25rem;
}

/* Two words at the end of a row, on one line. `.row-actions` is a MODAL's foot and carries its
   spacing; a row's acts sit tight against each other. */
.row-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  white-space: nowrap;
}

/* The act on a card: small and quiet, sitting where a card's act sits. */
.mark-read {
  margin: 0;
}

.mark-read button {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.7rem;
  font-size: 0.8125rem;
}

/* A card-bar action that is a word rather than an icon: the ⊕'s quiet sibling. */
.card-action-text {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.7rem;
  border: 0;
  background: none;
  color: var(--pico-primary);
  font-size: 0.875rem;
}

.card-action-text:hover,
.card-action-text:focus-visible {
  background: var(--pico-secondary-focus);
  text-decoration: underline;
}

/* ── the TIMELINE card (NT-7, PR-225) ─────────────────────────────────────── */

/* LEGACY-2's own drawing, with its hard-coded blues replaced by tokens so both schemes follow.
   Capped and scrolling, because a long calendar should not push the cards under it off the
   dashboard; the cap is legacy's 320px in rem. */
.timeline {
  display: flex;
  flex-direction: column;
  max-height: 20rem;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
  scrollbar-width: thin;
}

.timeline-item {
  list-style: none;
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 0.75rem;
  align-items: center;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.timeline-item:last-child {
  border-bottom: none;
}

/* The date box: the weekday small and lettered, the day large, the month and year small under
   it. Three sizes in 3.5rem, which is what makes the day readable at a glance. */
.tl-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.25rem 0.35rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  background: var(--pico-card-sectioning-background-color);
  line-height: 1.15;
  text-align: center;
}

.tl-weekday {
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pico-primary);
}

.tl-day {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1;
  color: var(--pico-color);
}

.tl-month {
  font-size: 0.65625rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
}

.tl-event {
  font-size: 0.9375rem;
  font-weight: 600;
}

/* A note under an authored entry, on its own line rather than beside the title. */
.tl-event .aside {
  display: block;
  font-size: 0.8125rem;
  font-weight: 400;
}

/* What a CYCLE gives is read off the calendar and is not somebody's news, so it is quieter than
   what STAFF wrote. The weight difference is the only signal, and both are legible. */
.timeline-opens .tl-event,
.timeline-draft .tl-event,
.timeline-closes .tl-event {
  font-weight: 400;
}

/* ── the calendar strip (PR-286) ─────────────────────────────────────────────────────────── */

/* A bar of the CYCLE's two halves with today and the TIMELINE's dates on it (ui/strip.jsx). Every
   position is an SVG attribute; everything it looks like is here. */
.strip {
  margin: 0 0 var(--pico-block-spacing-vertical);
  padding: 0.6rem var(--pico-spacing) 0.25rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  background: var(--pico-card-sectioning-background-color);
}

.strip-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.125rem 1rem;
  margin: 0 0 0.25rem;
  font-size: 0.9375rem;
}

.strip-count {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.strip-drawing {
  display: block;
  overflow: visible;
}

.strip-collection {
  fill: color-mix(in srgb, var(--pico-primary) 16%, var(--pico-background-color));
}

.strip-hatch-ground {
  fill: color-mix(in srgb, var(--pico-primary) 14%, var(--pico-background-color));
}

.strip-hatch-line {
  fill: color-mix(in srgb, var(--pico-primary) 30%, var(--pico-background-color));
}

/* The next CYCLE, drawn between CYCLES: the same halves, paler. */
.strip-later {
  opacity: 0.55;
}

.strip-gap {
  fill: var(--pico-background-color);
  stroke: color-mix(in srgb, var(--pico-muted-color) 60%, transparent);
  stroke-dasharray: 4 3;
}

.strip-done {
  fill: color-mix(in srgb, var(--pico-primary) 30%, transparent);
}

.strip-join {
  stroke: var(--pico-background-color);
  stroke-width: 2;
}

.strip-half-word {
  fill: var(--pico-color);
  font-size: 0.75rem;
  font-weight: 600;
}

.strip-today {
  stroke: var(--pico-primary);
  stroke-width: 3;
  stroke-linecap: round;
}

.strip-today-word {
  fill: var(--pico-primary);
  font-size: 0.75rem;
  font-weight: 600;
}

.strip-tick {
  fill: var(--pico-muted-color);
  font-size: 0.75rem;
}

/* A date's mark, on the strip and first on its row: the CYCLE's own in the primary colour, an entry
   STAFF wrote in the text colour. */
/* A nested <svg> is clipped to its own box by the browser's own stylesheet, whatever the element's
   overflow attribute says — which cut the flag in half at the end of the bar, the mark being drawn
   around its date rather than after it. */
.glyph {
  display: block;
  overflow: visible;
  color: var(--pico-primary);
}

.glyph-entry {
  color: var(--pico-color);
}

/* The last seven days, in the danger colour (PR-286). */
.strip-soon .strip-count {
  color: var(--pico-del-color);
}

.strip-soon .strip-today {
  stroke: var(--pico-del-color);
}

.strip-soon .strip-today-word {
  fill: var(--pico-del-color);
}

.strip-soon .glyph-closes {
  color: var(--pico-del-color);
}

/* ── TIMELINE rows with their marks (PR-286, NT-20) ───────────────────────────────────────── */

.timeline-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.timeline-row {
  list-style: none;
  display: grid;
  grid-template-columns: 14px 3.5rem minmax(0, 1fr);
  gap: 0.75rem;
  align-items: center;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.timeline-row:last-child {
  border-bottom: none;
}

/* "Only" and the PROGRAMS an entry is limited to, for a reader who sees every entry (NT-20). */
.tl-reach {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.4rem;
  margin-top: 0.2rem;
  font-weight: 400;
}

.tl-reach > span:first-child {
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
}

/* ── who to ask (O-28) ────────────────────────────────────────────────────────────────────── */

/* The name on the left with its telephone number under it, the role on the right: a table, because
   that is what two columns of people are, and flush with the card's own edges rather than indented
   inside it (CIYJ, 2026-09-17). It is the card's own text and reads at the card's size, where a
   table elsewhere on a page is a size smaller. The roles set the second column's width, so a name
   too long for what is left is cut off with an ellipsis and carries its full self as a hover. */
.contacts {
  width: 100%;
  font-size: 0.9375rem;
}

/* The role's column shrinks to its words (the rule above), and the name's takes what is left: a
   `max-width` of nought is what stops a long name widening the column instead of being cut off. */
.contacts td:first-child {
  max-width: 0;
  padding-left: 0;
}

.contacts td:first-child a {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.contacts td:last-child {
  padding-right: 0;
}

.contacts-role {
  text-align: right;
  white-space: nowrap;
  color: var(--pico-muted-color);
}

.contacts-phone {
  display: block;
  margin: 0;
  font-size: 0.78125rem;
  color: var(--pico-muted-color);
}

/* Who supports the SITE from outside — its COACHES and CIYJ's help desk — under a rule, because
   they are asked different things from the SITE's own people (CIYJ, 2026-09-17). */
.contacts-support td {
  border-top: 1px solid var(--pico-muted-border-color);
}

/* ── a card's lines: one thing each, what it is and what it comes to ──────────────────────── */

/* Two columns of two words want no heading over them, so a card's short list is lines rather than
   a table: the thing on the left, its number or its state on the right (CIYJ, 2026-09-17). */
.lines {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* One line, and it stays one: with the line allowed to wrap, a long name pushed what it comes to
   onto a second line instead of giving way to it (CIYJ, 2026-09-17). */
.lines li {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.2rem 0.75rem;
  margin: 0;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
  list-style: none;
}

/* What the line comes to stays at its right end, and the name gives way to it: a name too long for
   the card is cut off with an ellipsis rather than wrapping the line in two (CIYJ, 2026-09-17). */
.lines li > :last-child {
  flex: none;
  margin-left: auto;
}

.line-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lines li:first-child {
  padding-top: 0;
}

.lines li:last-child {
  padding-bottom: 0;
  border-bottom: none;
}

/* ── the needs-attention row (PR-287) ─────────────────────────────────────────────────────── */

.attention {
  margin: 0 0 var(--pico-block-spacing-vertical);
}

.attention-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.25rem 1rem;
  margin: 0 0 0.5rem;
}

.attention-head h2 {
  margin: 0;
  font-size: 1rem;
}

.attention-items {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.attention-items li {
  margin: 0;
  list-style: none;
}

/* A count and its name, with the edge and the icon in the VIEW's own colour (CIYJ, 2026-09-18).
   They wore a tone each — warning, danger, info — and the dark orange and the dark red were near
   enough to be one colour in the light scheme, while the icon in a third colour read as unrelated
   to the same icon in the table. One colour, and the words carry the meaning, which is what PR-168
   asks of a colour anyway. */
.attention-item {
  --pbs-attention: var(--pico-primary);
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.35rem 0.75rem 0.35rem 0.6rem;
  border: 1px solid var(--pico-muted-border-color);
  border-left: 4px solid var(--pbs-attention);
  border-radius: var(--pico-border-radius);
  background: var(--pico-background-color);
  /* The page's own ink and never a link's: what the chip says is a fact, and the border in its own
     tone is what marks it as something to act on (CIYJ, 2026-09-18). */
  color: var(--pbs-ink);
  font-size: 0.9375rem;
  text-decoration: none;
}

.attention-item b {
  color: var(--pico-contrast);
  font-size: 1.0625rem;
  font-variant-numeric: tabular-nums;
}

/* The same mark this chip, a SITE's row and the MODAL all wear (CIYJ, 2026-09-18), so a reader who
   has met it in one place knows it in the others. In the chip's own tone, which is the one thing
   here that is not the page's ink. */
.attention-item .icon {
  width: 1rem;
  height: 1rem;
  margin-right: -0.15rem;
  color: var(--pbs-attention);
  vertical-align: -0.2em;
}




.attention-item:hover,
.attention-item:focus-visible {
  border-color: var(--pbs-attention);
  color: var(--pbs-ink);
  text-decoration: none;
}

.attention-item[aria-current="true"] {
  border-color: var(--pbs-attention);
  background: color-mix(in srgb, var(--pbs-attention) 13%, var(--pico-background-color));
  font-weight: 600;
}

/* ── the filters over a table (PR-287, PR-291) ────────────────────────────────────────────── */

/* A list's filters read across above its table, so the table keeps the whole width under them. Each
   control is a label over its input, and they wrap on a narrow screen rather than shrinking. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.5rem 0.75rem;
  margin: 0 0 var(--pico-block-spacing-vertical);
}

/* Each control is as wide as it needs to be and no wider. Pico gives a label inside a form
   `flex: 1 1 auto`, so on a row of two the search box came out 550px and on a row of three 385px —
   a control that changes size with what is beside it (measured 2026-09-17). */
.filter-bar label {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 0.15rem;
  margin: 0;
  max-width: 100%;
  font-size: 0.8125rem;
  color: var(--pico-muted-color);
}

/* A box or a menu, sized to itself. Never a switch: that is a fixed shape of Pico's own, and
   `width: auto` on one stretched it across the row (LOURA, 2026-09-21). */
.filter-bar input:not([role="switch"]),
.filter-bar select {
  margin: 0;
  width: auto;
  min-width: 9rem;
}

.filter-bar input[type="search"] {
  min-width: 14rem;
}

/* A switch is a row wherever it sits: the control beside its word, not under it. The rule above
   puts every label in the bar into a column — which is right for a label over a box and wrong for
   a toggle — and `min-width` there would otherwise give the checkbox nine rems of its own. The
   padding stands it on the same line as the text inside the boxes beside it, the bar aligning its
   controls by their bottoms. */
.filter-bar label.switch {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  padding-bottom: 0.4rem;
}

.filter-bar-end {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.filter-bar-end button {
  margin: 0;
  width: auto;
}

/* A link dressed as a button standing beside Search — a SURVEY's Download — is the same size as
   the button it stands next to. `.modal-link` is deliberately small, being worn on a line in a
   list; here it is one of two controls at the end of a row and a smaller pill beside a button
   reads as a mistake. Pico's own form spacing, so the two agree whatever the theme does. */
.filter-bar-end .modal-link {
  padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
  font-size: inherit;
}

/* Nothing a focused control does here draws a new shape (CIYJ, 2026-09-18, twice). Pico rings one
   with a wide halo of the primary colour, and does it on `:focus` rather than `:focus-visible`, so
   the button kept a blue glow long after it was clicked; an outline of our own in its place was the
   same complaint again. So: no halo, and the indicator is the border the control already has, gone
   darker on `:focus-visible` alone. A mouse click on a button matches `:focus` and not
   `:focus-visible`, so pressing Search leaves no mark at all, and a USER moving through the row on
   the keyboard can still see where they are, which WCAG 2.4.7 asks for and the accessibility crawl
   cannot check. */
.filter-bar :is(input, select, button):focus {
  box-shadow: none;
}

.filter-bar :is(input, select):focus {
  border-color: var(--pico-form-element-border-color);
}

.filter-bar :is(input, select):focus-visible {
  border-color: var(--pico-contrast);
}

.filter-bar button:focus-visible {
  outline: 2px solid var(--pico-contrast);
  outline-offset: 2px;
}

/* ── a table of CIYJ USERS ────────────────────────────────────────────────────────────────── */

/* The same treatment the SITES table gets below, and for the same two reasons: the shares are
   declared here rather than worked out from the words in the cells, and nothing is cut off until a
   column has been told how wide it is. `width` on a <col> is deprecated markup and a style
   attribute is N-25's, so the shares are classes.

   Two shapes: COACHES carry a Reaches column and the other two kinds do not, so the room it takes
   goes back to the three columns that hold long words. */
.user-table .col-name {
  width: 26%;
}
.user-table .col-email {
  width: 32%;
}
.user-table .col-phone {
  width: 16%;
}
.user-table .col-account {
  width: 18%;
}
.user-table .col-change {
  width: 8%;
}

.user-table-reach .col-name {
  width: 20%;
}
.user-table-reach .col-email {
  width: 24%;
}
.user-table-reach .col-phone {
  width: 13%;
}
.user-table-reach .col-reach {
  width: 24%;
}
.user-table-reach .col-account {
  width: 13%;
}
.user-table-reach .col-change {
  width: 6%;
}

/* The floor is what stops a column being squeezed to nothing (LOURA, 2026-09-21, twice). Set too
   high the table refuses to narrow, so the long addresses never reach their ellipsis and the box
   scrolls under them; set too low there is never anything to scroll and a narrow window crushes
   six columns into slivers. So: wide enough that every column is still readable, and the scroller
   takes over below it. Above it the columns give way and the long ones cut off, each carrying
   itself as a hover. */
.user-table {
  width: 100%;
  min-width: 48rem;
  table-layout: fixed;
}

.user-table-reach {
  min-width: 56rem;
}

/* Nothing spills into the next column. The three that hold long words clip on the span inside
   them; this is the cell itself, for the ones that carry bare text. */
.user-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A name, an address and the agencies a COACH reaches are cut off rather than wrapped, each
   carrying itself in full as a hover. The Account column holds badges and the last holds one word,
   so neither wants cutting. */
.user-table :is(.user-name, .user-email, .user-reach) .clipped,
.user-table .user-email > a {
  max-width: 100%;
}

.user-table .user-phone {
  white-space: nowrap;
}

/* A line of explanation under a card's heading, before whatever acts on it. Its own class rather
   than a bare `.aside`, because what this wants is the space under it that a note written beside
   something never does — left to `.aside` the next control sits against its last line (LOURA,
   2026-09-21). */
.card-note {
  display: block;
  margin-bottom: var(--pico-spacing);
  color: var(--pico-muted-color);
}

/* A note whose parts each take their own line: a row of badges over the sentence beneath it
   (LOURA, 2026-09-22). Written inline, the Critical pill sat against the first word of the
   description and read as part of it. `flex-start` so the badge row is as wide as its badges. */
.card-note-lines {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.35rem;
}

/* A second line under a note: what a card settled without being asked, where the line above it
   says what is left to do (LOURA, 2026-09-22). Its own line because it is a different kind of
   sentence, and reads as an aside rather than as more work. */
.card-note-more {
  display: block;
  margin-top: 0.35rem;
}

/* ── STAFF's SURVEY VERSIONS (pages/versions.jsx) ─────────────────────────────────────────── */

/* What a SURVEY is, over its versions: where it goes (S-19) and when it is open (S-17), a line
   each. One `.card-note` around the pair, so the space under them is the note's and not doubled. */
.survey-facts {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

/* The PROGRAMS a SURVEY reaches. A row of pills that wraps, with air between them and with the
   words that introduce them: `.badges` sets no gap of its own, so two PROGRAM pills written one
   after the other touch. */
.distribution {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.5rem;
}

/* The latest thing said about a VERSION, one line, cut off with the whole of it as a hover. The cap
   is on a block inside the cell because `max-width` on a `td` in an auto-layout table is advisory,
   and without it one long remark takes the column the four short facts beside it need. */
.version-comment {
  display: block;
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The whole thread in its MODAL, and the form under it for the next remark. A `.site-line` wears a
   rule on top, so with nothing between them the first remark began hard against the dialog's own
   bar and the textarea hard against the last remark (LOURA, 2026-09-22). Each group stands off
   what is above it, and the button is a button rather than a bar across the dialog. */
.comment-thread,
.comment-form {
  margin-top: var(--pico-block-spacing-vertical);
}

.comment-form {
  margin-bottom: 0;
}

/* ── one person's MEMBERSHIPS, on their own record ────────────────────────────────────────── */

/* A row per SITE: where, whose, what they may do there, and the way out. The two long columns cut
   off and carry themselves as a hover, as every other table here does; the two that hold controls
   take what the controls need and no more. */
.member-table .col-site {
  width: 34%;
}
.member-table .col-customer {
  width: 26%;
}
.member-table .col-access {
  width: 40%;
}

/* The level and the two acts on one line, side by side. Two forms, because they are two acts, but
   one group of controls: given a column each, the slack in the wider one fell between them. */
.member-acts {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
}

.member-table {
  width: 100%;
  min-width: 34rem;
  table-layout: fixed;
}

.member-table td {
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}

/* The controls in a row sit on one line. `.inline-fields` gives a select `flex: 0 1 15rem`, which
   is right in a builder's column and too wide for a cell: the select took the column and the
   button beside it wrapped under (LOURA, 2026-09-21). Here it gives way instead, and the button
   never does. */
.member-table .inline-fields {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

.member-table .inline-fields select {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  margin: 0;
}

/* Smaller than a button standing on its own, as a card header's search box already is: a row of
   full-sized controls is a table of buttons with some text beside them. */
.member-table button {
  flex: none;
  width: auto;
  margin: 0;
  padding: 0.25rem 0.75rem;
  font-size: 0.875rem;
  white-space: nowrap;
}

.member-table .inline-fields select {
  padding: 0.25rem 1.75rem 0.25rem 0.5rem;
  font-size: 0.875rem;
  height: auto;
  background-position: center right 0.5rem;
}

.member-table form {
  margin: 0;
  max-width: none;
}

/* ── a table of CUSTOMER USERS (A-49) ─────────────────────────────────────────────────────── */

/* Seven columns, and four of them hold something that can run long: a name, an address, and the
   CUSTOMERS and SITES somebody is a MEMBER of. Same treatment as the two tables above — declared
   shares and a fixed layout, so a column is the share it declares whatever is in it and the long
   ones cut off instead of pushing the table out through the scroller. */
.people-table .col-name {
  width: 15%;
}
.people-table .col-email {
  width: 20%;
}
.people-table .col-phone {
  width: 11%;
}
.people-table .col-customer {
  width: 18%;
}
.people-table .col-site {
  width: 18%;
}
.people-table .col-level {
  width: 9%;
}
.people-table .col-account {
  width: 9%;
}

.people-table {
  width: 100%;
  min-width: 64rem;
  table-layout: fixed;
}

.people-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}

.people-table .person-phone {
  white-space: nowrap;
}

/* ── a table of SITES (PR-287) ────────────────────────────────────────────────────────────── */

/* The shares each column takes. Five without STAFF's Coach column and six with it; they are here
   rather than on the <col> because `width` there is deprecated markup and a style attribute is
   N-25's (CIYJ, 2026-09-18). */
.site-table .col-site {
  width: 32%;
}
.site-table .col-program {
  width: 14%;
}
.site-table .col-jurisdiction {
  width: 26%;
}
.site-table .col-flags {
  width: 16%;
}
.site-table .col-entry {
  width: 12%;
}

.site-table-coaches .col-site {
  width: 24%;
}
.site-table-coaches .col-program {
  width: 12%;
}
.site-table-coaches .col-jurisdiction {
  width: 20%;
}
.site-table-coaches .col-coach {
  width: 18%;
}
.site-table-coaches .col-flags {
  width: 14%;
}

/* The columns are the shares the table declares (ui/site-table.jsx) rather than what the browser
   works out from the words in them: a table sized from its content pushed its width up through the
   scrolling box and scrolled the whole page sideways (measured 2026-09-18). Below 48rem it is the
   scroller's business, which is what that box is for. */
.site-table {
  width: 100%;
  min-width: 48rem;
  table-layout: fixed;
}

/* A SITE's name and its JURISDICTION are cut off rather than wrapped, each carrying itself in full
   as a hover (CIYJ, 2026-09-18), which is what a card's lines already do (PR-292). A cell will not
   shrink below its content without a max-width, so the two that hold long names name one; the
   others size themselves. */
.site-cell {
  display: flex;
  flex-wrap: nowrap;
  align-items: baseline;
  gap: 0.15rem 0.5rem;
  min-width: 0;
}

.site-cell > a {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Every cut-off name is cut off by the thing inside the cell and never by the cell (CIYJ,
   2026-09-18). `overflow` clips at the padding box, so a cell that clips its own text paints it
   right through the padding and up against the next column; an inner box sits in the content box
   and the padding stays where it is. With the columns fixed, 100% is the column, so nothing has to
   guess a width either. */
.site-name .site-cell > a,
.site-jurisdiction .clipped,
.site-coaches .clipped,
.site-program .program {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-entry {
  white-space: nowrap;
}

/* G-6's longest, "Community Supervision", was costing every row the width of its longest name, so
   the PROGRAM is cut off like the two names beside it and carries itself in full as a hover
   (CIYJ, 2026-09-18). The badge is what clips, so its own shape is kept.

   The width is the badge's own and never the cell's. `max-width` on a table cell is a hint an auto
   table layout may ignore, and does ignore the moment the page has width to spare — which is why
   the COACH's list stopped clipping the day it went full width and STAFF's, packed into a narrower
   table, never did (CIYJ, 2026-09-18). On the badge it is an ordinary inline-block and always
   holds. */
/* The PROGRAM as ordinary text in the row, where a reader is scanning a hundred of them: the pills
   were chrome and six colours down a column were a distraction from the names (CIYJ, 2026-09-18).
   No border, no weight and no colour of its own — it reads as the cell beside it. The colour still
   carries a PROGRAM everywhere it is a badge, which is every other screen.

   Written long enough to outrank the colours themselves. Each PROGRAM's own is set once for the
   light scheme at one class, and twice more for the dark one behind `:root:not([data-theme=light])`
   and `:root[data-theme=dark]` — three classes each — so a single class here won in the light scheme
   by coming later in the file and lost in the dark one (CIYJ, 2026-09-18). This outweighs all three,
   so which scheme a reader is in stops mattering. */
.site-table td.site-program .program-plain {
  padding: 0;
  border: 0;
  color: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
}

.site-program .program {
  display: inline-block;
  vertical-align: bottom;
}

/* An ASSIGNMENT at the CUSTOMER reaches every SITE under it (O-16), so this can be eight names and
   ran to three lines. The count leads and the names follow it, cut off, with the whole list as the
   hover (CIYJ, 2026-09-18). */


/* A STATE and the control beside it are two things, and they stay on one line (CIYJ, 2026-09-18):
   the control is icons rather than words now, so there is room for both. */
.site-flags {
  white-space: nowrap;
}

.site-flags > * + * {
  margin-left: 0.35rem;
}

/* One icon per mark, at the size of the words they sit among rather than the 18px an icon is
   elsewhere: four of them have to fit a cell beside a badge. */
.site-flags .modal-link .icon {
  width: 1rem;
  height: 1rem;
  vertical-align: -0.2em;
}

.site-flags .modal-link .icon + .icon {
  margin-left: 0.3rem;
}

/* The same icon again in the MODAL, beside the line it belongs to, which is what ties the two
   together (PR-294). */
.marks-list .icon {
  width: 1rem;
  height: 1rem;
  margin-right: 0.4rem;
  vertical-align: -0.2em;
}

/* The mark in front of Entry status: what the SITE owes this collection, where the reader would go
   to look into it (CIYJ, 2026-09-18). It stands outside the control and takes the warning colour,
   which the control does not, so the row reads as something to open with a mark beside it rather
   than as a warning that can be pressed. */
.entry-warning {
  display: inline-flex;
  vertical-align: -0.3em;
  margin-right: 0.35rem;
  color: var(--pbs-warning-color);
}

.site-jurisdiction {
  font-size: 0.875rem;
  color: var(--pico-muted-color);
}

.clipped {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.aside-quiet {
  color: var(--pico-muted-color);
}

/* ── bars and columns (PR-287, PR-288) ────────────────────────────────────────────────────── */

.bar-list {
  display: grid;
  gap: 0.45rem;
  margin: 0.25rem 0;
  padding: 0;
  list-style: none;
}

/* The name, the bar and the number on one line (CIYJ, 2026-09-17). A name too long for the column
   is cut off with an ellipsis and the row carries it in full as a hover. */
.bar-list li {
  display: grid;
  grid-template-columns: minmax(4.5rem, 9.5rem) minmax(0, 1fr) 2.25rem;
  align-items: center;
  gap: 0.6rem;
  margin: 0;
  list-style: none;
}

.bar-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bar-label .program {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
}

.bar-track {
  display: block;
}

.bar {
  fill: var(--pico-primary);
}

.bar-value {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.column-chart {
  margin: 0.25rem 0;
}

.cc-drawing {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
}

.cc-axis,
.cc-plot {
  display: block;
  overflow: visible;
}

.cc-axis text,
.cc-tick {
  fill: var(--pico-muted-color);
  font-size: 0.6875rem;
}

.cc-grid {
  stroke: var(--pico-muted-border-color);
  stroke-width: 1;
}

.cc-bar {
  fill: color-mix(in srgb, var(--pico-primary) 55%, var(--pico-card-sectioning-background-color));
}

/* The day still running. */
.cc-bar-last {
  fill: var(--pico-primary);
}

.cc-part {
  fill: var(--pico-del-color);
}

.cc-value {
  fill: var(--pico-color);
  font-size: 0.6875rem;
  font-weight: 600;
}

.cc-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.75rem;
  color: var(--pico-muted-color);
}

.cc-key li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
  list-style: none;
}

.cc-key li::before {
  content: "";
  width: 0.8rem;
  height: 0.8rem;
  border-radius: 2px;
}

.cc-key-value::before {
  background: color-mix(in srgb, var(--pico-primary) 55%, var(--pico-card-sectioning-background-color));
}

.cc-key-part::before {
  background: var(--pico-del-color);
}

/* ── the disclosure marker (CIYJ, 2026-09-10) ─────────────────────────────── */

/* Pico floats its chevron to the right end of the summary, which on a wide row leaves it inches
   from the title it belongs to and reads as an unrelated control at the far edge. It goes before
   what it opens instead, on every screen: the messages list, the JURISDICTION folds, a person's
   form and the card that adds one.
 *
 * A dropdown keeps Pico's own, which both of ours draw as nothing (.bell-menu, .user-menu), so it
 * is excluded rather than fought with. */
details:not(.dropdown) > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

details:not(.dropdown) > summary::after {
  order: -1;
  float: none;
  margin-inline-start: 0;
}

/* A modal trigger that is the name at the head of a row rather than a control at the end of a
   line: a link, with none of the button dressing (CIYJ, 2026-09-10). */
.modal-link-plain {
  padding: 0;
  border: 0;
  background: none;
  color: var(--pico-primary);
  font-weight: 600;
  text-decoration: underline;
}

.modal-link-plain:hover,
.modal-link-plain:focus-visible {
  background: none;
  text-decoration: none;
}

/* The one act a whole page is for, beside its breadcrumb (CIYJ, 2026-09-18): filled rather than
   outlined, because a page has one of these and it is what somebody came to press. */
.modal-link-primary {
  border-color: var(--pico-primary);
  background: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
  font-weight: 600;
}

.modal-link-primary:hover,
.modal-link-primary:focus-visible {
  border-color: var(--pico-primary-hover-border);
  background: var(--pico-primary-hover-background);
  color: var(--pico-primary-inverse);
}

/* A trigger whose contents already say what they are: the marks on a SITE's row (PR-287). It wears
   no colour and no underline of its own — the badges inside carry their own tones, and a link's
   blue over them would read as a third one. The pointer says it can be opened, and hovering lifts
   what is inside. */
.modal-link-bare {
  display: inline-block;
  color: inherit;
  text-decoration: none;
}

.modal-link-bare:hover {
  outline: 1px solid var(--pico-muted-border-color);
  outline-offset: 2px;
  border-radius: var(--pico-border-radius);
}

.modal-link-bare:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

/* Among the acts at the end of a line, or on a line of acts under a fold, the same dressings are
   one word among others — "Comments" beside "Edit", "New version" under a list of versions — and
   read at the weight of the links beside them; the weight above is for a name heading a row
   (CIYJ, 2026-09-15). */
.site-line-end .modal-link-plain,
.site-line-whole .modal-link-plain,
.site-line-end button.link-act,
.site-line-whole button.link-act {
  font-weight: inherit;
}

/* An act in a table cell that happens to write, beside links that do not: the same word at the
   same weight, because which of them posts is not something a reader is choosing between (LOURA,
   2026-09-22). `button.link-act` carries the weight for the place it was written for, a line of
   acts under a fold, where it is the heading of the row it sits on. */
td button.link-act {
  font-weight: inherit;
}

/* **A button in a table cell is the size a `.modal-link` is** (LOURA, 2026-09-22). The two stand in
   the same column and often on the same row — one posts, one opens a panel — and Pico's form
   element spacing made the one that posts half again as tall, which read as the more important of
   two acts that are the same size of thing. A button dressed as a link keeps its own size: those
   rules carry a class and beat this one.

   **Never a button inside a MODAL** (LOURA, 2026-09-22). A row's edit panel is written in the cell
   the row's name is in, and stays there in the DOM when the browser lifts it into the top layer —
   so this rule reached the Save at the foot of that form and shrank it, while the same form opened
   from the corner pill kept its size. The exclusion is what keeps a dialog's buttons the form's
   own, wherever the dialog happens to be written. */
td button:not(dialog button) {
  width: auto;
  padding: 0 0.75rem;
  font-size: 0.875rem;
  line-height: 1.5;
}

/* Pico gives a submit and a field a bottom margin, for the space between the rows of a form. In a
   table cell that margin is a line and a half of row height on every row that has an act in it
   (LOURA, 2026-09-24). The row is the layout here; the control sits in it. */
td :is(button, input, select):not(dialog *) {
  margin-bottom: 0;
}

/* The form behind a table's own add row: it holds the hidden fields, and the name and the act that
   join it by `form=` are cells in the table. It draws nothing and takes no room. */
.row-form {
  margin: 0;
}

/* A list of names in a cell: how many, then the names, cut off with all of them as a hover — the
   shape the Coach column has (ui/site-table.jsx). The count is the fact a reader wants and the
   names follow it. The cap is on the element rather than the cell, `max-width` on a `td` in an
   auto-layout table being advisory. */
.name-list {
  display: block;
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A cell whose content is one short fact and reads worse broken over two lines. */
.nowrap {
  white-space: nowrap;
}

/* A note under the name at the head of a row, rather than trailing after it. Two classes, because
   `td .aside` above turns one inline — which is right beside a name in a cell and wrong under one —
   and a single class loses to it whatever the order. */
.aside.row-note {
  display: block;
  margin-left: 0;
}

/* A list whose first column is a name that can run long. The cap is on a block inside the cell
   and not on the cell: `max-width` on a `td` in an auto-layout table is advisory, and a long
   title simply ran under the column beside it (measured 2026-09-10). */
.listing-name {
  display: block;
  max-width: 22rem;
  overflow-wrap: anywhere;
}

/* ── a group of checkboxes read across the line ───────────────────────────── */

/* The PROGRAMS a DISTRIBUTION is ticked into. Deliberately not `.chip-list`, which is a different
   thing — a short list of things that are each a word and a way to take it away, an EMAIL DOMAIN
   or a COACH ASSIGNMENT — and dressing one as the other put a border round every program (CIYJ,
   2026-09-10). Nothing here reaches `.chip-list`, so those lists are untouched. */
.check-inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A little air above and below, so that two boxes in a stack of them are far enough apart to be
   aimed at: an 18px box wants 24px between centres, and at this text size the lines were 23 apart
   (axe's target-size, found by the crawl on the GLOSSARY's edit form, 2026-09-17). */
/* Between two of them, not between a control and its own words: Pico already puts `.5em` after a
   checkbox, and the row gap was landing on top of it (CIYJ, 2026-09-19). */
label.check-inline {
  column-gap: 0;
}

label.check-inline {
  margin-block: 0.15rem;
}

.check-inline li {
  margin: 0;
  padding: 0;
  border: 0;
  list-style: none;
}

.check-inline .choice {
  margin-bottom: 0;
}

/* The PROGRAMS belong to the option above them, so they sit under it and indented from it. */
.audience-programs {
  margin-top: 0.35rem;
  margin-left: 1.75rem;
}

/* And they are that option's question, so the other option answers it by taking them off the form
   (LOURA, 2026-09-22). The same argument the RESOURCE kinds make: the server writes them either
   way, `:has()` hides them while Everyone is the answer, and a browser without it keeps the form
   it has always had — where the ticks are moot rather than wrong, the write ignoring them. */
.choice:has(.audience-pick-everyone:checked) ~ .audience-programs {
  display: none;
}

/* ── paging a list (NT-17) ─────────────────────────────────────────────────── */

/* Two links and where you are, under the list. The ends are drawn even where there is nothing
   beyond them, so the control does not change shape as you walk it. */
.pager {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--pico-muted-border-color);
  font-size: 0.875rem;
}

.pager-where {
  color: var(--pico-muted-color);
}

.pager-where .aside {
  display: inline;
}

.pager-off {
  color: var(--pico-muted-color);
}

/* ── a note under a field ──────────────────────────────────────────────────── */

/* Pico puts a full gap under every form control, so a note written after one lands as far from
   the field it explains as from the label below it — and reads as though it belonged to the next
   field (CIYJ, 2026-09-11). It is pulled back up against its own field, and carries the gap
   itself, so the rhythm of the form is unchanged and only the note moves. */
input + .aside,
select + .aside,
textarea + .aside,
/* And a note under a pair of fields, which follows the row rather than a control. */
.two-up + .aside {
  display: block;
  margin-top: calc(var(--pico-spacing) * -0.7);
  margin-bottom: var(--pico-spacing);
  margin-inline-start: 0;
}

/* ── Creator (pages/survey-creator.jsx) ─────────────────────────────────────────────────── */

/* Creator fills its container and wants one with a height: most of the viewport, and never so
   little that its toolbox and property grid fold into each other. */
.creator-mount {
  /* Two rems back of the fourteen, which is what the breadcrumb over it was taking before that
     page dropped its trail (LOURA, 2026-09-22). Conservative: too tall only scrolls the page. */
  height: calc(100vh - 12rem);
  min-height: 36rem;
  margin-bottom: var(--pico-spacing);
}

/* ── a QUESTION's KEY in a cell (pages/go-live.jsx) ──────────────────────────────────────── */

/* A generated KEY can be a sentence long (PR-8), and a cell cannot make it shorter, so it is
   free to break anywhere rather than run under the column beside it (PR-229). On its own line
   under the wording, which comes first because it is what a reader recognises (S-4). */
.key {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* ── what a QUESTION's answers were and are (pages/go-live.jsx) ─────────────────────────── */

/* Two labelled sets of tokens rather than one sentence of commas (LOURA, 2026-09-22): the reader
   is comparing two lists, and a run of them read as prose hid the one that had moved.
   One above the other (LOURA, 2026-09-22): side by side the two sets ran together at the seam and
   a long set pushed the second onto its own line anyway, so which row a token belonged to was the
   next thing to work out. Stacked, the answers line up under each other and the eye reads down. */
.choice-set {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem;
}

.choice-set + .choice-set {
  margin-top: 0.3rem;
}

.choice-set-label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
}

/* One answer. Named `.choice-token` and not `.choice`, which is a radio with a note under it and
   has been since long before this: the name collided, this rule came later in the file, and every
   checkbox on nine screens grew a border (LOURA, 2026-09-22). */
.choice-token {
  display: inline-block;
  padding: 0 0.35rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* The two that carry meaning. Each has a second channel besides colour — a line through it, or
   the weight and the edge — because colour is never the only carrier (WCAG 1.4.1), and each says
   which it is in a word to a screen reader, which has neither. */
.choice-token-gone {
  color: var(--pico-muted-color);
  text-decoration: line-through;
}

.choice-token-new {
  border-color: var(--pico-primary);
  color: var(--pico-primary);
  font-weight: 600;
}

/* A KEY beside the wording it belongs to, on one line, cut off with the whole of it as a hover
   (LOURA, 2026-09-22). PR-8 lets a generated one run a sentence long, and free to break it gave a
   table three and four lines a row and buried the wording a reader scans for. The cap is on the
   element and not the cell, `max-width` on a `td` in an auto-layout table being advisory. It is
   identity here, to be recognised rather than read; where a KEY is the content — an API account's,
   a KIOSK's setup code — it keeps `.key` alone and stays whole. */
.key-clipped {
  display: block;
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* An account's name is an address with no spaces in it, some of them sixty characters long
   ("circlevillejuvenilecorrectionalfacilitySureMDM1@dys.ohio.gov"), and a table cell sizes itself
   to the longest word it holds — so an unbreakable one pushed the date columns off the card and
   the table scrolled sideways. On a line of its own, with the SITE on the line under it
   (`.row-note`), and free to break anywhere: PR-229's lesson about a KEY in a cell again. */
.account-name {
  display: block;
  overflow-wrap: anywhere;
}

/* The same name as a card's heading (pages/site-keys.jsx), which a card header otherwise keeps to
   one line: there it would run out of the card rather than off a table. The bar wraps, and the
   name takes the whole first line, so the status badge sits under it rather than beside it —
   beside it, a wide badge ("Not connected in 6 months") squeezed a sixty-character name into a
   column a third of the card wide and four lines tall (CIYJ, 2026-09-14). */
.card > header.account-head {
  flex-wrap: wrap;
  row-gap: 0.4rem;
}

.card > header > h2.account-name {
  flex-basis: 100%;
  white-space: normal;
}

/* ── IMPROVEMENT PLANS across SITES (pages/plans-across-sites.jsx, IP-20) ─────────────────── */

/* Seven columns, three of which run long — the SITE, its CUSTOMERS and what the PLAN is aiming at.
   Declared shares and a fixed layout, as the tables above: a column is the share it declares
   whatever is in it, and the long ones cut off rather than pushing the table through the scroller.
   The number is the narrowest thing on the row and the way into the PLAN. */
.plans-table {
  width: 100%;
  /* 60rem and not 64: at a 1280px window the eighth column put the table twenty-three pixels over
     its own floor, which buys a scrollbar and nothing else. Measured 2026-09-26. */
  min-width: 60rem;
  table-layout: fixed;
}

.plans-table td {
  overflow: hidden;
}

.plans-table .col-number {
  width: 5%;
}
/* IP-23's mark, beside the number. Pixels rather than a share, being one icon wide whatever the
   table is: the three columns that cut off with a hover give it the room. */
.plans-table .col-flag {
  width: 2.2rem;
}
.plans-table .col-site {
  width: 19%;
}
.plans-table .col-customer {
  width: 15%;
}
.plans-table .col-status {
  width: 10%;
}
.plans-table .col-focus {
  width: 19%;
}
.plans-table .col-when {
  width: 10%;
}
.plans-table .col-who {
  width: 10%;
}

/* The two CIYJ acts, side by side, and the NOTE count beside the second.
   **Nine per cent because the content is pixels and the column is a share**: the pair measure
   80px with their padding and never anything else, so the share has to clear that at the table's
   narrowest — 60rem — where six per cent left 60px and clipped the count (LOURA, 2026-09-26).
   The two columns that give it up are the ones that were already cutting off with a hover. */
.plans-table .col-acts {
  width: 9%;
}

/* Which leaves the cell wider than its content on a big screen, so the pair sit against the
   table's edge rather than floating in the middle of their own column. */
.plans-table .plan-acts .row-icons {
  justify-content: flex-end;
}

/* IP-23: the row's own copy of what the attention row counts, in the tone that row gives it. It
   is a mark and never a control — the way to narrow to these is the row above the filters. */
.plan-flag {
  text-align: center;
  padding-inline: 0;
}

.plan-quiet {
  display: inline-flex;
  /* The chip's own tone, so the mark a reader picked in the attention row is the mark they find
     down the rows it left — which is the rule a SITE's row already follows. */
  color: var(--pbs-attention);
}

.plan-quiet .icon {
  width: 18px;
  height: 18px;
}

/* IP-19's mark. A form, because it writes, so it needs the margin a form brings taken off it
   before it will sit on a row beside a link. */
.mark {
  margin: 0;
  display: inline-flex;
}

/* Marked, and readable as marked from across the table: the star fills, and the button takes the
   accent rather than the muted grey the unmarked one shares with the rest of the row's icons. */
.icon-button-on {
  color: var(--pico-primary);
}

/* How many NOTES are on a PLAN, riding on the panel's own icon rather than taking a column of its
   own: a count nobody sorts by and most rows do not have. */
.note-count {
  font-size: 0.7rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  margin-left: 0.1rem;
  vertical-align: super;
}

/* IP-21's two tabs. Not Pico's `role="tablist"`, which is for panels swapped in place: these are
   links to two addresses that answer differently, and the browser's back button should work
   between them. Drawn as the filter row's own furniture, directly above it.

   **`justify-content` is spelled out because Pico claims `nav` by tag** and gives it
   `space-between`, which threw Library against the right-hand edge of the page with a thousand
   pixels of nothing between the two (LOURA, 2026-09-26). The third time this has been the answer;
   a class on a tag Pico dresses inherits whatever Pico decided that tag was for. */
.list-tabs {
  display: flex;
  justify-content: flex-start;
  gap: 1.25rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.list-tab {
  padding: 0.35rem 0;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--pico-secondary);
  text-decoration: none;
  font-weight: 500;
}

.list-tab:hover,
.list-tab:focus-visible {
  color: var(--pico-primary);
}

.list-tab-on {
  color: var(--pico-contrast);
  border-bottom-color: var(--pico-primary);
}

/* The CIYJ panel: a verdict, then the thread. The lead says who never sees it, which is the one
   thing about this panel a reader has to hold in their head while they type. */
.panel-lead {
  color: var(--pico-muted-color);
  font-size: 0.85rem;
  margin-bottom: var(--pico-spacing);
}

.notes {
  margin: 0 0 var(--pico-spacing);
  padding: 0;
  list-style: none;
}

.notes li + li {
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--pico-muted-border-color);
}

.note-who {
  margin: 0 0 0.15rem;
  font-size: 0.8rem;
  color: var(--pico-muted-color);
}

.note-body {
  margin: 0;
  white-space: pre-wrap;
}

/* A date in those tables, "Sep 10th 2026", kept on one line: the name beside it is what breaks. */
.when {
  white-space: nowrap;
}

/* ── an account's keys (pages/api-accounts.jsx, pages/kiosks.jsx) ──────────────────────────── */

/* Read, never typed, so ruled off below the form rather than drawn like one more field: a label
   and its value on a line, the labels in one column. The setup code's button sits under its code,
   the one act the section has. */
.keys {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.5rem 1rem;
  align-items: baseline;
  margin: var(--pico-spacing) 0 0;
  padding-top: var(--pico-spacing);
  border-top: 1px solid var(--pico-muted-border-color);
}

.keys dt {
  margin: 0;
  font-weight: normal;
  color: var(--pico-muted-color);
}

.keys dd {
  margin: 0;
}

.keys .badge {
  margin-inline-start: 0.5rem;
}

.keys form {
  margin: 0.5rem 0 0;
}

.keys form button {
  width: auto;
  margin-bottom: 0.25rem;
}

/* ── the MEASURE builder (pages/measures.jsx) ────────────────────────────────────────────── */

/* A side inside a side — arithmetic's two halves — is set in from its parent and ruled on the
   left, so the nesting reads without a box in a box. A filter group is the same shape. */
.side-editor .side-editor,
.filter-group .filter-group,
.filter-list .filter-group {
  margin-inline-start: 1rem;
  padding-inline-start: 0.75rem;
  border-inline-start: 2px solid var(--pico-muted-border-color);
}

/* A section's heading inside a side: the card's h2 is above it, so it is an h3 — axe's
   heading-order caught an h4 here on the builder's final crawl — dressed small. */
.side-editor h3 {
  margin-top: var(--pico-spacing);
  margin-bottom: calc(var(--pico-spacing) * 0.75);
  font-size: 0.875rem;
}

/* A row of controls that reads as one sentence: question, comparison, value, save. Every
   control is one fixed width (CIYJ, 2026-09-12), so a select never grows to its longest KEY —
   a generated one can be a sentence long (PR-8) — and the button after it stays on the line;
   narrow enough that "Take the [sum] of [a question's number]" and its button share a row in
   the right-hand column. `min-width: 0` is what lets a select be narrower than its longest
   option. Rows in a side stand a full space apart, so a pick, its wording and the next pick
   read as separate things. */
.inline-fields {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: calc(var(--pico-spacing) * 0.5);
}

.side-editor .inline-fields {
  margin-bottom: var(--pico-spacing);
}

.inline-fields select,
.inline-fields input {
  width: 15rem;
  max-width: 100%;
  min-width: 0;
  flex: 0 1 15rem;
  margin-bottom: 0;
}

.inline-fields button {
  width: auto;
  margin-bottom: 0;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.filter-row form:last-child {
  margin-bottom: calc(var(--pico-spacing) * 0.5);
}

.filter-row form:last-child button {
  width: auto;
  margin-bottom: 0;
}

/* A fold whose summary carries two lines: the name with its badges, and a line under it — the
   MEASURE's description (CIYJ, 2026-09-11). The count stays on the summary's own line. */
.fold-title {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  flex: 1 1 auto;
  min-width: 0;
}

/* An act that reads as a link: the word alone, underlined, no pill. A button all the same,
   because it posts — the same dress .modal-link-plain gives a link that opens a panel. */
button.link-act {
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--pico-primary);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
}

button.link-act:hover,
button.link-act:focus-visible {
  background: none;
  text-decoration: none;
}

/* One pick: a select and its Change button, and under them the wording of the QUESTION chosen
   (CIYJ, 2026-09-11) — the KEY fits a control, the wording does not. */
.pick {
  margin-bottom: var(--pico-spacing);
}

.pick .inline-fields {
  margin-bottom: 0;
}

.pick .aside,
.filter-steps .aside {
  display: block;
  margin-inline-start: 0;
  margin-top: 0.35rem;
}

/* The kind's own controls sit under a rule, apart from the picker that chose the kind, and
   each section of them — what is read, the rows counted, the responses included — under a
   rule of its own (CIYJ, 2026-09-12). */
.side-editor > .side-body,
.side-part + .side-part {
  margin-top: calc(var(--pico-spacing) * 0.75);
  padding-top: calc(var(--pico-spacing) * 0.75);
  border-top: 1px solid var(--pico-muted-border-color);
}

.side-part > h3:first-child {
  margin-top: 0;
}

/* A comparison in steps — the question, then the comparison and its value — stacked, with the
   Remove beside the stack rather than on its last line. */
.filter-row {
  align-items: flex-start;
}

.filter-steps {
  flex: 1 1 20rem;
  min-width: 0;
}

/* ── the GLOSSARY (GL-4, GL-5, PR-268) ─────────────────────────────────────────────────── */
/*
 * One TERM a line: its name with STAFF's Edit at the far end of the same row, then its definition.
 * A mark jumps down the page to a TERM, so a TERM leaves room above itself for where it lands.
 */
.term {
  margin-bottom: 1.5rem;
  scroll-margin-top: 1rem;
}

.term-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.term-head h3 {
  margin-bottom: 0.5rem;
}

.term-earlier {
  border-top: 1px solid var(--pico-muted-border-color);
  padding-top: 0.5rem;
}

/* A survey page's TERM, opened over the SURVEY: as wide as a reader's line, no wider. */
.term-dialogs dialog > article {
  max-width: 42rem;
}

/* ── RESOURCES (RS-1, RS-2, RS-7, PR-270) ──────────────────────────────────────────────── */
/*
 * The library folds by SECTION, a SECTION inside another indented under it. A RESOURCE is one line:
 * its title as a link, what kind of thing it is, and its description under both, with STAFF's Edit
 * at the far end of the line where the screen is STAFF's.
 */
.resource-section .resource-section {
  margin-left: 1.25rem;
}

.resource-section-body {
  color: var(--pico-muted-color);
  font-size: 0.9375rem;
}

.resource-section-acts {
  margin: 0 0 0.25rem;
}

.resource-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.35rem 1rem;
  padding: 0.4rem 0 0.4rem 1.25rem;
}

/* What kind of thing it is, as a shape rather than a word (CIYJ, 2026-09-16). The word is still
   in the line for a screen reader, and the shape carries no meaning a colour has to hold. */
.resource-kind {
  flex: 0 0 auto;
  align-self: center;
  color: var(--pico-muted-color);
  line-height: 0;
}

.resource-line-name {
  flex: 1 1 auto;
}

/* Who a RESOURCE reaches, on its own row under the title, since a line of them wraps badly beside
   one. Off the print sheet: it is STAFF's working detail, not part of the library. */
.resource-tags {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.15rem;
}

/* A search result, which has more to say than a line in one SECTION's list: what the thing is,
   where in the library it lives, what it is about, who it reaches, and the two acts (CIYJ,
   2026-09-19). Room to breathe between them, because a search is read rather than scanned. */
/* The card holds the count and the list; a result inside it wears no box of its own, so the whole
   reads as one list rather than a stack of cards (CIYJ, 2026-09-19). */
.search-results-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.search-result {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.search-result:last-of-type {
  border-bottom: 0;
}

/* The kind icon leads the row rather than centring itself in it, the row being several lines. */
.search-result > .resource-kind {
  align-self: flex-start;
  padding-top: 0.2rem;
}

.search-result-what {
  flex: 1 1 auto;
  min-width: 0;
}

.search-result-title {
  display: block;
  color: var(--pbs-ink);
}

/* The path to the SECTION it is filed in: the one thing a search result cannot otherwise say. */
.search-result-where {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.8125rem;
}

.search-result .resource-tags {
  margin-top: 0.55rem;
}

.search-result-about {
  margin: 0.4rem 0 0;
  color: var(--pico-muted-color);
  font-size: 0.875rem;
}

.search-result-acts {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

/* The order arrows lead the acts, where a SECTION's list draws them (CIYJ, 2026-09-19). */
.search-result-acts .order-form {
  display: flex;
  gap: 0.15rem;
  margin: 0;
}

/* Where this SECTION sits, as the page's own words (CIYJ, 2026-09-19). The layout's breadcrumb
   named the VIEW and went with PR-296; this names the path through the tree, which is the only way
   back up more than one level. */
.section-trail {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  /* Pico's third default on a bare <nav>, after `display: flex` on it and on its list: it sets
     `justify-content: space-between`, which pushed the last crumb to the far side of the page
     (CIYJ, 2026-09-19). A Pico component claimed by a tag, again. */
  justify-content: flex-start;
  gap: 0.25rem 0.5rem;
  margin: 0 0 0.75rem;
  font-size: 0.875rem;
  color: var(--pico-muted-color);
}

.section-trail > * + *::before {
  content: "›";
  margin-right: 0.5rem;
}

.section-trail [aria-current] {
  color: var(--pbs-ink);
}

/* STAFF read the SECTIONS at a level as a table: what each holds, who it reaches, what order they
   come in (CIYJ, 2026-09-16). The shares are declared under a fixed layout, so a long name or a
   long introduction is cut off rather than pushing the table wider than the page (PR-296). */
.section-table {
  table-layout: fixed;
}

.section-table .col-name {
  width: 24%;
}

.section-table .col-places {
  width: 12rem;
}

/* The introduction takes what the fixed columns leave, which is what the ellipsis bites against. */
.section-table .col-intro {
  width: auto;
}

.section-intro {
  color: var(--pico-muted-color);
  font-size: 0.875rem;
}

/* One line of it however much was written, the rest on hover. On the box inside the cell and never
   the cell: `overflow` clips at the padding box, so a cell that clips its own text paints it
   through the padding and up against the next column (PR-296; CIYJ, 2026-09-19). */
.section-intro > span {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* One mark per row, so the column is as wide as its heading and no wider. */
.section-table .col-active {
  width: 5rem;
}

.section-table .col-order {
  width: 5.5rem;
}


/* Who everything filed under this SECTION reaches, above the SECTIONS in it. */
.section-reach {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  margin-bottom: 0.5rem;
}

/* Up and down, as a pair that keeps its width whether or not an end of the list disables one. */
.order-form {
  display: flex;
  gap: 0.15rem;
  margin: 0;
}

.order-button {
  width: auto;
  margin: 0;
  padding: 0.15rem 0.3rem;
  border: none;
  border-radius: var(--pico-border-radius);
  background: none;
  color: var(--pico-muted-color);
  line-height: 0;
}

.order-button:hover:not(:disabled) {
  background: var(--pico-secondary-background);
  color: var(--pico-primary-inverse);
}

.order-button:disabled {
  opacity: 0.3;
  cursor: default;
}

/* A SECTION shown whole at a place (RS-9): its name and words above what is filed under it. */
.resource-group {
  margin-top: 0.75rem;
}

.resource-group > h3 {
  margin-bottom: 0.15rem;
  font-size: 1rem;
}

.resource-line-name {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5rem;
  min-width: 0;
}

.resource-abstract {
  flex-basis: 100%;
  color: var(--pico-muted-color);
}

.resource-line-end {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  white-space: nowrap;
}

/* A card listing RESOURCES straight, as a SURVEY's page does, has no fold to indent under. */
.card > .resource-line {
  padding-left: 0;
}

/* STAFF's "who sees it": the choices under "Only these" sit under it. */
.indented {
  margin-left: 1.5rem;
}

.resource-page img {
  max-width: 100%;
  height: auto;
}

/* ── the Help popup (PR-270) ─────────────────────────────────────────────────────────── */
/*
 * LEGACY-2's popup, drawn again: two headed groups, each a column of an icon and a word beside what
 * it leads to. The same partial is the Help page a browser with no script lands on.
 */
.help-dialog > article {
  width: 100%;
  max-width: 44rem;
}

.help-content h2 {
  margin: 0 0 0.5rem;
  font-size: 1.125rem;
  color: var(--pico-muted-color);
}

.help-content section + section {
  margin-top: calc(var(--pico-spacing) * 1.5);
}

.help-table {
  margin: 0;
}

.help-table th,
.help-table td {
  padding: 0.35rem 0.75rem;
  border: 0;
  background: none;
}

.help-table th {
  width: 12rem;
  font-weight: normal;
  white-space: nowrap;
}

.help-table .icon,
.help-links .icon {
  margin-right: 0.5rem;
  vertical-align: -3px;
}

.help-links {
  margin: 0;
  padding-left: 0.75rem;
}

.help-links li {
  margin: 0.35rem 0;
  list-style: none;
}

.help-page {
  max-width: 44rem;
}

/* ── printing, for every page (GL-4, PR-269) ──────────────────────────────────────────── */
/*
 * A page prints as itself. The browser's print dialog is the only way a page becomes paper or a PDF,
 * whether opened from a page's print control (PrintButton, static/print.js) or from the browser's
 * menu, and these rules are the same either way. What goes: the shell — sidebar, top bar, tabs,
 * WARNING band, breadcrumb, footer, toasts — and every control, since nothing on paper can be
 * pressed. What arrives: the label saying whose page it is. A page marks anything else itself,
 * `no-print` for what is only for the screen (a filter card) and `print-only` for what is only for
 * paper (the TERMS a search set aside, which a printed GLOSSARY still holds, GL-11).
 */
.print-label {
  display: none;
}

/* The print control stands beside Search at the end of the filter row, dressed the same way
   (LOURA, 2026-09-22): both are outline buttons, one narrowing the list and one putting it on
   paper. It was an underlined link under a filter card until the filters became a row.
   Pico's own padding, so it and Search come out the same height; the printer rides inside it. */
.print-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  width: auto;
  margin: 0;
  white-space: nowrap;
}

@media screen {
  .print-only {
    display: none !important;
  }
}

@media print {
  .sidebar,
  .shell-head,
  .warning-band,
  .site-footer,
  .save-toast,
  .page-progress,
  .page > nav[aria-label="breadcrumb"],
  .no-print,
  button,
  .card-action,
  .modal-link,
  dialog:not([open]) {
    display: none !important;
  }

  /* The sidebar's column goes with the sidebar, collapsed or not. */
  .shell,
  .shell.shell-collapsed {
    display: block;
  }

  .print-label {
    display: block;
    margin: 0 0 1rem;
    padding: 0 0 0.5rem;
    border-bottom: 1px solid currentColor;
    font-weight: 600;
  }

  /* Paper has its own margins, so the page's padding, its columns and its boxes all go: the
     content starts where the label does, and a column pair stacks, so a column whose content was
     only for the screen leaves no gap behind it. */
  .page,
  .page-centred {
    max-width: none;
    padding: 0;
  }

  .page > main {
    padding: 0;
  }

  .aside-and-main {
    display: block;
  }

  .card,
  article {
    margin-inline: 0;
    border: 0;
    box-shadow: none;
    background: none;
    padding: 0;
  }

  .term {
    break-inside: avoid;
  }

  /* A page whose heading is for assistive technology alone on screen, because the top bar names the
     SITE there, says whose it is on paper, where the top bar is gone: a printed REPORT that does not
     name its SITE is a page of numbers about nobody (PR-277). */
  .visually-hidden.print-heading {
    position: static;
    width: auto;
    height: auto;
    margin: 0 0 0.5rem;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
    font-size: 1.25rem;
  }
}

/* ── the library: a contents column beside a reading pane (RS-10, PR-291) ──────────────── */

/* How far below the top of the page an anchored SECTION has to start, because the top bar stays on
   screen and would otherwise cover what a contents link just jumped to. Chosen to clear the bar; it
   wants measuring against the real bar rather than trusting this. */
.library {
  --pbs-head-clear: 4.5rem;
  --pbs-pane-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--pico-block-spacing-horizontal);
}

/* The narrow column: the SITES a COACH may go and look at, then the way about this page.
   It does not stick and it does not scroll inside itself (CIYJ, 2026-09-18). Sticking it wanted a
   height and a height wanted a scrollbar, and with a COACH's fifteen SITES listed above the contents
   the column is taller than the window — so the choice was an inner scrollbar or a bottom nobody
   could reach. It scrolls with the page, like the page. */
.library-side {
  flex: 1 1 13rem;
  min-width: 0;
  /* Level with the pane's first heading: its own top margin, plus the pane's top border. */
  padding-top: calc(var(--pbs-pane-top) + 1px);
  font-size: 0.875rem;
}

.library-sites {
  margin: 0 0 2.25rem;
}

/* Pico dresses a <nav> as a bar of links — `display: flex` on the nav AND on its `ul`, with
   `inline-block` items — which is right for a navbar and wrong for a column of them. It cost two
   rounds: first the entries ran across the page, then, with the list fixed, the heading sat beside
   the list instead of over it (CIYJ, 2026-09-18). All three need saying. It is the same class of
   thing as `role="search"` on a filter row: a Pico component claimed by a tag, not by a class. */
.library-side nav,
.library-side nav ul,
.library-side nav li {
  display: block;
  list-style: none;
}

/* Not the nav itself: a margin in this reset outranks the gap between the two blocks below, which
   is how Contents came to sit hard against the last SITE (CIYJ, 2026-09-18). */
.library-side nav ul,
.library-side nav li {
  margin: 0;
  padding: 0;
}

.library-side nav {
  padding: 0;
}

/* A label with room round it and the page's own ink (CIYJ, 2026-09-18). Muted and tight against
   its list, it read as part of the list rather than as the name of it — and these two are what tell
   a reader which column they are in. `--pbs-ink` because Pico redefines `--pico-color` inside a
   link, and these headings sit among links. */
.library-side nav h2 {
  margin: 0 0 0.9rem;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--pbs-ink);
}

/* Each level a step in, with a rule down it saying what the step belongs to. */
.library-contents ul ul {
  margin: 0.2rem 0 0.5rem 0.25rem;
  padding-left: 0.75rem;
  border-left: 1px solid var(--pico-muted-border-color);
}

.library-side nav li {
  margin-bottom: 0.55rem;
}

.library-side nav a {
  display: block;
  line-height: 1.35;
  text-decoration: none;
}

.library-side nav a:hover {
  text-decoration: underline;
}


/* The pane a reader reads: the hairline box a list's table wears (PR-294), and no ground of its
   own — this is the page's own text rather than a card sitting on it (CIYJ, 2026-09-18). */
.library-pane {
  flex: 3 1 24rem;
  min-width: 0;
  padding: 0 1.25rem 1.5rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
}

/* Where an anchor lands, clear of the bar that stays on screen. */
.library-section {
  scroll-margin-top: var(--pbs-head-clear);
}

/* This is the one screen that may breathe (CIYJ, 2026-09-18): it is read rather than worked, and
   a heading with nothing round it reads as part of what came before it. */
.library-depth-1 > .library-heading {
  margin: 2.75rem 0 1rem;
}

/* A nested SECTION steps in, so the pane shows the filing rather than only saying it. The classes
   stop at three (deeper sections all wear it), and each one nests inside the last, so the steps add
   up on their own. */
.library-depth-2,
.library-depth-3 {
  margin-left: 1rem;
}

.library-depth-2 > .library-heading {
  margin: 2rem 0 0.75rem;
}

.library-depth-3 > .library-heading {
  margin: 1.5rem 0 0.6rem;
}

/* A top section opens the pane, so it takes less space above itself — and that space is what the
   side column pads itself by, so the two columns start on the same line. */
.library-pane > .library-section:first-child > .library-heading {
  margin-top: var(--pbs-pane-top);
}

/* Below the second level a heading stops being a size and becomes a label: five levels of section
   drawn as five sizes is a scale nobody can hold, and by then a reader is already where they meant
   to be (measured 2026-09-18: 43 sections at level three, 16 and 12 below it). */
.library-depth-3 > .library-heading {
  font-size: 1rem;
  color: var(--pico-muted-color);
}

.library-intro {
  margin-bottom: 0.9rem;
  color: var(--pico-muted-color);
}

.library-intro > :last-child {
  margin-bottom: 0;
}

/* The lines under a section: icon, title, description. A table because three things line up down
   the column, with its shares declared under a fixed layout so a long title cannot push the
   description out of the pane (PR-296). No heading row — the section's own heading is the name of
   it, and the caption says so for a screen reader. */
.library-table {
  width: 100%;
  margin-bottom: 0.6rem;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.library-table .col-kind {
  width: 2rem;
}

.library-table .col-name {
  width: 38%;
}

.library-table td {
  padding: 0.55rem 0.6rem 0.55rem 0;
  vertical-align: top;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.library-table tr:last-child td {
  border-bottom: 0;
}

.library-kind {
  color: var(--pico-muted-color);
  line-height: 0;
  padding-top: 0.55rem;
}

.library-what {
  color: var(--pico-muted-color);
}

/* The rest of a long section, shut. A <details> rather than a declared parameter: no script, no
   round trip, and the anchors above it still land (CIYJ, 2026-09-18). */
.library-more {
  margin-bottom: 0.4rem;
}

.library-more > summary {
  display: inline-block;
  margin-bottom: 0.3rem;
  color: var(--pico-primary);
  font-size: 0.875rem;
  cursor: pointer;
}

.library-more[open] > summary {
  margin-bottom: 0.1rem;
}

.library-empty {
  color: var(--pico-muted-color);
}

/* The gate as a mark on one row rather than a box round a group (CIYJ, 2026-09-18). Same words and
   same colours; what goes is the positioning, which is what made it a lid on a box. */
.gated-badge-inline {
  position: static;
  transform: none;
  display: inline-block;
  margin-right: 0.45rem;
  vertical-align: 1px;
}

/* A search inside a SECTION still crosses the whole library, and the group says so (CIYJ,
   2026-09-19). Pico dresses a fieldset with no border, so the legend carries it. */
.search-everything {
  margin: 0 0 var(--pico-block-spacing-vertical);
  padding: 0;
  border: 0;
}

.search-everything > legend {
  margin-bottom: 0.3rem;
  padding: 0;
  color: var(--pico-muted-color);
  font-size: 0.8125rem;
  font-weight: 600;
}

.search-everything > .filter-bar {
  margin-bottom: 0;
}

/* The first kind of place a SECTION or a RESOURCE can be put, with room for the kinds to come
   (RS-2; CIYJ, 2026-09-19). A label over its checkboxes rather than a heading in the page's scale. */
.place-kind {
  margin: 0 0 0.4rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
}

/* ── the recorded API requests (R-49, R-51) ───────────────────────────────────────────────── */

/* Eight columns (LOURA, 2026-09-23), three of which hold something that can run long: an address,
   the address a key is registered under, and a SITE's name. Declared shares and a fixed layout, as
   the other big tables have it, so a column is the share it declares whatever is in it and the
   long ones cut off instead of pushing the table out through the scroller.

   The key is two columns and so is the result: an address over an agency in smaller type, and a
   status badge beside its code, each made a row two deep and a column of two different things. */
.log-table .col-when {
  width: 14%;
}
.log-table .col-key {
  width: 16%;
}
.log-table .col-kind {
  width: 9%;
}
.log-table .col-customer {
  width: 12%;
}
.log-table .col-request {
  width: 22%;
}
.log-table .col-site {
  width: 11%;
}
.log-table .col-result {
  width: 6%;
}
.log-table .col-code {
  width: 10%;
}

.log-table {
  width: 100%;
  min-width: 72rem;
  table-layout: fixed;
}

/* Kiosk or API account, and neither of them wraps. */
.log-table .log-kind {
  white-space: nowrap;
}

.log-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The date and the time on one line and in one size, so the column reads straight down. */
.log-table .log-when {
  white-space: nowrap;
}

/* A select sizes itself to its widest option, and a route is long enough that the row ran onto
   four lines (LOURA, 2026-09-23). Capped here rather than on `.filter-bar`, which every list uses
   and none of the others has an option this long. The search box keeps its own width. */
.log-filters .filter-bar select {
  max-width: 13rem;
}

/* The facts about one request take the card rather than the 34rem measure: an address and the
   sentence a refusal carries are read across, which is the exception a table already makes. */
.card-stack .card > .facts {
  max-width: none;
}

/* An address is a machine's word, and reads as one. Only in this column: the rest of the row is
   a person's. */
.log-table .log-request {
  font-family: var(--pico-font-family-monospace);
  font-size: 0.8125rem;
}

/* What a request sent (R-49), shown as it arrived. Wrapped rather than one long line, because
   the JSON an agency's system sends has no newlines in it at all and a reader looking for one
   field should not have to scroll a mile sideways to find it. */
.sent {
  margin: 0;
  max-height: 32rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.8125rem;
}

/* ── the health of the WEB APP (A-45) ─────────────────────────────────────────────────────── */

/* The page's verdict, over the cards. Not a row count — the thing a list never carries (LOURA,
   2026-09-21) — but what the checks add up to, which is what a reader opens this page for and on
   a good day the only thing they read. */
.health-tally {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.4rem;
  margin: 0 0 var(--pico-block-spacing-vertical);
  color: var(--pico-muted-color);
}

.health-tally b {
  margin-right: 0.25rem;
  color: var(--pico-color);
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
}

/* One check a row: the state, what was asked beside it, and the answer under that. The state comes
   first and every one is the same width, so a reader runs an eye down the states and stops at the
   one that is not fine — which is the whole of how this page is read. */
.checks {
  list-style: none;
  margin: 0;
  padding: 0;
}

.checks li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.15rem 0.6rem;
  align-items: baseline;
  padding-block: 0.45rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
}

.checks li:last-child {
  border-bottom: 0;
}

/* Sized from the longest word so the badges line up down the column rather than each sitting where
   its own word ends. */
.check-state {
  display: inline-block;
  min-width: 7.75rem;
}

.check-value {
  grid-column: 2;
  font-size: 0.8125rem;
  color: var(--pico-muted-color);
}


/* ── a key's fortnight, in a table cell (R-49) ────────────────────────────────────────────── */

/* Wide enough that fourteen days are fourteen marks and not a smudge, and fixed, so every row's
   fortnight is the same width down the column even though each is scaled to its own busiest day. */
.spark-cell {
  width: 8.5rem;
}

.spark {
  display: block;
  width: 8.5rem;
  height: 22px;
}

/* Answered and refused, in the words the request says them: green served, red said no. The same
   two Pico tokens the badges wear, so a status means one colour everywhere. */
.spark-answered {
  fill: var(--pico-ins-color);
}

.spark-refused {
  fill: var(--pico-del-color);
}

/* Under the whole fortnight, so a key that sent nothing reads as an empty fortnight rather than
   as a cell that failed to draw. */
.spark-base {
  fill: var(--pico-muted-border-color);
}

/* The Connections lists carry a chart now, so the columns beside it give up what they can spare:
   a name and a list of SITES cut off sooner than they did (LOURA, 2026-09-23). Without a width a
   `.clipped` cell in a content-sized table never cuts anything — the cell simply grows, and the
   table pushes out through its scrolling box instead. */
.connections-table .clipped,
.connections-table .name-list {
  max-width: 13rem;
}

/* ── STAFF's survey responses (DQ-1, DQ-9, A-22) ──────────────────────────────────────────── */

/* Ten columns, four of which hold something that can run long: a SURVEY's name, a SITE's with its
   agency under it, a PROGRAM and a CYCLE. Declared shares and a fixed layout, as the API log and
   the other big tables have it, so a column is the share it declares whatever is in it and the
   long ones cut off rather than pushing the table out through the scroller. */
/* Wide enough for its heading and the mark under it — "Super admin" is the wider of the two, and
   a column narrower than its own mark is what chopped it in half the first time. */
.response-table .col-tick {
  width: 7rem;
}
.response-table .col-reference {
  width: 7%;
}
.response-table .col-survey {
  width: 15%;
}
.response-table .col-site {
  width: 17%;
}
.response-table .col-program {
  width: 11%;
}
.response-table .col-cycle {
  width: 11%;
}
/* A tick or a cross, so it needs its heading's width and nothing more. */
.response-table .col-state {
  width: 6rem;
}
.response-table .col-status {
  width: 6rem;
}
.response-table .col-source {
  width: 8%;
}
.response-table .col-when {
  width: 10%;
}
.response-table .col-updated {
  width: 10%;
}

/* Percentages, so the table gives its width back as the filters take columns away: narrowed to one
   SURVEY at one SITE it is five columns and fits without a scroller at all. The two fixed ones are
   the tick and the marks, which hold the same thing however few columns are left. */
.response-table {
  width: 100%;
  min-width: 68rem;
  table-layout: fixed;
}

.response-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The tick is a column of its own (LOURA, 2026-09-23). Beside the reference it read as decoration
   on the link rather than as a control, and a reader ticking a run of rows wants one target down
   the left edge. */
.response-table .col-tick,
.response-table .response-tick {
  text-align: center;
}

.response-table .response-tick input {
  margin: 0;
}

.response-table .response-reference {
  white-space: nowrap;
}

/* The Reportable column is one glyph, centred under its heading. */
.response-table .col-state,
.response-table .response-state {
  text-align: center;
}

/* The mark saying whose the act is goes under the heading rather than beside it, so it sets the
   column's height and not its width. Only a SUPER ADMIN ever sees it: the column does not exist
   for anybody else.

   `fit-content` because the pill is a block here and a block fills its column — which drew the
   dashed ring a good inch wider than the words inside it (LOURA, 2026-09-23). The inline form's
   own right margin goes with it; there is nothing beside it to be spaced from. */
.response-table th .gated-badge-inline {
  display: block;
  width: fit-content;
  margin: 0.15rem 0 0;
}

/* `.gated` is a dashed box round a group of controls, and the act below the table is one. The
   buttons keep their own row inside it. */
.exclude-bar .gated {
  margin-block: 0.75rem 0;
}

.exclude-bar .gated .exclude-actions {
  margin-block: 0;
}

/* A second line under a cell — the agency under its SITE, who entered it under when — is the
   shape the API log's key column takes, and it is quiet so the column still reads straight down. */
.response-table small {
  display: block;
  font-size: 0.75rem;
  line-height: 1.3;
}

.response-table .response-when,
.response-table .response-updated {
  white-space: nowrap;
}

/* Thirteen filters, and four of the selects carry an option as long as a SURVEY's name. Capped
   here rather than on `.filter-bar`, which every list uses; without it the row ran to five lines
   and the table under it started below the fold. */
.response-filters .filter-bar select {
  max-width: 12rem;
}

/* The download sits between the filters and the table, where the reader has just finished
   narrowing and is deciding whether to read or to take it away. */
.download-row {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
  margin-block: 0.5rem 1rem;
}

/* The same again on STAFF's list: a link worn as a button, at the size of the buttons on the page
   around it rather than at the pill size `.modal-link` wears on a line in a list. */
.download-row .modal-link {
  padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
  font-size: inherit;
}

/* A-22's act, below the rows it acts on: the ticks are in the table and the buttons are here, so
   the reader picks first and chooses second. */
.exclude-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
  margin-block: 0.75rem 0;
}

.exclude-actions button {
  width: auto;
  margin-bottom: 0;
}

/* The marks a RESPONSE wears, in place of five badges (LOURA, 2026-09-23). A badge has room for
   two words and five of them wrapped the row three deep; the icon is the whole of the mark, as a
   SITE's row already has it, and `.response-legend` over the table carries the words. Sized here
   rather than per icon so all five match whether or not their icon takes a size. */
.response-marks {
  display: inline-flex;
  gap: 0.35rem;
  align-items: center;
}

.response-mark .icon {
  width: 1rem;
  height: 1rem;
  display: block;
}

/* The same five tones the badges wore, so a reader moving between this list and a RESPONSE's own
   page meets one vocabulary. The shape carries the meaning and the colour only seconds it: every
   mark is a different glyph, and its word is in the legend, the hover and the hidden text. */
.response-mark-success {
  color: var(--pico-ins-color);
}
.response-mark-warning {
  color: var(--pbs-warning-color);
}
.response-mark-danger {
  color: var(--pico-del-color);
}
.response-mark-info {
  color: var(--pbs-info-color);
}
.response-mark-muted {
  color: var(--pbs-badge-muted, var(--pico-muted-color));
}

/* The legend, once, over the table. Small and quiet: it is a key to be glanced at, not a row of
   controls, and it wraps to as many lines as the width leaves it. */
.response-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  align-items: center;
  margin-block: 0 0.5rem;
  font-size: 0.8125rem;
  color: var(--pico-muted-color);
}

.response-legend-item {
  display: inline-flex;
  gap: 0.3rem;
  align-items: center;
}

/* A CYCLE's name and an agency's are as long as somebody typed them, and both sit in a column
   sized for something shorter. Clipped with the whole name as the hover, as the SITE above them
   already is (LOURA, 2026-09-23). `max-width` because a fixed table lays a cell out to its content
   before the overflow rule ever sees it. */
.response-table .clipped {
  max-width: 100%;
}

/* ── the email screens ────────────────────────────────────────────────────────────────────── */

/* The three scope folds side by side where there is room and stacked where there is not. Six
   PROGRAMS, thirty-nine CUSTOMERS and a hundred and twenty-one SITES all fit a fold with a
   checkbox each; a select multiple was what the OM dashboard left behind for the same reason. */
.pickers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
  align-items: start;
}

/* A count reads right-aligned, so a column of them lines up on its digits. */
.numeric {
  text-align: right;
}

/* The two ways of sending, across rather than down: they are alternatives, and one above the other
   read as an act with a setting under it. Each column carries its own heading and its own button,
   so neither button can be mistaken for the other one's. Each button is its own width: stretched
   across the column they read as two halves of one control rather than two answers to one
   question (LOURA, 2026-09-27). Said explicitly, because Pico stretches a button to its container
   by default and removing a rule of our own left its rule standing. */

.send-ways {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.send-ways button {
  width: auto;
}

.send-ways h3 {
  margin-bottom: 0.25rem;
  font-size: 1rem;
}

@media (max-width: 48rem) {
  .send-ways {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The shortcut names, in as many columns as the fold is wide. Each is read and copied rather than
   scanned, so they are set in the same monospace the rest of the WEB APP uses for a literal. */
.shortcut-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: 0.15rem 1rem;
}

/* ── the QUESTION lookup (S-52, S-53) ─────────────────────────────────────────────────────── */

/* Seven columns, four of which hold something that can run long: the KEY, the wording, the note,
   and the list of SURVEYS it is on. Declared shares over `.response-table`'s fixed layout, as the
   other big tables have it, so a column is the share it declares whatever is in it.

   The marks column is fixed because it holds the same six things however narrow the rest goes. */
.question-table .col-marks {
  width: 8rem;
}
.question-table .col-key {
  width: 17%;
}
.question-table .col-question {
  width: 22%;
}
.question-table .col-note {
  width: 15%;
}
.question-table .col-type {
  width: 9%;
}
.question-table .col-on {
  width: 19%;
}
.question-table .col-measures {
  width: 18%;
}

/* The KEY is what somebody copies into a MEASURE definition, so it is set as code — and clipped,
   because the converted ones run past two hundred characters and one of them would otherwise
   take the table's whole width. The hover carries the rest of it. */
.question-table .question-key {
  font-size: 0.8125rem;
  font-family: var(--pico-font-family-monospace, monospace);
}

/* Where a KEY is asked, and what reads it: a list rather than a run-on line, because a KEY on
   four SURVEYS is four facts and a comma-separated string is read as one. */
.question-table .question-on,
.question-table .question-measures {
  list-style: none;
  margin: 0;
  padding: 0;
}

.question-table .question-on li + li,
.question-table .question-measures li + li {
  margin-top: 0.3rem;
}

/* The VERSION and whether it is LIVE sit under the SURVEY's name rather than beside it: beside it
   they set the column's width, and the name is the part being read. */
.question-table .question-on small {
  display: block;
  font-size: 0.75rem;
}

/* A MATRIX column's own wording says nothing — "Date" — so the MATRIX's stands over it (S-23). */
.question-table .question-in {
  display: block;
  font-size: 0.75rem;
  color: var(--pico-muted-color);
}
