/* Admin console — the "bNear Admin" design canvas (project 03dece21).
 *
 * Values come from that file: 22px panels, 16px gaps, a 34px page inset, the
 * 40px weight-200 stat figure, 16px circle tiles tinted with the accent, the
 * people table's four columns, 10px distribution bars, and the danger panel.
 *
 * Two deliberate departures, both noted where they occur:
 *  - the topbar's inner row is constrained to the page box rather than the bar
 *    carrying the inset, so the brand lines up with the panels past 1240px;
 *  - the login takes a username as well as a password, because that is what
 *    the app's auth actually needs.
 *
 * Palette and scale come from tokens.css; nothing here hard-codes a colour.
 */
:root {
  --fg: var(--text);
  --fg-dim: var(--text-dim);
  --border: var(--line);
  --page: 1240px;
  --inset: var(--s-7);          /* 34px */
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
/* An author `display` beats the UA stylesheet's `[hidden] { display: none }`,
   and the button rules below set `display: inline-flex`. */
[hidden] { display: none !important; }

/* `strong` defaults to `font-weight: bolder`, which is RELATIVE — against this
   console's 300 body it computes to 400, which is ordinary text. Every bold
   word here has to say a number. */
strong, b { font-weight: 600; }

body {
  font-family: var(--bn-font);
  font-weight: 300;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  /* Temperatures and counts line up in columns without per-element overrides. */
  font-feature-settings: 'tnum' 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }

@keyframes bnRise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

#admin[data-state="login"]     .state-dashboard { display: none; }
#admin[data-state="dashboard"] .state-login     { display: none; }

/* ── Brand ──────────────────────────────────────────────────────── */
.brand-row { display: flex; align-items: center; gap: 12px; }
.logo { width: 30px; height: 30px; display: block; }
/* Wordmark rule: b in 400, Near in 700, .me in 400, Nunito. Only Near is bold. */
.wordmark { font-family: var(--bn-wordmark); font-size: 19px; color: var(--bn-ink); line-height: 1; }
.wm-b    { font-weight: 400; }
.wm-near { font-weight: 700; }
.wm-me   { font-weight: 400; }

/* Trial (2026-09-06, Sergio): b-Near.me. The hyphen is full size — the same
   as the `b` it follows — so it needs none of the compensations a shrunken
   one did: at 1em the font's own hyphen metrics already place it correctly,
   and a margin would only open a gap the kerning did not ask for. Weight 400
   as well, matching the `b` rather than Near's 700, or the dash reads as
   belonging to the bold half.

   Still its own span, and still one class per stylesheet, so the trial can be
   pulled back out by deleting the rule and the spans — and so the dash can
   stay aria-hidden, which is what makes a screen reader say the name instead
   of spelling it. */
.wm-dash { font-weight: 400; }

/* The heart's colour, the same #f9484a as the landing's one call to action
   and the heart in the 3D mark. It was a mint tint, which read as one more
   accent chip rather than as a mark of where you are. Solid rather than
   tinted, so it matches that button and not the filter pills. */
.chip-admin {
  padding: 4px 11px;
  border-radius: var(--r-pill);
  background: var(--bn-coral);
  color: var(--on-accent);
  font-size: var(--fs-cap);
  font-weight: 500;
}

/* ── Buttons ────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 10px 20px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-family: inherit;
  font-size: var(--fs-body-sm);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease, color .2s ease, transform .2s ease;
}
.btn:disabled { opacity: .55; pointer-events: none; }
.btn-block { width: 100%; }
.btn-sm { padding: 7px 15px; font-size: var(--fs-meta); }

.btn-accent { background: var(--accent); color: var(--on-accent); box-shadow: var(--shadow-teal); }
.btn-accent:hover { background: var(--color-accent-600); transform: translateY(-2px); }

.btn-ink { background: var(--bn-ink); color: var(--on-accent); }
.btn-ink:hover { background: var(--accent); transform: translateY(-1px); }

.btn-outline { background: var(--card); color: var(--text); border-color: var(--line); font-weight: 400; }
.btn-outline:hover { border-color: var(--accent); transform: translateY(-1px); }

.btn-danger { background: var(--bn-danger-btn); color: var(--on-accent); }
.btn-danger:hover { background: var(--bn-danger-btn-hi); transform: translateY(-1px); }

.icon-btn {
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--fg-dim);
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.icon-btn:hover { border-color: var(--color-accent-300); color: var(--accent); }
#refreshBtn.refreshing svg { animation: spin .6s linear; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* ── Inputs ─────────────────────────────────────────────────────── */
input {
  width: 100%;
  padding: 11px 18px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-body-sm);
  font-weight: 300;
}
input::placeholder { color: var(--text-faint); }
input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-tint); }

