:root {
  --border: #d8dee5;
  --text: #1f2933;
  --muted: #6b7785;
  --accent: #1565c0;
  --accent-dark: #0d47a1;
  --accent-gradient: linear-gradient(135deg, #1976d2, #0d47a1);
  --danger: #c62828;
  --bg-subtle: #eaf6fb;
  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 14px;
  --mention: #1b7a3d;
  --mention-bg: #e3f5e8;
  /* Elevation scale -- a soft/cool-toned shadow (not plain black) used
     consistently for anything meant to read as "raised above the page":
     cards, popovers, the sticky topbar, hovered rows/buttons. Keeping
     these as tokens instead of one-off box-shadow values on
     button/.btn-ehr/.visit-card etc. means every raised surface in the
     app now shares the same depth language instead of each having its
     own hand-tuned blur/opacity. */
  --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
  --shadow-sm: 0 2px 8px rgba(15, 23, 42, 0.07);
  --shadow-md: 0 6px 20px rgba(15, 23, 42, 0.1);
  --shadow-lg: 0 16px 48px rgba(15, 23, 42, 0.18);
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

* { box-sizing: border-box; }

html { scrollbar-color: #c3ccd6 transparent; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: #fff;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; transition: color 0.15s var(--ease); }
a:hover { text-decoration: underline; }

/* Thin, unobtrusive scrollbars (Firefox above via scrollbar-color; webkit
   here) for every internally-scrolling panel -- .table-scroll, the
   message thread, the chat widget, the map list -- instead of each
   browser's bulky default, which reads noticeably "un-designed" next to
   everything else. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #c3ccd6; border-radius: 999px; border: 2px solid #fff; }
::-webkit-scrollbar-thumb:hover { background: #a7b1bd; }

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(180%) blur(8px);
  -webkit-backdrop-filter: saturate(180%) blur(8px);
  box-shadow: var(--shadow-xs);
}

.topbar-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--text);
  letter-spacing: -0.01em;
}
.topbar-brand:hover { text-decoration: none; }

.topbar-logo {
  display: inline-block;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.topbar-actions { display: flex; align-items: center; gap: 12px; }

.topbar-link {
  font-size: 0.9rem;
  padding: 6px 4px;
  border-bottom: 2px solid transparent;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.topbar-link:hover { text-decoration: none; color: var(--accent); }

.admin-nav {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  background: #fafbfc;
}
.admin-nav a {
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.admin-nav a:hover { text-decoration: none; background: #eef1f4; color: var(--text); }
.admin-nav a.active { color: #fff; background: var(--accent-gradient); box-shadow: var(--shadow-sm); }
.topbar-link.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

.new-badge { color: var(--danger); font-weight: 700; font-size: 0.75rem; }
.nav-badge {
  display: inline-block;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  vertical-align: middle;
  margin-left: 2px;
}
/* Messages nav badge cluster -- 7 tiny numbered pills (SOC/Patient/
   Physician/Case Manager/Tagged/Included/Audit -- same categories/colors
   as messages.html's filter chips and row tags) arranged as a 4+3 grid
   next to the "Messages" nav link so all 7 fit without turning it into
   one long strip. Each badge's color is set inline per-span (see
   base.html) rather than via a class, matching CATEGORY_COLORS in
   messages.html -- one set of hex values, not two hand-matched copies. */
.messages-badge-grid {
  /* All five counts (SOC / Patient / Tagged / Included / Audit) on one
     row beside the "Messages" nav link. This was a 4-column inline-grid,
     which was right when there were four badges and silently wrapped the
     fifth -- Audit, the grey one -- onto a second row under the nav the
     day it was added. inline-flex instead of bumping the count to 5, so
     a sixth badge can't reintroduce the same wrap. */
  display: inline-flex;
  align-items: center;
  gap: 1px;
  /* .admin-nav is a flex row, so this span is a flex item -- vertical-align
     does nothing here, which is why the counts sat centred against the tall
     nav links instead of riding up by the label. align-self lifts them to
     the top like a superscript; the negative margin cancels the nav's own
     4px gap so they sit against "Messages" rather than a step away from it.
     Sized down from the standalone .nav-badge too: five in a row beside a
     nav link is a different job from one badge on a button, and at full
     size they were the loudest thing in the bar. */
  align-self: flex-start;
  margin-top: -2px;
  margin-left: -5px;
}
.messages-badge-grid .nav-badge {
  min-width: 13px;
  height: 13px;
  line-height: 13px;
  padding: 0 3px;
  font-size: .56rem;
  font-weight: 600;
  margin-left: 0;
}
.notify-opt-in {
  position: fixed;
  bottom: calc(16px + env(safe-area-inset-bottom));
  right: 16px;
  left: 16px;
  max-width: 380px;
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  font-size: 0.85rem;
  z-index: 50;
}

/* Per-account debug identifier (Nurse.debug_id) -- meaningless on its own,
   but lets a provider quote a short, non-identifying string when
   reporting an issue so it's obvious which account hit it, without
   showing their name/email or putting anything in a URL. */
.debug-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  color: var(--muted);
  padding: 2px 5px;
  border: 1px solid var(--border);
  border-radius: 4px;
  user-select: all;
  cursor: text;
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #e3e8ee;
  color: var(--text);
  font-size: 0.75rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.avatar:hover, .avatar.active { background: var(--accent); color: #fff; }

.page { width: 90%; max-width: 1600px; margin: 0 auto; padding: 16px; }

.flash {
  background: var(--bg-subtle);
  border: 1px solid #bfe3f0;
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 16px;
  font-size: 0.9rem;
  box-shadow: var(--shadow-xs);
}
/* A genuine failure (e.g. an EHR push/sync exception) -- muted red, not
   the saturated --danger red used for destructive-action buttons, since
   this is informational, not something to click through a warning on. */
.flash-error { background: #fdf0ef; border-color: #e3a9a3; color: #7a2e26; }
/* Something to double-check but not a hard failure (e.g. "N pushed, M
   failed" partial bulk-push result) -- same soft-amber family as the
   Patient message category color used elsewhere (/messages). */
.flash-warning { background: #fdf6e3; border-color: #e0c589; color: #6b4e00; }

/* A small accent-colored tick to the left of every page/section heading --
   a quieter, more deliberate accent than a plain uppercase label sitting
   on a full-width bottom border, and a consistent "this is a heading"
   marker wherever it's reused across the app. */
.section-label {
  text-transform: uppercase;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.04em;
  padding: 2px 0 10px 11px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
  position: relative;
}
.section-label::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1px;
  bottom: 12px;
  width: 3px;
  border-radius: 3px;
  background: var(--accent-gradient);
}

.visit-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-xs);
  transition: box-shadow 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.15s var(--ease);
}
.visit-card:hover { box-shadow: var(--shadow-md); border-color: #c3ccd6; transform: translateY(-1px); }

/* Day divider on the Documents/Upcoming Visits provider tabs -- a plain
   flat list otherwise gives no sense of "these N visits are the same
   day" when scanning date-sorted results. */
.day-strip {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--text);
  white-space: nowrap;
  background: #e9edf1;
  border-radius: 6px;
  padding: 8px 12px;
  margin-bottom: 10px;
}
.day-strip:not(:first-child) { margin-top: 14px; }

.visit-card-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 4px 10px;
  margin-bottom: 6px;
}

.visit-card-patient { font-weight: 600; min-width: 0; overflow-wrap: break-word; }

.visit-card-action { font-size: 0.9rem; font-weight: 600; }
.visit-card-action.status-submitted { color: var(--muted); }
.visit-card-action.status-not_started,
.visit-card-action.status-in_progress { color: var(--danger); }
.visit-card-action.status-assigned { color: #4b3f9e; }

.visit-card-date { color: var(--muted); font-size: 0.85rem; margin-bottom: 8px; }

.visit-card-rows {
  border-top: 1px solid var(--border);
  padding-top: 8px;
  font-size: 0.9rem;
}

.visit-card-row {
  display: flex;
  justify-content: space-between;
  padding: 3px 0;
}

.visit-card-row .label { color: var(--muted); }

.empty-state {
  text-align: center;
  color: var(--muted);
  padding: 40px 16px;
}

form.stacked label {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 4px;
}

form.stacked input[type=text],
form.stacked input[type=email],
form.stacked input[type=tel],
form.stacked input[type=password],
form.stacked input[type=number],
form.stacked input[type=date],
form.stacked input[type=time],
form.stacked input[type=search],
form.stacked select,
form.stacked textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 1rem;
  margin-bottom: 6px;
  font-family: inherit;
}

form.stacked textarea { min-height: 80px; resize: vertical; }

form.stacked input[type=file] {
  display: block;
  width: 100%;
  padding: 10px 0;
  margin-bottom: 6px;
  font-size: 0.9rem;
}

/* Compact variant of form.stacked for forms with many short fields (Add
   Patient, Add Provider) -- lays label+input pairs out 3-per-row instead
   of one full-width row each, so the form doesn't take a full screen of
   vertical space. Each field is wrapped in a .field div (label+input
   stay stacked within their own cell); .field-wide spans every column
   for anything that benefits from the full row (address, free-text
   fields, help text, the submit button). */
form.stacked.compact-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(160px, 1fr));
  gap: 0 16px;
  align-items: start;
}
form.stacked.compact-grid .field-wide { grid-column: 1 / -1; }
@media (max-width: 640px) {
  form.stacked.compact-grid { grid-template-columns: 1fr; }
}

/* /settings -- Change Password + Change Email side by side in one card
   instead of two full-width stacked cards, so the page reads as a
   couple of compact panels rather than a long column of mostly-empty
   boxes for what's genuinely a small amount of content. */
