/* SMART Compliance — shared stylesheet.

   LIGHT CORPORATE THEME, built for an oil & gas compliance client whose
   users skew 50–70 years old. Insurance-portal clarity: white page, white
   cards with visible borders, dark slate text, one corporate blue accent.

   Older-user hard rules honored throughout:
   - body text 17px minimum, no font weight below 400
   - primary actions are solid filled buttons with borders
   - every icon is paired with a visible word label
   - links are underlined; contrast ≥ 7:1 for body text, 4.5:1 floor
   - minimal motion; prefers-reduced-motion disables the rest */

:root {
  /* ==========================================================
     PROVISIONAL PALETTE — client brand hexes swap here.
     Every color in the app flows from these tokens; there are
     no hardcoded colors outside this block.
     ========================================================== */

  /* page + surfaces */
  --page: #f7fafc;            /* barely-tinted page background */
  --surface: #ffffff;         /* cards, sheets, table rows */
  --surface-sunken: #f1f5f8;  /* inputs, wells inside cards */
  --border: #c9d4de;          /* visible light card borders */
  --border-strong: #8fa1b0;   /* input borders, emphasis rules */

  /* ink */
  --ink: #16232e;             /* body text — ~15:1 on white */
  --ink-soft: #3d4c5a;        /* secondary text — ~9:1 on white */

  /* corporate blue accent (client brand: white with light blue) */
  --brand: #0f5e99;           /* solid buttons, selected borders */
  --brand-strong: #0b4674;    /* hover / pressed / text on tint */
  --brand-tint: #e4eff8;      /* pale blue selected/highlight fill */
  --brand-tint-border: #9ec4e0;
  --link: #0b4674;            /* underlined links — ~9:1 on white */
  --focus-ring: #0f5e99;

  /* status: done-green (plain fill = up to date; striped = officer-confirmed) */
  --ok-ink: #0d5c2a;
  --ok-fill: #e2f3e7;
  --ok-stripe: #c5e6d0;
  --ok-border: #4d9a66;

  /* status: overdue-red */
  --danger-ink: #a11a12;
  --danger-fill: #fbe6e4;
  --danger-border: #d4675f;
  --danger-solid: #b3261e;

  /* status: no-record gray (still dark enough to read) */
  --norec-ink: #414c58;
  --norec-fill: #edf1f4;
  --norec-border: #93a2b0;

  /* status: pending — deliberately teal, NOT the brand blue, plus
     stripes + hourglass so it never reads as a brand element */
  --pending-ink: #085a60;
  --pending-fill: #ddf0f1;
  --pending-stripe: #b9e0e2;
  --pending-border: #2e8a91;

  /* neutral slate for future-deadline markers (never severity-colored) */
  --neutral-ink: #3d4c5a;
  --neutral-fill: #e8edf2;
  --neutral-border: #93a2b0;

  /* ==========================================================
     ADMIN two-axis families — OFFICER-ONLY, kept SEPARATE from
     the crew status tokens above (crew never references these).
     COLOR carries urgency; the leading icon carries type. Every
     text/background pair is contrast-checked >= 4.5:1 (most are
     >= 7:1). The admin ORANGE (nudge) is sanctioned here and is a
     distinct hue from every crew token — no crew surface uses it.
     ========================================================== */
  /* CRIMSON — late (past due / expired, missed test, retraining) */
  --adm-late-ink: #8e1230;
  --adm-late-fill: #fbe1e9;
  --adm-late-border: #d16a86;
  /* ORANGE — nudge (needs booking, window open): DARK text on orange fill */
  --adm-nudge-ink: #6b3200;
  --adm-nudge-fill: #ffd8a8;
  --adm-nudge-border: #d98a3d;
  /* PURPLE — records gap (no record on file) */
  --adm-gap-ink: #55208a;
  --adm-gap-fill: #efe3fb;
  --adm-gap-border: #a984d6;
  /* BLUE — on the calendar: calm tint (scheduled / class) + solid event (team test) */
  --adm-cal-ink: #0b4674;
  --adm-cal-fill: #dcebf9;
  --adm-cal-border: #90bfe4;
  --adm-cal-solid: #0f5e99;
  /* GRAY — a future fact (deliberately never a severity color) */
  --adm-future-ink: #37434f;
  --adm-future-fill: #e9eef2;
  --adm-future-border: #8c9ba9;
  /* GREEN — done (solid fill) / confirmed (stripes, and ONLY confirmed) */
  --adm-done-ink: #0d5c2a;
  --adm-done-fill: #e2f3e7;
  --adm-done-stripe: #c5e6d0;
  --adm-done-border: #4d9a66;
  /* TEAL — waiting on you: SOLID, white text, NO stripes anywhere on admin teal */
  --adm-wait-solid: #0a6b71;
  --adm-wait-border: #075257;

  /* overlays + shadows (the only translucent colors in the app) */
  --backdrop: rgba(22, 35, 46, 0.55);
  --shadow-soft: 0 2px 10px rgba(22, 35, 46, 0.10);
  --shadow-raised: 0 8px 28px rgba(22, 35, 46, 0.18);

  /* type scale — 3 levels + body; nothing below 400 weight anywhere */
  --font-body: 17px;
  --font-small: 16px;   /* metadata floor — never smaller in flowing text */
  --font-h1: 28px;
  --font-h2: 22px;
  --font-h3: 18px;

  /* 8px spacing grid */
  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 40px;

  --radius: 8px;
  --radius-lg: 12px;
  --touch: 48px; /* minimum touch target */
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--page);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: var(--font-body);
  line-height: 1.55;
}

main {
  max-width: 1000px;
  margin: 0 auto;
  padding: var(--s3) var(--s2) 96px;
}

h1,
h2,
h3 {
  margin: 0 0 var(--s1);
  color: var(--ink);
  line-height: 1.25;
}

h1 { font-size: var(--font-h1); font-weight: 700; }
h2 { font-size: var(--font-h2); font-weight: 700; }
h3 { font-size: var(--font-h3); font-weight: 700; }

p {
  margin: 0 0 var(--s2);
}

a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
}

a:hover {
  color: var(--brand);
}

.muted,
.text-soft {
  color: var(--ink-soft);
}

/* visible keyboard focus everywhere */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 4px;
}

/* screen-reader-only text (status words on chips, etc.) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- icons ------------------------------------------------------------- */

.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  vertical-align: -4px;
}

/* --- top-of-page header shared by both zones ----------------------------
   EXACTLY ONE LINE, same token scale in both apps: the mobile hamburger
   (when the app shows one) + shield + wordmark, and — at desktop — the
   app's tab bar inline on the same row (margin-left:auto pushes it
   right). The old subtitle line is deleted. Vertical padding is the
   shared 6px compact scale; the safe-area padding-top rules further
   down still win where they apply. */

.app-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 6px var(--s2);
}

.app-header .inner {
  max-width: 1000px;
  margin: 0 auto;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}

.app-header .wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--font-h3);
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink);
  text-decoration: none;
}

.app-header .wordmark:hover {
  color: var(--brand-strong);
  text-decoration: underline;
}

.app-header .wordmark .icon {
  color: var(--brand);
}

.app-header .wordmark .wordmark-logo {
  height: 20px;
  width: auto;
  flex-shrink: 0;
  vertical-align: -4px;
}

/* --- desktop tab bar (crew, >=840px only) ---------------------------------
   The hash router's visible face at desktop width: the crew tabs riding
   the header's one line, real anchors so keyboard operation comes for
   free, active state drawn from the existing brand tokens. Never
   rendered below the breakpoint — the hamburger drawer owns mobile
   navigation. */

.desktop-tabs {
  display: none;
}

@media (min-width: 840px) {
  .desktop-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--s1);
    margin-left: auto;
    min-width: 0;
  }

  .desktop-tabs[hidden] { display: none; }

  .desktop-tab {
    display: inline-flex;
    align-items: center;
    min-height: var(--touch);
    padding: 10px var(--s3);
    background: var(--surface);
    border: 2px solid var(--border-strong);
    border-radius: var(--radius);
    color: var(--ink);
    font-weight: 600;
    text-decoration: none;
  }

  .desktop-tab:hover {
    border-color: var(--brand);
    background: var(--brand-tint);
    color: var(--ink);
  }

  .desktop-tab:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 1px;
  }

  .desktop-tab.is-current {
    border-color: var(--brand);
    background: var(--brand-tint);
    color: var(--brand-strong);
  }
}

/* --- unread badge on nav destinations (crew Messages tab/drawer item,
   admin Inbox tab): a small red count pill, nothing at zero --- */

.nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  margin-left: var(--s1);
  padding: 0 6px;
  border-radius: 999px;
  background: var(--danger-solid);
  color: var(--surface);
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
}

/* --- officer tab bar (admin, DESKTOP only) --------------------------------
   Same anatomy as the crew desktop tabs: real hash anchors, >=48px
   targets, token-drawn active state, riding the header's one line.
   Below the breakpoint the tab row is GONE — the crew-style hamburger
   drawer owns officer navigation there (Menu button in the header,
   drawer over the dialog machinery). */

.admin-tabs {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s1);
  margin-left: auto;
  min-width: 0;
}

@media (max-width: 839.98px) {
  .admin-tabs { display: none; }
}

.admin-tab {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch);
  padding: 10px var(--s3);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

.admin-tab:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--ink);
}

.admin-tab:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}

.admin-tab.is-current {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-strong);
}


/* --- buttons -------------------------------------------------------------
   Primary actions are SOLID FILLED buttons with borders — they must
   unmistakably look like buttons. No ghost/text-only primary actions. */

.btn-primary,
.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  min-height: var(--touch);
  padding: 10px 22px;
  border-radius: var(--radius);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
}

.btn-primary {
  background: var(--brand);
  color: var(--surface);
  border: 2px solid var(--brand-strong);
}

.btn-primary:hover:not(:disabled) {
  background: var(--brand-strong);
}

.btn-primary:disabled {
  background: var(--norec-fill);
  border-color: var(--border);
  color: var(--ink-soft);
  cursor: not-allowed;
}

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--border-strong);
}

.btn-secondary:hover:not(:disabled) {
  border-color: var(--brand);
  color: var(--brand-strong);
  background: var(--brand-tint);
}

.btn-secondary:disabled {
  color: var(--ink-soft);
  border-color: var(--border);
  background: var(--surface-sunken);
  cursor: not-allowed;
}

.btn-danger-secondary {
  border-color: var(--danger-border);
  color: var(--danger-ink);
}

.btn-danger-secondary:hover:not(:disabled) {
  background: var(--danger-fill);
  border-color: var(--danger-solid);
  color: var(--danger-ink);
}

/* in-text button, e.g. "Start over" — underlined like a link, still ≥ touch height */
.link-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--touch);
  background: none;
  border: none;
  color: var(--link);
  font: inherit;
  font-weight: 600;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.link-btn:hover {
  color: var(--brand);
}

/* =====================================================================
   Status chips — ONE component for every context.
   Every status renders icon + word + color (all three channels).
   The calendar legend renders these exact same chips, so the legend can
   never drift from the live UI.
   ===================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1.5px solid;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
}

.chip .icon {
  width: 16px;
  height: 16px;
}

/* the TWO greens: plain fill = "Up to date" (self-reported, in date);
   stripe marks = "Done — confirmed" (the officer signed off). Same hue —
   the stripes + check are what say "verified". */
.chip--up-to-date {
  color: var(--ok-ink);
  border-color: var(--ok-border);
  background: var(--ok-fill);
}

.chip--confirmed {
  color: var(--ok-ink);
  border-color: var(--ok-border);
  background: repeating-linear-gradient(
    135deg,
    var(--ok-fill),
    var(--ok-fill) 5px,
    var(--ok-stripe) 5px,
    var(--ok-stripe) 10px
  );
}

/* RED — act now. The ONE red family class every red chip wears:
   "Book by [date]", "Due today", "Expired [date]", "No record" — the
   color says act, the chip's words say what. */
.chip--act {
  color: var(--danger-ink);
  border-color: var(--danger-border);
  background: var(--danger-fill);
}

/* scheduled: calm brand-blue tint — a class is booked, nothing to do */
.chip--scheduled {
  color: var(--brand-strong);
  border-color: var(--brand-tint-border);
  background: var(--brand-tint);
}

/* team test: the one SOLID brand-blue chip — an officer-scheduled event,
   not a personal status */
.chip--team-test {
  color: var(--surface);
  border-color: var(--brand-strong);
  background: var(--brand);
}

/* small progress badge inside a chip (admin team-test chip: "3/12") */
.chip-badge {
  padding: 0 6px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--brand-strong);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.4;
}

/* admin-only "Not taken" result chip — a neutral gray fact */
.chip--no-record {
  color: var(--norec-ink);
  border-color: var(--norec-border);
  background: var(--norec-fill);
}

/* pending: teal + stripes + hourglass — never reads as the brand blue */
.chip--waiting {
  color: var(--pending-ink);
  border-color: var(--pending-border);
  background: repeating-linear-gradient(
    135deg,
    var(--pending-fill),
    var(--pending-fill) 5px,
    var(--pending-stripe) 5px,
    var(--pending-stripe) 10px
  );
}

/* GRAY — a future fact, deliberately NOT a severity color. Carries the
   item's TYPE icon like every other chip (color says urgency, icon says
   kind) — it just never turns red or gets an action. */
.chip--neutral {
  color: var(--neutral-ink);
  border-color: var(--neutral-border);
  background: var(--neutral-fill);
}

/* class marker — outlined blue, SOLID border: a booked class day */
.chip--class-day {
  color: var(--brand-strong);
  border: 2px solid var(--brand);
  background: var(--surface);
}

.chip--demo {
  color: var(--brand-strong);
  border-color: var(--brand-tint-border);
  background: var(--brand-tint);
}

/* --- ADMIN two-axis chip families (officer-only) -----------------------
   COLOR carries urgency; the leading icon (chipHtml 'type' mode) carries
   the requirement kind. None of these render a status icon. */
.chip--adm-late {
  color: var(--adm-late-ink);
  border-color: var(--adm-late-border);
  background: var(--adm-late-fill);
}

/* PAST CEILING — the dual clock's breach tier. The SAME red family as
   chip--adm-late (no new color system); it reads stronger through
   non-color treatment alone: solid ink fill + heavier weight. */
.chip--adm-breach {
  color: var(--surface);
  border-color: var(--adm-late-ink);
  background: var(--adm-late-ink);
  font-weight: 800;
}

.chip--adm-nudge {
  color: var(--adm-nudge-ink);
  border-color: var(--adm-nudge-border);
  background: var(--adm-nudge-fill);
}

.chip--adm-gap {
  color: var(--adm-gap-ink);
  border-color: var(--adm-gap-border);
  background: var(--adm-gap-fill);
}

.chip--adm-calendar {
  color: var(--adm-cal-ink);
  border-color: var(--adm-cal-border);
  background: var(--adm-cal-fill);
}

/* the ONE solid blue event chip (team test) — white text + progress badge */
.chip--adm-team {
  color: var(--surface);
  border-color: var(--adm-cal-solid);
  background: var(--adm-cal-solid);
}

.chip--adm-future {
  color: var(--adm-future-ink);
  border-color: var(--adm-future-border);
  background: var(--adm-future-fill);
}