/* ── Login ──────────────────────────────────────────────────────── */
.state-login { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 40px; }
.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 38px 34px;
  box-shadow: var(--shadow-login);
  animation: bnRise .6s var(--ease-lift) both;
}
.login-card .brand-row { margin-bottom: 26px; }
.login-card h1 { font-size: var(--fs-h1-login); font-weight: 200; letter-spacing: -0.025em; margin-bottom: var(--s-2); }
.login-card .sub { font-size: var(--fs-body-sm); color: var(--fg-dim); margin-bottom: var(--s-5); }
.login-card input { margin-bottom: var(--s-3); }
.login-card .error { margin-top: var(--s-3); }

/* ── Topbar ─────────────────────────────────────────────────────── */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  padding: var(--s-4) 0;
  background: rgba(251, 251, 250, 0.9);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
/* The design lets the bar carry the inset while main is centred at 1240 —
   those are different boxes and they drift apart past 1240px. Same box here. */
.topbar-inner {
  max-width: var(--page);
  margin-inline: auto;
  width: 100%;
  padding-inline: var(--inset);
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}
/* The brand pushes everything else right, rather than space-between spreading
   three groups across the bar. */
.topbar-inner > .brand-row { margin-right: auto; }
.topbar-utils,
.topbar-primary { display: flex; align-items: center; gap: var(--s-2); }
/* On a wide bar the check-in comes first and Sign out ends the row, which is
   the order the design canvas uses. The DOM order is the phone's, so this
   swaps them back. */
.topbar-primary { order: 1; margin-left: var(--s-3); }
.topbar-utils   { order: 2; }
.auto-status {
  font-size: var(--fs-meta);
  color: var(--fg-dim);
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  white-space: nowrap;
}
.auto-status:empty { display: none; }
.auto-status.sent { color: var(--accent-deep); border-color: var(--color-accent-200); background: var(--accent-tint); }

/* ── Page ───────────────────────────────────────────────────────── */
.page {
  max-width: var(--page);
  margin: 0 auto;
  padding: 30px var(--inset) 60px;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.panel {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 26px 28px;
}
.panel h2 { font-size: var(--fs-h2); font-weight: 500; letter-spacing: -0.02em; margin-bottom: 18px; }
.panel h3 { font-size: var(--fs-h3); font-weight: 500; letter-spacing: -0.015em; margin-bottom: var(--s-2); }
.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: 18px;
}
.panel-head h2 { margin-bottom: 0; }
.note { font-size: var(--fs-meta); color: var(--fg-dim); }
.error { color: var(--bn-danger-head); font-size: var(--fs-meta); }
.muted { color: var(--fg-dim); font-size: var(--fs-body-sm); }