.settings-security-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 32px;
}
.settings-security-grid > div:first-child {
  border-right: 1px solid var(--border);
  padding-right: 32px;
}
@media (max-width: 640px) {
  .settings-security-grid { grid-template-columns: 1fr; }
  .settings-security-grid > div:first-child {
    border-right: none;
    border-bottom: 1px solid var(--border);
    padding-right: 0;
    padding-bottom: 16px;
    margin-bottom: 16px;
  }
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
  padding: 8px 4px;
  min-height: 44px;
  cursor: pointer;
}
.checkbox-row input {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  accent-color: var(--accent);
}
.form-subsection-label {
  text-transform: uppercase;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text-muted, #6b7280);
  margin: 16px 0 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.form-subsection-label:first-child { margin-top: 0; }
.ability-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 4px 10px;
}
.ability-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.82rem;
  font-weight: 400;
  padding: 3px 0;
  cursor: pointer;
}
.ability-chip input {
  width: 15px;
  height: 15px;
  accent-color: var(--accent);
}
.checkbox-row label { margin: 0; cursor: pointer; }
.checkbox-row.suggested-participant { background: var(--bg-subtle); border-radius: 8px; }

/* .btn-secondary is included here (not just its own rule below) because
   it's used on both real <button> elements (which already match "button"
   below regardless of class) and plain <a class="btn-secondary"> links
   (e.g. every "Clear"/pagination link) -- an <a> doesn't match "button",
   so without this it would silently miss the shared box model
   (display/padding/radius/min-height/shadow) that makes it look like an
   actual button instead of bordered inline text. */
button, .btn, .btn-secondary {
  display: inline-block;
  background: var(--accent-gradient);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 13px 18px;
  min-height: 44px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  box-shadow: var(--shadow-xs);
  transition: opacity 0.15s var(--ease), transform 0.1s var(--ease), box-shadow 0.15s var(--ease);
}
button:hover, .btn:hover { opacity: 0.95; text-decoration: none; box-shadow: var(--shadow-sm); transform: translateY(-1px); }
button:active, .btn:active { transform: scale(0.98) translateY(0); box-shadow: var(--shadow-xs); }
button:disabled, .btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
button:focus-visible, .btn:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.btn-secondary {
  background: #fff;
  color: var(--text);
  border: 1px solid var(--border);
  box-shadow: none;
}
.btn-secondary:hover { background: #fafbfc; border-color: #c3ccd6; box-shadow: var(--shadow-xs); }

.btn-secondary.active { color: #fff; background: var(--accent-gradient); border-color: var(--accent); }

.btn-danger { background: var(--danger); }

/* CSV/log export links -- consistent "this downloads a file" look across
   every report page (Visits, Admin dashboard, Recurring Visits, Completed
   Documents, Audit Log), instead of being visually identical to a plain
   Filter/Clear button. */
.btn-download {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-subtle);
  color: var(--accent);
  border: 1px solid var(--accent);
  font-weight: 600;
  box-shadow: none;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), box-shadow 0.15s var(--ease), transform 0.1s var(--ease);
}
.btn-download:hover { background: var(--accent); color: #fff; box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.btn-download::before { content: "\2b07"; font-size: 0.85em; }

.btn-small { min-height: 36px; padding: 8px 12px; font-size: 0.85rem; }

/* EHR import/sync/push actions -- deliberately a step up from a plain
   btn-secondary so "this talks to an outside system" reads as its own
   category of action at a glance, not just another form button buried in
   a box. */
.btn-ehr {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--accent-gradient);
  color: #fff;
  border: none;
  font-weight: 700;
  border-radius: 999px;
  padding: 10px 20px;
  box-shadow: 0 3px 10px rgba(13, 71, 161, 0.35);
  transition: box-shadow 0.15s var(--ease), transform 0.1s var(--ease);
}
.btn-ehr::before { content: "\21c4"; font-size: 1rem; }
.btn-ehr:hover { box-shadow: 0 5px 16px rgba(13, 71, 161, 0.45); transform: translateY(-1px); opacity: 1; }
.btn-ehr:active { transform: translateY(0); }
.btn-ehr:disabled, .btn-ehr[disabled] {
  background: #dbe1e8; color: #8b97a3; box-shadow: none; cursor: not-allowed; transform: none;
}
.btn-ehr.btn-small { padding: 7px 14px; font-size: 0.85rem; }

.form-section {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 16px;
  box-shadow: var(--shadow-xs);
  transition: box-shadow 0.15s var(--ease);
}
.form-section:focus-within,
.form-section:hover { box-shadow: var(--shadow-sm); }

.form-section h3 {
  margin: 0 0 12px;
  font-size: 1rem;
}

.section-card { padding: 0; overflow: hidden; }

/* EVV (Electronic Visit Verification) panel on a Medicaid visit's note
   page -- left border color flags whether it still needs attention
   (amber/status-not_started) or is already satisfied (green/
   status-submitted), matching the same status-badge colors used
   elsewhere rather than inventing a new palette just for this. */
.evv-panel { border-left: 4px solid var(--warning, #b8860b); }
.evv-panel:has(.status-submitted) { border-left-color: var(--success, #2e7d32); }
.section-card summary {
  cursor: pointer;
  padding: 14px 16px;
  font-weight: 600;
  font-size: 1rem;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
}
.section-card summary::-webkit-details-marker { display: none; }
.section-card summary::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg);
  transition: transform 0.15s;
  flex-shrink: 0;
  margin-left: 12px;
}
.section-card[open] summary::after { transform: rotate(45deg); }
.section-card-body { padding: 0 16px 16px; }

.section-toggle-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.sticky-action-bar {
  position: sticky;
  bottom: 0;
  background: #fff;
  border-top: 1px solid var(--border);
  padding: 12px 0;
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  margin-top: 8px;
  display: flex;
  gap: 10px;
  z-index: 5;
}
.sticky-action-bar button { flex: 1; }

.locked-banner {
  background: #fff4e5;
  border: 1px solid #ffd699;
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 16px;
  font-size: 0.9rem;
  box-shadow: var(--shadow-xs);
}
/* A note QA sent back for modification (ClinicalNote.qa_returned_at) --
   more saturated than the plain amber .locked-banner above so it reads
   as "needs your attention," same yellow as .cal-chip.qa-returned. */
.qa-return-banner { background: #fff3c4; border-color: #c99a00; }
.qa-return-icon { display:inline-block; cursor:help; font-size:1.05rem; margin-right:4px; }

table.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  margin-bottom: 20px;
}
table.admin-table th, table.admin-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  word-break: break-word;
  overflow-wrap: anywhere;
}
table.admin-table th {
  color: var(--muted);
  font-weight: 700;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  border-bottom: 2px solid var(--border);
}
/* Row hover highlight so a wide/dense table is easier to track across
   visually -- skipped for divider rows (.table-day-divider,
   .on-call-month-divider) and any row that already carries its own
   semantic background (unread/mention/poll highlights use !important
   precisely so this never fights them). */
table.admin-table tbody tr:hover {
  background: #fafbfe;
}
table.admin-table tbody tr.table-day-divider:hover,
table.admin-table tbody tr:has(.on-call-month-divider):hover {
  background: inherit;
}
/* Denser variant for long, mostly-scanning lists (admin/documents.html,
   admin/index.html's Open Documents) -- same columns, tighter rows so
   more fit on screen without scrolling. */
table.admin-table.admin-table-compact th,
table.admin-table.admin-table-compact td { padding: 4px 8px; font-size: 0.82rem; }
table.admin-table.admin-table-compact td:last-child .btn-small { min-height: 24px; padding: 3px 7px; font-size: 0.74rem; }
/* Grey double-line divider grouping a documents/visits table's rows by
   day, same visual language as .on-call-month-divider -- generalized
   here since it's not on-call-specific. */
.table-day-divider td {
  font-weight: 700;
  font-size: 0.8rem;
  color: var(--text);
  background: #e9edf1;
  border-top: 3px double #aab2bc;
  border-bottom: 3px double #aab2bc;
  padding: 6px 10px !important;
}
table.admin-table td[data-label="Active"],
table.admin-table td[data-label="Calendar"],
table.admin-table td[data-label="Phone"],
table.admin-table td[data-label="DOB"],
table.admin-table td[data-label="Role"],
table.admin-table td[data-label="Nurse"] { white-space: nowrap; }

/* Compact action buttons in a table's last column -- several stacked
   forms (edit/reset/deactivate/remove) shouldn't cost more vertical
   space than the row's other cells need. */
table.admin-table td:last-child {
  min-width: 140px;
}
table.admin-table td:last-child .btn-small {
  min-height: 30px;
  padding: 5px 9px;
  font-size: 0.78rem;
}
.table-actions {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-top: 4px;
  align-items: center;
}
/* admin/providers.html has up to five icon actions per row -- keep them on
   one line (the table already scrolls horizontally via .table-scroll)
   instead of wrapping to two, which looked broken. Note: .table-actions
   is a <div> nested inside the last <td>, not the <td> itself -- putting
   display:flex directly on a <td> breaks border-collapse alignment with
   sibling cells in the same row. */
table.admin-table td .table-actions {
  flex-wrap: nowrap;
}

/* Icon-only row actions (admin/patients.html, admin/nurses.html,
   admin/documents.html, messages.html) -- an <a>/<button> (Calendar, View,
   Download) or a <details><summary> (Edit, Documents, pulling down its
   form same as before) styled as a small square button instead of a text
   link/label, so a row full of actions doesn't force every other column
   narrower than it needs to be. title="..." on each one is what shows the
   action name on hover -- no visible label otherwise. Sizing/shape lives
   here for every variant; color/background comes from whatever other
   class is paired with it (.btn-secondary, .btn-danger, or -- for a bare
   <button class="icon-btn"> with neither, e.g. the messages.html
   hide/delete icons -- the transparent/borderless look set below). */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  min-height: 30px;
  padding: 0;
  font-size: 0.95rem;
  line-height: 1;
  border-radius: 6px;
  text-decoration: none;
  position: relative;
  flex-shrink: 0;
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.15s var(--ease), transform 0.1s var(--ease);
}
/* Flat/borderless variant (no .btn-secondary or .btn-danger paired in) --
   e.g. messages.html's hide icon -- deliberately opts out of the base
   button's shadow+lift-on-hover so it stays a quiet, minimal glyph
   instead of looking like a floating chip with nothing under it. */
