/* CarRider forms.css — global resets, form controls, buttons, flashes,
 * pills/badges, toggles, modals, settings inputs.
 * Tokens are defined in tokens.css (loaded first via _base.html).
 * Tokens trace to docs/ux/reference/build-spec.md (Final Bell `--fb-*`).
 */

* { box-sizing: border-box; }

/* ── Skip-link (GLEAM-129, WCAG 2.4.1 Bypass Blocks) ────────────────
 * Hidden off-screen until focused via keyboard; snaps to top-left on
 * focus-visible so a keyboard user can reach #admin-main with one Tab.
 * z-index 1200 clears the admin topbar (z-index 10) and all drawers. */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  transform: translateY(-200%);
  padding: var(--fb-s-3) var(--fb-s-4);
  background: var(--fb-surface);
  color: var(--fb-blue-strong);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  z-index: 1200;
  transition: transform 120ms ease;
  text-decoration: none;
  font-size: 14px;
}
.skip-link:focus-visible {
  transform: translateY(0);
  outline: 2px solid var(--fb-focus);
  outline-offset: 2px;
}

html, body {
  margin: 0;
  padding: 0;
  font-family: "sofia-pro", -apple-system, "Segoe UI", system-ui, sans-serif;
  font-size: 14px;
  color: var(--fb-text);
  background: var(--fb-canvas);
  min-height: 100vh;
  min-height: 100dvh;
}

/* GLEAM-126: --fb-blue (#3FA9F5) is ~2.5:1 on white — fails WCAG AA.
 * --fb-blue-strong (#1F6FAA) clears 4.6:1 on white, identical to the
 * GLEAM-106 fix for .dash-tile-cta.
 * GLEAM-006: Hover lands on --fb-blue-pressed (#175A8A) — see .btn-primary:hover in forms.css. */
a { color: var(--fb-blue-strong); text-decoration: none; }
a:hover { color: var(--fb-blue-strong); text-decoration: underline; }

/* ── Form controls (build-spec §5.4–5.5) ───────────────────── */
.form-row { margin-bottom: var(--fb-s-4); }

label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--fb-text-section);
  margin-bottom: var(--fb-s-2);
}

input[type=email],
input[type=password],
input[type=text],
input[type=date],
input[type=time],
select {
  width: 100%;
  height: 44px;
  padding: 0 var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
input:focus-visible,
select:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}
input[type=file] {
  width: 100%;
  height: 44px;
  padding: 0 var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
  display: flex;
  align-items: center;
  cursor: pointer;
}
input[type=file]:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}

/* GLEAM-035: use element+class selector (0,1,1) so font-size: 18px beats
 * input[type=text] (0,1,1 same count, wins by source order coming later).
 * height: 48px gives the field visual weight to match the larger text. */
input.code-input {
  letter-spacing: 6px;
  font-size: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  height: 48px;
}

.btn {
  display: inline-block;
  padding: 0 var(--fb-s-5);
  height: 44px;
  line-height: 44px;
  font-weight: 500;
  font-size: 14px;
  border-radius: var(--fb-r-md);
  border: 0;
  cursor: pointer;
  transition: background 120ms ease;
  white-space: nowrap;
  text-decoration: none;
  text-align: center;
  vertical-align: middle;
}
/* GLEAM-031: --fb-blue (#3FA9F5) on white ~2.56:1 fails WCAG AA;
 * --fb-blue-strong (#1F6FAA) ≈5.4:1 — mirror GLEAM-021/GLEAM-126 pattern. */
.btn-primary {
  background: var(--fb-blue-strong);
  color: var(--fb-text-inverted);
}
.btn-primary:hover { background: var(--fb-blue-pressed); }
/* GLEAM-079: use --fb-text-input instead of --fb-text-muted so ghost buttons
 * (e.g. topbar Sign-out) pass WCAG AA contrast and read as real controls,
 * not de-emphasised text. */
.btn-ghost {
  background: transparent;
  color: var(--fb-text-input);
  border: 1px solid var(--fb-border);
}
.btn-block { width: 100%; }
.btn-danger { background: var(--fb-danger); color: var(--fb-text-inverted); }
/* GLEAM-046: 44px floor at all viewports — admin tablet (820-1024px) WCAG 2.5.8. */
.btn-sm { height: 44px; line-height: 42px; padding: 0 var(--fb-s-3); font-size: 13px; }
.btn:focus-visible,
.btn-primary:focus-visible,
.btn-ghost:focus-visible,
.btn-danger:focus-visible {
  box-shadow: var(--fb-sh-focus-ring);
  outline: none;
}
/* GLEAM-091: disabled buttons must visually communicate state — no blue fill,
 * not-allowed cursor. pointer-events:none closes the gap for a11y-API clicks. */
.btn:disabled,
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* ── Mute toggle (.cd-mute) — shared control ─────────────────────────────
 * Used on classroom-dispatcher, curb-dispatcher, and greeter surfaces.
 * Moved here (forms.css) from classroom-dispatcher.css so both dispatcher
 * sheets and greeter.css share a single source of truth.
 * Surface-specific padding or font overrides belong in the per-surface sheet.
 * Touch target ≥44pt per accessibility AC.
 * Trace: spec §3.3.1 mute parity; GLEAM-147 (deduplicated from
 * classroom-dispatcher.css:102-129 and greeter.css:416-444). */
.cd-mute {
  background: transparent;
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  color: var(--fb-text-input);
  padding: 0 var(--fb-s-5);
  height: 44px;
  min-width: 48px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}

/* Mute state spans — flex so the inline SVG and label text are
   vertically centred. SVG is aria-hidden; text provides the label. */
.cd-mute .cd-mute-on,
.cd-mute .cd-mute-off {
  display: flex;
  align-items: center;
  gap: var(--fb-s-2);
}
.cd-mute .cd-mute-off { display: none; }
.cd-mute[aria-pressed="true"] .cd-mute-on { display: none; }
.cd-mute[aria-pressed="true"] .cd-mute-off {
  display: flex;
  color: var(--fb-text-muted);
}

.cd-mute:focus-visible {
  box-shadow: var(--fb-sh-focus-ring);
  outline: none;
}
/* GLEAM-179: touch press feedback — mirrors .curb-release:active pattern
 * (curb-dispatcher.css). Gives touch users visible confirmation between
 * finger-down and aria-pressed state flip on the topbar mute toggle. */
.cd-mute:active {
  background: var(--fb-row-hover);
}