/* ── Stat row ───────────────────────────────────────────────────── */
.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
.stat {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 22px 26px;
}
.stat-label {
  font-size: var(--fs-cap);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-dim);
  margin-bottom: var(--s-3);
}
.stat-figure { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.stat-value { font-size: var(--fs-stat); font-weight: 200; letter-spacing: -0.03em; line-height: 1; }
.stat-note { font-size: var(--fs-body-sm); color: var(--fg-dim); }
.stat-chip {
  padding: 3px 10px;
  border-radius: var(--r-pill);
  background: var(--accent-tint);
  color: var(--color-accent-800);
  font-size: var(--fs-cap);
  white-space: nowrap;
}
/* A rising average is the thing worth noticing, so it is not tinted calm. */
.stat-chip.up { background: var(--bn-danger-bg); color: var(--bn-danger-head); }

/* ── Circle tiles ───────────────────────────────────────────────── */
/* minmax(0, 1fr), not 1fr: a bare 1fr is minmax(auto, 1fr) and a track will
   not shrink below its content's min-content width, so the tile carrying the
   most buttons quietly grew wider than its neighbours and the four stopped
   lining up. The zero floor makes the columns equal whatever is inside them,
   and .ct-actions wraps rather than pushing. */
.circle-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); }
.circle-tile {
  border: 1px solid var(--color-accent-200);
  background: var(--accent-tint);
  border-radius: var(--r-tile);
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
  font: inherit;
  cursor: pointer;
  transition: border-color .2s ease, transform .2s ease;
}
.circle-tile:hover { transform: translateY(-2px); }
/* The tiles are the circle selector — there is no separate row of pills. */
.circle-tile.current { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ct-name { font-size: var(--fs-body); font-weight: 500; }
.ct-meta { font-size: var(--fs-meta); color: var(--color-accent-800); }
.ct-code {
  font-family: var(--bn-mono);
  font-size: var(--fs-cap);
  color: var(--color-accent-800);
  margin-top: 10px;
  user-select: all;
}
.ct-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
/* Round icon buttons, the topbar's .icon-btn a size down: three of them fit a
   quarter-width tile with room to spare, where three labelled pills did not.
   The meaning lives in title and aria-label, so it survives both a hover and a
   screen reader. */
.ct-icon { width: 32px; height: 32px; }
.ct-icon svg { width: 15px; height: 15px; }
/* Broadcast is the one action a caregiver is here to take, so it keeps the
   accent while the other two stay quiet. */
/* Confirmation is the accent, briefly — the same colour the primary action
   uses, so "it worked" reads the same way everywhere in this console. */
.ct-icon.did-copy { border-color: var(--accent); color: var(--accent); }
.ct-icon.cc-share { border-color: var(--color-accent-200); color: var(--accent); }
.ct-icon.cc-share:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* On the tinted tile the buttons need their own ground to read as controls. */
.circle-tile .ct-icon { background: var(--card); }

.circle-tile.empty {
  border: 1px dashed var(--line);
  background: var(--bg);
  justify-content: space-between;
  cursor: default;
}
.circle-tile.empty:hover { transform: none; }
.circle-tile.empty .ct-name { color: var(--fg-dim); }
.circle-tile.empty .ct-meta { color: var(--fg-dim); }
.circle-tile.empty .btn { align-self: flex-start; margin-top: 14px; }

/* Deactivated: still a real circle with real history, so it keeps its tile and
   stays selectable — it just stops wearing the accent. Neutral rather than
   coral: a pause is reversible and nothing has been destroyed, and coral means
   danger everywhere else in this console. */
.circle-tile.paused {
  border-color: var(--line);
  background: var(--bg);
}
.circle-tile.paused .ct-name,
.circle-tile.paused .ct-meta,
.circle-tile.paused .ct-code { color: var(--fg-dim); }
.circle-tile.paused.current { border-color: var(--fg-dim); box-shadow: 0 0 0 1px var(--fg-dim); }
.ct-flag {
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--fg-dim);
  font-size: var(--fs-cap);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* ── Split ──────────────────────────────────────────────────────── */
/* The two columns are one row, so they end level: `stretch` (the default,
   which `align-items: start` was overriding) makes People as tall as the
   distribution and timeline stacked together, whichever side is taller. */
.split { display: grid; grid-template-columns: 1.35fr 1fr; gap: var(--s-4); }
.split-side { display: flex; flex-direction: column; gap: var(--s-4); }
/* The timeline takes up whatever slack the row has, so the side column fills
   its half rather than leaving a gap under the last panel. */
.split-side .grow { flex: 1; }

/* ── People ─────────────────────────────────────────────────────── */
.people-row {
  display: grid;
  /* The last track holds Message and Remove side by side. It was 92px, sized
     for Remove alone, and the second button overflowed it silently — grid
     does not clip, it just lets the content run over the neighbour. */
  grid-template-columns: 1.4fr 0.7fr 0.8fr 1fr 170px;
  gap: var(--s-3);
  align-items: center;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-body-sm);
}
.people-row.head {
  padding-bottom: 10px;
  font-size: var(--fs-cap);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-dim);
}
.people-row:last-child { border-bottom: 0; }
/* A row followed by its note hands the rule to the note, or the pair would
   draw two. :has is already relied on elsewhere in this codebase. */
.people-row:has(+ .people-note) { border-bottom: 0; }
.people-note:last-child { border-bottom: 0; }
.p-name { display: flex; align-items: center; gap: 9px; min-width: 0; }
.p-name span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p-name em { font-style: italic; color: var(--fg-dim); }
/* Name over phone in one cell. The dot stays vertically centred against the
   pair rather than against the name alone. */