button.icon-btn:not(.btn-secondary):not(.btn-danger) {
  background: transparent;
  border: none;
  color: var(--muted);
  box-shadow: none;
}
button.icon-btn:not(.btn-secondary):not(.btn-danger):hover { background: var(--bg-subtle); opacity: 1; box-shadow: none; transform: none; }
.icon-btn-danger { color: var(--danger); }
/* admin/providers "Points"/"Payments" -- per-visit-type points/price
   editor. -3col caps at 3 columns (more rows instead of stretching wide
   on large screens); the plain version fills as many ~160px columns as
   fit. Both collapse to 1 column under the mobile breakpoint below. */
.visit-type-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 2px 16px; }
.visit-type-grid-3col { display: grid; grid-template-columns: repeat(3, minmax(160px, 1fr)); gap: 2px 16px; }

.icon-no-home { position: relative; display: inline-block; line-height: 1; }
.icon-no-home::after {
  content: "";
  position: absolute;
  left: -15%;
  top: 50%;
  width: 130%;
  height: 0.12em;
  background: var(--danger);
  transform: translateY(-50%) rotate(-30deg);
}
summary.icon-btn {
  list-style: none;
  cursor: pointer;
  background: #fff;
  color: var(--text);
  border: 1px solid var(--border);
}
summary.icon-btn::-webkit-details-marker { display: none; }
summary.icon-btn::marker { content: ""; }
summary.icon-btn:hover { background: #f1f3f5; }
.icon-btn-count {
  position: absolute;
  bottom: -3px;
  right: -3px;
  background: var(--accent);
  color: #fff;
  font-size: 0.55rem;
  font-weight: 700;
  border-radius: 8px;
  padding: 0 3px;
  line-height: 1.4;
  min-width: 12px;
  text-align: center;
}
table.admin-table td:last-child details {
  margin-bottom: 2px;
}
table.admin-table td:last-child details summary {
  font-size: 0.8rem;
}

.table-scroll {
  overflow-x: auto;
  /* -webkit-overflow-scrolling: touch was here -- a legacy WebKit property
     for momentum scrolling on old iOS (<13), unnecessary on any modern
     device. Removed 2026-08-25 (ported from fshc-homecare-app, same shared
     CSS pattern, same real bug): it creates its own compositing context,
     which was trapping row-level .visit-popover-panel popups nested inside
     these tables -- they're position:fixed and meant to escape to the
     viewport/render above the global backdrop, but got stuck compositing
     inside THIS container instead, which is what actually made them look
     grey/washed-out and unresponsive on a real device. */
  margin-bottom: 4px;
}
.table-scroll table.admin-table { margin-bottom: 0; }

/* Access Levels tab (/admin/providers?tab=access): one column per admin
   tier (super_admin, agency_admin, cnm, intake, scheduler, qa,
   finance_admin, business_developer, other -- see
   app/auth/admin_capabilities.py's TIERS) made every header text
   horizontal, which forced each column wide enough for its longest label
   ("Business Developer") and pushed the table well past typical viewport
   width. Rotating just the tier labels (not the leading "Capability"
   column) lets each column shrink toward its checkmark-cell width.

   table-layout: fixed with explicit per-column widths, not auto: with
   auto layout, browsers computed each vertical-text <th>'s intrinsic
   width inconsistently once the labels' lengths varied (confirmed via a
   real screenshot, 2026-08-25) -- later columns' checkmark cells
   rendered visibly offset from their own header text, worse the further
   right the column sat. Pinning every tier column to the same fixed
   width removes that guesswork entirely; the capability column gets the
   remaining space via its own explicit width. Scoped to >640px: below
   that, .responsive-table's own media rule (below) switches this same
   table to stacked full-width cards, which these fixed pixel widths
   would otherwise fight. */
@media (min-width: 641px) {
  .access-levels-table { table-layout: fixed; }
  .access-levels-table th:first-child, .access-levels-table td:first-child { width: 300px; }
  .access-levels-table th:not(:first-child), .access-levels-table td:not(:first-child) { width: 58px; }
  .access-levels-table th:has(.access-levels-tier-label) {
    vertical-align: bottom;
    text-align: center;
    padding-bottom: 8px;
    overflow: visible;
  }
}
.access-levels-tier-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  display: inline-block;
  white-space: nowrap;
}

/* Below ~640px, responsive-table rows become stacked cards instead of a
   horizontally-scrolling table -- much easier to read one-handed on a phone.
   Requires each <td> to carry a data-label attribute. */
@media (max-width: 640px) {
  table.responsive-table thead { display: none; }
  table.responsive-table, table.responsive-table tbody,
  table.responsive-table tr, table.responsive-table td { display: block; width: 100%; }
  table.responsive-table tr {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 10px 12px;
    margin-bottom: 12px;
  }
  table.responsive-table td {
    border: none;
    padding: 6px 0;
    display: flex;
    justify-content: space-between;
    gap: 12px;
    text-align: right;
  }
  /* Cells holding a LIST of items render each item as its own top-level
     <div>/<form> -- that's multiple flex children in the row layout
     above, squeezing them onto one unwrapped horizontal line instead of
     letting each stack on its own line. :has() with 2+ direct children
     reliably distinguishes these multi-item cells from simple
     single-value ones (Name, Phone, ...) without needing every template
     call site to opt in with a class. Ported from fshc-homecare-app,
     2026-08-25, same shared CSS pattern, same real bug (a patient's
     document badges ran off the right edge of the screen). */
  table.responsive-table td:has(> :nth-child(2)) {
    display: block;
    text-align: left;
  }
  table.responsive-table td:has(> :nth-child(2))::before {
    display: block;
    margin-bottom: 4px;
  }
  table.responsive-table td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--muted);
    font-size: 0.78rem;
    text-align: left;
    flex-shrink: 0;
  }
  table.responsive-table td:empty,
  table.responsive-table td[data-label]:not([data-label=""]):has(> *:only-child:empty) { display: none; }

  /* Action cells (no label -- buttons/forms) stack full-width instead of
     sitting in the label/value flex row. */
  table.responsive-table td[data-label=""] {
    display: block;
    text-align: left;
  }
  table.responsive-table td[data-label=""]::before { content: none; }
  table.responsive-table td[data-label=""] form { margin-bottom: 6px; }
  table.responsive-table td[data-label=""] form:last-child { margin-bottom: 0; }
  table.responsive-table td[data-label=""] > form > button:not(.icon-btn),
  table.responsive-table td[data-label=""] > details > summary:not(.icon-btn) { width: 100%; }
  /* Icon-button rows (.table-actions, a <div> nested one level deeper than
     the plain form/details case above) wrap instead of forcing a single
     scrolling line -- there's no horizontal table-scroll once the table
     is stacked into cards, so a forced nowrap row would just get clipped. */
  table.responsive-table td .table-actions {
    flex-wrap: wrap;
  }

  /* Touch targets: 30px icon-btns are ~30% under the 44px WCAG/platform
     minimum -- bump the hit area on touch viewports without changing their
     look (or the desktop mouse-click size, which doesn't need this) by
     growing the tap target beyond the visible glyph via padding removed
     from the gap around it instead. */
  .icon-btn { min-width: 44px; min-height: 44px; }
  .table-actions, td[data-label=""] .icon-btn + .icon-btn,
  .table-actions form + form { gap: 10px; }

  /* iOS Safari auto-zooms the page when a focused input/select computes
     under 16px -- these all sit below that on the compact desktop sizing. */
  .search-bar input[type=search], .search-bar input[type=text], .search-bar input[type=date], .search-bar select,
  .chat-widget-footer input, .chat-popup-footer input, .people-picker-input {
    font-size: 16px;
  }

  .visit-type-grid, .visit-type-grid-3col { grid-template-columns: 1fr; }
}

.help-text { color: var(--muted); font-size: 0.8rem; margin-top: 0; margin-bottom: 14px; }

/* "What to include" box above a guided-narrative textarea (Clinical
   Narrative, PT/OT/ST treatment notes, re-eval -- see note_templates.py's
   Field.checklist) -- a scannable numbered checklist a provider can check
   against mid-visit, instead of parsing help_text's run-on sentence. */
.narrative-checklist {
  background: var(--bg-subtle);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 10px;
  font-size: 0.85rem;
  color: var(--accent-dark);
}
.narrative-checklist strong { color: var(--accent-dark); }
.narrative-checklist ol { margin: 6px 0 0; padding-left: 20px; }
.narrative-checklist li { margin-bottom: 4px; }
.narrative-checklist li:last-child { margin-bottom: 0; }

.map-layout { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
.map-list {
  flex: 1 1 340px;
  max-width: 400px;
  max-height: 640px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow-xs);
}
.map-list-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.map-list-row:last-child { border-bottom: none; }
.map-list-row:hover { background: var(--bg-subtle); }
/* Hovering a marker on the map (see highlightListRow in map.html) --
   distinct from the plain :hover above so it's obvious this row is
   highlighted because of the MAP, not the mouse being over the row
   itself. */
.map-list-row.map-row-hover { background: var(--bg-subtle); box-shadow: inset 3px 0 0 var(--accent); }
.map-list-info { flex: 1; min-width: 0; }
.map-list-name { font-weight: 600; }
/* Scheduling note (app/routers/visits.py::set_visit_scheduling_note) shown
   inline in the day map's left-side visit list -- distinct from the plain
   .help-text rows above it so a note doesn't get lost among the address/
   visit-type lines. */