/* GREEN done: plain fill = up to date; STRIPES = confirmed (and nowhere else) */
.chip--adm-done {
  color: var(--adm-done-ink);
  border-color: var(--adm-done-border);
  background: var(--adm-done-fill);
}

.chip--adm-confirmed {
  color: var(--adm-done-ink);
  border-color: var(--adm-done-border);
  background: repeating-linear-gradient(
    135deg,
    var(--adm-done-fill),
    var(--adm-done-fill) 5px,
    var(--adm-done-stripe) 5px,
    var(--adm-done-stripe) 10px
  );
}

/* TEAL waiting: SOLID, white text, never striped */
.chip--adm-waiting {
  color: var(--surface);
  border-color: var(--adm-wait-border);
  background: var(--adm-wait-solid);
}

/* --- calendar-grid variant of the SAME chip component -------------------
   Fixed height, single line, ellipsis truncation, icon always visible —
   markers never overflow their day cell. */

.chip-cal {
  width: 100%;
  max-width: 100%;
  min-width: 0; /* flex item: may shrink below min-content, never overflow the cell */
  height: 26px;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 14px;
  justify-content: flex-start;
  cursor: pointer;
  overflow: hidden;
}

.chip-cal .icon {
  width: 14px;
  height: 14px;
}

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

button.chip-cal {
  font-family: inherit;
}

button.chip-cal:hover {
  border-width: 2px;
  padding-left: 7px;
  padding-right: 7px;
}

/* --- basis chips (admin/data layer, parked) --- */

.chip-basis {
  font-size: 13px;
  background: var(--surface);
  color: var(--ink-soft);
  border-color: var(--border-strong);
}

.verify-marker {
  display: inline-block;
  margin-left: var(--s1);
  color: var(--ink-soft);
  font-size: var(--font-small);
  border-bottom: 1px dotted var(--border-strong);
  cursor: help;
}

/* --- persona picker (root index) ----------------------------------------- */

.picker {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s4) var(--s2);
}

.picker-logo {
  display: block;
  height: 56px;
  width: auto;
  margin: 0 auto var(--s2);
}

.picker h1 {
  margin-bottom: var(--s1);
}

.picker .subtitle {
  margin: 0 0 var(--s4);
  color: var(--ink-soft);
}

.persona-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  justify-content: center;
}

.persona-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  width: 300px;
  max-width: 100%;
  padding: var(--s3) var(--s3) var(--s3);
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--ink);
  text-decoration: none;
  box-shadow: var(--shadow-soft);
}

.persona-card:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--ink);
}

.persona-card .persona-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--brand-tint);
  border: 2px solid var(--brand-tint-border);
  color: var(--brand);
  margin-bottom: var(--s1);
}

.persona-card:hover .persona-icon {
  background: var(--surface);
}

.persona-card .persona-icon .icon {
  width: 38px;
  height: 38px;
}

.persona-card .persona-title {
  font-size: var(--font-h3);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  color: var(--link);
}

.persona-card .hint {
  font-size: var(--font-small);
  font-weight: 400;
  color: var(--ink-soft);
  margin: 0;
}

/* the third door — SAFE Chart Intake. A FULL card, matching the two
   persona doors exactly (same .persona-card component: icon medallion,
   title, shadow, hover — the old slim-row modifier rules are deleted).
   The three cards sit three-across at desktop widths and stack at phone
   widths through the same flex-wrap the pair always used. */

/* --- facility stats strip (admin, parked but legible) -------------------- */

.stats-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--s2);
  margin: var(--s3) 0;
}

.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s2);
  box-shadow: var(--shadow-soft);
}

.stat-value {
  font-size: var(--font-h1);
  font-weight: 700;
  color: var(--ink);
}

.stat-label {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin-top: 4px;
}

.stat-card.good .stat-value { color: var(--ok-ink); }
.stat-card.bad .stat-value { color: var(--danger-ink); }
.stat-card.idle .stat-value { color: var(--norec-ink); }

.coming-note {
  margin-top: var(--s4);
  padding: var(--s3);
  background: var(--surface);
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink-soft);
  text-align: center;
}

/* --- requirement table (admin/data layer, parked) ------------------------ */

.req-table-wrap { overflow-x: auto; }

table.req-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
}

.req-table th {
  text-align: left;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
  padding: var(--s1) var(--s2);
  border-bottom: 2px solid var(--border-strong);
  white-space: nowrap;
}

.req-table td {
  padding: var(--s1) var(--s2);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.req-table .cell-sub {
  display: block;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.req-table .cell-missing {
  color: var(--ink-soft);
  font-style: italic;
}

/* =====================================================================
   Crew flow — landing, wizard, calendar, sheets, assistant
   ===================================================================== */

/* --- floating demo pill --- */

.demo-pill {
  position: fixed;
  right: var(--s2);
  bottom: var(--s2);
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--touch);
  padding: 10px 20px;
  background: var(--surface);
  border: 2px solid var(--brand);
  border-radius: 999px;
  color: var(--brand-strong);
  font-size: var(--font-small);
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow: var(--shadow-raised);
}

.demo-pill[hidden] { display: none; }

.demo-pill:hover { background: var(--brand-tint); }

.demo-pill-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--brand);
  animation: demo-pulse 1.8s ease-in-out infinite;
}

@keyframes demo-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* --- landing --- */

.landing {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--s1);
  padding: var(--s4) var(--s2);
}

.landing-sub {
  color: var(--ink-soft);
  margin: 0 0 var(--s2);
  max-width: 34rem;
}

.landing-cta {
  font-size: var(--font-h3);
  padding: 14px 40px;
}

.landing-hint {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin-top: var(--s3);
}

/* --- friendly reset screen (corrupted saved profile) --- */

.reset-screen {
  max-width: 560px;
  margin: var(--s5) auto;
  padding: var(--s4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  text-align: center;
  box-shadow: var(--shadow-soft);
}

.reset-screen p {
  color: var(--ink-soft);
}

/* --- wizard shell --- */

.wiz-shell {
  max-width: 680px;
  margin: 0 auto;
}

.wiz-progress {
  height: 8px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: var(--s1);
}

.wiz-progress-bar {
  height: 100%;
  background: var(--brand);
  transition: width 0.25s ease;
}

.wiz-step-count {
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
  margin: 0 0 var(--s3);
}

.wiz-step {
  min-height: 200px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--s3);
  box-shadow: var(--shadow-soft);
}

.wiz-question {
  margin-bottom: var(--s1);
}

.wiz-sub {
  color: var(--ink-soft);
  margin: 0 0 var(--s2);
}

.wiz-ask {
  font-weight: 600;
  margin: var(--s2) 0 var(--s1);
}

.wiz-group-tag {
  display: inline-block;
  color: var(--brand-strong);
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  border-radius: 6px;
  padding: 2px 10px;
  font-size: var(--font-small);
  font-weight: 600;
  margin-bottom: var(--s2);
}

.wiz-text-input,
.wiz-date-input {
  width: 100%;
  min-height: var(--touch);
  padding: 10px 14px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
}

/* Native date inputs carry an intrinsic minimum width and UA chrome that
   can punch through width:100% on narrow phones (the iOS wheel/input
   overflowed the booking dialog at 380px) — strip the native appearance
   and pin them to their container so they render exactly like text
   inputs everywhere. */
.wiz-date-input {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: var(--touch); /* >= 48px */
  font-size: var(--font-body); /* >= 17px */
  text-align: left;
}

/* iOS/WebKit centers the date value by default — keep it left-aligned
   like every other input */
.wiz-date-input::-webkit-date-and-time-value {
  text-align: left;
}

.wiz-choice-date {
  min-width: 0;
  overflow: hidden;
}

.wiz-text-input:focus,
.wiz-date-input:focus {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--brand);
}

.wiz-choices {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.wiz-choice {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--s2);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
  min-height: var(--touch);
}

.wiz-choice:hover {
  border-color: var(--brand);
}

.wiz-choice.selected {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.wiz-choice-simple { font-weight: 600; }

.wiz-choice-date { cursor: default; }

.wiz-choice-head {
  display: flex;
  align-items: baseline;
  gap: var(--s1);
  background: none;
  border: none;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  padding: 0;
  margin-bottom: var(--s1);
  cursor: pointer;
  width: 100%;
  text-align: left;
  min-height: 32px;
}

.wiz-choice-hint {
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 400;
}

.wiz-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
}

.wiz-cancel-row {
  margin-top: var(--s2);
  text-align: center;
}

/* --- position picker grid --- */

.position-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--s1);
  margin: var(--s2) 0 var(--s1);
}

.position-button {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  text-align: left;
  padding: 12px var(--s2);
  min-height: 64px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.position-button:hover { border-color: var(--brand); }

.position-button.selected {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.position-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  color: var(--brand);
}

.position-button.selected .position-icon {
  background: var(--surface);
}

.position-icon .icon {
  width: 24px;
  height: 24px;
}

.position-button .pos-title {
  font-weight: 600;
  display: block;
}

.position-button .category {
  display: block;
  margin-top: 2px;
  font-size: var(--font-small);
  color: var(--ink-soft);
}

/* --- review step --- */

.review-list {
  list-style: none;
  margin: 0 0 var(--s2);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.review-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: 12px var(--s2);
  border-bottom: 1px solid var(--border);
}

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

.review-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.review-row-label { font-weight: 600; }

.review-row-answer {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.review-row-answer.review-missing {
  color: var(--danger-ink);
  font-weight: 600;
}

.review-edit {
  flex-shrink: 0;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: 6px;
  color: var(--link);
  padding: 8px 16px;
  min-height: 44px;
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.review-edit:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.review-group-title {
  font-size: var(--font-small);
  color: var(--ink-soft);
  font-weight: 600;
  margin: var(--s2) 0 var(--s1);
}

.review-warning {
  margin: 0 0 var(--s2);
  padding: 12px var(--s2);
  background: var(--danger-fill);
  border: 1px solid var(--danger-border);
  border-radius: var(--radius);
  color: var(--danger-ink);
  font-weight: 600;
}

.wiz-finish-btn {
  width: 100%;
  margin-top: var(--s2);
  font-size: var(--font-h3);
  padding: 14px;
}

/* --- needs attention (pinned above the calendar) --- */

.needs-attention {
  margin-bottom: var(--s3);
}

.needs-attention-title {
  color: var(--danger-ink);
  margin-bottom: var(--s1);
}

.needs-attention-list {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.needs-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: var(--s2);
  background: var(--surface);
  border: 2px solid var(--danger-border);
  border-radius: var(--radius);
}

.needs-card-open {
  display: block;
  flex: 1;
  min-width: 0;
  text-align: left;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
}

.needs-card-title {
  margin: var(--s1) 0 4px;
}

.needs-card-blurb {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.needs-card-btn { flex-shrink: 0; }

/* --- recently confirmed strip --- */

.confirmed-strip {
  margin-bottom: var(--s3);
  padding: var(--s2);
  background: var(--ok-fill);
  border: 1px solid var(--ok-border);
  border-radius: var(--radius);
}

.confirmed-strip-title {
  color: var(--ok-ink);
  margin-bottom: var(--s1);
}

.confirmed-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  padding: var(--s1) 0;
}

.confirmed-row + .confirmed-row {
  border-top: 1px solid var(--ok-border);
}

.confirmed-row-label { font-weight: 600; }

.confirmed-row-date {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.confirmed-dismiss {
  margin-left: auto;
  min-height: 44px;
  padding: 6px 14px;
  background: var(--surface);
  border: 2px solid var(--ok-border);
  border-radius: 6px;
  color: var(--ok-ink);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
}

.confirmed-dismiss:hover { border-color: var(--ok-ink); }

/* --- calendar top: the four consolidated control rows --- */

/* row 1 — title + Demo-data chip inline (+ the Start over link at
   desktop; mobile carries nothing else — My Info owns Start over) */
.cal-title-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-bottom: var(--s2);
  min-width: 0;
}

.cal-title-row .cal-title { margin: 0; }

/* row 2 — [Month | Week | Day] left, the compact Guide button right
   (mobile only; desktop has the side panel) */
.cal-toolbar-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s1);
  margin: 0 0 var(--s1);
}

.cal-toolbar-row .cal-viewswitch { margin: 0; }

/* row 4 — the compact test-jump: two half-width secondary buttons,
   visually lighter than the main controls but still >=48px targets */
.cal-test-nav {
  display: flex;
  align-items: stretch;
  gap: var(--s1);
  margin: 0 0 var(--s1);
}

.cal-testjump-btn {
  flex: 1;
  justify-content: center;
  min-height: var(--touch);
  padding: 6px 12px;
  font-size: var(--font-small);
  color: var(--ink-soft);
}

.cal-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  margin: var(--s1) 0 var(--s2);
}

.cal-nav-btn { padding: 8px 18px; }

.cal-month-label {
  font-size: var(--font-h2);
  font-weight: 700;
  min-width: 11rem;
  text-align: center;
}

/* --- all-clear month banner (replaces the old dark grayed film) --- */

.allclear-banner {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: 0 0 var(--s2);
  padding: 12px var(--s2);
  background: var(--ok-fill);
  border: 1px solid var(--ok-border);
  border-radius: var(--radius);
  color: var(--ok-ink);
  font-weight: 600;
}

/* an empty month for someone who still has attention items: neutral
   statement of fact, not a green celebration */
.allclear-banner--muted {
  background: var(--neutral-fill);
  border-color: var(--neutral-border);
  color: var(--neutral-ink);
}

/* --- calendar layout: legend + main grid --- */

.cal-layout {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.cal-main { min-width: 0; }

.cal-grid-wrap {
  margin: var(--s1) 0 var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s1);
  box-shadow: var(--shadow-soft);
}

.cal-weekday-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-bottom: 4px;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
  text-align: center;
}

.cal-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.cal-week {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.cal-day {
  position: relative;
  min-height: 84px;
  min-width: 0;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.cal-day-empty {
  background: var(--page);
  border-color: transparent;
}

/* every real day is tappable on the crew month grid (a bare tap opens the
   day sheet — app.js delegation, no extra cell markup); scoped to the
   crew grid zone so other calendars keep their own affordances */
#cal-grid-zone .cal-day:not(.cal-day-empty) {
  cursor: pointer;
}

.cal-day.is-today {
  border: 2px solid var(--brand);
  background: var(--brand-tint);
}

.cal-day-num {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: var(--font-small);
  font-weight: 600;
  color: var(--ink-soft);
}

.cal-day.is-today .cal-day-num { color: var(--brand-strong); }

.cal-today-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-strong);
}

.cal-day-count {
  display: none;
  font-weight: 700;
  color: var(--ink-soft);
  font-size: 13px;
}

.cal-day-markers {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 4px;
  min-width: 0;
}

/* --- Up next (the ONE card list, both widths) --- */

.upnext { margin: 0 0 var(--s3); }

.upnext-title { margin-bottom: var(--s1); }

.upnext-list {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.upnext-empty { margin: 0; color: var(--ink-soft); }

.upnext-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s2);
  box-shadow: var(--shadow-soft);
}

.upnext-card-title { margin-bottom: 4px; }

.upnext-card-meta {
  margin: 0 0 var(--s1);
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.upnext-card-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-bottom: var(--s1);
}

.upnext-note {
  font-size: var(--font-small);
  font-weight: 600;
}

/* the one note kind: a book-by date still ahead is a planning fact, not
   an alarm (an open window has no note — the red chip's words say it) */