.flash {
  border-radius: var(--fb-r-md);
  padding: var(--fb-s-3);
  font-size: 13px;
  margin-bottom: var(--fb-s-4);
}
.flash-error {
  background: var(--fb-flash-error-bg);
  color: var(--fb-danger);
  border: 1px solid var(--fb-flash-error-border);
}
.flash-success {
  background: var(--fb-flash-success-bg);
  color: var(--fb-flash-success-fg);
  border: 1px solid var(--fb-flash-success-border);
}
.flash-warning {
  background: var(--fb-warn-bg);
  color: var(--fb-warn-fg);
  border: 1px solid var(--fb-warn-border);
}
/* GLEAM-002: info flash — light-blue tint analogous to success/warning/error
 * variants; used by six call sites that flash category='info' and by
 * admin_change_requests_edit.html's read-only notice. */
.flash-info {
  background: var(--fb-flash-info-bg);
  color: var(--fb-flash-info-fg);
  border: 1px solid var(--fb-flash-info-border);
}
/* GLEAM-043: edge-pinned banner modifier — pins a flash banner flush to the
 * page edge (e.g. noscript idle-timeout notice). Reusable for any future
 * edge-anchored system banner (maintenance notice, etc.). */
/* GLEAM-051: keep flash banners below modal stack (scrim 1099, card 1100). */
.flash-edge {
  margin: 0;
  border-radius: 0;
  position: relative;
  z-index: 100;
}

/* ── Textarea styled as a form input ────────────────────────────
 * Matches the input/select recipe (border, radius, font, focus ring).
 * Use class="form-input" on <textarea> elements. */
textarea.form-input {
  width: 100%;
  padding: var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
  resize: vertical;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
textarea.form-input:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}

/* ── Field hint / helper text (below label, above control) ───── */
.form-hint {
  margin: 0 0 var(--fb-s-2);
  font-size: 12px;
  color: var(--fb-text-muted);
  line-height: 1.4;
}

/* ── Password reveal toggle (GLEAM-007) ──────────────────────────────
 * .password-field wraps a password <input> and positions an eye-icon
 * toggle button at the right edge (44×44 hit area, WCAG 2.5.8).
 * JS (password-reveal.js) flips type=password ↔ type=text and syncs
 * aria-pressed; CSS swaps .pr-icon-show / .pr-icon-hide accordingly. */
.password-field {
  position: relative;
}
.password-field input[type=password],
.password-field input[type=text] {
  padding-right: 48px;
}
.password-reveal {
  position: absolute;
  right: 0;
  top: 0;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--fb-text-muted);
  border-radius: 0 var(--fb-r-md) var(--fb-r-md) 0;
}
.password-reveal:hover { color: var(--fb-text-input); }
.password-reveal:focus-visible {
  box-shadow: var(--fb-sh-focus-ring);
  outline: none;
}
/* Icon swap: show eye by default; switch to eye-slash when revealed */
.password-reveal .pr-icon-hide { display: none; }
.password-reveal[aria-pressed="true"] .pr-icon-show { display: none; }
.password-reveal[aria-pressed="true"] .pr-icon-hide { display: block; }

/* ── Brand-mark eyebrow (global — all surfaces) ──────────────────
 * Promoted from auth.css scoped selectors (parallel to GLEAM-044) —
 * admin pages that load forms.css + chrome.css now pick up the eyebrow
 * style emitted by macros/chrome.html page_header(). */
.brand-mark {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  color: var(--fb-text-section);
  text-transform: uppercase;
  margin-bottom: var(--fb-s-4);
}

/* ── Lede / sub-heading paragraph (global — all surfaces) ────────
 * Promoted from auth.css scoped selectors (GLEAM-044) so that admin
 * pages using chrome.css + forms.css (no auth.css) get consistent
 * styling for <p class="lede"> emitted by macros/chrome.html. */
.lede {
  color: var(--fb-text);
  margin: 0 0 var(--fb-s-5);
  font-size: 14px;
  line-height: 1.55;
}

/* ── Top margin after a preview card (intake / detail forms) ──── */
.form-top { margin-top: var(--fb-s-4); }

/* ── Inline field validation error ─────────────────────────────────────
 * Replaces inline color/font-size/margin on .form-error elements
 * in admin_corrections_review (GLEAM-021). */
.form-error {
  color: var(--fb-danger);
  font-size: 12px;
  margin: var(--fb-s-1) 0 0;
}

/* ── Inline per-field validation errors ──────────────────────── */
.field-error {
  margin: var(--fb-s-2) 0 0;
  font-size: 12px;
  font-weight: 500;
  color: var(--fb-danger);
  line-height: 1.35;
}
.form-row.has-error input[type=text],
.form-row.has-error input[type=email],
.form-row.has-error input[type=password],
.form-row.has-error select {
  border-color: var(--fb-danger);
  background: var(--fb-flash-error-bg);
}
.form-row.has-error input:focus-visible,
.form-row.has-error select:focus-visible {
  border-color: var(--fb-danger);
  box-shadow: var(--fb-sh-focus-ring-error);
}
/* GLEAM-123: the global body *:focus-visible rule in chrome.css paints a blue
 * 2px outline (--fb-focus) that collides with the red border + red inset ring
 * on errored fields. Override the outline colour to danger-red so all three
 * signals (border, inset ring, outline) carry a consistent hue.
 * Token --fb-danger = #B82D2D, mirrors --fb-sh-focus-ring-error. */
.form-row.has-error input:focus-visible,
.form-row.has-error select:focus-visible,
.form-row.has-error textarea:focus-visible {
  outline-color: var(--fb-danger);
}

.toggle-form { margin: 0; display: inline; }
.toggle-form button {
  background: transparent;
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  padding: var(--fb-s-1) var(--fb-s-3);
  min-height: 44px;
  font-size: 12px;
  cursor: pointer;
  color: var(--fb-text-muted);
}
.toggle-form button.on  { background: var(--fb-success); color: var(--fb-text-inverted); border-color: var(--fb-success); }
.toggle-form button.off { background: var(--fb-canvas); color: var(--fb-text-muted); }
.toggle-form button:focus-visible {
  box-shadow: var(--fb-sh-focus-ring);
  outline: none;
}

.role-pill {
  display: inline-block;
  background: var(--fb-canvas);
  border-radius: var(--fb-r-pill);
  padding: var(--fb-s-1) var(--fb-s-3);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--fb-text-input);
}
/* GLEAM-075: .role-pill-warn and .role-pill-muted removed — dead rules after
 * GLEAM-064 migrated all call sites to .badge .badge-warn / .badge .badge-muted. */

.mfa-yes { color: var(--fb-success); font-weight: 600; }
.mfa-no  { color: var(--fb-danger); font-weight: 600; }