.p-name { align-items: center; }
.p-ident { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.p-who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p-phone {
  font-family: var(--bn-mono);
  font-size: var(--fs-cap);
  color: var(--fg-dim);
  text-decoration: none;
  white-space: nowrap;
}
.p-phone:hover { color: var(--accent); }

/* The note, on its own line under the row it belongs to. It is the only prose
   in this table, so it gets the full width instead of a cell that would clip
   it — and it is absent entirely when nobody wrote one, so a table of numbers
   stays a table of numbers. */
.people-note {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  padding: 0 0 13px 17px;
  margin-top: -6px;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-meta);
  color: var(--fg-dim);
}
.p-tag {
  padding: 2px 9px;
  border-radius: var(--r-pill);
  background: var(--accent-tint);
  border: 1px solid var(--color-accent-200);
  color: var(--color-accent-800);
  font-size: var(--fs-cap);
  white-space: nowrap;
}
.p-note-text { flex: 1 1 12rem; min-width: 0; }

/* The dot carries the band, so the average does not have to be read to see it. */
.p-dot { width: 8px; height: 8px; border-radius: 50%; display: block; flex: none; }
.p-dot.t-365 { background: var(--t-365); }
.p-dot.t-370 { background: var(--t-370); }
.p-dot.t-380 { background: var(--t-380); }
.p-dot.t-390 { background: var(--t-390); }
.p-dot.t-400 { background: var(--t-400); }
.p-avg { font-weight: 500; }
.p-dim { color: var(--fg-dim); }

/* Removing someone is a rare, destructive thing next to rows that are mostly
   read, so the button stays quiet until it is hovered or armed. */
.p-actions { display: flex; justify-content: flex-end; }
.btn-quiet {
  padding: 6px 12px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-faint);
  font-family: inherit;
  font-size: var(--fs-cap);
  cursor: pointer;
  transition: color .2s ease, background .2s ease, border-color .2s ease;
}
.people-row:hover .btn-quiet { color: var(--fg-dim); border-color: var(--line); }
.btn-quiet:hover { color: var(--bn-danger-head); border-color: var(--bn-danger-line); background: var(--bn-danger-bg); }
.btn-quiet.danger {
  color: var(--bn-danger-head);
  border-color: var(--bn-danger-line);
  background: var(--bn-danger-bg);
  font-weight: 500;
}
.people-row.removing { background: var(--bn-danger-bg); }

/* ── Distribution ───────────────────────────────────────────────── */
/* ── The person filter ──────────────────────────────────────────
   One control, rendered twice — over the people table and over the
   distribution — driven by one selection, so filtering either filters both.
   It replaced a row of pills, which was fine for four departments and unusable
   for a circle of thirty people. */
.person-filter { position: relative; margin-bottom: 16px; }
.pf-control { position: relative; display: flex; align-items: center; }
.pf-input {
  width: 100%;
  box-sizing: border-box;
  padding: 7px 30px 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-cap);
}
.pf-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
/* The placeholder carries the current selection, so it has to read as a value
   rather than as a hint when something is chosen. */
.person-filter.filtered .pf-input {
  border-color: var(--color-accent-200);
  background: var(--accent-tint);
}
.person-filter.filtered .pf-input::placeholder { color: var(--color-accent-800); }
.pf-caret,
.pf-clear {
  position: absolute;
  right: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  color: var(--fg-dim);
  font: inherit;
  font-size: var(--fs-cap);
  line-height: 1;
  pointer-events: none;
}
.pf-clear {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  cursor: pointer;
  pointer-events: auto;
  color: var(--color-accent-800);
}
.pf-clear:hover { background: var(--color-accent-200); }

.pf-menu {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px;
  /* Long circles have to stay reachable without the panel growing past the
     card it sits in. */
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  background: var(--card);
  box-shadow: var(--shadow-card);
}
.pf-option {
  padding: 8px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-cap);
  text-align: left;
  cursor: pointer;
  /* A row is a target, not a label. */
  min-height: 34px;
}
.pf-option:hover { background: var(--bg); }
.pf-option.active {
  background: var(--accent-tint);
  color: var(--color-accent-800);
  font-weight: 500;
}
.pf-empty { margin: 0; padding: 8px 10px; color: var(--fg-dim); font-size: var(--fs-cap); }