.upnext-note--planned { color: var(--ink-soft); }

.upnext-action {
  display: flex;
  width: 100%;
}

/* --- color legend --- */

.legend {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}

/* the desktop Guide panel's head row: the "Guide" title + the horizontal
   collapse control. The panel only ever renders at >=840px (both apps),
   so these carry no mobile variant; the rail button below hides until the
   panel collapses. */
.guide-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s1);
  padding: 4px 6px 4px var(--s2);
  border-bottom: 1px solid var(--border);
}

.guide-panel-title { font-weight: 700; }

.guide-collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--touch);
  min-height: var(--touch);
  background: none;
  border: none;
  border-radius: 8px;
  color: var(--ink-soft);
  cursor: pointer;
}

.guide-collapse-btn:hover { background: var(--brand-tint); color: var(--ink); }

/* chevrons: collapse points toward the panel's edge (right), expand back
   toward the calendar (left) — the shared chevron-down sprite, rotated */
.guide-collapse-chevron { transform: rotate(-90deg); }
.guide-expand-chevron { transform: rotate(90deg); }

/* the collapsed panel's slim vertical rail: one full-height expand button
   wearing the rotated "Guide" label; hidden while the panel is expanded */
.guide-rail-btn { display: none; }

.guide-panel.is-collapsed .guide-panel-head,
.guide-panel.is-collapsed .guide-panel-scroll { display: none; }

.guide-panel.is-collapsed .guide-rail-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  width: 100%;
  height: 100%;
  min-height: 180px;
  padding: var(--s2) 0;
  background: none;
  border: none;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.guide-rail-btn:hover { background: var(--brand-tint); }

.guide-rail-label {
  writing-mode: vertical-rl;
  letter-spacing: 0.04em;
}

.legend-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: var(--s2);
}

.legend-row {
  display: flex;
  align-items: center;
  gap: 6px var(--s1);
  flex-wrap: wrap;
}

.legend-row .chip { flex-shrink: 0; }

/* the plain-words line sits on its own line under the row's chip(s) —
   grouped rows can carry two chips, and the meaning reads as one sentence */
.legend-desc {
  color: var(--ink-soft);
  font-size: var(--font-small);
  flex-basis: 100%;
  line-height: 1.35;
}

/* the Guide's ICON KEY row (crew + admin) — the four TYPE icons with
   one-word labels, teaching "icon = what kind" before the color rows */
.guide-iconkey {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1) var(--s2);
}

.guide-iconkey-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  color: var(--ink);
}

.guide-iconkey-item .icon {
  width: 18px;
  height: 18px;
  color: var(--ink-soft);
}

@media (min-width: 840px) {
  .cal-layout {
    flex-direction: row;
    align-items: flex-start;
  }

  .cal-main {
    flex: 1;
    order: 1;
  }

  .legend {
    order: 2;
    flex: 0 0 250px;
    position: sticky;
    top: var(--s2);
    /* the horizontal collapse rides a simple width transition — the global
       prefers-reduced-motion block flattens it to instant */
    transition: flex-basis 200ms ease;
  }
}

/* ADMIN desktop: the facility-calendar + Guide pair is an EQUAL-HEIGHT row.
   The calendar drives the row height; the Guide panel stretches to match it
   and scrolls INTERNALLY when its content runs longer — so neither element
   ever dangles past the other. Scoped to body.admin: the crew calendar
   keeps its own sticky side panel, untouched. The panel's scroll wrapper is
   a zero-intrinsic-height flex fill holding an absolute body (the calendar
   alone sizes the row). */
@media (min-width: 840px) {
  body.admin .cal-layout {
    align-items: stretch;
  }

  body.admin .cal-layout .legend {
    order: 2;
    flex: 0 0 260px;
    position: relative;
    top: auto;
    align-self: stretch;
    min-height: 0;
    display: flex;
    flex-direction: column;
  }

  body.admin .cal-layout .legend .guide-panel-scroll {
    position: relative;
    flex: 1;
    min-height: 0;
  }

  body.admin .cal-layout .legend .legend-body {
    position: absolute;
    inset: 0;
    overflow-y: auto;
  }

  /* BOTH apps: the collapsed panel is the slim vertical rail — the flexed
     calendar column consumes the freed width on its own. Selector
     specificity deliberately matches the admin base rule above so the
     collapsed basis wins in either app. */
  .cal-layout .legend.guide-panel.is-collapsed,
  body.admin .cal-layout .legend.is-collapsed {
    flex: 0 0 56px;
  }
}

/* --- detail / email sheets (real dialogs) --- */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  background: var(--backdrop);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s3);
  z-index: 100;
}

.sheet {
  position: relative;
  width: 100%;
  max-width: 520px;
  max-height: 88vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--s3);
  box-shadow: var(--shadow-raised);
}

.sheet-close-btn {
  position: absolute; /* the drawer's corner Close; sheet dialogs re-seat it in .sheet-head */
  top: var(--s1);
  right: var(--s1);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--touch);
  padding: 8px 14px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
}

.sheet-close-btn:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

/* the ONE sheet-header pattern (shared/ui.js sheetHeadHtml): title block
   left, Close right, a guaranteed 16px column gap at any width — the
   title wraps under itself, never under Close */
.sheet-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s2);
  margin: 0 0 var(--s2);
}

.sheet-head-main {
  flex: 1;
  min-width: 0;
}

.sheet-head .sheet-close-btn {
  position: static;
  flex-shrink: 0;
}

.sheet-head .sheet-title {
  margin-bottom: 0;
}

.sheet-title {
  margin-bottom: var(--s1);
}

.sheet-blurb {
  color: var(--ink-soft);
  margin: 0 0 var(--s2);
}

.sheet-facts {
  margin: var(--s2) 0;
  border-top: 1px solid var(--border);
}

.sheet-fact {
  display: flex;
  justify-content: space-between;
  gap: var(--s2);
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.sheet-fact dt { color: var(--ink-soft); }

.sheet-fact dd {
  margin: 0;
  font-weight: 600;
  text-align: right;
}

.sheet .btn-primary,
.sheet-block-btn {
  display: flex;
  width: 100%;
  margin-top: var(--s1);
}

.sheet-links {
  margin-top: var(--s2);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
  text-align: center;
}

/* --- pending notice + completion form --- */

.pending-notice {
  margin: var(--s2) 0;
  padding: 12px var(--s2);
  border: 2px solid var(--pending-border);
  border-radius: var(--radius);
  background: repeating-linear-gradient(
    135deg,
    var(--pending-fill),
    var(--pending-fill) 6px,
    var(--pending-stripe) 6px,
    var(--pending-stripe) 12px
  );
}

.pending-notice-text {
  margin: 0;
  color: var(--pending-ink);
  font-weight: 600;
}

.complete-form {
  margin: var(--s2) 0;
  padding: var(--s2);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.complete-form-label {
  display: block;
  margin-bottom: var(--s1);
  color: var(--ink);
  font-weight: 600;
}

.complete-form .wiz-date-input {
  margin-bottom: var(--s1);
  background: var(--surface);
}

.complete-form-hint {
  margin: 0 0 var(--s1);
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* --- toast --- */

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--s3);
  transform: translateX(-50%);
  z-index: 200;
  max-width: calc(100% - 32px);
  padding: 12px var(--s3);
  background: var(--ink);
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  color: var(--surface);
  font-weight: 600;
  text-align: center;
  box-shadow: var(--shadow-raised);
  pointer-events: none;
}

.toast--error {
  background: var(--danger-solid);
  border-color: var(--danger-ink);
}

/* --- SMART Assistant bubbles ---------------------------------------------
   (The old embedded page-section shell — .assistant card, head row,
   "Guided demo" tag, capped thread — is DELETED. Every chat surface now
   mounts the ONE shared chat shell; see the chat-page section below.) */

.bubble {
  max-width: 92%;
  padding: 12px var(--s2);
  border-radius: var(--radius-lg);
  line-height: 1.5;
}

.bubble-assistant {
  align-self: flex-start;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-bottom-left-radius: 4px;
  color: var(--ink);
}

.bubble-user {
  align-self: flex-end;
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  border-bottom-right-radius: 4px;
  color: var(--ink);
  font-weight: 600;
}

.bubble p { margin: 0 0 var(--s1); }
.bubble p:last-child { margin-bottom: 0; }

.bubble-list {
  margin: var(--s1) 0 0;
  padding-left: 22px;
}

.bubble-list li + li { margin-top: var(--s1); }

.bubble-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  margin-top: var(--s1);
}

.bubble-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--touch);
  padding: 8px 16px;
  background: var(--surface);
  border: 2px solid var(--brand);
  border-radius: var(--radius);
  color: var(--brand-strong);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
}

.bubble-action-btn:hover { background: var(--brand-tint); }

.assistant-options {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin-top: var(--s2);
}

.assistant-option-btn {
  display: flex;
  align-items: center;
  gap: var(--s1);
  width: 100%;
  min-height: var(--touch);
  padding: 10px var(--s2);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.assistant-option-btn:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.assistant-option-btn .icon { color: var(--brand); }

.assistant-option-btn--primary {
  border-color: var(--brand);
  color: var(--brand-strong);
  justify-content: center;
}

.assistant-textrow {
  display: flex;
  gap: var(--s1);
  margin-top: var(--s2);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
}

.assistant-text-input {
  flex: 1;
  min-width: 0;
  min-height: var(--touch);
  padding: 10px 14px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
}

.assistant-text-input:focus {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--brand);
}

/* mobile floating "Ask SMART" bubble — navigates to the #assistant view.
   app.js hides it (the hidden attribute) on the assistant view and while
   any dialog/sheet/drawer is open, and publishes its measured height as
   --fab-clearance so scrollable views always keep clear of it. */
.ask-smart-fab {
  position: fixed;
  right: var(--s2);
  bottom: var(--s2);
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--touch);
  padding: 12px 20px;
  background: var(--brand);
  color: var(--surface);
  border: 2px solid var(--brand-strong);
  border-radius: 999px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--shadow-raised);
}

.ask-smart-fab[hidden] { display: none; }

@media (min-width: 840px) {
  .ask-smart-fab { display: none; }
}

/* --- email panel --- */

.email-field {
  display: flex;
  gap: var(--s1);
  padding: var(--s1) 0;
  border-bottom: 1px solid var(--border);
}

.email-field-label {
  color: var(--ink-soft);
  min-width: 5rem;
  flex-shrink: 0;
  font-weight: 600;
}

.email-body {
  white-space: pre-wrap;
  font-family: inherit;
  font-size: inherit;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s2);
  margin: var(--s2) 0;
  line-height: 1.6;
}

.email-note {
  color: var(--ink-soft);
  font-size: var(--font-small);
  text-align: center;
  margin: var(--s2) 0 0;
}

/* =====================================================================
   Requirement rows — ONE component (shared/ui.js reqRowHtml) used by the
   crew "Up next" strip and the admin person sheet.
   ===================================================================== */

.req-row {
  display: flex;
  align-items: stretch;
  gap: var(--s1);
}

.req-row-open {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  padding: 12px var(--s2);
  min-height: var(--touch);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  text-align: left;
}

button.req-row-open { cursor: pointer; }

button.req-row-open:hover { border-color: var(--brand); }

.req-row-main {
  display: flex;
  flex: 1;
  min-width: 0;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px var(--s2);
}

.req-row-label { font-weight: 600; }

.req-row-meta {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* the booking line — full width under the label + chip */
.req-row-booking {
  flex-basis: 100%;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-small);
  font-weight: 600;
}

.req-row-booking .icon {
  width: 16px;
  height: 16px;
}

.req-row-booking--booked { color: var(--brand-strong); }

.req-row-booking--unbooked { color: var(--danger-ink); }

/* --- booking form + warning (detail sheet) --- */

.booking-form {
  margin: var(--s2) 0;
  padding: var(--s2);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.booking-form-label {
  display: block;
  margin-bottom: var(--s1);
  color: var(--ink);
  font-weight: 600;
}

.booking-form .wiz-date-input {
  margin-bottom: var(--s1);
  background: var(--surface);
}

.booking-warning {
  margin: 0 0 var(--s1);
  padding: 10px var(--s2);
  background: var(--danger-fill);
  border: 1px solid var(--danger-border);
  border-radius: var(--radius);
  color: var(--danger-ink);
  font-size: var(--font-small);
  font-weight: 600;
}

.booking-warning:empty { display: none; }

.sheet-btn-row {
  display: flex;
  gap: var(--s1);
  margin-top: var(--s1);
}

.sheet-btn-row .btn-secondary,
.sheet-btn-row .btn-danger-secondary {
  flex: 1;
}

/* =====================================================================
   Test Runner (crew) — intro sheet, question flow, results
   ===================================================================== */

.test-fact-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin: var(--s2) 0;
}

.test-progress-label {
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
  margin: 0 0 var(--s1);
}

.test-question {
  font-size: var(--font-h3);
  font-weight: 700;
  margin: 0 0 var(--s2);
}

.test-options {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin-bottom: var(--s2);
}

.test-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px var(--s2);
  min-height: var(--touch);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.test-option:hover { border-color: var(--brand); }