/* ── School-settings card ──────────────────────────────────── */
.settings-card {
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-lg);
  padding: var(--fb-s-6);
  margin-top: var(--fb-s-5);
  /* 880px gives admin edit/detail pages room for data-rich controls
   * (member pickers, multi-select widgets, wider text fields) without
   * leaving the right two-thirds of the page-content area empty.
   * Single-input forms still left-align comfortably within. Was 560px
   * before — too narrow once any non-trivial widget shipped. */
  max-width: 880px;
}
/* GLEAM-088: align .settings-section color to .matrix-card h2 (--fb-text-heading)
 * so both card-chrome H2s resolve to the same #3D4144 across all admin pages. */
.settings-section {
  font-size: 16px;
  font-weight: 600;
  color: var(--fb-text-heading);
  margin: 0 0 var(--fb-s-2);
}
.settings-help {
  color: var(--fb-text-muted);
  font-size: 13px;
  line-height: 1.5;
  margin: 0 0 var(--fb-s-5);
}
.settings-current {
  margin-top: var(--fb-s-2);
  font-size: 12px;
  color: var(--fb-text-muted);
}
/* Settings cards stack vertically with breathing room */
.settings-card + .settings-card { margin-top: var(--fb-s-5); }

/* ── School-settings inputs ────────────────────────────────────
 * Settings forms reuse the same input recipe with their own
 * width/height conventions.
 *
 * Width policy (post-redesign):
 *   * .form-row defaults to filling its card container — no row-
 *     level cap. The previous 240px cap on every form-row was a
 *     belt-and-suspenders for narrow numeric inputs that ended up
 *     choking every text/date/select/widget on every admin edit page.
 *   * Each input type picks a sensible max-width based on its
 *     semantic content (numeric ~200px, text/date/email ~480px,
 *     pickers fill the card). The 240-cap-then-opt-out-via-form-
 *     row-wide pattern is gone — wide-by-default, narrow-where-
 *     numeric.
 *   * `.form-row-wide` is preserved as a no-op alias so any
 *     existing markup keeps rendering unchanged. */
.settings-form .form-row.form-row-wide { max-width: none; }
.settings-form .form-row.form-row-narrow { max-width: 240px; }
.settings-form input[type=number] {
  width: 100%;
  /* Numeric inputs are intrinsically narrow — capacity figures,
   * timeouts, sort orders. 200px is enough for any 5-digit value
   * with breathing room and avoids the "ocean of empty input" look
   * an 880-wide card would otherwise produce. */
  max-width: 200px;
  height: 44px;
  padding: 0 var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
}
.settings-form input[type=number]:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}
/* Text-shaped inputs (text/email/date/search/url) share one recipe.
 * 480px is the readable-line width for short-text content (school
 * name, family label, person name, address-1, email) — wider than
 * needed for short codes but never so wide that the input visually
 * dominates the card. */
.settings-form input[type=text],
.settings-form input[type=email],
.settings-form input[type=date],
.settings-form input[type=time],
.settings-form input[type=search],
.settings-form input[type=url],
.settings-form select {
  width: 100%;
  max-width: 480px;
  height: 44px;
  padding: 0 var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
}
.settings-form input[type=text]:focus-visible,
.settings-form input[type=email]:focus-visible,
.settings-form input[type=date]:focus-visible,
.settings-form input[type=time]:focus-visible,
.settings-form input[type=search]:focus-visible,
.settings-form input[type=url]:focus-visible,
.settings-form select:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}
.settings-form input[type=file] {
  font-size: 13px;
  color: var(--fb-text-input);
}
/* Textarea: full row width, 4 rows tall by default. Pages that want
 * a taller box override via `rows` or a per-page class. */
.settings-form textarea {
  width: 100%;
  min-height: 96px;
  padding: var(--fb-s-2) var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
}
.settings-form textarea:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}

/* Current-logo preview row.  256px storage box collapses to a 64px
 * preview thumbnail; flex layout pairs the thumbnail with a meta
 * column that holds the helper copy and the Remove button. */
.branding-current-form { margin-bottom: var(--fb-s-5); }
.branding-current-row {
  display: flex;
  align-items: center;
  gap: var(--fb-s-4);
  max-width: none;
}
.branding-current-logo {
  width: 64px;
  height: 64px;
  object-fit: contain;
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  background: var(--fb-surface);
  flex: 0 0 auto;
}
.branding-current-logo--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: var(--fb-text-muted);
  text-align: center;
  background: var(--fb-canvas);
}
.branding-current-meta {
  flex: 1 1 auto;
  min-width: 0;
}
.branding-current-meta .settings-current {
  margin-top: 0;
  margin-bottom: var(--fb-s-2);
}

/* ── Monospace settings textarea (e.g. CIDR whitelist) ───────────
 * Matches the settings-form input recipe minus height — textarea is
 * resizable vertically. Focus ring uses the locked token per §3. */
textarea.settings-mono {
  width: 100%;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 13px;
  padding: var(--fb-s-3);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  color: var(--fb-text-input);
  background: var(--fb-surface);
  resize: vertical;
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
textarea.settings-mono:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}

/* GLEAM-035: monospace pre block (e.g. cert JSON viewer).
 * Shares the same font/size/padding recipe as textarea.settings-mono
 * but is not interactive — no border, no focus ring, no resize. */
pre.settings-mono {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 13px;
  padding: var(--fb-s-3);
  background: var(--fb-canvas);
  border-radius: var(--fb-r-md);
  white-space: pre-wrap;
  color: var(--fb-text-input);
  margin: 0;
}

/* ── Retention purge dashboard ────────────────────────────────
 * Used by admin_retention.html. Re-uses settings-card / settings-help
 * tokens; adds badge chips for run/request status and a basic
 * tabular layout for run history.
 */
.settings-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin: var(--fb-s-3) 0;
}
.settings-table th,
.settings-table td {
  text-align: left;
  padding: var(--fb-s-2) var(--fb-s-3);
  border-bottom: 1px solid var(--fb-border);
  vertical-align: top;
}
/* GLEAM-087: align .settings-table th to the matrix-table section-label recipe:
 * 11px / 600 / 1px tracking / --fb-text-section — identical to .matrix-table th. */
.settings-table th {
  font-weight: 600;
  color: var(--fb-text-section);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
}