.dist-row { display: grid; grid-template-columns: 54px 1fr 30px; gap: var(--s-3); align-items: center; margin-bottom: var(--s-3); }
.dist-row:last-child { margin-bottom: 0; }
.dist-label { font-size: var(--fs-meta); font-weight: 500; }
.dist-track { height: 10px; border-radius: 5px; background: var(--line); overflow: hidden; }
.dist-fill { display: block; height: 100%; border-radius: 5px; transition: width .3s ease; }
.dist-fill.t-365 { background: var(--t-365); }
.dist-fill.t-370 { background: var(--t-370); }
.dist-fill.t-380 { background: var(--t-380); }
.dist-fill.t-390 { background: var(--t-390); }
.dist-fill.t-400 { background: var(--t-400); }
.dist-count { font-size: var(--fs-meta); color: var(--fg-dim); text-align: right; }

.timeline svg { width: 100%; height: auto; display: block; overflow: visible; }
/* Axis labels. Sized in the SVG's own user units, so they scale with the
   chart rather than staying 9px while the drawing doubles. */
.tl-tick {
  font-family: var(--font-body, inherit);
  font-size: 8px;
  fill: var(--fg-dim);
  font-variant-numeric: tabular-nums;
}

/* ── Utility row ──────────────────────────────────────────────────
   Three cards in one row, so they end level: `stretch` (the grid default,
   which `align-items: start` was overriding) sizes all three to the tallest.
   The body text inside each is flex:1, so a card that gains height puts the
   slack above its button rather than under it. */
/* minmax(0, ...) for the same reason the circle tiles use it: a bare 1fr will
   not shrink below its content, so the card with the most buttons quietly
   grows wider than its neighbours. */
.utility-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4); }
.util { display: flex; flex-direction: column; padding: 24px 26px; }
.util p { font-size: var(--fs-meta); line-height: 1.6; color: var(--fg-dim); margin-bottom: 18px; flex: 1; }
.util .btn { align-self: flex-start; }
.field-stack { display: flex; flex-direction: column; gap: var(--s-2); }
.field-stack .btn { margin-top: 4px; }

/* ── Danger ─────────────────────────────────────────────────────── */
.danger {
  background: var(--bn-danger-bg);
  border: 1px solid var(--bn-danger-line);
  border-radius: var(--r-card);
  padding: 24px 28px;
}
.danger-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-5); flex-wrap: wrap; }
.danger h3 { color: var(--bn-danger-head); margin-bottom: 6px; }
.danger p { font-size: var(--fs-meta); line-height: 1.6; color: var(--bn-danger-body); max-width: 78ch; }
.reset-confirm { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--bn-danger-line); }
.reset-confirm .hint { font-size: var(--fs-meta); color: var(--bn-danger-body); margin-bottom: var(--s-2); }
.reset-actions { display: flex; gap: var(--s-2); justify-content: flex-end; margin-top: var(--s-3); }

/* ── Narrow ─────────────────────────────────────────────────────── */
@media (max-width: 1040px) {
  .split { grid-template-columns: 1fr; }
  .circle-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .utility-row { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 720px) {
  :root { --inset: var(--s-4); }
  .topbar { padding: 10px 0; }
  .topbar-inner { gap: var(--s-3); }
  /* Refresh and Sign out ride on the brand's line, hard right; the check-in
     and its status take the row below, side by side. Everything used to sit
     in one wrapping list, which put them wherever they happened to land. */
  .topbar-utils { order: 1; }
  .topbar-primary { order: 2; flex: 1 1 100%; margin-left: 0; }
  #notifyBtn { flex: 1; }
  .page { padding: 16px var(--inset) 40px; }
  .panel, .stat { padding: 18px 20px; }
  .stat-row { grid-template-columns: 1fr 1fr; }
  .stat-row .stat:last-child { grid-column: 1 / -1; }
  .stat-value { font-size: 32px; }
  /* A phone gets two columns and two rows, not five narrow tracks.
     Squeezing five into 390px is what printed the date over the buttons.

     No explicit placement: with Answers hidden the four remaining children
     fall into the grid in document order — name, average / last check-in,
     actions — which is the layout we want anyway. Add a cell to the row
     markup and check this still holds.

     The header goes with it: labels for five columns cannot describe a two
     by two block, and a name, a temperature with a degree sign, a date and
     two buttons need no labels. */
  .people-row { grid-template-columns: minmax(0, 1fr) auto; gap: 6px var(--s-3); align-items: start; }
  .people-row.head { display: none; }
  .people-row .p-responses { display: none; }
  .p-avg { justify-self: end; font-weight: 600; }
  .p-actions { justify-self: end; gap: 4px; }
  .danger-head { flex-direction: column; align-items: stretch; }
  .danger-head .btn { align-self: flex-start; }
}

@media (max-width: 460px) {
  .circle-tiles { grid-template-columns: minmax(0, 1fr); }
}

/* Below this the brand plus both buttons is a few pixels too wide and the
   pair drops to its own line. The chip is the first thing to go — the page
   says Admin everywhere else. */
@media (max-width: 380px) {
  .chip-admin { display: none; }
  .brand-row { gap: 10px; }
}

/* ── Plan ───────────────────────────────────────────────────────
   A free dashboard simply has no comments in it, which reads as members not
   writing any rather than as a plan. The badge and one sentence say which. */
.plan-chip {
  margin-left: 8px;
  padding: 2px 9px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--fg-dim);
  font-size: var(--fs-cap);
  font-weight: 500;
  vertical-align: middle;
  white-space: nowrap;
}
.plan-chip.plan-private,
.plan-chip.plan-professional,
.plan-chip.plan-student {
  background: var(--accent-tint);
  border-color: var(--color-accent-200);
  color: var(--color-accent-800);
}
#planHint { margin: 0 0 var(--s-3); }