.test-option.selected {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.test-score {
  text-align: center;
  margin: var(--s2) 0;
}

.test-score-number {
  display: block;
  font-size: 56px;
  font-weight: 700;
  line-height: 1.1;
}

.test-score--passed .test-score-number { color: var(--ok-ink); }

.test-score--failed .test-score-number { color: var(--danger-ink); }

.test-score-label {
  display: block;
  font-weight: 700;
  font-size: var(--font-h3);
}

.test-review {
  margin: var(--s2) 0 0;
  border-top: 1px solid var(--border);
  padding-top: var(--s2);
}

.test-review-item {
  padding: var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  margin-bottom: var(--s1);
}

.test-review-q {
  font-weight: 600;
  margin: 0 0 var(--s1);
}

.test-review-a {
  margin: 0 0 4px;
  color: var(--ok-ink);
  font-weight: 600;
}

.test-review-why {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* =====================================================================
   Admin dashboard — facility calendar, roster, day/person/test sheets,
   Test Builder. Reuses the crew calendar grid + chip components; these
   rules only size and arrange the admin skin.
   ===================================================================== */

/* (the old .adm-headline block is gone — the app header now carries the
   facility name and the as-of date; the facility-calendar section is the
   dashboard's first content under the tabs) */

.adm-section {
  margin-top: var(--s4);
}

.adm-section:first-child {
  margin-top: 0;
}

.adm-section-head {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin-bottom: var(--s1);
}

.adm-section-head .icon { color: var(--brand); }

.adm-section-head h2 { margin: 0; }

.adm-section-sub {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin: 0 0 var(--s2);
}

/* facility calendar day cells are taller — they hold up to 3 mini-chips */
.adm-cal .cal-day { min-height: 108px; }

/* Geometry-constrained like the grid mini-chips (the sanctioned sub-48px
   exception): as tall as a capped day cell allows, and a miss still lands
   on the day cell itself, which opens the same day sheet. */
.cal-day-more {
  display: block;
  width: 100%;
  min-height: 30px;
  padding: 2px 6px;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--link);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  text-align: left;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.cal-day-more:hover { background: var(--brand-tint); }

/* --- roster table --- */

.roster-wrap {
  /* positioned so absolutely-placed descendants (chip sr-only text) stay
     clipped by this scroll container instead of overflowing the page */
  position: relative;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}

table.roster {
  width: 100%;
  border-collapse: collapse;
}

.roster th {
  text-align: left;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
  padding: var(--s1) var(--s2);
  border-bottom: 2px solid var(--border-strong);
  white-space: nowrap;
}

.roster td {
  padding: 10px var(--s2);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.roster tr:last-child td { border-bottom: none; }

.roster tbody tr { cursor: pointer; }

.roster tbody tr:hover td { background: var(--brand-tint); }

.roster-name-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--touch);
  background: none;
  border: none;
  padding: 0;
  color: var(--link);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  white-space: nowrap;
}

.roster-position {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.roster-counts {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.roster-next {
  font-size: var(--font-small);
}

.roster-next .cell-sub {
  display: block;
  color: var(--ink-soft);
}

/* --- day / person / test sheets --- */

.adm-sheet-rows {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin: var(--s2) 0;
}

.adm-sheet-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  padding: 10px var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.adm-sheet-row-person {
  font-weight: 700;
  min-width: 9rem;
}

.adm-sheet-row-item {
  flex: 1;
  min-width: 10rem;
}

.adm-sheet-row-sub {
  flex-basis: 100%;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.adm-sheet-row .link-btn { margin-left: auto; }

/* the row chip keeps the shared right rail even when a narrow sheet wraps
   it onto its own line (flex:1 on the item makes this a no-op one-line) */
.adm-sheet-row > .chip { margin-left: auto; }

/* day-sheet item rows: the whole row is the button that opens the item
   action sheet (same anatomy, button chrome layered on) */
.adm-sheet-row-btn {
  width: 100%;
  min-height: var(--touch);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.adm-sheet-row-btn:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.adm-sheet-row-btn:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}

.adm-sheet-row-btn > .chip { margin-left: auto; }

/* the item action sheet's action group — full-width stacked buttons */
.sheet-actions {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin-top: var(--s2);
}

.sheet-actions .sheet-btn-row { margin-top: 0; }

/* crew day-sheet rows: the same row anatomy, but the whole row is the
   button that opens the item's detail/test sheet */
.crew-day-row {
  width: 100%;
  min-height: var(--touch);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.crew-day-row:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.crew-day-row:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}

.crew-day-row .chip { margin-left: auto; }

/* a picker row that can't book right now — already booked, or waiting on
   the officer. It KEEPS its place in the urgency-ordered list (never
   hidden); the quiet face + no pointer say it offers nothing. */
.crew-day-row--disabled,
.crew-day-row--disabled:hover {
  cursor: default;
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink-soft);
}

/* the day-tap booking door: the primary action under the day's rows, the
   picker's Back, and the plain nothing-to-book line */
.crew-day-book-btn,
.crew-day-back-btn {
  display: flex;
  width: 100%;
  margin-top: var(--s1);
}

.crew-day-nobook {
  color: var(--ink-soft);
  margin: var(--s2) 0 var(--s1);
}

.person-sheet-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}

.person-sheet-head .position-icon { width: 52px; height: 52px; flex-shrink: 0; }

.person-sheet-head > div { min-width: 0; }

.person-sheet-sub {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin: 2px 0 0;
}

/* the standalone worst-state chip: one 16px beat below the name block,
   one 16px beat above the first requirement row */
.person-sheet-state {
  margin: 0 0 var(--s2);
}

.person-sheet-list {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  margin-top: var(--s2);
}

/* person-sheet requirement rows: name (with the date/days line always
   under it) left, the status chip vertically centered on a consistent
   right rail, the booking line spanning underneath */
.person-sheet-list .req-row-open {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 4px var(--s2);
}

.person-sheet-list .req-row-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}

.person-sheet-list .req-row-open > .chip {
  justify-self: end;
}

.person-sheet-list .req-row-booking {
  grid-column: 1 / -1;
}

.test-sheet-score {
  margin-left: auto;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
}

/* --- Test Builder cards (rich assistant messages IN the transcript) -------
   The card IS the bubble (.bubble.bubble-assistant + .builder-card): the
   drafted question with its four unmarked options, or the schedule date
   row — with decisions on the standard .bubble-actions row. Lifecycle:
   an approved card keeps its look and loses its actions; a superseded
   (redrafted-away) card mutes and loses its actions but stays in the
   history. The correct answer is never marked. */

.builder-card-kicker {
  margin: 0 0 2px;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 700;
}

.builder-card-question {
  font-weight: 700;
}

.builder-card-options {
  list-style: upper-alpha;
  margin: var(--s1) 0 0;
  padding-left: 26px;
}

.builder-card-options li + li { margin-top: 4px; }

.builder-card--superseded { opacity: 0.55; }

.builder-date-row {
  display: flex;
  gap: var(--s1);
  align-items: center;
  flex-wrap: wrap;
}

.builder-date-row .wiz-date-input { flex: 1; min-width: 12rem; width: auto; }

/* =====================================================================
   Admin stats zone — two SVG donut rings + the four plain counts.
   The rings are plain stroke-dasharray SVG (no libraries), tokens only,
   static (nothing to animate, nothing to disable under reduced motion).
   ===================================================================== */

.adm-stats-zone {
  margin: var(--s3) 0;
}

.donut-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s2);
  margin-bottom: var(--s2);
}

.donut {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  text-align: center;
}

.donut-svg {
  width: 132px;
  height: 132px;
}

.donut-track {
  fill: none;
  stroke: var(--surface-sunken);
  stroke-width: 12;
}

.donut-value {
  fill: none;
  stroke: var(--ok-border);
  stroke-width: 12;
}

.donut-center {
  fill: var(--ink);
  font-size: 26px;
  font-weight: 700;
}

.donut-label {
  font-weight: 700;
}

.donut-sub {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* the four plain counts ride the shared stat-card component */
.adm-counts {
  margin: 0;
}

@media (max-width: 480px) {
  /* 2-up count grid + smaller (still >=44px visual) rings on a phone */
  .adm-counts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .donut-svg {
    width: 96px;
    height: 96px;
  }

  .donut-center {
    font-size: 30px; /* scales down with the viewBox — keeps ~24px rendered */
  }
}

/* =====================================================================
   "Visualize" disclosure — the dashboard's collapsible stats section
   (donut rings + counts live here, under the calendar). A real button
   on the 48px touch floor; the reveal is a pure-CSS grid-rows + opacity
   transition that the global prefers-reduced-motion block flattens to
   an instant flip.
   ===================================================================== */

.visualize-toggle {
  display: flex;
  align-items: center;
  gap: var(--s1);
  width: 100%;
  min-height: var(--touch);
  padding: var(--s1) var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  color: var(--ink);
  font: inherit;
  font-size: var(--font-h3);
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}

.visualize-toggle:hover { background: var(--brand-tint); }

.visualize-toggle .icon { color: var(--brand); }

.visualize-toggle-label { flex: 1; }

.visualize-toggle .visualize-chevron {
  color: var(--ink-soft);
  transition: transform 0.25s ease;
}

.visualize-toggle[aria-expanded='true'] .visualize-chevron { transform: rotate(180deg); }

.visualize-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}

/* collapsed: zero height AND hidden (visibility waits for the height
   transition to finish, so the close animates too) */
.visualize-body {
  overflow: hidden;
  min-height: 0;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s 0.25s, opacity 0.25s ease;
}

.visualize-panel.is-open { grid-template-rows: 1fr; }

.visualize-panel.is-open .visualize-body {
  visibility: visible;
  opacity: 1;
  transition: visibility 0s, opacity 0.25s ease;
}

/* =====================================================================
   Admin roster cards — the phone rendering of the ONE roster list.
   The table (with its own overflow scroll) serves wide screens; below
   the roster breakpoint the card stack takes over.
   ===================================================================== */

.roster-cards {
  display: none;
}

.roster-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s1);
  width: 100%;
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
}

.roster-card:hover {
  border-color: var(--brand);
}

.roster-card-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.roster-card .position-icon {
  width: 44px;
  height: 44px;
}

.roster-card-names {
  display: flex;
  flex-direction: column;
}

.roster-card-name {
  font-weight: 700;
}

.roster-card-position {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

@media (max-width: 700px) {
  .roster-cards {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
  }

  .roster-wrap {
    display: none;
  }
}

/* =====================================================================
   Admin #assistant page — NO admin branch anymore. The page renders the
   bare shared chat shell (same markup as the crew chat page) and rides
   the SAME stylesheet branches: the base phone skeleton + body.chat-mode
   chrome below 840px, the full-height centered column (chat-page media
   block) + body.chat-desktop page padding at and above it. The old
   admin fork — title row, "Guided demo" chip, its own hardcoded column
   height — is DELETED.
   ===================================================================== */

/* =====================================================================
   Message channel — crew Messages view + admin Inbox. One family of
   classes for both sides; bubbles reuse the chat message-row component.
   ===================================================================== */

.msg-view {
  max-width: 760px;
  margin: 0 auto;
}

.msg-boundary-note {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin: 0 0 var(--s2);
}

.msg-empty {
  color: var(--ink-soft);
}

.msg-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.msg-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  width: 100%;
  min-height: var(--touch);
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
}

.msg-row:hover {
  border-color: var(--brand);
}

.msg-row.is-unread {
  border-color: var(--brand);
  border-width: 2px;
}

.msg-row-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}

.msg-row-name {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-weight: 700;
  min-width: 0;
}

.msg-row-time {
  color: var(--ink-soft);
  font-size: var(--font-small);
  flex-shrink: 0;
}

.msg-row-tags {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1);
  min-width: 0;
}

.msg-row-preview {
  color: var(--ink-soft);
  font-size: var(--font-small);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.msg-unread-dot {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--danger-solid);
}

/* the context chip may carry a long requirement name — truncate, never
   overflow the row */
.msg-context {
  display: inline-flex;
  min-width: 0;
  max-width: 100%;
}

.msg-context .chip {
  max-width: 100%;
  overflow: hidden;
}

.msg-context .chip-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.msg-confirm-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1.5px solid var(--danger-border);
  background: var(--danger-fill);
  color: var(--danger-ink);
  font-size: 15px;
  font-weight: 700;
  white-space: nowrap;
}

.msg-confirm-badge .icon {
  width: 16px;
  height: 16px;
}

/* --- thread view: topbar (back + name), chat-shell transcript, and the
   pinned composer bar. ONE markup, both apps; at mobile widths the app
   controllers set body.chat-mode and the assistants' phone skeleton
   applies (rules further down); at >=840px the desktop overrides in the
   chat-page media block keep the in-flow card layout. --- */

.msg-thread-topbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-bottom: var(--s2);
}

.msg-thread-topbar .msg-back-btn {
  flex-shrink: 0;
}

.msg-thread-name {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  margin: 0;
  font-size: var(--font-h2);
  min-width: 0;
}

.msg-action-banner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin: 0 0 var(--s1);
  padding: 12px var(--s2);
  background: var(--danger-fill);
  border: 2px solid var(--danger-border);
  border-radius: var(--radius);
  color: var(--danger-ink);
}

.msg-action-text {
  font-weight: 600;
}

.msg-thread {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  margin-bottom: var(--s2);
}

/* the compact chrome row pinned above the composer: context chip + the
   view's quick actions (admin: View person / Confirm completion) */
.msg-threadbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1);
  margin: 0 0 var(--s1);
}

/* the in-transcript footer under the thread's last message: the honest
   boundary note (plus, officer side, the unresolved-action banner) */
.msg-thread-foot {
  margin-top: var(--s1);
}

.msg-thread-foot .msg-boundary-note {
  margin: 0;
  text-align: center;
}

.msg-view .chat-text-input {
  background: var(--surface);
}

/* --- schedule cards (the interactive scheduling mechanic) ------------------
   A first-class message ROW (not a text bubble): a bordered card with a
   status-colored accent left rail, rendered identically by the crew Messages
   view and the admin Inbox (ONE shared component). Aligned by sender like a
   bubble — the creator's own side is flex-end, the other party flex-start. */
.msg-card {
  align-self: flex-start;
  max-width: 92%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 2px 0;
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--brand);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.msg-card--own {
  align-self: flex-end;
}
/* status accent rail: blue is the calendar/class family (open = needs a
   class), green = booked (a class date is on file) */
.msg-card--open {
  border-inline-start-color: var(--brand);
}
.msg-card--booked {
  border-inline-start-color: var(--ok-border);
}
.msg-card-head {
  display: flex;
  align-items: center;
  gap: var(--s1);
  min-width: 0;
}
.msg-card-icon {
  display: inline-flex;
  flex: none;
  color: var(--brand);
}
.msg-card-icon .icon {
  width: 22px;
  height: 22px;
}
.msg-card-title {
  font-weight: 700;
  min-width: 0;
}
.msg-card-status {
  margin: 0;
  font-weight: 600;
}
.msg-card--open .msg-card-status {
  color: var(--brand-strong);
}
.msg-card--booked .msg-card-status {
  color: var(--ok-ink);
}
.msg-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  margin-top: 2px;
}
.msg-card-btn {
  flex: 1 1 auto;
}
.msg-card-history {
  margin: 0;
  font-size: var(--font-small);
  color: var(--norec-ink);
  opacity: 0.85;
}
/* the inbox-row scheduling badge — the "Needs a date" / "Needs your OK"
   sibling of the red "Needs confirmation" badge, tinted the calendar blue */
.msg-card-badge {
  background: var(--brand-tint);
  border-color: var(--brand-tint-border);
  color: var(--brand-strong);
}

/* --- staged card attachment chip (docked above the composer, INSIDE the
   observed .chat-inputbar — see shared/chat.js composerInnerHtml) --------- */
.msg-attachment-chip {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: 0 0 8px;
  padding: 8px var(--s2);
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  border-radius: var(--radius);
}
.msg-attachment-icon {
  display: inline-flex;
  flex: none;
  color: var(--brand);
}
.msg-attachment-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}
.msg-attachment-label {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.msg-attachment-kind {
  font-size: var(--font-small);
  color: var(--brand-strong);
}
.msg-attachment-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--touch);
  height: var(--touch);
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink);
}
.msg-attachment-remove:hover {
  background: var(--surface);
  border-color: var(--border);
}
.msg-attachment-remove .icon {
  width: 20px;
  height: 20px;
}

/* =====================================================================
   Crew routed views — hamburger + drawer (mobile-only chrome), Guide /
   My Info, and the SMART Assistant chat page. The hamburger, drawer,
   and floating bubble only mount below 840px; Guide, My Info, and the
   chat page mount on BOTH widths (the chat page's desktop column
   variant lives at the end of this section — the mobile keyboard
   skeleton below is untouched).
   ===================================================================== */

/* --- header hamburger (mobile only) --- */

.menu-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--touch);
  min-width: var(--touch);
  padding: 8px 14px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
}

.menu-btn:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.menu-btn[hidden] { display: none; }

@media (min-width: 840px) {
  .menu-btn { display: none; }
}

/* the unread dot on the admin Menu button — lit while the Inbox holds
   unread or needs-confirmation threads (the drawer badge carries the
   count; this is the glanceable "something's waiting" mark) */
.menu-btn-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--danger-solid);
}

.menu-btn-dot[hidden] { display: none; }

/* --- slide-in navigation drawer (rides the sheet dialog machinery) --- */

.drawer-backdrop {
  justify-content: flex-start;
  align-items: stretch;
  padding: 0;
}