.map-list-note { background: #fdf6e3; color: #6b5c1e; border-radius: 3px; padding: 2px 5px; }
.map-pin-badge {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
}
.map-label-select { flex: 0 0 auto; padding: 4px; border-radius: 6px; border: 1px solid var(--border); }
/* The A-Z label, Open, Scheduling note, and Reschedule controls for one
   visit row -- stacked in a column so they read as one action group next
   to the address, instead of spreading the row out horizontally and
   wrapping awkwardly at the list's fixed max-width. */
.map-list-row-actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.map-list-row-actions .map-label-select { width: 100%; }
.map-canvas { flex: 2 1 480px; min-height: 500px; height: 500px; border-radius: 8px; border: 1px solid var(--border); box-shadow: var(--shadow-xs); }
.map-pin-marker {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8rem;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
/* Grown on hover (see highlightMarker in map.html) -- scaling the INNER
   span, never the L.divIcon wrapper Leaflet itself positions via its own
   CSS transform, so this never fights that positioning. */
.map-pin-marker, .map-lab-marker, .map-loc-marker { transition: transform 0.12s ease; }
.map-marker-hover .map-pin-marker, .map-marker-hover .map-lab-marker, .map-marker-hover .map-loc-marker {
  transform: scale(1.4);
}
.map-pin-marker-wrap, .map-lab-marker-wrap, .map-loc-marker-wrap {
  background: transparent;
  border: none;
}
/* Provider-added locations (home, errands, etc.) -- same pin shape as a
   patient visit but green, not blue, and labeled Z-down instead of
   A-up (see _assign_location_labels in app/routers/map.py) so the two
   sets read as distinct at a glance. */
.map-loc-marker {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #0f9d3f;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8rem;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.map-loc-badge {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #0f9d3f;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
}
.map-lab-marker {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.9rem;
  line-height: 1;
  border: 4px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.map-lab-legend {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  background: #fafbfc;
}
/* Provider's own home pin (Nurse.address) -- a distinct shape (square,
   not the round pins above) so it never reads as another visit/location
   stop; warm brown so it doesn't compete with the blue/green/star palette
   already in use. */
.map-home-marker {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: #8a5a2b;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.05rem;
  line-height: 1;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.map-home-marker-wrap { background: transparent; border: none; }

.top-actions { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 16px; }

body:has(.login-wrap) {
  background: linear-gradient(180deg, #f3f6fa 0%, #fff 320px);
  min-height: 100vh;
}
.login-wrap {
  max-width: 380px;
  margin: 72px auto;
  padding: 32px 28px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.login-wrap h1 {
  font-size: 1.15rem;
  margin: 0 0 24px;
  text-align: center;
  letter-spacing: -0.01em;
}
.login-wrap form.stacked button { width: 100%; margin-top: 6px; }
.login-wrap p { text-align: center; }

/* Unified login (login.html) -- patient and staff panels in one row. */
.login-split-wrap { max-width: 1020px; padding: 32px 0; }
.login-split-wrap h1 { padding: 0 32px; }
.login-split { display: grid; grid-template-columns: 1fr 1fr; }
.login-panel { padding: 0 32px; }
.login-panel + .login-panel { border-left: 1px solid var(--border); }
.login-panel h2 { font-size: 0.95rem; margin: 0 0 16px; text-align: center; color: var(--muted); }
.login-panel h2.login-panel-title-patient { color: var(--accent); }
.login-panel h2.login-panel-title-staff { color: #6a1b9a; }
@media (max-width: 640px) {
  .login-split { grid-template-columns: 1fr; }
  .login-panel + .login-panel { border-left: none; border-top: 1px solid var(--border); padding-top: 24px; margin-top: 24px; }
}

.error-text { color: var(--danger); font-size: 0.9rem; margin-bottom: 12px; }

.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 32, 0.55);
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 1000;
}
.modal-box {
  background: #fff;
  border-radius: var(--radius-lg);
  padding: 22px 20px;
  max-width: 380px;
  width: 100%;
  box-shadow: var(--shadow-lg);
}
.modal-box h3 { margin: 0 0 10px; font-size: 1.05rem; }
.modal-box p { margin: 0 0 18px; line-height: 1.4; }
.modal-actions { display: flex; gap: 10px; flex-direction: column-reverse; }
.modal-actions button { width: 100%; }

.sort-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  font-size: 0.9rem;
}
.sort-bar a { font-weight: 600; }

.search-bar {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
  align-items: center;
}
/* Every control in a filter bar -- search box, <select>, date picker --
   shares one compact size instead of each falling back to its own native
   browser sizing (a <select>/<input type=date> with no explicit styling
   renders taller/wider than a styled search box, so a filter row ends up
   with controls of visibly different heights). Narrow and uniform on
   purpose: these are filters, not the main content. */
.search-bar input[type=search],
.search-bar input[type=text],
.search-bar input[type=date],
.search-bar select {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.85rem;
  font-family: inherit;
  height: 34px;
  box-sizing: border-box;
  background: #fff;
}
.search-bar input[type=search],
.search-bar input[type=text] {
  flex: 1 1 160px;
  min-width: 140px;
}
.search-bar select {
  flex: 0 1 auto;
  max-width: 220px;
}
.search-bar input[type=date] {
  flex: 0 0 auto;
  width: 148px;
}

.status-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.status-badge.status-assigned { background: #f1f0fb; color: #4b3f9e; }
.status-badge.status-not_started { background: #fdecea; color: #9a2c22; }
.status-badge.status-in_progress { background: #e8f0fe; color: var(--accent); }
.status-badge.status-submitted { background: #e6f4ea; color: #226b3a; }
.status-badge.status-qa_returned { background: #fff3c4; color: #6b4e00; cursor: pointer; }
.status-badge.status-orange { background: #fde3c7; color: #9a5b0f; }
.status-badge.status-plain { background: var(--bg-subtle); color: var(--text); }
/* Whole visit card gets a soft yellow wash too (not just the badge) --
   "highlighted yellow" per Omid's ask, so a returned note stands out at
   a glance while scanning the Documents list, not just on hover/click. */
.visit-card.qa-returned { background: #fffdf3; border-color: #c99a00; }

.source-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  background: #e8f0fe;
  color: var(--accent);
}

/* Scheduling calendar -- Google-Calendar-style day/week time grid +
   month overview, sharing one visual language (clean 1px borders,
   generous cell size, a blue-accent "today" marker). */
.calendar-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.calendar-nav-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.calendar-today-btn { text-decoration: none; }
.calendar-nav-arrows { display: flex; gap: 2px; }
.calendar-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--text);
}
.calendar-arrow:hover { background: #eef1f4; text-decoration: none; }
.calendar-label { font-size: 1.2rem; font-weight: 700; }
/* Visit-count summaries -- deliberately a different font/weight/color from
   the day-number badges (calendar-day-number/calendar-col-daynum above) so
   they read as a separate piece of information, not part of the date
   itself, per Omid's request 2026-08-15. */
.calendar-period-count {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 400;
  font-size: 0.95rem;
  color: var(--accent);
  margin-left: 10px;
}
/* Patient portal calendar (patient_calendar.html) -- a per-day "not
   home" toggle. Marked days get a dark grey background across whichever
   container that day renders as (month table cell, week/day column
   header, or the timegrid column body itself) so the whole day reads as
   unavailable at a glance, not just a small icon. */
.not-home-day, table.calendar-month td.not-home-day, .calendar-col-header.not-home-day,
.calendar-timegrid-col.not-home-day, .calendar-untimed-col.not-home-day {
  background: #4a4a4a !important;
  color: #f1f1f1;
}
.not-home-day .calendar-day-number, .not-home-day .calendar-col-daynum, .not-home-day .calendar-col-weekday {
  color: #f1f1f1 !important;
}
.not-home-toggle-btn { background: transparent; border: none; }
.calendar-day-count {
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 0.72rem;
  color: var(--accent);
  white-space: nowrap;
}
/* "You're on call this day" flag on the nurse/admin's own My Calendar
   (month/week/day views) -- app/routers/calendar.py::my_calendar passes
   my_on_call_dates so this shows up right alongside actual patient
   visits, not just on the separate My On-Call Shifts page. */
.on-call-badge {
  display: inline-block;
  background: #6a2c8f;
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 4px;
  white-space: nowrap;
  margin-top: 2px;
}
.calendar-view-select {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 32px 8px 14px;
  font-size: 0.88rem;
  font-weight: 600;
  background: #fff;
  cursor: pointer;
}
.calendar-print-heading { display: none; }

@media print {
  .topbar, .admin-nav, .top-actions, .calendar-nav, .flash,
  .calendar-modal-backdrop, .visit-popover-panel, .no-print {
    display: none !important;
  }
  .calendar-print-heading {
    display: block;
    font-size: 1.1rem;
    font-weight: 700;
    margin-bottom: 14px;
  }
  .calendar-frame { border-color: #999; }
  table.calendar-month, .calendar-timegrid-wrap { break-inside: avoid; }
  table.calendar-month tr { break-inside: avoid; }
  .calendar-visit-block, .cal-chip, .visit-card {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    break-inside: avoid;
  }
}

.calendar-frame {
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
  box-shadow: var(--shadow-sm);
}

/* Month view */
table.calendar-month { width: 100%; border-collapse: collapse; table-layout: fixed; }
table.calendar-month th {
  text-align: center;
  padding: 10px 4px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  background: #fafbfc;
}
table.calendar-month td {
  vertical-align: top;
  border: 1px solid var(--border);
  padding: 6px;
  height: 130px;
  width: 14.28%;
  overflow: hidden;
}
table.calendar-month td.muted { background: #fbfbfc; }
table.calendar-month td.muted .calendar-day-number { color: var(--muted); }
table.calendar-month td.past { background: #fbfbfc; }
table.calendar-month td.past .calendar-day-chips .cal-chip { opacity: 0.55; filter: grayscale(60%); }
/* On-call day, month view -- wins over .past/.muted's grey (declared
   after them) so a day you're on call for never reads as a plain faded/
   past cell, purple to match .on-call-badge and stand out clearly. */
table.calendar-month td.on-call-day { background: rgba(106, 44, 143, 0.12); }
.calendar-day-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: 0.85rem;
  font-weight: 700;
  margin-bottom: 4px;
  color: var(--text);
}
.calendar-day-number:hover { background: #eef1f4; text-decoration: none; }
.calendar-day-number.today { background: var(--accent); color: #fff; }
.calendar-day-number.today:hover { background: #0d47a1; }
.calendar-day-number.past:not(.today) { color: var(--muted); }
.calendar-day-chips { display: flex; flex-direction: column; gap: 2px; }
.cal-chip {
  display: block;
  width: 100%;
  font-family: inherit;
  font-size: 0.68rem;
  font-weight: 600;
  background: rgba(21, 101, 192, 0.12);
  color: #0d3f75;
  border: none;
  border-left: 3px solid var(--accent);
  border-radius: 3px;
  padding: 2px 5px;
  margin-bottom: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: left;
  cursor: pointer;
}
button.cal-chip:hover { filter: brightness(0.95); }
.cal-chip.unassigned { background: rgba(198, 40, 40, 0.08); color: #8a1f1f; border-left-color: var(--danger); }
.cal-chip.submitted { background: #0f9d3f; color: #fff; border-left-color: #0b6b2c; font-weight: 700; }
/* A Start of Care visit a NURSE (not admin) moved to a different day --
   requires soc_reschedule_reason (see app/scheduling.py's clinically-
   locked reschedule rules) -- flagged purple+bold here so an admin
   scanning the calendar can't miss it. Once the visit is actually
   submitted, .submitted (green) takes over instead -- this class is
   only ever added for a still-pending SOC move (see _calendar.html). */
.cal-chip.soc-moved { background: #6a2c8f; color: #fff; border-left-color: #4a1d63; font-weight: 700; }
.cal-chip.soc-moved .recur-icon { color: #fff; }
.cal-chip.missed { background: #e67e22; color: #fff; border-left-color: #a85d18; font-weight: 700; }
.cal-chip.missed .recur-icon { color: #fff; }
/* QA sent this submitted note back for modification (see ClinicalNote.
   qa_returned_at, app/routers/admin/visits.py::return_note_for_modification)
   -- amber/yellow so it reads as "needs your attention" without
   colliding with the red (unassigned), green (submitted), orange
   (missed), or purple (SOC moved) states above. Cleared automatically
   once the provider fixes and resubmits the note. */
.cal-chip.qa-returned { background: #fff3c4; color: #6b4e00; border-left-color: #c99a00; font-weight: 700; }
a.cal-more { display: block; }
.cal-more { font-size: 0.64rem; color: var(--muted); padding-left: 5px; }
.calendar-add-btn {
  min-height: 0;
  width: 16px;
  height: 16px;
  padding: 0;
  line-height: 1;
  font-size: 0.65rem;
  font-weight: 700;
  border-radius: 50%;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.calendar-add-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.calendar-col-header .calendar-add-btn { margin: 4px auto 0; }

/* Day/week time grid */
.calendar-timegrid-wrap { display: flex; flex-direction: column; }
.calendar-hour-gutter { flex: 0 0 56px; }
.calendar-timegrid-header {
  display: flex;
  border-bottom: 1px solid var(--border);
  background: #fafbfc;
}
.calendar-col-header {
  flex: 1;
  text-align: center;
  padding: 10px 4px;
  border-left: 1px solid var(--border);
  color: var(--text);
}
.calendar-col-header:hover { background: #f1f3f5; text-decoration: none; }
.calendar-col-header.past { background: #fbfbfc; }
/* On-call day, week view -- wins over .past/.muted's grey (declared after
   them) so a day you're on call for never reads as a plain faded/past
   cell; see .on-call-day below (month view) for the same treatment. */
.calendar-col-header.on-call-day { background: rgba(106, 44, 143, 0.12); }
.calendar-col-weekday { display: block; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.calendar-col-daynum {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  font-size: 1rem;
  font-weight: 700;
  margin-top: 2px;
}
.calendar-col-daynum.today { background: var(--accent); color: #fff; }
.calendar-col-daynum.past:not(.today) { color: var(--muted); }
.calendar-untimed-row { display: flex; border-bottom: 1px solid var(--border); min-height: 28px; }
.calendar-untimed-label { font-size: 0.65rem; color: var(--muted); padding: 4px 6px 0 0; text-align: right; }
.calendar-untimed-col {
  flex: 1;
  min-width: 0; /* without this, a long chip's text pushes this column wider than
                   its fair share, throwing every column after it out of alignment
                   with the hourly grid/header below -- classic flexbox pitfall:
                   flex items default to min-width:auto (sized to fit content). */
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 4px 6px;
  border-left: 1px solid var(--border);
}
.calendar-timegrid-body { position: relative; display: flex; }
.calendar-hour-labels { position: relative; }
.calendar-hour-label {
  position: absolute;
  right: 8px;
  transform: translateY(-50%);
  font-size: 0.68rem;
  color: var(--muted);
  white-space: nowrap;
}
.calendar-timegrid-col {
  position: relative;
  flex: 1;
  border-left: 1px solid var(--border);
}
.calendar-hour-line { position: absolute; left: 0; right: 0; border-top: 1px solid var(--border); }
.calendar-timegrid-col.today { background: rgba(21, 101, 192, 0.035); }
.calendar-timegrid-col.past { background: #fbfbfc; }
.calendar-timegrid-col.past .calendar-visit-block,
.calendar-untimed-col.past .cal-chip { opacity: 0.55; filter: grayscale(60%); }
.calendar-month td.calendar-drop-target,
.calendar-untimed-col.calendar-drop-target,
.calendar-timegrid-col.calendar-drop-target {
  background: var(--bg-subtle);
  outline: 2px dashed var(--accent);
  outline-offset: -2px;
}
.cal-chip[draggable="true"], .calendar-visit-block[draggable="true"] { cursor: grab; }
.calendar-now-line { position: absolute; left: 0; right: 0; border-top: 2px solid var(--danger); z-index: 3; pointer-events: none; }
.calendar-now-dot {
  position: absolute;
  left: -5px;
  top: -5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--danger);
}
.calendar-visit-block {
  position: absolute;
  background: rgba(21, 101, 192, 0.14);
  border: none;
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  padding: 3px 6px;
  overflow: hidden;
  font-size: 0.7rem;
  font-family: inherit;
  line-height: 1.25;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
  cursor: pointer;
  text-align: left;
}
.calendar-visit-block:hover { box-shadow: 0 2px 6px rgba(0,0,0,0.18); z-index: 4; }
.calendar-visit-block.unassigned { background: rgba(198, 40, 40, 0.08); border-left-color: var(--danger); }
.calendar-visit-block.submitted { background: #0f9d3f; border-left-color: #0b6b2c; color: #fff; }
.calendar-visit-block.submitted .calendar-visit-who,
.calendar-visit-block.submitted .calendar-visit-type,
.cal-chip.submitted .recur-icon,
.calendar-visit-block.submitted .recur-icon { color: #fff; }
/* Same "SOC moved, still pending" purple+bold flag as .cal-chip.soc-moved
   above, for the timed day/week grid block variant. */
.calendar-visit-block.soc-moved { background: #6a2c8f; border-left-color: #4a1d63; color: #fff; }
.calendar-visit-block.soc-moved .calendar-visit-who,
.calendar-visit-block.soc-moved .calendar-visit-type,
.calendar-visit-block.soc-moved .recur-icon { color: #fff; }
.calendar-visit-block.missed { background: #e67e22; border-left-color: #a85d18; color: #fff; }
.calendar-visit-block.missed .calendar-visit-who,
.calendar-visit-block.missed .calendar-visit-type,
.calendar-visit-block.missed .recur-icon { color: #fff; }
/* Same amber "QA returned this" flag as .cal-chip.qa-returned above, for
   the timed day/week grid block variant. */
.calendar-visit-block.qa-returned { background: #fff3c4; border-left-color: #c99a00; color: #6b4e00; }
.calendar-visit-block.qa-returned .calendar-visit-who,
.calendar-visit-block.qa-returned .calendar-visit-type { color: #6b4e00; }
.calendar-visit-time { font-weight: 700; }
.calendar-visit-who { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-visit-type { color: var(--accent); font-size: 0.62rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-visit-notes { color: var(--muted); font-size: 0.65rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-resize-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
  cursor: ns-resize;
  touch-action: none;
}
.calendar-resize-handle::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 2px;
  transform: translateX(-50%);
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.22);
}
.calendar-visit-block:hover .calendar-resize-handle::after { background: var(--accent); }
.past-day .visit-card { opacity: 0.7; filter: grayscale(40%); }

/* Visit detail popover (opened from month/week chips) */
.calendar-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 60;
}
.calendar-modal-backdrop.open { display: block; }
.visit-popover-panel {
  display: none;
  position: fixed;
  /* Anchored to a fixed offset from the top, not vertically centered via
     top:50%/transform -- centering recalculates against the live viewport
     height, which iOS shrinks the instant an <input> inside the panel is
     focused (on-screen keyboard). That shift mid-tap could land the touch
     on the backdrop instead of the field, closing the modal before it
     could focus. A top-anchored position doesn't move when the keyboard
     opens, since it isn't a function of viewport height.
     translateZ(0) forces this onto its own GPU-composited layer -- iOS
     Safari has a known bug where a position:fixed element toggled from
     display:none to display:block via a JS class change (exactly what
     openRowModal does) doesn't always get promoted/re-composited above a
     sibling fixed element in the correct order on the first paint, even
     with a higher z-index -- the visible symptom is the backdrop's 45%
     black tint appearing to render OVER the panel, a solid white panel
     looking grey/washed out. background-color is pinned explicitly (not
     just background) since that's the property compositing keys off.
     All three fixes ported from fshc-homecare-app, 2026-08-25, after
     being found on a real device and confirmed not a device color-filter
     setting -- same shared CSS pattern, same bug. */
  top: 6vh;
  left: 50%;
  transform: translateX(-50%) translateZ(0);
  z-index: 61;
  background-color: #fff;
  border-radius: 12px;
  padding: 20px;
  max-width: 380px;
  /* Per-popover inline max-width (320-720px depending on content) sets a
     ceiling, but every popover should still read as a complete view, not a
     sliver -- min-width beats a smaller max-width by spec, so this floors
     every popup at 60% of viewport width regardless of its own cap, up to
     a sane ceiling so it doesn't sprawl on an ultrawide monitor. */
  min-width: min(60vw, 960px);
  width: 90%;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}
.visit-popover-panel.open { display: block; }
.visit-popover-panel .visit-card { border: none; padding: 0; box-shadow: none; }
.visit-popover-close {
  float: right;
  border: none;
  background: none;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
  padding: 2px 4px;
}
.visit-popover-close:hover { color: var(--text); }

@media (max-width: 640px) {
  table.calendar-month td { height: 72px; padding: 3px; }
  .calendar-day-chips { display: none; }
  .cal-chip { font-size: 0.6rem; }
  .calendar-label { font-size: 1rem; }
  .calendar-hour-gutter { flex-basis: 42px; }
  .calendar-hour-label { font-size: 0.6rem; right: 4px; }
  .calendar-col-header { padding: 6px 2px; }
}
.message-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 4px;
}
.message-bubble {
  align-self: flex-start;
  max-width: 75%;
  background: #eef1f4;
  border-radius: 12px;
  padding: 8px 12px;
}
.message-bubble.mine {
  align-self: flex-end;
  background: var(--bg-subtle);
}
.message-meta { font-size: 0.7rem; color: var(--muted); margin-bottom: 3px; }
.message-body { font-size: 0.9rem; white-space: pre-wrap; word-break: break-word; }
.message-attachment { margin-top: 6px; }
.message-attachment-thumb {
  max-width: 200px;
  max-height: 160px;
  border-radius: 8px;
  border: 1px solid var(--border);
  display: block;
}
.message-attachment-file {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.85rem;
  background: rgba(0, 0, 0, 0.04);
  border-radius: 6px;
  padding: 4px 8px;
}
.message-delete {
  border: none;
  background: none;
  color: var(--danger);
  font-size: 0.7rem;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
}

/* Floating chat widget -- deliberately NOT a full-page backdrop like
   .modal-overlay/.calendar-modal-backdrop: the point is the rest of the
   page/nav stays visible and clickable behind it. */
.chat-widget-trigger {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 1.2rem;
  line-height: 1;
  padding: 2px 4px;
  position: relative;
  color: var(--text);
}
.chat-widget-trigger:hover { color: var(--accent); }
.chat-widget-panel {
  display: none;
  position: fixed;
  bottom: calc(16px + env(safe-area-inset-bottom));
  right: calc(16px + env(safe-area-inset-right));
  width: 340px;
  max-width: calc(100vw - 32px);
  height: 480px;
  max-height: calc(100vh - 32px);
  background: #fff;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  z-index: 900;
  flex-direction: column;
  overflow: hidden;
}
.chat-widget-panel.open { display: flex; }
.chat-widget-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--accent);
  color: #fff;
  flex-shrink: 0;
}
.chat-widget-header h4 { margin: 0; font-size: 0.95rem; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-widget-header button {
  border: none;
  background: none;
  color: #fff;
  cursor: pointer;
  font-size: 1rem;
  padding: 2px 6px;
  line-height: 1;
}
.chat-widget-body { flex: 1; overflow-y: auto; padding: 8px; }
.chat-widget-thread-row {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: none;
  cursor: pointer;
  padding: 8px;
  border-radius: 8px;
  border-bottom: 1px solid var(--border);
  font: inherit;
  color: inherit;
}
.chat-widget-thread-row:hover { background: var(--bg-subtle); }
.chat-widget-thread-subject { font-weight: 600; font-size: 0.85rem; display: flex; align-items: center; gap: 6px; }
.chat-widget-thread-preview { color: var(--muted); font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.chat-widget-unread-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); display: inline-block; flex-shrink: 0; }
.chat-widget-footer { flex-shrink: 0; border-top: 1px solid var(--border); padding: 8px; display: flex; gap: 6px; }
.chat-widget-footer input { flex: 1; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 0.9rem; }
.chat-widget-back { border: none; background: none; color: #fff; cursor: pointer; font-size: 0.9rem; padding: 2px 4px; }
.chat-widget-empty { color: var(--muted); font-size: 0.85rem; text-align: center; padding: 24px 8px; }
.chat-widget-thread-row-wrap { display: flex; align-items: stretch; gap: 2px; }
.chat-widget-thread-row-wrap .chat-widget-thread-row { flex: 1; min-width: 0; }
.chat-widget-thread-delete {
  flex-shrink: 0;
  align-self: center;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 4px 8px;
  border-radius: 6px;
}
.chat-widget-thread-delete:hover { color: var(--danger); background: var(--bg-subtle); }
.chat-widget-load-more { display: block; width: calc(100% - 16px); margin: 8px; }

/* Messenger-style floating chat popup window -- opened from a chat-list
   row (see base.html's openChatPopup), independent of the list panel
   above so browsing the rest of the app stays fully usable while it's
   open. Resizable (drag the bottom-right corner) on desktop/tablet;
   fixed comfortable size with no resize handle on phones, where a
   drag-to-resize corner is impractical -- see the max-width:640px rule
   below. */
.chat-popup-window {
  position: fixed;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  width: 300px;
  min-width: 240px;
  max-width: calc(100vw - 32px);
  height: 380px;
  min-height: 180px;
  max-height: calc(100vh - 32px);
  background: #fff;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  z-index: 950;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  resize: both;
}
.chat-popup-window.minimized {
  height: auto;
  min-height: 0;
  max-height: none;
  resize: none;
}
.chat-popup-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 12px;
  background: var(--accent);
  color: #fff;
  flex-shrink: 0;
  cursor: pointer;
}
.chat-popup-title { flex: 1; font-weight: 600; font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-popup-badge { background: var(--danger); }
.chat-popup-header button {
  border: none;
  background: none;
  color: #fff;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 2px 6px;
  flex-shrink: 0;
}
.chat-popup-body { flex: 1; overflow-y: auto; padding: 8px; }
.chat-popup-add-people { flex-shrink: 0; border-top: 1px solid var(--border); padding: 8px; max-height: 220px; overflow-y: auto; }
.chat-popup-footer { flex-shrink: 0; border-top: 1px solid var(--border); padding: 8px; display: flex; gap: 6px; align-items: center; }
.chat-popup-footer input { flex: 1; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 0.9rem; }
.chat-popup-add-btn { flex-shrink: 0; }
.chat-popup-window.minimized .chat-popup-body,
.chat-popup-window.minimized .chat-popup-footer,
.chat-popup-window.minimized .chat-popup-add-people {
  display: none;
}

@media (max-width: 640px) {
  .chat-popup-window {
    left: calc(8px + env(safe-area-inset-left));
    right: calc(8px + env(safe-area-inset-right));
    bottom: calc(8px + env(safe-area-inset-bottom));
    width: auto;
    max-width: none;
    height: min(70vh, 520px);
    resize: none;
  }
}

/* iMessage-style people search + multi-select, shared by "new chat" and
   "add people to this chat" (see base.html's createPeoplePicker). */
.people-picker-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
.people-picker-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bg-subtle);
  border-radius: 999px;
  padding: 3px 6px 3px 10px;
  font-size: 0.82rem;
}
.people-picker-chip button { border: none; background: none; cursor: pointer; font-size: 0.95rem; line-height: 1; color: var(--muted); padding: 2px; }
.people-picker-chip button:hover { color: var(--danger); }
.people-picker-input { width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; font-size: 0.9rem; box-sizing: border-box; }
.people-picker-quickselect { width: 100%; margin-top: 6px; padding: 7px 8px; border: 1px solid var(--border); border-radius: 8px; font-size: 0.85rem; box-sizing: border-box; color: var(--muted); }

/* @mentions -- green everywhere, distinct from the normal unread accent
   color, so "someone tagged me" always reads differently from plain
   unread activity. */
.nav-badge.mentioned-badge { background: var(--mention); }
.chat-widget-unread-dot.mentioned { background: var(--mention); }
.message-bubble.mentioned { background: var(--mention-bg); border: 1px solid var(--mention); }
.message-bubble.mentioned.mine { background: var(--mention-bg); }
.chat-widget-thread-row.mentioned { background: var(--mention-bg); }
/* Message category filter chips (/messages -- SOC/Patient/Physician/
   Case Manager/Tagged/Included/Audit) -- an outlined pill in the
   category's own color, filled solid (+ a checkmark) once active. Colors
   come from CATEGORY_COLORS in messages.html so the chip, the row's
   category tag, and the legend swatch are always the exact same color
   for a given category -- one source of truth, not three hand-matched
   hex values. */
.category-filter-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 20px;
  border: 1.5px solid;
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
}
.category-filter-link:hover { text-decoration: none; opacity: 0.85; }

/* Page-level sub-tab strip (_calendar_subtabs.html, _documents_subtabs.html,
   _visits_subtabs.html) -- deliberately echoes .admin-nav's pill shape so
   it reads as "more tabs," but centered, smaller, and with a soft tinted
   active state instead of a solid fill, so it's never mistaken for the
   main nav strip at the top of the page. The row itself doubles as the
   page's heading -- its active pill already says what section you're on,
   so pages using it don't also need a separate .section-label repeating
   the same text. */
.subtab-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  margin: 0 0 16px;
  padding: 5px;
  background: #fafbfc;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-xs);
}
.subtab-link {
  padding: 6px 14px;
  border-radius: 20px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.subtab-link:hover { text-decoration: none; background: #eef1f4; color: var(--text); }
.subtab-link.active { background: var(--bg-subtle); color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.subtab-link.on-call-reminder,
.subtab-link.on-call-reminder.active { color: #c0392b !important; background: #fdecea !important; box-shadow: inset 0 0 0 1px #c0392b !important; }

.chat-widget-thread-row.unread { background: var(--bg-subtle); }

/* Row background on the /messages thread list is now purely read-state
   (Gmail-style), decoupled from category -- category shows as the
   colored tag next to the subject instead (see category_of() in
   messages.html). Unread stays the page's plain white/surface color
   (with bolder text, like an inbox), read fades to a light grey. */
.msg-unread-row td { font-weight: 600; }
.msg-read-row { background: #f4f6f8; }
.msg-read-row td { font-weight: 400; }
/* The list's actions column only ever holds 1-2 icon buttons, but the
   shared table.admin-table td:last-child rule (min-width:140px, sized for
   pages with several text/icon actions) was forcing it much wider than
   that here, stealing space Subject/Participants could use instead --
   shrink both the cell and its buttons just for this table. */
.messages-table td:last-child { min-width: 0; }
.messages-table .icon-btn { width: 24px; height: 24px; min-height: 24px; font-size: 0.82rem; }
.messages-table .table-actions { gap: 2px; }

/* Sender-based message color coding: a plain ("general") message from any
   provider is blue; @mentioned stays green (above); a message carrying
   an Intake/Scheduler patient-acceptance poll is red regardless of
   mention status, since that's the one message type that needs a yes/no
   response -- applied in message_thread.html as mutually-exclusive
   classes, never combined. */
.message-bubble.sender-general { background: var(--bg-subtle); border: 1px solid var(--accent); }
.message-bubble.sender-general.mine { background: var(--bg-subtle); }
.message-bubble.sender-alert { background: #fdeaea; border: 1px solid var(--danger); }
.message-bubble.sender-alert.mine { background: #fdeaea; }

/* Intake/Scheduler patient-acceptance poll card -- phrased as an explicit
   yes/no question with the proposed patient's info auto-filled and
   clearly labeled, so a provider can decide without hunting through the
   message body for it. */
.patient-poll-card {
  margin-top: 8px;
  border: 1px solid var(--danger);
  border-radius: 8px;
  padding: 10px 12px;
  background: #fdeaea;
}
.patient-poll-question { font-weight: 700; margin-bottom: 8px; }
.patient-poll-fields {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  margin: 0 0 10px;
  font-size: 0.85rem;
}
.patient-poll-fields > div { display: contents; }
.patient-poll-fields dt { color: var(--muted); font-weight: 600; }
.patient-poll-fields dd { margin: 0; }

/* Closest-provider hint (app/provider_matching.py) -- advisory only, same
   green as the app's other "good to know" affordances (see --mention). */
.patient-poll-suggestion { color: var(--mention); font-weight: 600; font-size: 0.85rem; margin: 0 0 10px; }

/* The literal "@Name" text inside a message body, for whoever it actually
   mentioned (see render_body_with_mentions) -- deliberately blue, distinct
   from the green used for the surrounding "you were mentioned" affordances
   (unread badge, bubble highlight, thread row) above. */
.mention-name { color: var(--accent); font-weight: 600; text-decoration: underline; text-decoration-color: rgba(21, 101, 192, 0.4); text-underline-offset: 2px; }

/* The role/discipline tag shown next to a provider's name (see
   provider_tag() in app/models/nurse.py) -- deliberately much smaller and
   muted than the surrounding text everywhere it appears, so it reads as
   a quiet label, not competing with the name itself. */
.provider-tag { font-size: 0.65rem; color: var(--muted); font-weight: 400; }

/* Marks a visit generated by the "Assign Visit" recurrence feature (a
   frequency other than "Does not repeat") wherever it shows up on the
   scheduling calendar -- chips, timed blocks, and the visit card/popover. */
.recur-icon { color: var(--accent, #2563eb); font-weight: 700; }

/* @mention autocomplete dropdown -- one shared instance appended to
   <body> and repositioned under whichever textarea/input is focused (see
   base.html's wireMentionAutocomplete). Every property that actually
   matters for it to render correctly is set inline by that same JS
   (this element lives outside any page layout, appended straight to
   <body>, so it can't lean on a stylesheet that might be stale-cached) --
   these two rules are just a class hook, not load-bearing. */
.mention-suggestions { box-sizing: border-box; }

/* Admin on-call rotation table (full current + next month, one row per
   weekend) -- app/templates/admin/on_call_shifts.html. Two columns
   (Saturday, Sunday) instead of a full 7-day grid, since weekdays never
   carry an assignment -- a ~4-5 row month this way instead of a
   mostly-empty calendar block. Every assignment is ONE compact line
   (kind badge + name/Open + inline reassign form), not a stacked block,
   since a two-month table can run to 60-70 rows. */
.on-call-weekend-table { table-layout: fixed; }
.on-call-weekend-table th { text-align: left; padding: 4px 8px; font-size: 0.85rem; }
.on-call-weekend-table td.on-call-weekend-cell { width: 50%; vertical-align: top; padding: 5px 8px; border: 1px solid var(--border, #e0e0e0); }

.on-call-day-header { display: flex; align-items: center; gap: 5px; margin-bottom: 3px; }
.on-call-day-date { font-weight: 600; font-size: 0.75rem; color: var(--muted); }
.on-call-day-today .on-call-day-date { color: var(--accent); }
.on-call-day-header .status-badge { font-size: 0.6rem; padding: 0 4px; }
.on-call-day-outside { opacity: 0.65; }

.on-call-assignment { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; border-left: 3px solid transparent; padding: 1px 0 1px 6px; line-height: 1.5; font-size: 0.75rem; border-radius: 3px; }
.on-call-assignment-rn { border-left-color: #1565c0; }
.on-call-assignment-cnm { border-left-color: #6a2c8f; }
/* On the self-service My On-Call Shifts list (on_call_shifts_mine.html)
   only -- this specific day/weekend is assigned to the person looking
   at the page, so it's easy to pick out of a long list of everyone
   else's days too. */
.on-call-assignment-mine { background: rgba(15, 157, 63, 0.14); border-left-color: #0f9d3f; }
.on-call-assignment-kind { font-size: 0.6rem; font-weight: 700; padding: 0 4px; border-radius: 3px; color: #fff; flex-shrink: 0; line-height: 1.4; }
.on-call-assignment-kind-rn { background: #1565c0; }
.on-call-assignment-kind-cnm { background: #6a2c8f; }
.on-call-assignment-name { font-weight: 600; }
.on-call-assignment-open { color: var(--muted); font-style: italic; }
.on-call-assignment-form { display: inline-flex; gap: 3px; margin-left: auto; }
.on-call-assignment-form select { font-size: 0.68rem; padding: 0 2px; max-width: 110px; min-height: 0; height: 20px; }
.on-call-assignment-form button { font-size: 0.65rem; padding: 0 6px; min-height: 0; height: 20px; flex-shrink: 0; }

/* Month divider, shared by the admin weekend table above (rendered as a
   colspan=2 <td>) and the self-service My On-Call Shifts list
   (on_call_shifts_mine.html, rendered as a standalone <div>) --
   otherwise a long run of individual weekend rows all look alike with no
   sense of where one calendar month ends and the next begins. */
.on-call-month-divider {
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--text);
  background: #e9edf1;
  border-top: 3px double #aab2bc;
  border-bottom: 3px double #aab2bc;
  padding: 8px 12px;
  margin: 18px 0 10px;
}
.on-call-month-divider:first-child { margin-top: 0; }
tr .on-call-month-divider { margin: 0; }

/* Rotation Frequency table -- one bulk "Save All" for the whole table
   instead of a button per row, so rows can be a single compact line. */
.on-call-frequency-table td, .on-call-frequency-table th { padding: 3px 8px; font-size: 0.8rem; }
.on-call-frequency-table select { font-size: 0.78rem; padding: 1px 4px; min-height: 24px; }
.on-call-remove-btn { display: inline-flex; align-items: center; justify-content: center; background: transparent; color: #c0392b; border: 1px solid transparent; font-size: 0.8rem; padding: 0; min-height: 0; width: 22px; height: 22px; border-radius: 4px; }
.on-call-remove-btn:hover { background: rgba(192, 57, 43, 0.08); }

/* Nudge shown on the "My Calendar" nav link / "Schedule" and "My
   On-Call Shifts" sub-tabs when the logged-in nurse/admin is in an
   on-call rotation and hasn't picked a day yet for the nearest OPEN
   month (see app/on_call.py::has_unpicked_upcoming_month) -- the
   "forgot to submit availability before next month" reminder. */
.on-call-reminder,
.on-call-reminder.active { color: #c0392b !important; font-weight: 700 !important; }
.mention-suggestion-item { box-sizing: border-box; }

/* File upload with photo preview + remove (app/static/file_upload_preview.js)
   -- shown once a file is picked/photographed, in place of the plain
   input, so the user can confirm (or clear/retake) before actually
   submitting the form. One .file-upload-row per selected file (multiple=""
   inputs can have more than one), each with its own Remove. Ported from
   fshc-homecare-app 2026-08-25. */
.file-upload-wrap { display: inline-flex; }
.file-upload-preview {
  display: none;
  flex-direction: column;
  gap: 4px;
}
.file-upload-preview.open { display: flex; }
.file-upload-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-subtle);
  max-width: 220px;
}
.file-upload-thumb {
  display: none;
  width: 32px;
  height: 32px;
  object-fit: cover;
  border-radius: 5px;
  flex-shrink: 0;
}
.file-upload-filename {
  font-size: 0.78rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-upload-remove { width: 22px; height: 22px; min-height: 22px; font-size: 0.85rem; flex-shrink: 0; margin-left: auto; }

/* ---------------------------------------------------------------------
   Dashboard (the "Dashboard" nav tab -- app/routers/dashboards.py and
   app/templates/dashboards/). Deliberately compact: this page's whole
   value is that a whole shift's worth of "what needs attention" fits
   above the fold, so the tiles and rows are tighter than the
   .form-section/.visit-card-row pattern the rest of the app uses for
   editable surfaces. Same tokens throughout -- no new colours, the
   severity tints reuse the .status-badge palette.
   --------------------------------------------------------------------- */
.dash-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
  gap: 8px;
  margin-bottom: 14px;
}
.dash-stat {
  display: block;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: #fff;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.15s var(--ease), border-color 0.15s var(--ease);
}
a.dash-stat:hover { box-shadow: var(--shadow-sm); border-color: var(--accent); }
.dash-stat-value { display: block; font-size: 1.45rem; font-weight: 650; line-height: 1.1; }
.dash-stat-label { display: block; font-size: 0.72rem; color: var(--muted); margin-top: 2px; }
/* A tile only turns red/amber when its number is non-zero -- a calm
   dashboard has to be visibly calm, or the colour stops meaning
   anything. The template picks the class; zero always stays neutral. */
.dash-stat.is-alert .dash-stat-value { color: var(--danger); }
.dash-stat.is-warn .dash-stat-value { color: #9a5b0f; }

.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 12px;
  align-items: start;
}
.dash-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #fff;
  padding: 10px 12px;
  box-shadow: var(--shadow-xs);
}
.dash-card.is-alert { border-color: #e4b4ae; }
.dash-card.is-warn { border-color: #e8cf9a; }
.dash-card-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 6px;
  font-size: 0.88rem;
  font-weight: 620;
}
.dash-card-head .dash-count {
  margin-left: auto;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-subtle);
  color: var(--text);
}
.dash-card.is-alert .dash-count { background: #fdecea; color: #9a2c22; }
.dash-card.is-warn .dash-count { background: #fff3c4; color: #6b4e00; }
.dash-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 0;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
}
.dash-row:first-of-type { border-top: none; }
.dash-row .dash-meta { color: var(--muted); font-size: 0.76rem; }
.dash-row .dash-spacer { margin-left: auto; }
.dash-more { font-size: 0.74rem; color: var(--muted); margin: 6px 0 0; }
.dash-empty { font-size: 0.8rem; color: var(--muted); padding: 4px 0; }

/* ==========================================================================
   Mobile / tablet hardening pass -- 2026-08-30
   Audited with a Playwright device matrix (iPhone 390, iPad portrait 768,
   iPad landscape 1024, desktop 1440) across every admin + patient page.
   Each rule below fixes a defect that pass actually observed, not a
   hypothetical one.
   ========================================================================== */

/* 1. Desktop-only column caps.
   A cap like max-width:150px is there to stop one long column stretching a
   wide desktop table. Below 640px the same <td> becomes a full-width
   label/value row in the .responsive-table stacked-card layout, where the
   cap instead leaves the value ~45px of space -- and because .admin-table
   sets overflow-wrap:anywhere, the text then breaks one character per line
   ("I48. 91 Atria l fibril latio n"). Cap on desktop, never on the card. */
@media (min-width: 641px) {
  .col-cap-150 { max-width: 150px; }
}

/* 2. Stacked-card rows must never crush the value.
   td is a flex row of [::before label][value]. With no flex sizing, a long
   label ("Main Diagnosis" = 92px) competes with the value for a 293px row
   and both shrink. Pin the label to its content width and let the value
   drop to its own full-width line when it can't sit beside the label. */
@media (max-width: 640px) {
  table.responsive-table td { flex-wrap: wrap; align-items: baseline; }
  table.responsive-table td::before { flex: 0 0 auto; }
  /* min-width:0 lets the value shrink to its container instead of forcing
     the row wider; combined with flex-wrap above it wraps as words, not
     as characters. */
  table.responsive-table td > * { min-width: 0; }
}

/* 3. Tablet: scroll wide admin tables, don't shred them.
   Between 641px and 1024px there was no breakpoint at all, so a 9-column
   patient table got the full desktop layout inside a 768px iPad and every
   column collapsed to a few characters wide (emails wrapped one letter per
   line, the right-hand columns were clipped mid-word). These tables live in
   a .table-scroll wrapper that already scrolls; giving the table a sensible
   min-width lets it use that scroll instead of compressing to illegibility. */
@media (min-width: 641px) and (max-width: 1024px) {
  .table-scroll { overflow-x: auto; }
  /* width:max-content sizes the table to what its content actually needs
     (1648px for the 11-column patient table) so no column is squeezed to
     the point that .admin-table's overflow-wrap:anywhere starts breaking
     mid-word; min-width:100% keeps a narrow 3-column table filling the
     page instead of shrinking to its text. A fixed min-width was tried
     first and is wrong -- 900px still left the email column at 59px, so
     addresses and emails kept shredding one character per line. */
  .table-scroll table.admin-table { width: max-content; min-width: 100%; }
  /* Scroll affordance -- a hairline inset shadow on the right edge so it's
     visible that the table continues past the screen. */
  .table-scroll {
    background:
      linear-gradient(to right, #fff 30%, rgba(255,255,255,0)) left center / 24px 100% no-repeat,
      linear-gradient(to left, #fff 30%, rgba(255,255,255,0)) right center / 24px 100% no-repeat,
      radial-gradient(farthest-side at 0 50%, rgba(15,23,32,0.14), transparent) left center / 10px 100% no-repeat,
      radial-gradient(farthest-side at 100% 50%, rgba(15,23,32,0.14), transparent) right center / 10px 100% no-repeat;
    background-attachment: local, local, scroll, scroll;
  }
}

/* 4. Touch targets.
   Scoped to pointer:coarse so desktop keeps its deliberately condensed
   density -- this only grows hit areas on an actual touch screen. WCAG 2.2
   AA (2.5.8) sets a 24x24px floor; Apple HIG asks 44x44pt. The nav pills
   measured 28px tall and the top-bar links 26px, both below the HIG target
   and awkward to hit while holding a phone one-handed. */
@media (pointer: coarse) {
  .admin-nav a,
  .subtab-link,
  .topbar-link {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  /* Row action links inside tables sit in dense lists; 24px keeps them
     WCAG-compliant without turning every table into a wall of buttons. */
  table.admin-table td a,
  table.responsive-table td a,
  .dash-row a,
  /* Standalone "<- Back" links sit alone in .top-actions -- they're nav
     controls, not prose, so the WCAG 2.5.8 inline-text exception doesn't
     cover them and they need the 24px floor like any other control. */
  .top-actions > a { display: inline-block; min-height: 24px; }
  /* Bare <select>s outside .search-bar (the "10 / page" pagination picker,
     popover form fields) inherit the browser's default 19px height, which
     is under the WCAG 24px floor and genuinely fiddly to hit. .search-bar
     selects already set their own 34px height, so they're untouched. */
  select { min-height: 32px; }
}

/* 5. Horizontal nav is scrollable but gave no sign of it -- the last tab
   ("Calendar", "Audit Log") was simply sliced off at the screen edge with
   no cue. Same edge-shadow affordance as the tables, plus momentum-free
   snap so a tab never comes to rest half-visible. */
.admin-nav {
  background:
    linear-gradient(to right, #fafbfc 30%, rgba(250,251,252,0)) left center / 24px 100% no-repeat,
    linear-gradient(to left, #fafbfc 30%, rgba(250,251,252,0)) right center / 24px 100% no-repeat,
    radial-gradient(farthest-side at 0 50%, rgba(15,23,32,0.12), transparent) left center / 10px 100% no-repeat,
    radial-gradient(farthest-side at 100% 50%, rgba(15,23,32,0.12), transparent) right center / 10px 100% no-repeat,
    #fafbfc;
  background-attachment: local, local, scroll, scroll, scroll;
  scrollbar-width: none;
}
.admin-nav::-webkit-scrollbar { display: none; }

/* 6. Respect the notch / home indicator when the PWA is installed
   full-screen on a phone. Without this the bottom row of a long page sits
   under the home indicator and the topbar tucks under the status bar. */
@media (max-width: 640px) {
  .topbar { padding-left: calc(16px + env(safe-area-inset-left));
            padding-right: calc(16px + env(safe-area-inset-right)); }
  body { padding-bottom: env(safe-area-inset-bottom); }
}

/* 7. Sortable column headers are links inside <th>; the td-only rule above
   missed them, leaving them 15px tall -- under the WCAG 24px floor and the
   hardest thing on the page to hit, since sorting is exactly what you reach
   for on a small screen. */
@media (pointer: coarse) {
  table.admin-table thead th a { display: inline-block; min-height: 24px; }
}

/* 8. Week/day calendar on a phone.
   The timegrid splits into one flex column per day. On a 390px screen that
   left each column ~19px wide, so a visit block was a 19px-wide sliver --
   unreadable, and far under the 24px target floor. Give the grid a real
   minimum width and let the frame scroll horizontally; the header row and
   the body scroll together because both are children of the same wrapper. */
@media (max-width: 640px) {
  /* .calendar-frame and .calendar-timegrid-wrap are the SAME element, so the
     min-width has to go on its children -- putting it on the wrapper itself
     just grows the scroll container and pushes the whole page sideways. The
     three rows below are the grid's full-width bands; sizing all three keeps
     the day headers, the untimed row and the hour body in column alignment
     as they scroll together. */
  .calendar-frame { overflow-x: auto; overflow-y: hidden; }
  .calendar-timegrid-wrap > .calendar-timegrid-header,
  .calendar-timegrid-wrap > .calendar-untimed-row,
  .calendar-timegrid-wrap > .calendar-timegrid-body { min-width: 700px; }
}