.plan-buttons { display: flex; flex-wrap: wrap; gap: 6px; }
.plan-buttons .btn { white-space: nowrap; }

/* When the next charge lands. It sits under the plan sentence and above the
   buttons, and must NOT inherit `.util p`'s `flex: 1` — two growing
   paragraphs in one column push the buttons around depending on which
   sentence is longer. */
/* `.util p` grows so each card's button lands on the same baseline as its
   neighbours. With two paragraphs that spacer ends up BETWEEN them, leaving
   the renewal date floating in the middle of the card away from the sentence
   it belongs to. So in this one panel the growth moves to the buttons, which
   is where it was always meant to be. */
#planPanelText { flex: 0 0 auto; }
.util .plan-buttons { margin-top: auto; }

/* `.util .plan-renewal`, not `.plan-renewal`: it is a <p> inside `.util`, so a
   bare class (0-1-0) loses to `.util p` (0-1-1) and the flex: 1 above would
   still apply. */
.util .plan-renewal {
  flex: 0 0 auto;
  margin: -10px 0 18px;
  font-size: var(--fs-meta);
  line-height: 1.5;
  color: var(--fg-dim);
  font-variant-numeric: tabular-nums;
  /* When the next charge lands is the one line on this card worth acting on,
     so the whole sentence carries weight rather than only its date. */
  font-weight: 600;
}
/* A date on this card is always something to diary — a renewal, an ending, a
   grant running out — so it is marked wherever it appears, in the plan
   sentence as well as the renewal line. `underline` with an offset rather
   than a border, so it follows the text if the line ever wraps. */
.pr-date {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
/* Two states worth acting on, and the same two colours the dev console gives
   them — the consoles sit side by side in one product, and the same fact
   reading differently in each is a fact nobody trusts.
   Cancelling: amber, because nothing is broken, it just ends.
   Payment failing: the danger ink, because a card needs attention today. */
.plan-renewal.ending { color: var(--bn-amber-ink); font-weight: 600; }
.plan-renewal.warn { color: var(--bn-danger-head); font-weight: 600; }
.note-inline { font-size: var(--fs-cap); color: var(--fg-dim); }

/* ── Tag tally ──────────────────────────────────────────────────
   The same row shape as the temperature distribution, so the two read as one
   idea seen two ways. Bars are relative to the commonest tag rather than to
   the total: the question is which reasons dominate, and a share of all tags
   would shrink everything once people pick several. */
.tally-row { display: grid; grid-template-columns: minmax(0, 1fr) 90px 30px; gap: var(--s-3); align-items: center; margin-bottom: var(--s-3); }
.tally-row:last-child { margin-bottom: 0; }
.tally-label { font-size: var(--fs-meta); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tally-track { height: 10px; border-radius: 5px; background: var(--line); overflow: hidden; }
.tally-fill { display: block; height: 100%; border-radius: 5px; background: var(--accent); transition: width .3s ease; }
.tally-count { font-size: var(--fs-meta); text-align: right; color: var(--fg-dim); }

/* ── The conversation sheet ─────────────────────────────────────
   Over the console, not beside it: a thread is somewhere you go and come
   back from, and the people table has no room for one. */
.sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-5);
  background: rgba(22, 33, 31, 0.38);
}
.sheet[hidden] { display: none !important; }
.sheet-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2, var(--shadow-1));
  width: min(560px, 100%);
  max-height: min(680px, 92vh);
  display: flex;
  flex-direction: column;
}
.sheet-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--line);
}
.sheet-head h2 { font-size: 1.05rem; font-weight: 600; margin: 2px 0 0; }