/* ── Idle-timeout warning modal ────────────────────────────── */
.modal-scrim {
  position: fixed;
  inset: 0;
  background: var(--fb-scrim);
  z-index: 1099;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(var(--fb-s-6), env(safe-area-inset-top))
           max(var(--fb-s-6), env(safe-area-inset-right))
           max(var(--fb-s-6), env(safe-area-inset-bottom))
           max(var(--fb-s-6), env(safe-area-inset-left));
}
.modal-scrim[hidden] { display: none; }
body.is-modal-open { overflow: hidden; }
.modal-card {
  background: var(--fb-surface);
  border-radius: var(--fb-r-lg);
  /* Spec-sanctioned overlay shadow per parity lock-in §3 — modals
   * carry the discrete drawer/scrim drop, not the flat-card stack. */
  box-shadow: var(--fb-sh-overlay);
  padding: var(--fb-s-6);
  width: 100%;
  max-width: 480px;
  z-index: 1100;
  animation: modal-in 0.15s ease;
}
@keyframes modal-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .modal-card { animation: none; }
}
/* GLEAM-101 / GLEAM-114: align modal-card h2 to a documented 18px
 * dialog-title tier — one scale step above body (14px) and below
 * page-h1 (28px).  See tokens.css heading-scale comment for full
 * tier table. */
.modal-card h2 {
  font-size: 18px;
  font-weight: 600;
  color: var(--fb-text-heading);
  margin: 0 0 var(--fb-s-3);
}
.modal-body {
  color: var(--fb-text);
  font-size: 14px;
  line-height: 1.5;
  margin: 0 0 var(--fb-s-5);
}
#stw-countdown {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--fb-danger);
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--fb-s-3);
}
/* DEPRECATED for admin list tables (2026-05-05): the per-row inline
 * <form> wrapper used to sit inside every <td class="col-actions">; it
 * has been replaced by the shared row-actions macro
 * (app/templates/macros/row_actions.html). Kept here because settings
 * cards (admin_retention.html), admin_setup.html, _admin_shell.html, and
 * several non-table surfaces still wrap individual buttons in
 * .inline-form for inline layout. */
.inline-form { display: inline; margin: 0; }
.gap-r { margin-right: var(--fb-s-3); }

/* ── IP whitelist lockout-prevention modal ─────────────────── */
.ipw-modal-card {
  max-width: 480px;
}
.ipw-modal-detail {
  margin: 0 0 var(--fb-s-3);
}
.ipw-modal-detail code {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 13px;
  background: var(--fb-surface-2);
  padding: 2px 6px;
  border-radius: var(--fb-r-sm);
  color: var(--fb-text-input);
}
.ipw-cidr-list {
  margin: 0 0 var(--fb-s-4);
  padding-left: var(--fb-s-5);
  font-family: ui-monospace, Menlo, monospace;
  font-size: 13px;
  color: var(--fb-text-input);
  max-height: 160px;
  overflow-y: auto;
}
.ipw-cidr-list li {
  margin: 2px 0;
}
.ipw-cidr-list .ipw-cidr-empty {
  font-family: inherit;
  color: var(--fb-danger);
}
.ipw-coverage {
  font-size: 13px;
  margin: 0 0 var(--fb-s-4);
  color: var(--fb-text-muted);
}
.ipw-coverage.ipw-covered {
  color: var(--fb-success);
}
.ipw-coverage.ipw-not-covered {
  color: var(--fb-danger);
  font-weight: 600;
}

/* GLEAM-044: vertical checkbox stack — replaces inline flex-column on group
 * containers. Pairs with .checkbox-label rows inside. */
.checkbox-stack {
  display: flex;
  flex-direction: column;
  gap: var(--fb-s-1);
}

/* GLEAM-044: tight lede variant — removes bottom margin for use inside
 * form-rows where a following element provides its own top spacing. */
.lede-tight {
  margin: 0 0 var(--fb-s-2);
}

/* Checkbox label rows */
.checkbox-label {
  display: flex;
  align-items: center;
  gap: var(--fb-s-2);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--fb-text);
  cursor: pointer;
}
.checkbox-label input[type=checkbox] { margin: 0; }

.form-actions {
  display: flex;
  align-items: center;
  gap: var(--fb-s-3);
  margin-top: var(--fb-s-5);
}

/* ── Fieldset styled as a bordered card (radio / checkbox groups) ──
 * Replaces per-instance inline border/padding/border-radius on
 * <fieldset> elements in admin forms (GLEAM-034). */
.fieldset-card {
  border: 1px solid var(--fb-border);
  padding: var(--fb-s-4);
  border-radius: var(--fb-r-lg);
  margin: 0;
}
.fieldset-card legend {
  padding: 0 var(--fb-s-2);
  font-weight: 600;
  font-size: 13px;
  color: var(--fb-text-input);
}

/* ── Radio / checkbox option row ───────────────────────────────────
 * Full-width tap target: flex label with leading control, consistent
 * spacing. Replaces per-label inline style attributes (GLEAM-034). */
.radio-row {
  display: flex;
  align-items: flex-start;
  gap: var(--fb-s-3);
  padding: var(--fb-s-2) 0;
  cursor: pointer;
  font-size: 13px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--fb-text);
  margin-bottom: 0;
}
.radio-row input[type=radio],
.radio-row input[type=checkbox] {
  margin-top: 4px;
  flex-shrink: 0;
}

.status-pill {
  display: inline-block;
  padding: var(--fb-s-1) var(--fb-s-3);
  border-radius: var(--fb-r-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--fb-text-inverted);
  background: var(--fb-text-section);
}
.status-pill-ok { background: var(--fb-success); }
/* GLEAM-034: mirror GLEAM-021/GLEAM-025 — --fb-blue (#3FA9F5) fails AA with white text;
 * --fb-blue-strong (#1F6FAA) ≈5.4:1 passes WCAG AA. */
.status-pill-update { background: var(--fb-blue-strong); }
.status-pill-error { background: var(--fb-danger); }