.drawer {
  position: relative;
  width: min(320px, 85vw);
  height: 100%;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: var(--s3) var(--s2);
  box-shadow: var(--shadow-raised);
  overflow-y: auto;
  animation: drawer-in 0.2s ease;
}

@keyframes drawer-in {
  from { transform: translateX(-100%); }
  to { transform: none; }
}

.drawer-title {
  margin: var(--s1) 0 var(--s2);
}

.drawer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.drawer-item {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1);
  width: 100%;
  min-height: var(--touch);
  padding: 12px var(--s2);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.drawer-item:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.drawer-item .icon { color: var(--brand); }

.drawer-item.is-current {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-strong);
}

.drawer-current-mark {
  margin-left: auto;
  font-size: 14px;
  font-weight: 700;
  color: var(--brand-strong);
}

/* My Info rides below the four nav destinations as a quiet utility link —
   a divider separates it from the nav proper */
.drawer-utility {
  margin-top: var(--s2);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
}

.drawer-item--utility {
  border-color: var(--border);
  font-weight: 500;
  color: var(--ink-soft);
}

.drawer-item--utility .icon { color: var(--ink-soft); }

.booking-dialog-cancel {
  display: flex;
  width: 100%;
  margin-top: var(--s1);
}

/* --- compact Guide button in the calendar toolbar row (opens the Guide
   popup; mobile only — its old solo row above the grid is deleted) --- */

.guide-btn {
  flex-shrink: 0;
  padding: 8px 14px;
  font-size: var(--font-small);
}

/* --- Guide popup (standard dialog; rows reuse the shared .legend-row
   styling the desktop legend uses) --- */

.guide-sub {
  color: var(--ink-soft);
  margin: 0 0 var(--s1);
  font-size: var(--font-small);
}

.guide-rows {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.guide-rows .legend-row { gap: 4px var(--s1); }

/* --- My Info view --- */

.info-view { max-width: 680px; margin: 0 auto; }

.info-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--s1) var(--s2);
  margin: var(--s2) 0 var(--s3);
  box-shadow: var(--shadow-soft);
}

.info-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

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

.info-label {
  color: var(--ink-soft);
  min-width: 6rem;
  font-size: var(--font-small);
  font-weight: 600;
}

.info-value { font-weight: 600; }

.info-position {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
}

.info-position .icon { color: var(--brand); }

.info-reset-hint {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.info-reset-btn { width: 100%; }

/* --- SMART Assistant chat page (mobile #assistant view) --------------------
   Skeleton ported from the proven mobile chat spec: fixed header on top
   (its measured height published as --chat-header-h), a chat shell that
   fills the view, an absolutely-positioned scrolling transcript overlay,
   and a pinned input bar with safe-area padding. */

body.chat-mode {
  overflow: hidden;
}

.chat-mode .app-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  padding-top: max(env(safe-area-inset-top), 6px);
}

.chat-mode main {
  max-width: none;
  margin: 0;
  padding: 0;
}

.chat-shell {
  position: relative;
  overflow: hidden;
  height: 100vh;
  height: 100dvh;
}

.chat-transcript {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: var(--chat-header-h, 76px) 12px 140px; /* JS overrides the bottom */
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.chat-inputbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 45;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: var(--s1) 12px var(--s1);
  padding-bottom: calc(var(--s1) + env(safe-area-inset-bottom, 0px));
}

/* --- the standing quick-reply strip: ONE horizontally scrollable row of
   pill chips at EVERY width in BOTH apps (no wrapped desktop variant). The
   collapse control is a swipe/click HANDLE stacked ABOVE a full-width chip
   row (expanded) or the "Guided prompts" pill in its place (collapsed) —
   the handle/pill component styling lives in the appended block at the END
   of this file. Scroll-snap + hidden scrollbar; the [data-chips-overflow]
   attribute on the wrap (set by the controllers) shows the right-edge fade
   hint while more chips lie off-screen. --- */

.chat-chipbar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  margin: 0 0 var(--s1);
  min-width: 0;
}

.chat-chipwrap {
  position: relative;
  flex: 1;
  min-width: 0;
}

.chat-chipwrap[hidden] { display: none; }

.chat-chipwrap[data-chips-overflow]::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 2px;
  width: 40px;
  background: linear-gradient(to left, var(--surface) 15%, transparent);
  pointer-events: none;
}

.chat-chiprow {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: var(--s1);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  padding-bottom: 2px;
}

.chat-chiprow::-webkit-scrollbar { display: none; }

.chat-chiprow > * { scroll-snap-align: start; }

.chat-chiprow .assistant-option-btn {
  width: auto;
  flex-shrink: 0;
  min-height: 44px;
  border-radius: 999px;
  padding: 8px 16px;
  font-size: var(--font-small);
}

.chat-chiprow .assistant-option-btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.chat-chiprow .assistant-option-btn.selected {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-strong);
}

.chat-textrow {
  display: flex;
  align-items: flex-end;
  gap: var(--s1);
}

.chat-text-input {
  flex: 1;
  min-width: 0;
  height: 40px;
  min-height: 40px;
  max-height: 100px;
  resize: none;
  padding: 8px 14px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  line-height: 1.4;
}

.chat-text-input:focus {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--brand);
}

.chat-send-btn { flex-shrink: 0; }

/* --- message anatomy: avatar circle + bubble + HH:MM line --- */

.chat-msg {
  display: flex;
  align-items: flex-end;
  gap: var(--s1);
  max-width: 100%;
}

.chat-msg--user {
  flex-direction: row-reverse;
  align-self: flex-end;
}

.chat-msg--assistant { align-self: flex-start; }

.chat-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  color: var(--brand);
  margin-bottom: 22px; /* sits beside the bubble, above the time line */
}

.chat-avatar .icon {
  width: 18px;
  height: 18px;
}

.chat-msg-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.chat-msg--user .chat-msg-body { align-items: flex-end; }

.chat-msg .bubble {
  width: fit-content;
  max-width: 90%;
  border-radius: 1rem;
  padding: 0.5rem 1rem;
}

.chat-msg--assistant .bubble-assistant {
  background: var(--surface);
  border: 1px solid var(--border);
}

.chat-time {
  font-size: 13px;
  opacity: 0.5;
  margin: 2px 4px 4px;
}

/* --- typing beat: three pulsing dots (~500ms before each reply) --- */

.chat-typing-bubble {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 40px;
}

.chat-typing-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-soft);
  opacity: 0.3;
  animation: chat-typing-pulse 1.4s ease-in-out infinite;
}

.chat-typing-dot:nth-child(2) { animation-delay: 0.2s; }
.chat-typing-dot:nth-child(3) { animation-delay: 0.4s; }

@keyframes chat-typing-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

/* --- desktop assistant page (>=840px): the SAME chat markup laid out as
   a centered flex column. No fixed input bar, no absolute transcript, no
   visualViewport plumbing — the mobile keyboard skeleton above never
   applies at this width, and nothing here reaches below 840px. --- */

@media (min-width: 840px) {
  body.chat-desktop main {
    padding-top: var(--s2);
    padding-bottom: var(--s2);
  }

  .chat-shell {
    /* header-aware height: the measured header var (the tab bar rides
       INSIDE the one-line header now) plus the main paddings, keeps the
       column inside one viewport so only the transcript scrolls */
    height: calc(100dvh - var(--chat-header-h, 64px) - 56px);
    min-height: 320px;
    max-width: 760px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    overflow: visible;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-soft);
  }

  .chat-transcript {
    position: static;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--s2);
  }

  .chat-inputbar {
    position: static;
    border-top: 1px solid var(--border);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    padding: var(--s1) var(--s2) var(--s2);
  }

  /* an OPEN message thread (crew #messages, admin #inbox) mounts this
     SAME chat column — the old in-flow thread card is deleted. The
     topbar (back + name) stays in flow above the column; the transcript
     is the scrollable flex region; the composer (with the context-chip /
     quick-actions chrome row) is pinned at the column's bottom. The
     column height also clears the topbar + its margin. */
  .msg-view--thread .chat-shell {
    height: calc(100dvh - var(--chat-header-h, 64px) - 128px);
  }

  /* inside the column the transcript sheds its old card chrome */
  .msg-view--thread .msg-thread {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    margin: 0;
  }

  .msg-view--thread .chat-inputbar .msg-threadbar {
    margin: 0 0 var(--s1);
  }
}

/* --- thread chat-mode (crew #messages AND admin #inbox, below 840px):
   the SAME phone skeleton the assistants mount — fixed header (crew) or
   header + pinned tab bar (admin) on top, the transcript as the absolute
   scrolling overlay, the composer fixed to the bottom — plus a slim
   fixed topbar for back-navigation + the thread name. The controllers
   publish --msg-topbar-h (ResizeObserver-driven, like the header vars). */

body.chat-mode .msg-view {
  max-width: none;
  margin: 0;
}

body.chat-mode .msg-thread-topbar {
  position: fixed;
  top: var(--chat-header-h, 76px);
  left: 0;
  right: 0;
  z-index: 40;
  margin: 0;
  padding: var(--s1) 12px;
  background: var(--page);
  border-bottom: 1px solid var(--border);
  flex-wrap: nowrap;
}