/* The thread scrolls; the composer below it does not move. */
.thread {
  flex: 1;
  min-height: 120px;
  overflow-y: auto;
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.msg { max-width: 82%; border-radius: var(--r-md); padding: 9px 12px; font-size: 0.9rem; line-height: 1.5; }
/* Yours on the right in the accent, theirs on the left in a neutral — the
   direction is carried by both position and colour, so it survives a
   screenshot in greyscale. */
.msg-caregiver { align-self: flex-end; background: var(--accent); color: #fff; }
.msg-member { align-self: flex-start; background: var(--bg); border: 1px solid var(--line); }
.msg-subject { display: block; font-weight: 600; margin-bottom: 2px; }
.msg-when { display: block; font-size: 0.72rem; opacity: 0.75; margin-top: 4px; font-variant-numeric: tabular-nums; }
.thread-empty { color: var(--muted); font-size: 0.9rem; margin: auto; text-align: center; }

.thread-form { border-top: 1px solid var(--line); padding: var(--s-4) var(--s-5) var(--s-5); display: grid; gap: var(--s-3); }
/* Squared off, near enough. These are the two boxes a caregiver writes a
   paragraph into, and --r-md is a 999px pill — which reads as a control you
   press rather than a sheet you write on, and wastes the first and last
   centimetre of every line to the curve. */
.thread-form input, .thread-form textarea {
  width: 100%;
  font: inherit;
  font-size: 0.9rem;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  padding: 9px 11px;
  resize: vertical;
}
.thread-form input:focus, .thread-form textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The error takes the slack so Send stays at the right edge, and neither
   wraps. This row used to also carry the private-note checkbox with its
   explanation beside it: a flex item shrinks to its longest word before the
   row wraps, so on a phone the sentence became a one-word column with the box
   stranded at the far left. The choice moved above the composer instead. */
.thread-send { display: flex; align-items: center; gap: var(--s-3); }
.thread-send .muted { flex: 1 1 auto; min-width: 0; }

/* Message or private note. A segmented pair rather than a checkbox: an
   unticked box looks the same whether it was considered or overlooked, and
   this is the one decision here that cannot be undone once it goes wrong. */
.thread-mode {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3px;
  padding: 3px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
}
.thread-mode input { position: absolute; opacity: 0; pointer-events: none; }
.thread-mode label {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  padding: 0 var(--s-3);
  border-radius: calc(var(--r-xs) - 2px);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  text-align: center;
  transition: background 0.12s, color 0.12s;
}
.thread-mode input:checked + label { background: var(--card); color: var(--text); box-shadow: var(--shadow-1); }
/* The radios are off-screen, so the ring has to be put back by hand or the
   selected segment is invisible to a keyboard. */
.thread-mode input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 1px; }
.thread-mode input#threadModeNote:checked + label { color: var(--accent-deep, var(--accent)); }

.thread-hint { margin: -4px 0 0; font-size: 0.78rem; color: var(--muted); }

/* In note mode the composer wears the same dashed frame the note itself will
   have in the thread, so what you are about to make is visible while you are
   making it rather than only afterwards. */
.thread-form[data-mode="note"] #threadText {
  border-style: dashed;
  background: var(--bg);
}
.thread-form[data-mode="note"] .thread-hint { color: var(--accent-deep, var(--accent)); font-weight: 600; }

/* A note is not a bubble. It is a line in a log: full width, no side, and
   marked, so it can never be mistaken at a glance for something the member
   was actually sent. */
.msg-note {
  align-self: stretch;
  max-width: none;
  background: transparent;
  border: 1px dashed var(--line);
  color: var(--muted);
}
.msg-note .msg-kind {
  display: block;
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-700, var(--accent));
  margin-bottom: 3px;
}
.thread-send .muted { font-size: 0.82rem; }

/* The unread badge on a row. Coral, because it is the one thing in this table
   that is waiting on the caregiver rather than describing a member. */
.p-unread {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 6px;
  border-radius: 999px;
  background: var(--bn-coral);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