.badge {
  display: inline-block;
  /* GLEAM-139: raised from off-scale 2px to on-scale var(--fb-s-1) (4px)
   * vertical padding; aligns micro-pill family across .cd-row-mode-chip,
   * .badge, .badge-status, .pickup-tags-count to a single scale value. */
  padding: var(--fb-s-1) var(--fb-s-2);
  border-radius: var(--fb-r-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.badge-ok    { background: var(--fb-badge-ok-bg);    color: var(--fb-badge-ok-fg); }
.badge-warn  { background: var(--fb-badge-warn-bg);  color: var(--fb-badge-warn-fg); }
.badge-error { background: var(--fb-badge-error-bg); color: var(--fb-badge-error-fg); }
.badge-muted { background: var(--fb-badge-muted-bg); color: var(--fb-badge-muted-fg); }

/* ── iOS Safari auto-zoom prevention ────────────────────────────
 * iOS Safari auto-zooms when a focused input's font-size is below 16px.
 * Bump all form controls to 16px on narrow (≤540px) viewports only;
 * desktop 14px default is preserved (GLEAM-027). */
@media (max-width: 540px) {
  input[type=email],
  input[type=password],
  input[type=text],
  input[type=date],
  input[type=time],
  input[type=file],
  select,
  textarea.form-input,
  textarea.settings-mono,
  .settings-form input[type=number] {
    font-size: 16px;
  }
  /* GLEAM-035: bump toggle-form buttons to 44px touch target on mobile (WCAG 2.5.8). */
  .toggle-form button {
    min-height: 44px;
    padding: 0 var(--fb-s-3);
    font-size: 13px;
  }
  /* GLEAM-133: filter-bar inputs/selects must meet 44px touch target on phone (WCAG 2.5.8). */
  .filter-bar input,
  .filter-bar select {
    min-height: 44px;
    padding: var(--fb-s-2) var(--fb-s-3);
  }
  /* GLEAM-098: reduce modal padding on phones and clamp width to viewport
   * so landscape-phone modals don't clip their action row.  Drops from
   * 24px (--fb-s-6) to 16px (--fb-s-4) and subtracts a 24px gutter on
   * each side via calc(100vw - var(--fb-s-6)). */
  .modal-card {
    padding: var(--fb-s-4);
    max-width: calc(100vw - var(--fb-s-6));
  }
}

/* ── Tablet iPad-class tier (768-1024px) ────────────────────────────
 * Added by the May 2026 tablet-breakpoint sweep. iPad portrait (820)
 * and iPad 10th gen (768) sit between the ≤540 phone tier (which fires
 * the iOS-zoom bump) and the desktop default. On a tablet a 14px
 * input is comfortable to read but a finger tap on a 44px control can
 * still skip rows; this block raises form-control / button heights
 * one tier and ups input font-size to 16px so iPadOS Safari does not
 * pinch-zoom on focus, matching the phone-tier rule.  The .btn-sm
 * 44px floor already covers all sm-variant buttons at every viewport
 * (GLEAM-046); this band only widens the touch target on the default
 * .btn so primary actions feel as substantial as on phone. */
@media (min-width: 768px) and (max-width: 1024px) {
  input[type=email],
  input[type=password],
  input[type=text],
  input[type=date],
  input[type=time],
  input[type=file],
  select,
  textarea.form-input,
  textarea.settings-mono,
  .settings-form input[type=number],
  .settings-form input[type=text],
  .settings-form input[type=date],
  .settings-form input[type=time] {
    font-size: 16px;
    height: 48px;
  }
  textarea.form-input,
  textarea.settings-mono {
    /* textareas have height: auto via the recipe; bump min-height
     * instead of overriding to a fixed 48 so the resize handle still
     * works as expected. */
    height: auto;
    min-height: 96px;
  }
  .btn {
    height: 48px;
    line-height: 48px;
  }
  /* GLEAM-137: align .cd-mute height to .btn's 48px iPad-tier value so
   * all topbar-right controls share a visual baseline on iPad.
   * Consolidated here from classroom-dispatcher.css and greeter.css
   * per GLEAM-147 (single source of truth). */
  .cd-mute { height: 48px; }
  /* GLEAM-007: match reveal-button height to the 48px input tier on tablet. */
  .password-reveal { height: 48px; }
  /* .btn-sm preserves its compact 44px height on tablet so the dense
   * matrix-table action columns (Edit / Disable / Reset) don't crowd
   * out the data they decorate. */
  .btn-sm {
    height: 44px;
    line-height: 42px;
  }
  /* GLEAM-124: capability ON/OFF chip touch target raised to 44px on tablet —
   * mirrors the GLEAM-035 phone-tier bump. The capability override toggle
   * lives on the per-staff edit page (was the wide capability matrix);
   * at ~26px the target passed WCAG 2.5.8 minimum but was too easy to
   * mis-tap. */
  .toggle-form button {
    min-height: 44px;
    padding: 0 var(--fb-s-3);
    font-size: 13px;
  }
  /* Modal: trim padding so a confirmation modal centred on iPad
   * portrait doesn't push its CTAs out of the viewport on landscape. */
  .modal-card {
    padding: var(--fb-s-5);
    max-width: 480px;
  }
  /* GLEAM-133: filter-bar touch targets raised to 48px on tablet to match .btn. */
  .filter-bar input,
  .filter-bar select {
    min-height: 48px;
    padding: var(--fb-s-2) var(--fb-s-3);
  }
}

/* ── Change-request queue: status pills + filter bar ───────────────
 * Used by /admin/change-requests (mig 0028).  status-pill colors
 * mirror the build-spec §11 palette: pending=warn, approved=ok,
 * rejected=error, expired=muted, superseded=muted with strikethrough. */
.badge-status {
  display: inline-block;
  /* GLEAM-139: on-scale token padding (same sweep as .badge above). */
  padding: var(--fb-s-1) var(--fb-s-2);
  border-radius: var(--fb-r-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.badge-status-pending    { background: var(--fb-badge-warn-bg);  color: var(--fb-badge-warn-fg); }
.badge-status-approved   { background: var(--fb-badge-ok-bg);    color: var(--fb-badge-ok-fg); }
.badge-status-rejected   { background: var(--fb-badge-error-bg); color: var(--fb-badge-error-fg); }
.badge-status-expired    { background: var(--fb-badge-muted-bg); color: var(--fb-badge-muted-fg); }
.badge-status-superseded { background: var(--fb-badge-muted-bg); color: var(--fb-badge-muted-fg); text-decoration: line-through; }

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--fb-s-2);
  margin: var(--fb-s-3) 0;
  align-items: center;
}
.filter-bar input,
.filter-bar select {
  padding: var(--fb-s-2) var(--fb-s-3);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  font-size: 14px;
}

.rrule-snippet {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
  font-size: 12px;
  color: var(--fb-text-muted);
  background: var(--fb-badge-muted-bg);
  padding: var(--fb-s-1) var(--fb-s-2);
  border-radius: var(--fb-r-md);
}

.checkbox-inline {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-right: var(--fb-s-3);
  font-weight: 400;
}

.form-fieldset {
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  padding: var(--fb-s-3);
  margin: var(--fb-s-3) 0;
}
.form-fieldset legend {
  font-weight: 600;
  padding: 0 var(--fb-s-2);
}

/* ── Pickup Tags admin surface ───────────────────────────────────
 * /admin/pickup-tags — filter sidebar (multi-select pills) + main
 * panel listing matching student / family cards.  Uses the shared
 * admin-shell tokens; pill / row chrome lives here so the page can
 * stay in admin_pickup_tags.html without bespoke <style> blocks.
 */
.pickup-tags-layout {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: var(--fb-s-4);
  align-items: start;
}
@media (max-width: 900px) {
  .pickup-tags-layout { grid-template-columns: 1fr; }
}
.pickup-tags-filter {
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  padding: var(--fb-s-3);
  background: var(--fb-surface);
}
.pickup-tags-filter-heading { margin: 0 0 var(--fb-s-3); font-size: 14px; }
.pickup-tags-filter-section { border: 0; padding: 0; margin: 0 0 var(--fb-s-3); }
.pickup-tags-filter-section legend { font-weight: 600; font-size: 12px; padding: 0; margin-bottom: var(--fb-s-2); }
.pill-group { display: flex; flex-wrap: wrap; gap: var(--fb-s-1); }
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px var(--fb-s-3); border-radius: var(--fb-r-pill);
  border: 1px solid var(--fb-border);
  background: var(--fb-surface); font-size: 13px; line-height: 1.2;
  cursor: pointer; user-select: none;
  transition: background-color 80ms ease-out, border-color 80ms ease-out, color 80ms ease-out;
}
.pill:hover { background: var(--fb-canvas); }
.pill input {
  /* Hide the native checkbox/radio — the pill itself is the visual
   * affordance. Keep it interactive for assistive tech: the input
   * still owns the focus ring + checked state. */
  position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0;
}
.pill:has(input:focus-visible) { box-shadow: var(--fb-sh-focus-ring); }
.pill:has(input:checked) {
  background: var(--fb-blue-strong); color: var(--fb-text-inverted);
  border-color: transparent; font-weight: 600;
}
.pill:has(input:checked) small { color: inherit; opacity: 0.85; }
/* Vertical pill list for long-label sets (classrooms, families) where
 * horizontal wrapping looks ragged. Same pill chrome, full-width rows,
 * scroll cap so a 30-classroom school doesn't dominate the sidebar. */
.pickup-tags-pill-list {
  display: flex; flex-direction: column; gap: var(--fb-s-1);
  max-height: 280px; overflow-y: auto;
  /* Inset the scroll-bar gutter so the rightmost pills don't slam into it. */
  padding-right: var(--fb-s-1);
}
.pill-block { width: 100%; justify-content: flex-start; }
/* Segmented control for the Surface radios — visually joined buttons,
 * selected state matches the pill checked state for consistency. */
.pickup-tags-segmented {
  display: flex; border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md); overflow: hidden; background: var(--fb-surface);
}
.pickup-tags-segmented-item {
  flex: 1; display: inline-flex; align-items: center; justify-content: center;
  padding: 6px var(--fb-s-2); font-size: 13px; line-height: 1.2;
  cursor: pointer; user-select: none; text-align: center;
  border-left: 1px solid var(--fb-border);
  transition: background-color 80ms ease-out, color 80ms ease-out;
}
.pickup-tags-segmented-item:first-child { border-left: 0; }
.pickup-tags-segmented-item:hover { background: var(--fb-canvas); }
.pickup-tags-segmented-item input {
  position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0;
}
.pickup-tags-segmented-item:has(input:focus-visible) { box-shadow: var(--fb-sh-focus-ring); z-index: 1; }
.pickup-tags-segmented-item:has(input:checked) {
  background: var(--fb-blue-strong); color: var(--fb-text-inverted);
  font-weight: 600;
}
.pickup-tags-filter-actions { display: flex; gap: var(--fb-s-2); margin-top: var(--fb-s-3); }