/* one compact row: the name yields (truncates) beside the back button */
body.chat-mode .msg-thread-name {
  display: block;
  flex: 1;
  min-width: 0;
  font-size: var(--font-h3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* the skeleton rules are stated in full (not inherited from the base
   .chat-shell block) so chat-mode wins over the >=840px in-flow thread
   overrides at any width — the same precedent as the admin assistant
   chat-mode block below */
body.chat-mode .msg-view .chat-shell {
  position: relative;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  display: block;
  overflow: hidden;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.chat-mode .msg-view .chat-transcript {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: calc(var(--chat-header-h, 76px) + var(--msg-topbar-h, 64px)) 12px 140px; /* JS overrides the bottom */
  display: flex;
  flex-direction: column;
  gap: 4px;
}

body.chat-mode .msg-view .chat-inputbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 45;
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: var(--s1) 12px var(--s1);
  padding-bottom: calc(var(--s1) + env(safe-area-inset-bottom, 0px));
}

/* the transcript overlay is full-bleed — the .msg-thread card chrome
   belongs to the desktop layout only */
body.chat-mode .msg-view .msg-thread {
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  margin: 0;
}

/* --- chat-mode restatement for the BARE-shell surfaces (assistants +
   Test Builder): the same phone-skeleton values as the base rules above,
   stated under body.chat-mode so chat-mode wins over the >=840px column
   overrides at any width — the exact precedent the thread block above
   sets. The thread rules keep their higher specificity (the topbar-aware
   transcript offset stays theirs). --- */

body.chat-mode .chat-shell {
  position: relative;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  max-width: none;
  margin: 0;
  display: block;
  overflow: hidden;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.chat-mode .chat-transcript {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: var(--chat-header-h, 76px) 12px 140px; /* JS overrides the bottom */
  display: flex;
  flex-direction: column;
  gap: 4px;
}

body.chat-mode .chat-inputbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 45;
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: var(--s1) 12px var(--s1);
  padding-bottom: calc(var(--s1) + env(safe-area-inset-bottom, 0px));
}

/* --- measured bottom clearance under the floating bubble (mobile) ---
   the reserved space is the bubble's measured height (--fab-clearance,
   published by the ResizeObserver in App.tsx) PLUS the device safe-area
   inset, so a notched phone's home-indicator strip can never sit inside
   the clearance and let content peek out from under the bubble there. */

@media (max-width: 839.98px) {
  main {
    padding-bottom: calc(var(--fab-clearance, 84px) + var(--s3) + env(safe-area-inset-bottom, 0px));
  }

  .chat-mode main { padding-bottom: 0; }
}

/* --- reduced motion: disable the pulse and every other animation --- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* --- mobile (clean down to 380px) --- */

@media (max-width: 480px) {
  main { padding: var(--s2) 12px calc(var(--fab-clearance, 108px) + var(--s3) + env(safe-area-inset-bottom, 0px)); }

  .cal-day {
    min-height: 64px;
    padding: 3px;
  }

  /* today cell: keep the full literal "Today" label by stacking it under
     the day number instead of letting it clip */
  .cal-day.is-today .cal-day-num {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    line-height: 1.15;
  }

  .cal-today-label {
    font-size: 11px;
  }

  /* month stepper: the label yields, buttons never leave the screen */
  .cal-nav {
    gap: var(--s1);
  }

  .cal-month-label {
    min-width: 0;
    flex: 1;
    font-size: var(--font-h3);
  }

  .cal-nav-btn {
    padding: 8px 12px;
    flex-shrink: 0;
  }

  /* mini-chips: icon stays visible inside a colored, bordered chip —
     never a bare color dot. Full name lives in the detail sheet. The
     word-hiding is scoped to DAY CELLS: a Guide sample chip outside the
     grid keeps its word at every width, so the legend never shows an
     empty pill. */
  .chip-cal {
    width: auto;
    min-width: 26px;
    height: 24px;
    padding: 2px 5px;
    justify-content: center;
  }

  .cal-day .chip-cal .chip-text { display: none; }

  .cal-day-markers {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 3px;
  }

  .cal-day-count { display: inline; }

  .cal-today-label { display: inline; }

  .sheet-backdrop { padding: 0; align-items: stretch; }

  .sheet {
    max-width: none;
    max-height: none;
    height: 100%;
    border-radius: 0;
    padding: var(--s2) var(--s2) var(--s3);
  }

  .needs-card { flex-direction: column; align-items: stretch; }

  .needs-card-btn { width: 100%; }

  .bubble { max-width: 100%; }

  .demo-pill {
    right: 12px;
    bottom: 12px;
  }

  .wiz-step { padding: var(--s2); }

  .wiz-nav .btn-primary,
  .wiz-nav .btn-secondary {
    flex: 1;
  }

  /* requirement rows stack vertically on narrow screens */
  .req-row { flex-direction: column; }

  /* admin calendar drops to the same compact cells as the crew grid */
  .adm-cal .cal-day {
    min-height: 64px;
    padding: 3px;
  }

  .test-score-number { font-size: 44px; }
}

/* =====================================================================
   ADMIN ASSISTANT PHONE CHAT + ADMIN HEADER SAFE-AREA — appended block.
   This branch's ONLY styles.css change (parallel-run fence: a sibling
   branch owns the #dashboard composition; nothing here touches it).

   Scope guards:
   - body.admin      — set by admin/index.html only; crew pages never match
   - body.chat-mode  — the admin controller sets it on its chat surfaces
                       (assistant / open inbox thread / Test Builder)
                       below the 840px breakpoint (same rule as crew)
   The crew chat page's own skeleton and chip row are untouched: every
   rule below requires .admin, which crew never renders.
   ===================================================================== */

/* --- C: admin header safe-area, ALL tabs, EVERY width ------------------
   viewport-fit=cover extends the layout under the iOS status bar, so the
   header pads by the safe-area inset and pins (sticky keeps it in normal
   flow — content offsets below it come free) so the title, the "as of"
   line, and the tab bar can never slide under the status bar at any
   scroll position. In chat-mode the crew skeleton's fixed treatment
   applies instead (position only — the padding rule below still wins). */

body.admin .app-header {
  padding-top: max(env(safe-area-inset-top), 6px);
}

body.admin:not(.chat-mode) .app-header {
  position: sticky;
  top: 0;
  z-index: 40;
}

/* chat-mode: the header is fixed (crew rule) with the Menu button riding
   it — navigation stays reachable above the transcript. The tab bar is
   already hidden below the breakpoint (the only widths chat-mode runs
   at), so the measured tab height the ResizeObserver publishes is 0 and
   --chat-header-h self-corrects to the header alone. */

/* --- A: phone chat skeleton on the admin chat surfaces -----------------
   No admin copy exists anymore: the #assistant page, an open #inbox
   thread, and the Test Builder all render the bare shared chat shell,
   so the base crew skeleton (and the body.chat-mode chrome rules) apply
   verbatim — full-bleed shell, absolute transcript overlay padded by
   the measured header var, input bar fixed to the bottom with safe-area
   padding. The old chat-mode restatement block for the admin assistant
   is DELETED with the fork it undid. */

/* --- B: quick-reply chip strip — the shared one-row scroller (snap,
   hidden scrollbar, edge-fade via [data-chips-overflow] on the wrap) now
   lives on the base .chat-chiprow / .chat-chipwrap rules and applies at
   EVERY width in BOTH apps; this block needs no chat-mode copy. --- */

/* =====================================================================
   HEADER / VIEWS / TESTS TAB — appended block for the one-line header,
   the Month/Week/Day calendar views, and the admin Tests tab + Test
   Builder surface. Tokens only — no new literal colors.
   ===================================================================== */

/* --- admin Dashboard context line — the ONE place the facility name and
   the as-of date render (the header subtitle is deleted) --- */

.adm-context-line {
  color: var(--ink-soft);
  font-size: var(--font-small);
  line-height: 1.3;
  margin: 0 0 var(--s2);
}

/* --- calendar view switch: [Month | Week | Day], >=48px targets --------- */

.cal-viewswitch {
  display: flex;
  gap: 0;
  margin: 0 0 var(--s1);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
  width: max-content;
  max-width: 100%;
}

.cal-viewswitch-btn {
  flex: 1;
  min-height: var(--touch);
  min-width: 72px;
  padding: 8px 16px;
  background: var(--surface);
  border: none;
  border-right: 2px solid var(--border-strong);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.cal-viewswitch-btn:last-child { border-right: none; }

.cal-viewswitch-btn:hover {
  background: var(--brand-tint);
}

.cal-viewswitch-btn:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: -3px;
}

.cal-viewswitch-btn.is-active {
  background: var(--brand);
  color: var(--surface);
}

/* --- Roster page: the sort control row (Urgency / Test score / Name) ---
   rides the shared .cal-viewswitch segmented anatomy (48px targets come
   with it); the label reads as one row with the control at every width */
.roster-sort-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin: var(--s2) 0 var(--s1);
}

.roster-sort-label {
  font-weight: 600;
  color: var(--ink-soft);
}

.roster-sortswitch { margin: 0; }

/* --- admin Up-next cards: the person subtitle line (position icon +
   "[Full name] · [Position]") between the title and the date line --- */
.upnext-card-person {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 4px;
  font-weight: 600;
}

.upnext-card-person .icon {
  width: 18px;
  height: 18px;
  color: var(--ink-soft);
  flex-shrink: 0;
}

/* --- Week / Day agenda (both apps): day heading rows + item rows -------- */

.cal-agenda {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.cal-agenda-dayhead {
  display: flex;
  align-items: baseline;
  gap: var(--s1);
  margin: var(--s1) 0 4px;
  font-size: var(--font-h3);
  font-weight: 700;
  color: var(--ink);
}

.cal-agenda-dayhead.is-today {
  color: var(--brand-strong);
}

.cal-agenda-today-tag {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-border);
  color: var(--brand-strong);
  font-size: var(--font-small);
  font-weight: 700;
}

/* the Day view's big heading */
.cal-agenda-bighead {
  margin: var(--s1) 0 var(--s1);
  font-size: var(--font-h2);
  font-weight: 700;
}

.cal-agenda-empty {
  color: var(--ink-soft);
  margin: 0 0 var(--s1);
  padding: 4px 2px;
}

/* agenda item rows reuse the day-sheet row anatomy (.adm-sheet-row) —
   only the list spacing lives here */
.cal-agenda-rows {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

/* --- admin Tests tab ------------------------------------------------------ */

.tests-head-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  justify-content: space-between;
}

.tests-group-title {
  margin: var(--s2) 0 var(--s1);
  font-size: var(--font-h3);
  font-weight: 700;
}

.tests-list {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.tests-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px var(--s2);
  align-items: center;
  width: 100%;
  min-height: var(--touch);
  padding: var(--s1) var(--s2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-align: left;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
}

.tests-row:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.tests-row-title {
  font-weight: 700;
}

.tests-row-meta,
.tests-row-summary {
  grid-column: 1 / -1;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.tests-empty {
  color: var(--ink-soft);
  margin: 0;
}

/* results view: back row + per-person rows (adm-sheet-row anatomy) */
.tests-back-row {
  margin: 0 0 var(--s1);
}

.tests-person-row {
  cursor: pointer;
}

.tests-person-row:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.tests-retake-note {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* --- Test Builder surface (its own home inside the Tests tab) ------------
   The builder mounts the ONE shared chat shell WITH the assistants'
   composer — standing chip strip, Hide/Show-suggestions toggle,
   textarea + Send — so it needs NO layout rules of its own. The old
   builder-only controls row and the guided-demo footer note are
   DELETED; the in-transcript cards' rules live with the bubbles above. */

/* ==========================================================================
   Test-day window + admin "Add to this day" + admin Guide (this build)
   ========================================================================== */

/* The disabled-looking "Opens on [Mon D]" note that REPLACES a locked
   test's take action — plainly not a button, nothing to tap. */
.test-opens-note {
  margin: var(--s1) 0 0;
  padding: 12px var(--s2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  color: var(--ink-soft);
  font-weight: 700;
  text-align: center;
}

.upnext-opens-note { margin-top: var(--s1); }

/* The red-outline Missed / Retraining-needed line ("talk to your
   compliance officer") — an outline, not a fill: it flags, not floods. */
.test-missed-note {
  padding: 10px var(--s2);
  border: 2px solid var(--danger-border);
  border-radius: var(--radius);
  color: var(--danger-ink);
  font-weight: 700;
}

/* --- admin day sheet: the "Add to this day" action rows ------------------- */

.adm-day-add {
  margin-top: var(--s2);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
}

.adm-day-add-title {
  margin: 0 0 var(--s1);
  font-size: var(--font-h3);
}

.adm-add-row {
  display: flex;
  align-items: center;
  gap: var(--s1);
  width: 100%;
  min-height: var(--touch);
  padding: 10px var(--s2);
  margin-bottom: var(--s1);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-weight: 700;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}

.adm-add-row .icon { color: var(--brand); flex-shrink: 0; }

.adm-add-row:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.adm-add-row:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}

.adm-day-empty { margin: 0; }

.adm-booking-readonly { margin: var(--s1) 0; }

/* --- admin calendar Guide button rides the section head's right edge ------ */

.adm-section-head .adm-guide-btn { margin-left: auto; }

/* the Guide's "+N more" overflow example is a face, not a control */
.legend-more-example {
  cursor: default;
  display: inline-block;
  width: auto;
  text-align: center;
}

/* ==========================================================================
   FINAL BOARD BATCH — appended block (strip collapse handle/pill + drag,
   Tests View sheet). Kept in ONE block at the end of the file per the batch
   convention; every selector below is new to this batch.
   ========================================================================== */

/* --- A: the strip collapse control — swipe/click HANDLE (expanded) and the
   "Guided prompts" PILL (collapsed). Both are real <button>s carrying
   data-role="chips-toggle"; the handle stacks ABOVE a full-width chip row,
   the pill docks in the row's place. --- */

.chat-strip-toggle {
  appearance: none;
  background: none;
  border: none;
  font: inherit;
  color: var(--ink-soft);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* EXPANDED: the slim handle — a centered grip line with a downward chevron
   at its right edge; padding lifts the real hit area past the 44px floor */
.chat-strip-toggle--handle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  padding: 8px 0;
  border-radius: var(--radius);
  touch-action: pan-x; /* let a horizontal intent through; we read the swipe */
}

.chat-strip-toggle--handle:hover { background: var(--brand-tint); }

.chat-strip-grip {
  display: block;
  width: 36px;
  height: 4px;
  border-radius: 999px;
  background: var(--border);
}

.chat-strip-toggle--handle:hover .chat-strip-grip { background: var(--brand); }

.chat-strip-toggle--handle .icon {
  position: absolute;
  right: 12px;
  top: 50%;
  width: 16px;
  height: 16px;
  transform: translateY(-50%);
  color: var(--ink-soft);
  transition: transform 0.15s ease;
}

/* COLLAPSED: the compact "Guided prompts" pill, docked (not full width) */
.chat-strip-toggle--pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  align-self: flex-start;
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
}

.chat-strip-toggle--pill:hover {
  color: var(--brand-strong);
  border-color: var(--brand);
  background: var(--brand-tint);
}

.chat-strip-toggle--pill .icon {
  width: 16px;
  height: 16px;
  transform: rotate(180deg); /* the shared chevron-down glyph, pointing UP */
  transition: transform 0.15s ease;
}

/* --- B: desktop chip-row drag scrolling — grab cursor, grabbing while a
   pointer-drag is live (mouse only; touch keeps native momentum) --- */
@media (hover: hover) and (pointer: fine) {
  .chat-chiprow { cursor: grab; }
  .chat-chiprow.chat-chiprow--dragging {
    cursor: grabbing;
    scroll-snap-type: none; /* free scrolling mid-drag, snap resumes on release */
  }
  .chat-chiprow.chat-chiprow--dragging .assistant-option-btn { cursor: grabbing; }
}

@media (prefers-reduced-motion: reduce) {
  .chat-strip-toggle--handle .icon,
  .chat-strip-toggle--pill .icon { transition: none; }
}

/* --- C: Tests list row split (results button + "View test" button) -------- */

.tests-row-wrap {
  display: flex;
  gap: var(--s1);
  align-items: stretch;
}

.tests-row-wrap .tests-row {
  flex: 1;
  min-width: 0; /* let the summary button shrink so the row never overflows */
}

.tests-row-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex-shrink: 0;
  min-width: 72px;
  min-height: var(--touch);
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--ink-soft);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  cursor: pointer;
}

.tests-row-view:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-strong);
}

.tests-row-view .icon { width: 20px; height: 20px; }

/* --- C: the "View test" sheet — the officer answer key + editing ---------- */

.sheet--test-view { max-width: 640px; }

.test-view-lock {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 var(--s1);
  padding: var(--s1) var(--s2);
  border-radius: var(--radius);
  background: var(--brand-tint);
  color: var(--brand-strong);
  font-size: var(--font-small);
  font-weight: 600;
}

.test-view-lock .icon { width: 18px; height: 18px; flex-shrink: 0; }

.test-view-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.test-view-q {
  padding: var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.test-view-q--editing { border-color: var(--brand); }

.test-view-kicker {
  margin: 0 0 4px;
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 700;
}

.test-view-question {
  margin: 0 0 var(--s1);
  font-weight: 600;
}

.test-view-options {
  list-style: none;
  margin: 0 0 var(--s1);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.test-view-option {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 6px 10px;
  border-radius: var(--radius);
  background: var(--surface-sunken);
  font-size: var(--font-small);
}

.test-view-option--correct {
  background: var(--brand-tint);
  color: var(--brand-strong);
  font-weight: 600;
}

.test-view-option--correct .icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  align-self: center;
}

.test-view-correct-tag { font-weight: 700; }

.test-view-why {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.test-view-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1);
  margin-top: var(--s1);
}

.test-view-edit-label {
  display: block;
  margin: var(--s1) 0 4px;
  font-size: var(--font-small);
  font-weight: 700;
  color: var(--ink-soft);
}

.test-view-edit-fieldset {
  margin: 0;
  padding: 0;
  border: none;
}

.test-view-edit-option {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin-bottom: 6px;
}

.test-view-edit-option input[type="radio"] {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

textarea.test-view-edit-input,
input.test-view-edit-input {
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  font-size: var(--font-small);
  color: var(--ink);
  background: var(--surface);
}

textarea.test-view-edit-input { resize: vertical; }

.test-view-edit-error {
  margin: var(--s1) 0 0;
  color: var(--danger-ink);
  font-size: var(--font-small);
  font-weight: 600;
}

/* =====================================================================
   DEVICES — EQUIPMENT SURFACES (appended block; append-only).
   START OF FENCE. Nothing above this line changed. Everything below
   styles the dev- namespaced equipment vocabulary shared by the
   public/devices/ mockup page, the crew "My equipment" mode, and the
   admin calendar's "Equipment" mode. (The retired .dev-equip-link admin
   nav anchor is gone — Equipment lives inside the admin calendar now.)
   Tokens only; no new literal colors.
   ===================================================================== */

/* --- the permanent honesty note (deliberately not dismissible) ----------
   One compact line in footnote voice now — no tinted block, no border,
   muted ink, 14px (the compact-secondary size .dev-group-count and
   .dev-day-more already wear; the 16px floor governs flowing text, not
   one-line metadata). Still permanent, still carrying no dismiss control. */

.dev-demo-banner {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 var(--s1);
  color: var(--ink-soft);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.3;
}

.dev-demo-banner .icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

/* --- the [People | Equipment] scope toggle — the two-calendar story ----- */

.dev-scope-toggle {
  display: flex;
  gap: 0;
  margin: 0 0 var(--s3);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
}

.dev-scope-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  min-height: var(--touch);
  padding: 10px var(--s2);
  background: var(--surface);
  border: none;
  border-right: 2px solid var(--border-strong);
  color: var(--ink);
  font: inherit;
  font-size: var(--font-h3);
  font-weight: 700;
  text-decoration: none;
}

.dev-scope-btn:last-child { border-right: none; }

a.dev-scope-btn:hover {
  background: var(--brand-tint);
  color: var(--ink);
}

.dev-scope-btn.is-active {
  background: var(--brand);
  color: var(--surface);
}

/* --- controls row: the List/Month switch + the Filter button ------------ */

.dev-controls {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin: 0 0 var(--s2);
}

.dev-viewswitch { margin: 0; }

/* Each segment sizes to its OWN label (basis auto): the shared
   .cal-viewswitch-btn's flex-basis 0 splits the max-content container
   equally, which squeezes the wider "Month" label into its right padding.
   Scoped to the device switches — the personnel [Month | Week | Day]
   control keeps its geometry untouched. */
.dev-viewswitch .cal-viewswitch-btn { flex: 1 1 auto; }

/* the ONE filter control: quiet "Filter" while nothing is applied, the
   solid brand fill + "(N)" count while any selection narrows the views */
.dev-filter-btn.is-active {
  background: var(--brand);
  border-color: var(--brand-strong);
  color: var(--surface);
}

.dev-filter-btn.is-active:hover:not(:disabled) {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
  color: var(--surface);
}

/* --- the filter sheet's two option groups + footer ---------------------- */

.dev-filter-group-head {
  margin: 0 0 var(--s1);
  font-size: var(--font-small);
  font-weight: 700;
  color: var(--ink-soft);
}

.dev-filter-options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  margin: 0 0 var(--s2);
}

/* multi-select option pills — the old area pills' anatomy, now inside the
   sheet and independently toggleable (aria-pressed per pill) */
.dev-filter-pill {
  min-height: var(--touch);
  padding: 8px 16px;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  border-radius: 999px;
  color: var(--ink);
  font: inherit;
  font-size: var(--font-small);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.dev-filter-pill:hover { background: var(--brand-tint); }

.dev-filter-pill.is-active {
  background: var(--brand);
  border-color: var(--brand-strong);
  color: var(--surface);
}

.dev-filter-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-top: var(--s2);
  padding-top: var(--s2);
  border-top: 1px solid var(--border);
}

.dev-filter-matchcount {
  color: var(--ink-soft);
  font-size: var(--font-small);
  font-weight: 600;
}

/* the subset banner the List and Month zones pin above their results
   while any filter is on — .adm-section-sub's voice, weighted so "you
   are looking at a subset" always reads */
.dev-filter-note {
  margin: 0 0 var(--s1);
  font-weight: 600;
}

@media (max-width: 839.98px) {
  /* the view switch and the Filter button share ONE controls row at phone
     width — trim the switch's padding (dev-controls only; the People
     calendar's switch is untouched) and let the button shrink with an
     ellipsis so the pair can never overflow the row */
  .dev-controls { flex-wrap: nowrap; }
  .dev-controls .cal-viewswitch { flex-shrink: 0; }
  .dev-controls .cal-viewswitch-btn { min-width: 0; padding: 8px 13px; }
  .dev-controls .dev-filter-btn { flex: 0 1 auto; min-width: 0; padding: 10px 14px; }
  .dev-controls .dev-filter-btn span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* --- due list groups ---------------------------------------------------- */

.dev-group { margin: 0 0 var(--s3); }

.dev-group-head {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: 0 0 var(--s1);
  font-size: var(--font-h3);
  font-weight: 700;
}

/* the breach group head: the overdue family's ink, weight-differentiated
   (heavier than the 700 base — never a new color) */
.dev-group-head--past_ceiling { color: var(--adm-late-ink); font-weight: 800; }
.dev-group-head--overdue { color: var(--adm-late-ink); }
.dev-group-head--due_week { color: var(--adm-nudge-ink); }
.dev-group-head--due_month { color: var(--adm-future-ink); }
.dev-group-head--current { color: var(--adm-done-ink); }

.dev-group-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  color: var(--ink-soft);
  font-size: 14px;
  font-weight: 700;
}

.dev-group-rows { margin: 0; }

.dev-row-tag {
  min-width: 7.5rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

/* the crew list rows' small leading TYPE icon — "what kind" survives the
   removed status chip there (the group head owns status; officer rows
   keep their chips and never render this) */
.dev-row-typeicon {
  width: 17px;
  height: 17px;
  margin-right: 6px;
  color: var(--ink-soft);
  vertical-align: -3px;
}

/* the collapsed Current group — a details disclosure styled like the
   other group heads, chevron rotating when open */
.dev-current { margin: 0 0 var(--s3); }

.dev-current summary {
  cursor: pointer;
  list-style: none;
  min-height: var(--touch);
}

.dev-current summary::-webkit-details-marker { display: none; }

.dev-current summary:hover { color: var(--ink); }

.dev-current-chevron { transform: rotate(-90deg); transition: transform 150ms ease; }

.dev-current[open] .dev-current-chevron { transform: rotate(0deg); }

.dev-current .dev-group-rows { margin-top: var(--s1); }

/* --- month view: whole-day buttons + severity count pills --------------- */

/* a day cell that is a button keeps the shared .cal-day look */
button.dev-day-btn {
  font: inherit;
  text-align: left;
  cursor: pointer;
  display: block;
  width: 100%;
}

button.dev-day-btn:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
}

button.dev-day-btn.is-today:hover { background: var(--brand-tint); }

.dev-cal .cal-day-markers { display: flex; }

/* count pills: aggregate counts, not per-device chips — a touch more
   compact than the people grid's chips so "1 overdue" fits the cell */
.dev-cal-pill {
  cursor: pointer;
  justify-content: flex-start;
  font-size: 13px;
  padding: 2px 6px;
}

/* the Guide's sample pill sizes to its text instead of the panel width */
.legend .dev-cal-pill { width: max-content; }

.dev-pill-num { display: none; }

.dev-day-more {
  display: block;
  min-height: 0;
  padding: 2px 6px;
  color: var(--link);
  font-size: 14px;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.dev-cal-hint { margin-top: 0; }

/* --- guide: side panel at desktop, button + popup on mobile ------------- */

.dev-guide-btn { display: none; }

@media (max-width: 839.98px) {
  .dev-guide-panel { display: none; }
  .dev-guide-btn { display: inline-flex; }
}

/* Desktop, body.admin surfaces (the officer Equipment mode + the
   standalone page): the personnel calendar's equal-height row treatment
   (body.admin .cal-layout .legend — the month grid sizes the row, the
   panel stretches to match with an absolute, internally-scrolling body)
   also caught these panels, and the device LIST view turned it into a
   bug: .cal-main is as tall as the whole due list, so the Guide stretched
   hundreds of px of empty panel past its own rows. Equipment guides opt
   back into the crew personnel panel's geometry instead — the panel hugs
   its content and sticks just below the admin sticky header while the
   list scrolls (the crew page's own equipment panel already rides the
   base sticky .legend rules). People mode carries no .dev-guide-panel,
   so its equal-height contract — pinned by the admin calendar Guide
   panel self-check — is untouched. */
@media (min-width: 840px) {
  body.admin .cal-layout .legend.dev-guide-panel {
    display: block;
    align-self: flex-start;
    position: sticky;
    /* --chat-header-h is the admin page's live-measured sticky header;
       the fallback covers the standalone page's shorter static header */
    top: calc(var(--chat-header-h, 36px) + var(--s2));
  }

  body.admin .cal-layout .legend.dev-guide-panel .guide-panel-scroll {
    position: static;
    flex: none;
  }

  body.admin .cal-layout .legend.dev-guide-panel .legend-body {
    position: static;
    overflow-y: visible;
  }
}

/* --- device detail sheet ------------------------------------------------ */

.dev-sheet-status { margin: 0 0 var(--s2); }

/* the ONE dual-clock line a late device's sheet carries (and the
   completed-late record line) — quiet prose, the danger ink; the breach
   variant only adds weight, never a new color */
.dev-sheet-clock {
  margin: 0 0 var(--s2);
  color: var(--danger-ink);
  font-size: var(--font-small);
}

.dev-sheet-clock--breach { font-weight: 700; }

.dev-sheet-note {
  margin: var(--s2) 0 0;
  font-size: var(--font-small);
}

/* the device-type test definition's regulatory citation — one quiet
   line ("Per 30 CFR 250.880"), muted, no link. Shared by the device
   sheet body and the crew capture form. */
.dev-sheet-citation {
  margin: 0 0 var(--s2);
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* the Guide's dual-clock explainer paragraph */
.dev-guide-dualclock { margin-top: var(--s2); }

/* --- day-cell pills: bare counts always, never a clipped word ----------
   Seven columns inside main's 1000px cap leaves each day cell too narrow
   for the longest tier word ("past ceiling") to fit — true at a phone
   width, still true at a wide desktop once the side Guide panel takes
   its share back, so no viewport-width breakpoint fixes it. Rather than
   let the .chip-cal component's built-in ellipsis fire (its comment
   above), the device day cell drops the word ENTIRELY, at every width:
   color + count only, the colored pill and its number, no text. Tier
   meaning still reads three ways without the word — color (the same
   five-family system every surface uses), the leading icon, and the day
   sheet's full labels one tap away. The Guide's legend sample pill is
   unaffected (scoped to .dev-cal, which the legend sits outside of) and
   keeps its full word, since it sizes to content and never sits inside a
   fixed-width day cell. */

.dev-cal .chip-cal {
  width: auto;
  min-width: 26px;
  height: 24px;
  padding: 2px 7px;
  justify-content: center;
}

.dev-cal .chip-cal .chip-text { display: none; }

.dev-cal .dev-pill-num { display: inline; font-weight: 700; }

.dev-cal .cal-day-markers {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 3px;
}

.dev-day-more { padding: 0 2px; font-size: 12px; }

@media (max-width: 480px) {
  .dev-scope-btn { font-size: var(--font-body); }

  .dev-row-tag { min-width: 0; }
}

/* --- "Run your rounds" — the crew equipment mode's guided workflow --------
   A full-screen MODE built for a phone on a platform: gloves, glare, one
   hand. Every tap target is at least the shared --touch (48px); the
   primary record action and the verify pair are deliberately bigger.
   body.rounds-mode hides the app header — the mode carries its own slim
   topbar (Exit · title · progress). */

body.rounds-mode .app-header { display: none; }

.rounds-view {
  max-width: 640px;
  margin: 0 auto;
}

.rounds-topbar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) 0 var(--s2);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s2);
}

.rounds-topbar-title {
  font-weight: 700;
  font-size: var(--font-h3);
  flex: 1;
  min-width: 0;
}

.rounds-exit-btn { flex-shrink: 0; }

.rounds-progress-btn {
  flex-shrink: 0;
  min-height: var(--touch);
  min-width: var(--touch);
  padding: 8px 16px;
  border: 2px solid var(--brand);
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand-strong);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.rounds-progress-btn:hover {
  background: var(--brand);
  color: var(--surface);
}

/* the at-a-glance position bar under the topbar: slim, unlabeled (the
   "N of M" button carries the accessible words), tracking recorded
   entries through the session */
.rounds-progressbar {
  height: 6px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
  margin: 0 0 var(--s2);
}

.rounds-progressbar-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--brand);
}

/* --- start screen ---------------------------------------------------------- */

.rounds-h1 { font-size: var(--font-h1); margin-bottom: var(--s1); }
.rounds-h2 { font-size: var(--font-h3); margin: 0; }

.rounds-count {
  color: var(--ink-soft);
  margin-bottom: var(--s2);
}

.rounds-overdue-note {
  display: flex;
  align-items: flex-start;
  gap: var(--s1);
  padding: 10px var(--s2);
  border: 2px solid var(--danger-border);
  border-radius: var(--radius);
  background: var(--danger-fill);
  color: var(--danger-ink);
  font-weight: 600;
  margin-bottom: var(--s2);
}

.rounds-overdue-note .icon { color: var(--danger-ink); margin-top: 2px; }

/* the big deliberate actions — larger than the standard button scale */
.rounds-cta {
  display: flex;
  width: 100%;
  min-height: 60px;
  font-size: var(--font-h3);
  margin: 0 0 var(--s2);
}

.rounds-cta-minor {
  display: flex;
  width: 100%;
  margin: 0 0 var(--s2);
}

.rounds-route-head {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: var(--s3) 0 var(--s1);
}

.rounds-info-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--touch);
  min-height: var(--touch);
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--brand-strong);
  cursor: pointer;
}

.rounds-info-btn:hover { background: var(--brand-tint); }

.rounds-info-btn .icon { width: 24px; height: 24px; }

.rounds-area-head {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: var(--s2) 0 var(--s1);
  font-size: var(--font-small);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.rounds-route-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.rounds-route-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px var(--s2);
  padding: 10px var(--s2);
  border-bottom: 1px solid var(--border);
}

.rounds-route-row:last-child { border-bottom: 0; }

.rounds-route-tag { font-weight: 700; }

.rounds-route-type {
  grid-column: 1;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.rounds-route-due {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  color: var(--ink-soft);
  font-size: var(--font-small);
  text-align: right;
}

.rounds-route-due.is-overdue { color: var(--danger-ink); font-weight: 700; }

.rounds-route-status {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  font-weight: 700;
  font-size: var(--font-small);
  color: var(--ink-soft);
  text-align: right;
}

.rounds-route-status--pass { color: var(--ok-ink); }
.rounds-route-status--fail { color: var(--danger-ink); }
.rounds-route-status--skip { color: var(--norec-ink); }
.rounds-route-status--here { color: var(--brand-strong); }
.rounds-route-status--pending { color: var(--pending-ink); }

/* --- the route as a WALK: location stops with a connecting spine -------- */

.rounds-stops {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.rounds-stop {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s1) var(--s2);
  flex-wrap: wrap;
  padding: 12px var(--s2) 12px 38px;
  border-bottom: 1px solid var(--border);
}

.rounds-stop:last-child { border-bottom: 0; }

/* the stop dot + the vertical line threading the stops together */
.rounds-stop::before {
  content: '';
  position: absolute;
  left: 17px;
  top: 19px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--brand);
}

.rounds-stop::after {
  content: '';
  position: absolute;
  left: 20px;
  top: 27px;
  bottom: -19px;
  width: 2px;
  background: var(--border-strong);
}

.rounds-stop:last-child::after { display: none; }

.rounds-stop-name { font-weight: 700; }

.rounds-stop-meta {
  display: inline-flex;
  gap: var(--s1);
  align-items: baseline;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.rounds-stop-state--done { color: var(--ok-ink); font-weight: 700; }

.rounds-stop-flag { color: var(--danger-ink); font-weight: 700; }

/* --- the step ---------------------------------------------------------------- */

.rounds-step { outline: none; }

/* the LOCATION breadcrumb leads every step — the operator navigates by
   where they stand, so the path outranks the old quiet context line */
.dev-crumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px var(--s1);
  margin: 0 0 4px;
  font-size: var(--font-h3);
  font-weight: 700;
  color: var(--ink);
}

.dev-crumb-area { color: var(--ink-soft); }

.dev-crumb-sep { color: var(--brand); font-weight: 800; }

/* the not-moved variant: same spot as the previous step, said out loud */
.dev-crumb--still {
  display: inline-flex;
  padding: 6px 12px;
  border: 2px solid var(--ok-border);
  border-radius: 999px;
  background: var(--ok-fill);
  color: var(--ok-ink);
}

/* entering a new area: a header band on the area's first device — a
   glance, not a blocking screen */
.rounds-area-transition {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: 0 0 var(--s1);
  padding: 10px var(--s2);
  border: 2px solid var(--brand);
  border-radius: var(--radius);
  background: var(--brand-tint);
  color: var(--brand-strong);
  font-weight: 700;
}

.rounds-area-transition .icon { flex-shrink: 0; }

.rounds-step-count {
  color: var(--ink-soft);
  font-size: var(--font-small);
  margin: 0 0 var(--s2);
}

.dev-identity { margin-bottom: var(--s1); }

.dev-identity-tag {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin: 0 0 2px;
  font-size: 34px;
  font-weight: 800;
  line-height: 1.15;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.dev-identity-tag .icon {
  width: 28px;
  height: 28px;
  color: var(--brand);
}

.dev-identity-type {
  margin: 0 0 2px;
  font-weight: 600;
}

.dev-identity-where {
  margin: 0;
  color: var(--ink-soft);
}

.rounds-step-due {
  margin: var(--s1) 0 var(--s2);
  font-weight: 600;
  color: var(--ink-soft);
}

.rounds-step-due.is-overdue { color: var(--danger-ink); }

/* the step's live urgency row: the equipment view's own status chip +
   the days/due fact, part of the identity block above the fold */
.rounds-step-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin: var(--s1) 0 var(--s2);
}

.rounds-step-status .rounds-step-due { margin: 0; }

/* the collapsible "How to test" section: collapsed by default, one tap
   (>=48px summary) to expand; generic per-TYPE method steps + the
   standing facility-procedures footer */