/* Filter-update progress indicator. pickup-tags-filter.js toggles
 * `.is-loading` on .pickup-tags-layout the instant the user changes a
 * filter; the bar at the top of the main panel and the dimmed result
 * list signal the request is in flight. The class is one-shot — the
 * eventual navigation replaces the page entirely. */
.pickup-tags-layout.is-loading .pickup-tags-main {
  position: relative;
  opacity: 0.55;
  pointer-events: none;
  transition: opacity 100ms ease-out;
}
.pickup-tags-layout.is-loading .pickup-tags-main::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--fb-blue-strong) 30%,
    var(--fb-blue-strong) 70%,
    transparent 100%
  );
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: pickup-tags-progress 900ms linear infinite;
}
@keyframes pickup-tags-progress {
  0%   { background-position: -40% 0; }
  100% { background-position: 140% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pickup-tags-layout.is-loading .pickup-tags-main::before {
    animation: none;
    background: var(--fb-blue-strong);
    background-size: 100% 100%;
  }
}
.pickup-tags-main { min-width: 0; }
.pickup-tags-results-heading { font-size: 16px; margin: 0 0 var(--fb-s-3); display: flex; align-items: center; gap: var(--fb-s-2); }
.pickup-tags-count {
  display: inline-block; min-width: 24px; text-align: center;
  /* GLEAM-139: on-scale token padding (same sweep as .badge above). */
  padding: var(--fb-s-1) var(--fb-s-2); border-radius: var(--fb-r-pill); background: var(--fb-badge-muted-bg);
  font-size: 12px; font-weight: 500;
}
.pickup-tags-card-list { display: flex; flex-direction: column; gap: var(--fb-s-2); }
.pickup-tags-row {
  display: flex; justify-content: space-between; align-items: center; gap: var(--fb-s-3);
  padding: var(--fb-s-3);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  background: var(--fb-surface);
}
.pickup-tags-row-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pickup-tags-row-actions { display: flex; gap: var(--fb-s-1); flex-wrap: wrap; justify-content: flex-end; }
.empty-state {
  padding: var(--fb-s-4); text-align: center;
  border: 1px dashed var(--fb-border); border-radius: var(--fb-r-md);
  background: var(--fb-surface);
}
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Per-staff capability override editor ──────────────────────────
 * Renders inside the staff edit page (admin_staff_edit.html) — replaces
 * the wide /admin/staff/capabilities matrix.  Layout is a stacked list
 * of rows, one row per capability inside the user's role ceiling, each
 * with a meta block (description + state) and a toggle chip.
 * Trace: tasks/20260505-replace-capability-matrix-with-per-staff-override-editor.md
 */
.cap-override-group + .cap-override-group { margin-top: var(--fb-s-4); }
.cap-override-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--fb-s-3);
}
.cap-override-row {
  display: flex;
  gap: var(--fb-s-4);
  align-items: flex-start;
  justify-content: space-between;
  padding: var(--fb-s-3) 0;
  border-top: 1px solid var(--fb-border);
}
.cap-override-row:first-child { border-top: 0; padding-top: 0; }
.cap-override-meta { flex: 1 1 auto; min-width: 0; }
.cap-override-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fb-s-2);
}
.cap-override-code {
  font-weight: 600;
  font-size: 13px;
  color: var(--fb-text-input);
  text-transform: capitalize;
}
.cap-override-badge { letter-spacing: 0.4px; }
.cap-override-desc {
  font-size: 12px;
  color: var(--fb-text-muted);
  margin: var(--fb-s-1) 0 0 0;
}
.cap-override-state {
  font-size: 11px;
  color: var(--fb-text-muted);
  margin: var(--fb-s-1) 0 0 0;
}
.cap-override-row.is-override .cap-override-code { color: var(--fb-text-heading); }

/* The override editor sits inside .settings-card, which has max-width: 560px.
 * The list rows benefit from a wider container so descriptions don't wrap
 * after every few words.  Apply a wider clamp to the cap-override card
 * specifically without affecting other settings-card instances. */
.settings-card:has(.cap-override-list) {
  max-width: 880px;
}

/* ── Family-member picker (admin_families_edit.html) ─────────────────
 * Search-filtered checkbox list with a pinned "Selected" section
 * above the filterable "All eligible" list.  Replaces the flat <ul>
 * of every eligible student so the admin surface scales past Hope's
 * ~500-student roster.  Form semantics unchanged — same checkbox
 * inputs named `member_student_ids`.
 * Trace: tasks/20260505-replace-family-members-checkbox-list-with-searchable-picker.md.
 */
.member-picker-fieldset {
  border: 0;
  padding: 0;
  margin: 0;
}
.member-picker-fieldset legend {
  padding: 0;
  font-weight: 600;
  font-size: 13px;
  color: var(--fb-text-input);
  margin-bottom: var(--fb-s-2);
}

/* ── Search-first family-member picker ──────────────────────────────
 * Layout: removable chips above search input; results panel below
 * shows "Suggested" (smart match against family label) by default,
 * "Search results" when a query is typed. Mirrors the consensus
 * shape used by Linear / GitHub / Notion / Stripe / Vercel for
 * "associate-with-this-record" pickers — no upfront roster dump. */

.member-picker {
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  padding: var(--fb-s-3);
  background: var(--fb-surface);
}

/* Selected chips ───────────────────────────────────────────────── */
.member-picker-chips {
  list-style: none;
  margin: 0 0 var(--fb-s-3);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--fb-s-2);
}
.member-picker-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--fb-s-2);
  padding: var(--fb-s-1) var(--fb-s-2) var(--fb-s-1) var(--fb-s-3);
  background: var(--fb-canvas);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-pill);
  font-size: 13px;
  color: var(--fb-text-input);
  max-width: 100%;
}
.member-picker-chip-name {
  /* Name grows preferentially; min-width:0 lets it shrink below
   * its content width so the meta + remove button stay reachable
   * inside narrow chips. */
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-picker-chip-meta {
  /* Meta shrinks first when the chip is narrow — name keeps its
   * legibility, room/grade ellipsis instead. */
  flex: 0 1 auto;
  min-width: 0;
  color: var(--fb-text-muted);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-picker-chip-remove {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fb-text-muted);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
}
.member-picker-chip-remove:hover {
  background: var(--fb-row-hover);
  color: var(--fb-text-input);
}
.member-picker-chip-remove:focus-visible {
  outline: none;
  box-shadow: var(--fb-sh-focus-ring);
}
.member-picker-empty-selected {
  margin: 0 0 var(--fb-s-3);
  font-size: 12px;
  color: var(--fb-text-muted);
  font-style: italic;
}

/* Search input ─────────────────────────────────────────────────── */
.member-picker-search {
  margin-bottom: var(--fb-s-3);
}
.member-picker-search-input {
  width: 100%;
  height: 44px;
  padding: 0 var(--fb-s-3);
  font: inherit;
  font-size: 14px;
  color: var(--fb-text-input);
  background: var(--fb-surface);
  border: 1px solid var(--fb-border);
  border-radius: var(--fb-r-md);
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.member-picker-search-input:focus-visible {
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}

/* Results / Suggested panel ─────────────────────────────────────── */
.member-picker-results-heading {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--fb-text-section);
  margin: 0 0 var(--fb-s-2);
}
.member-picker-results-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 320px;
  overflow-y: auto;
}
.member-picker-result {
  margin: 0;
}
.member-picker-result-add {
  appearance: none;
  display: flex;
  align-items: baseline;
  gap: var(--fb-s-3);
  width: 100%;
  padding: var(--fb-s-2) var(--fb-s-3);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--fb-r-md);
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: var(--fb-text);
  transition: background 120ms ease, border-color 120ms ease;
}
.member-picker-result-add:hover {
  background: var(--fb-row-hover);
}
.member-picker-result-add:focus-visible {
  outline: none;
  border-color: var(--fb-blue-strong);
  box-shadow: var(--fb-sh-focus-ring);
}
.member-picker-result-name {
  flex: 1 1 auto;
  font-weight: 500;
  color: var(--fb-text-input);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-picker-result-meta {
  flex: 0 1 auto;
  color: var(--fb-text-muted);
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.member-picker-result-plus {
  flex: 0 0 auto;
  color: var(--fb-blue-strong);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.member-picker-results-empty {
  margin: 0;
  font-size: 13px;
  color: var(--fb-text-muted);
  font-style: italic;
}

/* ──────────────────────────────────────────────────────────────────
 * Layout primitives — see docs/ux/reference/build-spec.md
 * §"Layout Primitives". Every admin edit/detail page composes these
 * classes; per-page CSS overrides are reserved for genuine one-offs.
 *
 * Breakpoints (literal — @media can't read CSS vars):
 *   mobile  ≤  720px   (aligns with chrome.css sidebar collapse)
 *   tablet  >  720px and ≤ 900px
 *   desktop >  900px   (aligns with forms.css pickup-tags-layout)
 * ──────────────────────────────────────────────────────────────── */

/* ── Page container ──────────────────────────────────────────────── */
.page {
  max-width: clamp(960px, 92vw, 1280px);
  margin: 0 auto;
  padding: var(--fb-s-6) var(--fb-s-6) max(var(--fb-s-6), env(safe-area-inset-bottom));
}
@media (max-width: 900px) {
  .page { padding: var(--fb-s-5) var(--fb-s-5) max(var(--fb-s-5), env(safe-area-inset-bottom)); }
}
@media (max-width: 720px) {
  .page { padding: var(--fb-s-4) var(--fb-s-4) max(var(--fb-s-4), env(safe-area-inset-bottom)); }
}

/* ── Form grid (12-col responsive) ───────────────────────────────── */
.form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--fb-s-4);
  row-gap: var(--fb-s-4);
}
.form-grid > .form-row { grid-column: 1 / -1; margin-bottom: 0; }
.form-grid > .form-row.span-3  { grid-column: span 3; }
.form-grid > .form-row.span-4  { grid-column: span 4; }
.form-grid > .form-row.span-6  { grid-column: span 6; }
.form-grid > .form-row.span-8  { grid-column: span 8; }
.form-grid > .form-row.span-9  { grid-column: span 9; }
.form-grid > .form-row.span-12 { grid-column: 1 / -1; }
@media (max-width: 720px) {
  .form-grid > .form-row.span-3,
  .form-grid > .form-row.span-4,
  .form-grid > .form-row.span-6,
  .form-grid > .form-row.span-8,
  .form-grid > .form-row.span-9 { grid-column: 1 / -1; }
}

/* ── Record-detail layout (two-zone at desktop) ──────────────────── */
.detail-page {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: var(--fb-s-5);
  row-gap: var(--fb-s-5);
}
.detail-page > .detail-primary,
.detail-page > .detail-aside,
.detail-page > .detail-full {
  grid-column: 1 / -1;
  min-width: 0;
}
.detail-page > .detail-primary > * + *,
.detail-page > .detail-aside   > * + *,
.detail-page > .detail-full    > * + * { margin-top: var(--fb-s-5); }
@media (min-width: 901px) {
  .detail-page { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
  .detail-page > .detail-primary { grid-column: 1; }
  .detail-page > .detail-aside   { grid-column: 2; }
  .detail-page > .detail-full    { grid-column: 1 / -1; }
}

/* ── Card density tiers ──────────────────────────────────────────── */
.card-primary,
.card-secondary,
.card-inline {
  background: var(--fb-surface);
  border-radius: var(--fb-r-lg);
  margin-top: 0;
}
.card-primary {
  border: 1px solid var(--fb-border);
  padding: var(--fb-s-6);
}
.card-secondary {
  border: 1px solid var(--fb-border);
  padding: var(--fb-s-4);
}
.card-inline {
  border: 0;
  padding: 0;
}
@media (max-width: 720px) {
  .card-primary { padding: var(--fb-s-4); }
}

/* ── Responsive table (label/value reflow at mobile) ─────────────── */
.table-stack {
  width: 100%;
  border-collapse: collapse;
}
.table-stack th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--fb-text-section);
  padding: var(--fb-s-2) var(--fb-s-4);
  border-bottom: 1px solid var(--fb-border);
}
.table-stack td {
  padding: var(--fb-s-3) var(--fb-s-4);
  font-size: 14px;
  color: var(--fb-text);
  vertical-align: middle;
}
.table-stack tbody tr + tr td { border-top: 1px solid var(--fb-border); }
.table-stack tbody tr:hover td { background: var(--fb-row-hover); }