.rounds-howto {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin: 0 0 var(--s2);
}

.rounds-howto-summary {
  display: flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--touch);
  padding: 10px var(--s2);
  font-weight: 700;
  color: var(--brand-strong);
  cursor: pointer;
  list-style: none;
}

.rounds-howto-summary::-webkit-details-marker { display: none; }

.rounds-howto-summary span { flex: 1; }

.rounds-howto-chevron { transition: transform 0.15s ease; }

.rounds-howto[open] .rounds-howto-chevron { transform: rotate(180deg); }

.rounds-howto-steps {
  margin: 0;
  padding: 0 var(--s2) var(--s1) calc(var(--s2) + 20px);
}

.rounds-howto-steps li {
  padding: 4px 0;
  color: var(--ink);
}

.rounds-howto-footer {
  padding: 0 var(--s2) 12px;
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.rounds-revisit-note {
  padding: 10px var(--s2);
  border: 2px solid var(--pending-border);
  border-radius: var(--radius);
  background: var(--pending-fill);
  color: var(--pending-ink);
  margin-bottom: var(--s2);
}

.rounds-form {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: var(--s2);
  margin-bottom: var(--s2);
}

.rounds-form .rounds-h2 { margin-bottom: var(--s2); }

.rounds-block-label {
  display: block;
  margin: 0 0 4px;
  font-weight: 700;
}

.rounds-field-unit { font-weight: 400; color: var(--ink-soft); }

/* the expectation is visible BEFORE the value goes in */
.rounds-field-expect {
  margin: 0 0 var(--s1);
  color: var(--ink-soft);
  font-size: var(--font-small);
}

.rounds-field { margin-bottom: var(--s2); }

.rounds-field-input {
  width: 100%;
  min-height: 56px;
  padding: 10px var(--s2);
  font: inherit;
  font-size: 24px;
  font-weight: 700;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  color: var(--ink);
}

.rounds-field-input:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 1px;
}

.rounds-field.is-ok .rounds-field-input { border-color: var(--ok-border); }
.rounds-field.is-out .rounds-field-input {
  border-color: var(--danger-solid);
  background: var(--danger-fill);
}

.rounds-field-flag {
  margin: 4px 0 0;
  min-height: 1.4em;
  font-size: var(--font-small);
  font-weight: 600;
  color: var(--ok-ink);
}

.rounds-field.is-out .rounds-field-flag { color: var(--danger-ink); }

/* verified / not-verified — the two huge function-test answers */
.rounds-verify { margin-bottom: var(--s2); }

.rounds-verify-btns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s1);
}

.rounds-verify-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1);
  min-height: 64px;
  padding: 10px;
  font: inherit;
  font-weight: 700;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

.rounds-verify-btn.is-active {
  border-color: var(--ok-border);
  background: var(--ok-fill);
  color: var(--ok-ink);
}

.rounds-verify-btn--no.is-active {
  border-color: var(--danger-solid);
  background: var(--danger-fill);
  color: var(--danger-ink);
}

.rounds-obs { margin-bottom: var(--s2); }

.rounds-reason-input {
  width: 100%;
  padding: 10px var(--s2);
  font: inherit;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  resize: vertical;
}

/* the derived verdict, in place, before the record tap */
.rounds-derived {
  margin: 0 0 var(--s1);
  padding: 10px var(--s2);
  border-radius: var(--radius);
  font-weight: 700;
}

.rounds-derived--pass {
  border: 2px solid var(--ok-border);
  background: var(--ok-fill);
  color: var(--ok-ink);
}

.rounds-derived--fail {
  border: 2px solid var(--danger-solid);
  background: var(--danger-fill);
  color: var(--danger-ink);
}

/* THE primary action — considerably larger than everything else */
.rounds-record-btn {
  display: flex;
  width: 100%;
  min-height: 68px;
  font-size: var(--font-h3);
  margin: 0 0 var(--s1);
}

.rounds-override-link {
  display: block;
  width: 100%;
  min-height: var(--touch);
  padding: 8px;
  border: 0;
  background: none;
  font: inherit;
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.rounds-skip-btn,
.rounds-back-btn {
  display: flex;
  width: 100%;
}

.rounds-reason-zone {
  border-top: 1px solid var(--border);
  padding-top: var(--s2);
}

.rounds-reason-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s1);
  margin-bottom: var(--s1);
}

.rounds-preset-btn { padding: 10px; }

.rounds-confirm-btn {
  display: flex;
  width: 100%;
  min-height: 56px;
  margin: var(--s1) 0;
}

.rounds-cancel-btn {
  display: flex;
  width: 100%;
}

/* Previous | Next as one NAVIGATION row — Next is deliberately styled as
   travel (secondary, beside Previous), never like the form's Skip
   decision, and the note under the pair says what Next does not do */
.rounds-stepnav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s1);
  margin-bottom: var(--s1);
}

.rounds-stepnav .rounds-back-btn,
.rounds-stepnav .rounds-next-btn {
  display: flex;
  width: 100%;
  min-height: 56px;
}

.rounds-stepnav-note {
  margin: 0 0 var(--s2);
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* the summary's leaving-work-undone warning — outstanding is explicit */
.rounds-outstanding-note {
  display: flex;
  align-items: flex-start;
  gap: var(--s1);
  padding: 10px var(--s2);
  border: 2px solid var(--pending-border);
  border-radius: var(--radius);
  background: var(--pending-fill);
  color: var(--pending-ink);
  font-weight: 600;
  margin-bottom: var(--s2);
}

.rounds-outstanding-note .icon { color: var(--pending-ink); margin-top: 2px; flex-shrink: 0; }

.rounds-step-note,
.rounds-demo-note { margin-bottom: var(--s2); }

.rounds-step-note {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* --- summary ------------------------------------------------------------------ */

.rounds-sum-group { margin-bottom: var(--s2); }

.rounds-sum-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.rounds-sum-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px var(--s2);
  border-bottom: 1px solid var(--border);
}

.rounds-sum-row:last-child { border-bottom: 0; }

.rounds-sum-detail {
  color: var(--ink-soft);
  font-size: var(--font-small);
}

/* --- the equipment page's head: its ONE heading (h1 wearing the section-
   head scale) --- */

.ceq-head .ceq-title {
  margin: 0;
  font-size: var(--font-h2);
}

/* --- the equipment page's door in ----------------------------------------------- */

.ceq-rounds-btn {
  display: flex;
  width: 100%;
  min-height: 60px;
  font-size: var(--font-h3);
  margin: 0 0 var(--s1);
}

/* the composition sub-line under the rounds button — what the round holds,
   most urgent first; quiet prose, the button above carries the weight */
.ceq-rounds-subline {
  margin: 0 0 var(--s2);
  color: var(--ink-soft);
  font-size: var(--font-small);
  text-align: center;
}

/* --- "Record a test" on the crew device sheet ----------------------------------
   The one action block under the sheet facts, and the capture step's
   shell. The capture form itself is the extracted rounds kit and wears
   the rounds-form families verbatim — one look for one job; only the
   sheet-specific pieces get rules here. */

.ceq-test-btn {
  display: flex;
  width: 100%;
  margin-top: var(--s2);
}

.ceq-test-recorded {
  display: flex;
  align-items: center;
  gap: var(--s1);
  min-height: var(--touch);
  margin: var(--s2) 0 0;
  padding: var(--s1) var(--s2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--neutral-fill);
  color: var(--ink);
}

.ceq-test-recorded .icon { flex-shrink: 0; }

.ceq-test-form { margin-top: var(--s2); }

.ceq-test-cancel-btn {
  display: flex;
  width: 100%;
  margin-top: var(--s1);
}

/* --- the quiet certifications link (the qualification thread) ------------------- */

.ceq-cert-row {
  margin: var(--s2) 0 0;
  text-align: center;
}

.ceq-cert-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 8px var(--s2);
  color: var(--link);
  font-size: var(--font-body);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ceq-cert-link .icon {
  width: 16px;
  height: 16px;
}

/* --- the officer Guides' split structure (dev-* — admin Equipment mode +
   /devices/) --------------------------------------------------------------------
   One geometry for the officer equipment Guides' two sections: the
   LEGEND header and the collapsed "How the timing works" disclosure.
   Both officer surfaces ride these rules; the crew page carries no
   equipment Guide anymore (its detail sheets teach the dual clock). */

.dev-guide-section-head {
  margin: 0 0 var(--s1);
  font-size: var(--font-small);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.dev-guide-timing { margin-top: var(--s2); }

.dev-guide-timing-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  font-size: var(--font-small);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  cursor: pointer;
  list-style: none;
}

.dev-guide-timing-head::-webkit-details-marker { display: none; }

.dev-guide-timing-head .dev-current-chevron {
  width: 14px;
  height: 14px;
  transition: transform 0.15s ease;
}

.dev-guide-timing[open] > .dev-guide-timing-head .dev-current-chevron {
  transform: rotate(180deg);
}

.dev-guide-timing .dev-guide-dualclock { margin-top: var(--s1); }

.dev-guide-timing .legend-row { margin-top: var(--s1); }

.ceq-rounds-done {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1) var(--s2);
  padding: 10px var(--s2);
  border: 2px solid var(--ok-border);
  border-radius: var(--radius);
  background: var(--ok-fill);
  color: var(--ok-ink);
  font-weight: 600;
  margin: 0 0 var(--s2);
}

.ceq-rounds-done .icon { color: var(--ok-ink); }

.ceq-rounds-done span { flex: 1; min-width: 200px; }

.ceq-rounds-again-btn { flex-shrink: 0; }

@media (max-width: 480px) {
  .dev-identity-tag { font-size: 28px; }
}

/* --- area status tiles (crew strip + /devices/ Platform Overview) --------
   ENCODING CONTRACT (device-ui.js areaTileHtml): the worst-child tier is
   the tile's LEFT-EDGE accent + badge family — never a full saturated
   fill. Current / Due-this-month worst = QUIET (neutral edge, neutral
   badge, muted sub); Due-this-week = the sanctioned admin amber; Overdue
   and Past ceiling = the existing late-family tokens. Tokens only. */

.dev-area-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s1);
  margin: 0 0 var(--s2);
}

/* the /devices/ overview grid goes four-up where the width allows */
@media (min-width: 840px) {
  .dev-area-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.dev-area-tile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  min-height: 44px;
  padding: 8px 10px 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 5px solid var(--border);
  border-radius: var(--radius);
  font: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
}

.dev-area-tile:hover { background: var(--brand-tint); }

.dev-area-tile:active { background: var(--brand-tint); border-color: var(--brand-strong); }

/* the pressed/selected face: the filter-selection convention (brand ring
   + tint), keeping the status accent edge and badge readable */
.dev-area-tile.is-active {
  background: var(--brand-tint);
  border-color: var(--brand-strong);
  box-shadow: inset 0 0 0 1px var(--brand-strong);
}

.dev-area-tile--due_week { border-left-color: var(--adm-nudge-border); }
.dev-area-tile--overdue { border-left-color: var(--adm-late-border); }
.dev-area-tile--past_ceiling { border-left-color: var(--adm-late-ink); }

.dev-area-tile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
}

.dev-area-tile-name {
  font-size: var(--font-small);
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dev-area-tile--past_ceiling .dev-area-tile-name { font-weight: 800; }

/* the worst-tier count badge — quiet neutral unless the tier is
   actionable, then the tier's own chip family */
.dev-area-tile-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border: 1px solid var(--neutral-border);
  border-radius: 999px;
  background: var(--neutral-fill);
  color: var(--neutral-ink);
  font-size: 13px;
  font-weight: 700;
  flex-shrink: 0;
}

.dev-area-tile--due_week .dev-area-tile-badge {
  background: var(--adm-nudge-fill);
  border-color: var(--adm-nudge-border);
  color: var(--adm-nudge-ink);
}

.dev-area-tile--overdue .dev-area-tile-badge {
  background: var(--adm-late-fill);
  border-color: var(--adm-late-border);
  color: var(--adm-late-ink);
}

.dev-area-tile--past_ceiling .dev-area-tile-badge {
  background: var(--adm-late-ink);
  border-color: var(--adm-late-ink);
  color: var(--surface);
}

.dev-area-tile-sub {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dev-area-tile--overdue .dev-area-tile-sub,
.dev-area-tile--past_ceiling .dev-area-tile-sub { color: var(--adm-late-ink); }

.dev-area-tile-alerticon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
}

/* --- the /devices/ Platform Overview hero -------------------------------- */

.dev-hero { margin-bottom: var(--s3); }

.dev-hero-head { flex-wrap: wrap; }

.dev-concept-tag {
  padding: 2px 8px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.dev-hero-rollup { margin-bottom: var(--s2); }

/* --- the deadline position bar (device sheets) ---------------------------
   Position/length is the magnitude channel: a neutral track from last
   test to the ceiling, the due + (late-only) ceiling ticks, the today
   marker. The due→ceiling zone is the amber-to-red semantic ramp on late
   devices; beyond-ceiling is the solid breach ink. Tokens only. */

.dev-bar { margin: var(--s1) 0 var(--s2); }

.dev-bar-captions {
  position: relative;
  height: 16px;
  margin-bottom: 6px;
}

.dev-bar-caption {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
}

.dev-bar-caption.is-anchor-start { transform: none; }
.dev-bar-caption.is-anchor-end { transform: translateX(-100%); }

.dev-bar-track {
  position: relative;
  height: 10px;
  border: 1px solid var(--neutral-border);
  border-radius: 6px;
  background: var(--neutral-fill);
}

/* zones clip to the rounded track; ticks and the marker ride outside the
   clip so they can overhang the 10px rail */
.dev-bar-fill {
  position: absolute;
  inset: 0;
  border-radius: 5px;
  overflow: hidden;
}

.dev-bar-zone {
  position: absolute;
  top: 0;
  bottom: 0;
}

.dev-bar-zone--late {
  background: linear-gradient(90deg, var(--adm-nudge-border), var(--adm-late-ink));
}

.dev-bar-zone--breach { background: var(--adm-late-ink); }

.dev-bar-tick {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  transform: translateX(-50%);
  background: var(--ink-soft);
}

.dev-bar-tick--ceiling {
  top: -5px;
  bottom: -5px;
  width: 3px;
  background: var(--adm-late-ink);
}

.dev-bar-marker {
  position: absolute;
  top: -6px;
  bottom: -6px;
  width: 5px;
  border-radius: 3px;
  transform: translateX(-50%);
  background: var(--ink-soft);
}

.dev-bar--overdue .dev-bar-marker { background: var(--adm-late-ink); }

.dev-bar--breach .dev-bar-marker {
  top: -8px;
  bottom: -8px;
  width: 7px;
  background: var(--adm-late-ink);
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--adm-late-ink);
}

.dev-bar-labels {
  position: relative;
  height: 16px;
  margin-top: 8px;
}

.dev-bar-labels--tall { height: 32px; }

.dev-bar-label {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
}

.dev-bar-label--start { left: 0; transform: none; }
.dev-bar-label.is-anchor-start { transform: none; }
.dev-bar-label.is-anchor-end { transform: translateX(-100%); }

.dev-bar-label--ceiling {
  color: var(--adm-late-ink);
  font-weight: 700;
}

.dev-bar-label--bump { top: 16px; }

/* =====================================================================
   END OF DEVICES FENCE — nothing below this line.
   ===================================================================== */