/* `.matrix-table` (existing admin list selector — see chrome.css) gets
 * the same responsive degradation as `.table-stack`. Pages that add
 * `data-label="…"` to <td> elements get readable labels in the mobile
 * stack; pages that haven't been migrated still degrade — values
 * stack without the synthetic header — instead of blowing past the
 * viewport. */
@media (max-width: 900px) {
  /* Tablet: drop low-priority columns. */
  .table-stack [data-priority="low"],
  .matrix-table [data-priority="low"] { display: none; }
}

@media (max-width: 720px) {
  /* Mobile: each row reflows to a labeled card stack. */
  .table-stack thead, .matrix-table thead { display: none; }
  .table-stack tbody, .table-stack tr, .table-stack td,
  .matrix-table tbody, .matrix-table tr, .matrix-table td {
    display: block;
    width: 100%;
  }
  .table-stack tr,
  .matrix-table tr {
    border: 1px solid var(--fb-border);
    border-radius: var(--fb-r-md);
    padding: var(--fb-s-2) var(--fb-s-3);
    margin-bottom: var(--fb-s-2);
  }
  .table-stack tbody tr + tr td,
  .matrix-table tbody tr + tr td { border-top: 0; }
  .table-stack td,
  .matrix-table td {
    padding: var(--fb-s-1) 0;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--fb-s-3);
    border-bottom: 0;
  }
  .table-stack td[data-label]::before,
  .matrix-table td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--fb-text-section);
  }
  .table-stack td[data-label=""]::before,
  .matrix-table td[data-label=""]::before { content: none; }
  /* Empty-state cell, full-row, ignores label. */
  .table-stack td.matrix-table-empty-cell,
  .matrix-table td.matrix-table-empty-cell {
    justify-content: center;
    text-align: center;
  }
  .table-stack td.matrix-table-empty-cell::before,
  .matrix-table td.matrix-table-empty-cell::before { display: none; }
}

/* ── Action bar (form footer strip) ──────────────────────────────── */
.form-actions {
  display: flex;
  align-items: center;
  gap: var(--fb-s-3);
  padding-top: var(--fb-s-5);
  margin-top: var(--fb-s-5);
  border-top: 1px solid var(--fb-border);
}
.form-actions > .btn-danger,
.form-actions > .form-actions-spacer { margin-left: auto; }
@media (max-width: 720px) {
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions > .btn,
  .form-actions > form { width: 100%; }
  .form-actions > .btn-danger { margin-left: 0; }
}

/* Heading inside .card-primary / .card-secondary — same visual size
 * as the prior `.settings-card h2` to preserve the heading hierarchy
 * the legacy `<h2>Quick actions</h2>` etc. relied on. */
.card-heading {
  margin: 0 0 var(--fb-s-2);
  font-size: 16px;
  font-weight: 600;
  color: var(--fb-text-heading);
}
.card-secondary > .card-heading + .lede,
.card-primary > .card-heading + .lede { margin-top: 0; }

/* Empty-state cell inside a .table-stack. Spans every column at
 * desktop/tablet; mobile reflow drops the colspan and the data-label
 * "" rule (above) hides the synthetic label so the empty copy reads
 * as a single centered line. */
.table-stack-empty {
  text-align: center;
  font-style: italic;
  color: var(--fb-text-muted);
  padding: var(--fb-s-4);
}
@media (max-width: 720px) {
  .table-stack td.table-stack-empty { display: block; justify-content: center; }
  .table-stack td.table-stack-empty::before { display: none; }
}
