/* ==========================================================================
   OrthoAgent — Components & base styles
   --------------------------------------------------------------------------
   Reusable UI vocabulary shared by every page: a light reset, sensible
   element defaults, then class-based components (buttons, badges, cards,
   forms, tables, alerts, the disclaimer footer, small utilities).

   Everything consumes tokens.css via var(--token) — no raw hex/px here except
   where a token genuinely does not apply. Pages COMPOSE these classes; they
   must not redefine component look-and-feel. Load order: tokens.css first,
   then this file.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Reset & base element defaults
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  min-height: 100vh;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  line-height: var(--leading-tight);
  font-weight: var(--weight-semibold);
}

a {
  color: var(--color-brand);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

img,
svg,
canvas {
  display: block;
  max-width: 100%;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* Visible focus for keyboard users on every interactive element. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid transparent;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* --------------------------------------------------------------------------
   2. Layout helpers
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.stack > * + * {
  margin-top: var(--space-4);
}

.row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* Used 16 times, mostly as a card header with a title on the left and an action on the
     right. Without wrapping, the pair simply refuses to fit on a phone and pushes the entire
     page sideways — measured as the last remaining overflow on the review page after the
     shell was fixed. Wrapping drops the action onto its own line instead. */
  flex-wrap: wrap;
  min-width: 0;
}

.grow {
  flex: 1;
}

/* --------------------------------------------------------------------------
   2b. Icons (Phase 9.1)
   Inline SVG from icons.js. `currentColor` + a shared 24-box geometry means an
   icon is always exactly the weight and colour of the text beside it.
   -------------------------------------------------------------------------- */
.icon {
  flex: none;
  display: block;
}

/* An icon-only control. Needs a real hit area (44px is the touch target floor) and a
   tooltip/aria-label, since it has no visible text of its own. */
.iconbtn {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.iconbtn:hover {
  background: var(--color-surface-alt);
  color: var(--color-text);
}

.iconbtn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* --------------------------------------------------------------------------
   3. Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast),
    color var(--transition-fast);
  white-space: nowrap;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.btn--primary {
  background: var(--color-brand);
  color: var(--color-brand-contrast);
}

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

.btn--secondary {
  background: var(--color-surface);
  border-color: var(--color-border-strong);
  color: var(--color-text);
}

.btn--secondary:hover:not(:disabled) {
  background: var(--color-surface-alt);
}

.btn--ghost {
  background: transparent;
  color: var(--color-brand);
}

.btn--ghost:hover:not(:disabled) {
  background: var(--color-brand-weak);
}

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

.btn--danger:hover:not(:disabled) {
  filter: brightness(0.94);
}

.btn--sm {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
}

.btn--block {
  width: 100%;
}

/* Loading state: pair a .btn-label with a .spinner inside the button and add
   .is-loading to swap them (used on submit/upload buttons). */
.btn .spinner {
  display: none;
}

.btn.is-loading {
  cursor: progress;
}

.btn.is-loading .spinner {
  display: inline-block;
  border-top-color: currentColor;
}

.btn.is-loading .btn-label {
  opacity: 0.75;
}

/* --------------------------------------------------------------------------
   4. Badges (status pills) — colour + TEXT label, never colour alone.
   Map a CaseStatus to a modifier in markup; the API is the source of status.
   -------------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: 1.4;
  border-radius: var(--radius-pill);
  background: var(--color-neutral-surface);
  color: var(--color-neutral-text);
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.badge--safe {
  background: var(--color-safe-surface);
  color: var(--color-safe-text);
}

.badge--warn {
  background: var(--color-warn-surface);
  color: var(--color-warn-text);
}

.badge--danger {
  background: var(--color-danger-surface);
  color: var(--color-danger-text);
}

.badge--neutral {
  background: var(--color-neutral-surface);
  color: var(--color-neutral-text);
}

.badge--info {
  background: var(--color-info-surface);
  color: var(--color-info-text);
}

/* Smart-triage priority badges (Phase 4.4) — four distinct urgency colours. The dot
   glyph is baked into the label text, so suppress the generic ::before dot. */
.triage-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  line-height: 1.4;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  cursor: help;
}

.triage-badge--critical {
  background: #fee2e2;
  color: #991b1b;
}

.triage-badge--urgent {
  background: #ffedd5;
  color: #9a3412;
}

.triage-badge--moderate {
  background: #fef9c3;
  color: #854d0e;
}

.triage-badge--routine {
  background: #dcfce7;
  color: #166534;
}

/* --------------------------------------------------------------------------
   5. Cards & panels
   -------------------------------------------------------------------------- */
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.card__body {
  padding: var(--space-5);
}

.card__header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  font-weight: var(--weight-semibold);
}

.card__title {
  font-size: var(--text-lg);
}

.panel-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}

.input,
.select,
.textarea {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.input::placeholder,
.textarea::placeholder {
  color: var(--color-text-faint);
}

/* Optional patient demographics on the upload card (Phase 3). */
.demographics {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.demographics .row .input {
  width: auto;
  flex: 1 1 8rem;
  min-width: 6rem;
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--color-brand);
  box-shadow: var(--focus-ring);
}

/* Invalid state (set via aria-invalid so styling follows the a11y attribute). */
.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--color-danger);
}

.input[aria-invalid="true"]:focus,
.select[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.28);
}

.textarea {
  min-height: 84px;
  resize: vertical;
}

.field__hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.field__error {
  font-size: var(--text-xs);
  color: var(--color-danger-text);
}

/* Inline, smoothly-revealed form error. Lives next to the relevant field
   (not a page-top banner). Toggle .is-visible to animate it in/out. */
/* A hint that is advice, not a failure — Caps Lock being on is not an error yet. Always in
   flow (hidden with [hidden]) so it cannot push the form around as it appears. */
.form-hint {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.form-hint[hidden] {
  display: none;
}
.form-hint--warn {
  color: var(--color-warn-text);
}
.form-hint__icon {
  display: inline-flex;
}

.form-error {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-danger-text);
  max-height: 0;
  opacity: 0;
  transform: translateY(-4px);
  overflow: hidden;
  transition: max-height var(--transition-base), opacity var(--transition-base),
    transform var(--transition-base), margin-top var(--transition-base);
}

.form-error.is-visible {
  max-height: 3rem;
  opacity: 1;
  transform: translateY(0);
  margin-top: var(--space-1);
}

.form-error__icon {
  flex: none;
  line-height: 1;
}

/* --------------------------------------------------------------------------
   7. Tables (worklist, queue, opinions, reports)
   -------------------------------------------------------------------------- */
.table-wrap {
  width: 100%;
  overflow-x: auto; /* wide tables scroll inside their own container */
  /* Tell the user the table scrolls. On a phone the worklist is cut off mid-column with no
     hint that more exists; this paints a soft fade on whichever edge still has content, using
     background-attachment: local so it appears only when there is somewhere to scroll and
     disappears at each end. Pure CSS — no scroll listeners to keep in sync. */
  background:
    linear-gradient(to right, var(--color-surface) 30%, rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, var(--color-surface) 30%, rgba(255, 255, 255, 0)) right center,
    linear-gradient(to right, rgba(15, 23, 42, 0.12), rgba(255, 255, 255, 0)) left center,
    linear-gradient(to left, rgba(15, 23, 42, 0.12), rgba(255, 255, 255, 0)) right center;
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%;
  background-attachment: local, local, scroll, scroll;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.table th,
.table td {
  padding: var(--space-3) var(--space-4);
  text-align: start;
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.table th {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-surface-alt);
}

.table tbody tr:hover {
  background: var(--color-surface-alt);
}

.table--clickable tbody tr {
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   8. Alerts / banners (errors, rejections, empty states)
   -------------------------------------------------------------------------- */
/* Global reset: a class like .alert{display:flex} otherwise overrides the UA
   [hidden]{display:none}, so toggling the `hidden` attribute silently fails. */
[hidden] {
  display: none !important;
}

.alert {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  font-size: var(--text-sm);
  background: var(--color-surface);
}

.alert--danger {
  background: var(--color-danger-surface);
  border-color: var(--color-danger);
  color: var(--color-danger-text);
}

.alert--warn {
  background: var(--color-warn-surface);
  border-color: var(--color-warn);
  color: var(--color-warn-text);
}

.alert--info {
  background: var(--color-info-surface);
  border-color: var(--color-info);
  color: var(--color-info-text);
}

.empty-state {
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   8b. Stat tiles (dashboard KPI cards) — a value + label with a status accent.
   -------------------------------------------------------------------------- */
.stat {
  position: relative;
  padding: var(--space-4) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* Left accent bar carries the traffic-light meaning. */
.stat::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--color-neutral);
}

.stat--safe::before {
  background: var(--color-safe);
}
.stat--warn::before {
  background: var(--color-warn);
}
.stat--danger::before {
  background: var(--color-danger);
}
.stat--brand::before {
  background: var(--color-brand);
}

.stat__value {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.stat__label {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-4);
}

/* --------------------------------------------------------------------------
   8c. Dropzone (upload) — dashed target with a drag-over state.
   -------------------------------------------------------------------------- */
.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-6);
  text-align: center;
  border: 2px dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.dropzone:hover,
.dropzone:focus-visible {
  border-color: var(--color-brand);
}

.dropzone.is-dragover {
  border-color: var(--color-brand);
  background: var(--color-brand-weak);
  color: var(--color-brand-hover);
}

.dropzone__icon {
  font-size: 1.75rem;
}

.dropzone__hint {
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------------------
   8d. Toasts — transient, bottom-right notifications.
   -------------------------------------------------------------------------- */
.toast-host {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  z-index: var(--z-toast);
}

.toast {
  min-width: 240px;
  max-width: 360px;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-neutral);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  animation: toast-in var(--transition-base);
}

.toast--safe {
  border-left-color: var(--color-safe);
}
.toast--danger {
  border-left-color: var(--color-danger);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* --------------------------------------------------------------------------
   8e. X-ray viewer (dark surface) + Grad-CAM overlay with opacity control.
   -------------------------------------------------------------------------- */
.viewer {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-viewer-bg);
  border: 1px solid var(--color-viewer-border);
  border-radius: var(--radius-lg);
  color: var(--color-viewer-text);
}

.viewer__stage {
  display: grid;
  place-items: center;
  min-height: 260px;
}

.viewer__frame {
  position: relative;
  display: inline-block;
  line-height: 0;
}

.viewer__img {
  display: block;
  max-width: 100%;
  max-height: 58vh;
  border-radius: var(--radius-md);
}

.viewer__overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: var(--radius-md);
  pointer-events: none;
}

.viewer__controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-viewer-text);
}

.viewer__controls input[type="range"] {
  flex: 1;
  accent-color: var(--color-brand);
}

.viewer__placeholder {
  color: rgba(229, 231, 235, 0.6);
  font-size: var(--text-sm);
  text-align: center;
}

/* Multi-view thumbnail strip (Phase 4.5 T6). */
.viewer__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  justify-content: center;
  margin-bottom: var(--space-2);
}

.viewer__thumb {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--space-1);
  border: 1px solid var(--color-viewer-border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: rgba(229, 231, 235, 0.75);
  cursor: pointer;
  font-size: var(--text-xs);
}

.viewer__thumb.is-active {
  border-color: var(--color-brand);
  color: var(--color-brand);
}

.viewer__thumb-img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

.viewer__thumb-label {
  line-height: 1.1;
}

/* --------------------------------------------------------------------------
   8f. Confidence meter (a labelled bar).
   -------------------------------------------------------------------------- */
.meter {
  height: 8px;
  background: var(--color-surface-alt);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.meter__fill {
  height: 100%;
  background: var(--color-brand);
  border-radius: var(--radius-pill);
  transition: width var(--transition-base);
}

/* --------------------------------------------------------------------------
   8g. Opinion grid (CNN vs VLM) + key/value rows.
   -------------------------------------------------------------------------- */
.opinions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}

/* Three equal model columns (consensus card: CNN / VLM / Detector). */
.opinions--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 640px) {
  .opinions--three {
    grid-template-columns: 1fr;
  }
}

.opinion {
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
}

.opinion__who {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.opinion__headline {
  margin-top: var(--space-1);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}

.kv {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  font-size: var(--text-sm);
}

.kv__key {
  color: var(--color-text-muted);
}

@media (max-width: 560px) {
  .opinions {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------------------
   8h. Timeline (pipeline stages, live via SSE).
   -------------------------------------------------------------------------- */
.timeline {
  display: flex;
}

.tl-step {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
}

/* connector line to the previous step */
.tl-step::before {
  content: "";
  position: absolute;
  top: 15px;
  right: 50%;
  width: 100%;
  height: 2px;
  background: var(--color-border);
  z-index: 0;
}

.tl-step:first-child::before {
  display: none;
}

.tl-step__dot {
  position: relative;
  z-index: 1;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--color-surface);
  border: 2px solid var(--color-border-strong);
  color: var(--color-text-muted);
  font-size: 0.85rem;
}

.tl-step__label {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.tl-step.is-active .tl-step__dot {
  border-color: var(--color-brand);
  color: var(--color-brand);
}
.tl-step.is-active .tl-step__label {
  color: var(--color-brand-hover);
  font-weight: var(--weight-semibold);
}

.tl-step.is-done .tl-step__dot,
.tl-step.is-done::before {
  background: var(--color-safe);
}
.tl-step.is-done .tl-step__dot {
  border-color: var(--color-safe);
  color: #fff;
}

.tl-step.is-warn .tl-step__dot {
  background: var(--color-warn);
  border-color: var(--color-warn);
  color: #fff;
}

.tl-step.is-failed .tl-step__dot {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: #fff;
}

/* A stage that did not run (e.g. treatment skipped for a non-fracture verdict). */
.tl-step.is-skipped .tl-step__dot {
  border-style: dashed;
  color: var(--color-text-muted);
}
.tl-step.is-skipped .tl-step__label {
  font-style: italic;
}

/* --------------------------------------------------------------------------
   8i. Chat (docked case chat + knowledge Q&A) + citation chips.
   -------------------------------------------------------------------------- */
.chat {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.chat__log {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-height: 420px;
  overflow-y: auto;
  padding-right: var(--space-2);
}

/* Full-height chat (Assistant page): fills the viewport, input pinned at the
   bottom, transcript scrolls — like a real chat app. */
.chat--page {
  height: calc(100vh - var(--header-height) - 2 * var(--space-6));
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
}

.chat--page .chat__log {
  flex: 1;
  max-height: none;
}

.msg {
  max-width: 80%;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  white-space: pre-wrap;
  word-wrap: break-word;
}

.msg--user {
  align-self: flex-end;
  background: var(--color-brand);
  color: var(--color-brand-contrast);
  border-bottom-right-radius: var(--radius-sm);
}

.msg--assistant {
  align-self: flex-start;
  background: var(--color-surface-alt);
  color: var(--color-text);
  border-bottom-left-radius: var(--radius-sm);
}

.msg--refused {
  background: var(--color-warn-surface);
  color: var(--color-warn-text);
}

/* ---- Rendered Markdown inside an assistant bubble --------------------------
   `.msg` sets white-space: pre-wrap for plain text; rendered Markdown is real block
   markup, so it must fall back to normal wrapping or every newline in the source
   would show up as a blank line between elements. */
.msg--md {
  white-space: normal;
  max-width: 88%;
}

.msg--md > *:first-child {
  margin-top: 0;
}

.msg--md > *:last-child {
  margin-bottom: 0;
}

.md-p {
  margin: 0 0 var(--space-2);
}

.md-h {
  margin: var(--space-3) 0 var(--space-1);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-brand-strong, var(--color-brand));
  letter-spacing: 0.01em;
}

.md-list {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-5);
}

.md-list li {
  margin-bottom: var(--space-1);
}

.md-list .md-list {
  margin: var(--space-1) 0 0;
}

.msg--md code {
  background: rgba(15, 23, 42, 0.07);
  padding: 0.1em 0.35em;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 0.92em;
}

.md-pre {
  background: var(--color-text);
  color: var(--color-surface);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  overflow-x: auto;
  margin: 0 0 var(--space-2);
}

.md-pre code {
  background: none;
  padding: 0;
  color: inherit;
  font-size: var(--text-xs);
}

.md-quote {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-3);
  border-left: 3px solid var(--color-border);
  color: var(--color-text-muted);
}

.msg--md hr {
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: var(--space-3) 0;
}

.msg--md a {
  color: var(--color-brand);
  text-decoration: underline;
}

/* ---- "The agent is working" strip -----------------------------------------
   Replaces an opaque spinner: says WHICH step is running (from the routing event),
   so a slow answer reads as progress rather than a hang. */
.chat-activity {
  align-self: flex-start;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  animation: fade-in var(--transition-fast);
  max-width: 88%;
}

.chat-activity__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The agent's DECLARED plan (Phase 11 D5): what it said it would do, before it does it. Shown
   above the live step so the wait has a shape — previously the user saw one tool name at a time
   with no idea how many were coming. */
.chat-activity__plan {
  border-left: 2px solid var(--color-brand);
  padding-left: var(--space-2);
  margin: 2px 0;
}

.chat-activity__intent {
  color: var(--color-text);
  font-weight: var(--weight-medium);
  margin-bottom: 2px;
}

.chat-activity__planlist {
  margin: 0;
  padding-left: 1.1em;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Steps already finished, kept visible so the user can see the path the agent took. */
.chat-activity__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.chat-activity__steps:empty {
  display: none;
}

.chat-activity__steps li {
  display: flex;
  align-items: center;
  gap: 6px;
  opacity: 0.75;
}

.chat-activity__tick {
  color: var(--color-safe);
  font-weight: var(--weight-bold);
}

.chat-activity__icon {
  font-size: 0.95rem;
  line-height: 1;
}

.chat-activity__dots {
  display: inline-flex;
  gap: 3px;
}

.chat-activity__dots i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-brand);
  animation: chat-dot 1.2s infinite ease-in-out;
}

.chat-activity__dots i:nth-child(2) {
  animation-delay: 0.15s;
}

.chat-activity__dots i:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes chat-dot {
  0%,
  60%,
  100% {
    opacity: 0.25;
    transform: translateY(0);
  }
  30% {
    opacity: 1;
    transform: translateY(-3px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .chat-activity__dots i {
    animation: none;
    opacity: 0.6;
  }
}

.chat__form {
  display: flex;
  gap: var(--space-2);
  align-items: flex-end;
}

.chat__form .textarea {
  min-height: 44px;
  max-height: 140px;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.chip {
  font-size: var(--text-xs);
  padding: 2px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-info-surface);
  color: var(--color-info-text);
  border: 1px solid transparent;
  cursor: help;
}

/* Inline report card (Phase 4) — rendered in chat when a report is generated. */
.report-card {
  align-self: flex-start;
  max-width: min(420px, 90%);
  margin-top: var(--space-2);
  padding: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.report-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-semibold);
}

.report-card__icon {
  font-size: 1.1rem;
}

.report-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: var(--space-2) 0;
  font-size: var(--text-sm);
}

.report-card__date {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.report-card__actions {
  display: flex;
  gap: var(--space-2);
}

/* Compact inline consensus/treatment cards (Phase 4) — rendered in chat. */
.mini-card {
  align-self: flex-start;
  max-width: min(460px, 92%);
  margin-top: var(--space-2);
  padding: var(--space-3);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.mini-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}

.mini-card__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.mini-card__body {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
}

.mini-card__body p {
  margin: 0;
}

.mini-card__meta {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
}

/* Review queue item (clickable). */
.queue-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-3);
  text-align: start;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.queue-item:hover {
  border-color: var(--color-border-strong);
  background: var(--color-surface-alt);
}

.queue-item.is-active {
  border-color: var(--color-brand);
  background: var(--color-brand-weak);
}

.queue-item__reason {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* PDF preview frame (reports page). */
.report-frame {
  width: 100%;
  min-height: 72vh;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
}

/* A single report row (version list). */
.report-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

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

/* --------------------------------------------------------------------------
   8j. Modal / confirmation dialog
   -------------------------------------------------------------------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: var(--space-4);
  background: rgba(15, 23, 42, 0.5);
  animation: fade-in var(--transition-fast);
}

.modal {
  width: 100%;
  max-width: 460px;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: modal-in var(--transition-base);
  /* The backdrop is position:fixed, so anything taller than the viewport used to be
     simply unreachable — you could not scroll to the rest of a long form (e.g. Create
     specialist) or to the end of a report. Cap the height and let the BODY scroll, so
     the header stays visible and the footer actions never scroll out of reach. */
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - var(--space-4) * 2);
  max-height: calc(100dvh - var(--space-4) * 2);
  overflow: hidden;
}

/* Wide variant — used by the in-app PDF report viewer (Phase 4.2). */
.modal--wide {
  max-width: 920px;
}

.report-modal__frame-wrap {
  width: 100%;
}

.report-modal__frame-wrap .report-frame {
  min-height: 70vh;
}

.modal__header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  flex: 0 0 auto;
}

/* The only scrolling region of the modal (see .modal). min-height:0 is required or the
   flex item refuses to shrink below its content and the overflow never engages. */
.modal__body {
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  overflow-y: auto;
  min-height: 0;
  flex: 1 1 auto;
  overscroll-behavior: contain;
}

.modal__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  flex: 0 0 auto;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes modal-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
}

/* --------------------------------------------------------------------------
   8k. Skeleton loading placeholders
   -------------------------------------------------------------------------- */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--color-surface-alt) 25%,
    var(--color-border) 37%,
    var(--color-surface-alt) 63%
  );
  background-size: 400% 100%;
  border-radius: var(--radius-sm);
  animation: shimmer 1.3s ease-in-out infinite;
}

.skeleton--text {
  height: 12px;
  margin: 6px 0;
}

.skeleton--line {
  height: 16px;
}

.skeleton--row {
  height: 40px;
  margin-bottom: var(--space-2);
}

.skeleton--card {
  height: 92px;
}

@keyframes shimmer {
  0% {
    background-position: 100% 0;
  }
  100% {
    background-position: 0 0;
  }
}

/* --------------------------------------------------------------------------
   9. Disclaimer footer — MANDATORY on every diagnostic surface.
   -------------------------------------------------------------------------- */
.disclaimer {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  text-align: center;
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}

/* --------------------------------------------------------------------------
   10. Utilities
   -------------------------------------------------------------------------- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

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

.text-mono {
  font-family: var(--font-mono);
}

.ml-1 {
  margin-left: var(--space-1);
}

.text-sm {
  font-size: var(--text-sm);
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--color-border-strong);
  border-top-color: var(--color-brand);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Respect users who prefer reduced motion. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------------------------------
   8j. Assistant page: conversation sidebar + full-height chat (Phase 1.1).
   -------------------------------------------------------------------------- */
.assistant {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: var(--space-4);
  /* Fill the viewport under the sticky topbar, leaving room for the content
     padding (2×space-6) and the mandatory disclaimer footer (~52px) so the
     chat never overflows on top of the footer. */
  height: calc(100vh - var(--header-height) - 2 * var(--space-6) - 52px);
  min-height: 360px;
}

.assistant__chat {
  display: flex;
  min-width: 0;
  min-height: 0; /* allow the chat (and its scrolling log) to shrink below content */
}

/* The chat widget fills its pane like a real chat app (replaces .chat--page). */
.assistant__chat .chat {
  flex: 1;
  min-width: 0;
  min-height: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
}

/* min-height:0 is what makes the log actually scroll inside the flex column
   instead of expanding to its full content height (which spilled over the footer). */
.assistant__chat .chat__log {
  flex: 1;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
}

.sessions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-3);
  min-height: 0;
}

.sessions__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.sessions__title {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.sessions__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
}

.session {
  display: flex;
  align-items: center;
  border-radius: var(--radius-md);
}

.session:hover {
  background: var(--color-surface-alt);
}

.session.is-active {
  background: var(--color-brand-weak);
}

.session__open {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
  padding: var(--space-2);
  font: inherit;
  color: var(--color-text);
}

.session__label {
  font-size: var(--text-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.session__meta {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.session__actions {
  display: flex;
  gap: 2px;
  padding-right: 2px;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.session:hover .session__actions,
.session.is-active .session__actions {
  opacity: 1;
}

.session__action {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  line-height: 1;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
}

.session__action:hover {
  color: var(--color-text);
  background: var(--color-surface);
}

/* Narrow screens: stack the sidebar above the chat. */
@media (max-width: 720px) {
  .assistant {
    grid-template-columns: 1fr;
    height: auto;
  }
  .sessions__list {
    max-height: 30vh;
  }
  .assistant__chat .chat {
    height: calc(100vh - var(--header-height) - 40vh);
  }
}

/* --------------------------------------------------------------------------
   8k. Notification bell (topbar) + dropdown panel (Phase 1.2).
   -------------------------------------------------------------------------- */
.notif {
  position: relative;
  display: inline-flex;
}

.notif__bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: none;
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.notif__bell:hover {
  background: var(--color-surface-alt);
}

.notif__icon {
  font-size: 1.1rem;
  line-height: 1;
}

.notif__badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-danger);
  color: #ffffff;
  font-size: 10px;
  font-weight: var(--weight-semibold);
  line-height: 16px;
  text-align: center;
}

.notif__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  width: 340px;
  max-width: 90vw;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md, var(--shadow-sm));
  z-index: 50;
  overflow: hidden;
}

.notif__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.notif__list {
  display: flex;
  flex-direction: column;
  max-height: 60vh;
  overflow-y: auto;
}

.notif__empty {
  padding: var(--space-4);
  text-align: center;
}

.notif__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer;
  padding: var(--space-3) var(--space-4);
  font: inherit;
  color: var(--color-text);
}

.notif__item:hover {
  background: var(--color-surface-alt);
}

.notif__item.is-unread {
  background: var(--color-brand-weak);
}

.notif__item.is-unread:hover {
  filter: brightness(0.98);
}

.notif__title {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.notif__body {
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   8l. Advanced case ops: actions bar, clinical notes, status history,
       dashboard filter bar (Phase 1.3).
   -------------------------------------------------------------------------- */
.case-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.case-actions {
  display: flex;
  gap: var(--space-2);
}

.note-form {
  display: flex;
  gap: var(--space-2);
  align-items: flex-end;
  margin-bottom: var(--space-3);
}

.note-form .textarea {
  min-height: 44px;
}

.note-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.note {
  padding: var(--space-3);
  background: var(--color-surface-alt);
  border-radius: var(--radius-md);
}

.note__body {
  white-space: pre-wrap;
  word-wrap: break-word;
  font-size: var(--text-sm);
}

.note__meta {
  margin-top: var(--space-1);
  font-size: var(--text-xs);
}

.history {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.history__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.history__change {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.history__arrow {
  color: var(--color-text-muted);
}

.history__time {
  font-size: var(--text-xs);
}

.filter-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  /* Breaks a circular sizing loop. The bar sized itself to its content while the <select>
     inside sized itself to the bar, so the pair settled 17px wider than the column and pushed
     the page sideways. With a zero floor the bar takes the column's width and the controls,
     which already shrink, fit inside it. */
  min-width: 0;
}

.filter-bar__search {
  width: auto;
  /* `min(180px, 100%)` rather than a bare 180px: the flat value is a floor the field cannot go
     under, so inside a narrow column it overflowed the filter bar and, through it, the page.
     On anything roomy this still resolves to 180px. */
  min-width: min(180px, 100%);
}

.filter-bar .select,
.filter-bar .input {
  width: auto;
  /* A <select> sizes itself to its longest option, and a flex item's default min-width:auto
     turns that intrinsic width into a hard floor. On a narrow column the control then sticks
     out of the bar and drags the page with it. */
  min-width: 0;
  max-width: 100%;
}

.filter-bar__date {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

/* Intake page (Phase 4.5 T5): patient search/register → multi-view upload → onboarding. */
.intake-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-4);
}

.intake-patient {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.kv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
}

.intake-views {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.intake-view {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.intake-view__thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--color-surface-alt);
  flex: none;
}

.intake-view__meta {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.card.is-disabled {
  opacity: 0.55;
  pointer-events: none;
}

.dropzone.is-drag {
  border-color: var(--color-brand);
  background: var(--color-surface-alt);
}

.intake-credentials {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.credential-line {
  font-family: var(--font-mono);
}

/* The hand-over block in the credentials dialog: one selectable lump, because this is the
   only moment the password exists and retyping it from separate lines is where it gets
   mistyped. Wraps rather than scrolls so nothing is hidden when it is copied by eye. */
.credential-block {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.7;
  background: var(--color-surface-2, #f1f5f9);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  user-select: all;
}

/* --------------------------------------------------------------------------
   Admin console (Phase 6a) — in-page tabs + settings editor.
   -------------------------------------------------------------------------- */
.tabs {
  display: flex;
  gap: var(--space-1);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-5);
}

.tab {
  padding: var(--space-3) var(--space-4);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px; /* sit the active underline on the container border */
  color: var(--color-text-muted);
  font: inherit;
  font-weight: var(--weight-medium);
  cursor: pointer;
}

.tab:hover {
  color: var(--color-text);
}

.tab.is-active {
  color: var(--color-brand);
  border-bottom-color: var(--color-brand);
}

/* One tunable knob: description on the left, editable value on the right. */
.setting {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-2) var(--space-4);
  align-items: start;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
}

.setting:last-child {
  border-bottom: none;
}

.setting__info {
  min-width: 0;
}

.setting__label {
  font-weight: var(--weight-semibold);
}

.setting__desc {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.setting__control {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  justify-self: end;
}

.setting__control .input {
  width: 110px;
  text-align: end;
}

.setting__hint {
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  text-align: end;
}

/* Compact action cluster in a table row (Edit / Reset / Deactivate …). */
.row-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ── Healing timeline (Phase 5) ─────────────────────────────────────────────── */
.healing-chart__wrap {
  overflow-x: auto;
}
.healing-chart {
  width: 100%;
  max-width: 420px;
  height: auto;
}
.healing-chart__axis {
  stroke: var(--color-border-strong);
  stroke-width: 1;
}
.healing-chart__ref {
  stroke: var(--color-safe);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.6;
}
.healing-chart__reflabel {
  fill: var(--color-safe);
  font-size: 8px;
}
.healing-chart__tick {
  fill: var(--color-text-faint);
  font-size: 8px;
  text-anchor: middle;
}
.healing-chart__line {
  fill: none;
  stroke: var(--color-brand);
  stroke-width: 2;
}
.healing-chart__dot {
  stroke: var(--color-surface);
  stroke-width: 1;
}
.healing-chart__dot--baseline {
  fill: var(--color-text-faint);
}
.healing-chart__dot--none {
  fill: var(--color-safe);
}
.healing-chart__dot--warning {
  fill: var(--color-warn);
}
.healing-chart__dot--critical {
  fill: var(--color-danger);
}

/* The sentence under the badges: what was detected and what it implies. A row of badges
   ("Watch", "Stalling", "40 %") assumed the reader already spoke the system's vocabulary
   (Phase 10 §3.3). */
.healing-summary__reading {
  font-size: var(--text-sm);
  color: var(--color-text);
}

/* "What these numbers mean" — collapsed, because it is a reference the reader needs once, but
   present, because a clinical number with no stated denominator is not safe to show. */
.healing-legend > summary {
  cursor: pointer;
}
.healing-legend p {
  margin-top: var(--space-1);
}

/* The alerts worklist explains each flag in the row itself; give that column room to breathe. */
.alerts-cell--detected {
  min-width: 18rem;
  max-width: 28rem;
}

.healing-visits {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.healing-visit {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.healing-visit:first-child {
  border-top: none;
  padding-top: 0;
}
.healing-visit__thumb {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  overflow: hidden;
}
.healing-visit__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.healing-visit__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  flex: 1 1 auto;
}
.healing-visit__title {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  font-weight: var(--weight-semibold);
}
.healing-visit__meter {
  width: 120px;
  flex: 0 0 auto;
}

/* ==========================================================================
   Patient portal (Phase 6b) — the calm, single-column patient-facing surface
   (role=patient). Deliberately distinct from the staff shell.
   ========================================================================== */
.portal {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
}
.portal__header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  z-index: var(--z-header);
}
.portal__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-bold);
  color: var(--color-brand);
  font-size: var(--text-lg);
}
.portal__greeting {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.portal__hello {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  margin: 0;
}
.portal__sub {
  margin-top: var(--space-1);
  font-size: var(--text-base);
  color: var(--color-text-muted);
}
.portal__spacer {
  flex: 1;
}
.portal__nav {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-5);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.portal__navlink {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  color: var(--color-text-muted);
  text-decoration: none;
  font-weight: var(--weight-medium);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.portal__navlink:hover {
  background: var(--color-surface-alt);
  color: var(--color-text);
}
.portal__navlink.is-active {
  background: var(--color-brand-weak);
  color: var(--color-brand-hover);
}
/* Width comes from the role's --reading-max token (Phase 9.1). At a fixed 820px the portal
   read as a cut-down staff page squeezed into a column — Ebrahim: "صفحات ال patient صغيرة". */
.portal__content {
  width: 100%;
  max-width: var(--reading-max);
  margin: 0 auto;
  padding: var(--space-5);
  flex: 1;
}
.portal__disclaimer {
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  padding: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.portal__h1 {
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  margin: 0;
}
.portal__h2 {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  margin: 0 0 var(--space-3);
}
.portal__lead {
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  margin: var(--space-2) 0;
}
.portal__back {
  display: inline-block;
  color: var(--color-brand);
  text-decoration: none;
  font-size: var(--text-sm);
}
.portal__back:hover {
  text-decoration: underline;
}

/* Case cards on the "My care" list */
.care-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.care-card__title {
  font-weight: var(--weight-semibold);
  font-size: var(--text-lg);
}
.care-card__next {
  margin-top: var(--space-2);
}
.care-card__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.portal-empty {
  text-align: center;
  padding: var(--space-6);
}
.portal-xray {
  display: block;
  max-width: 100%;
  border-radius: var(--radius-md);
  background: var(--color-viewer-bg);
}

/* Plain-language care plan (key/value rows) */
.plan {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.plan-row {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: var(--space-3);
  align-items: start;
}
.plan-row__key {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* Healing progress bar + per-follow-up points */
.healingbar-wrap {
  margin: var(--space-3) 0;
}
.healingbar {
  height: 12px;
  background: var(--color-surface-alt);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.healingbar__fill {
  height: 100%;
  background: linear-gradient(90deg, var(--color-brand), var(--color-safe));
  border-radius: var(--radius-pill);
  transition: width var(--transition-base);
}
.healingbar__label {
  margin-top: var(--space-1);
}
.healing-points {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.healing-points li {
  display: flex;
  justify-content: space-between;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.healing-points__wk {
  color: var(--color-text-muted);
}

/* Recommended follow-up steps */
.appt-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.appt {
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-neutral);
  border-radius: var(--radius-md);
}
.appt--upcoming {
  border-left-color: var(--color-safe);
}
.appt__when {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.appt__what {
  font-weight: var(--weight-medium);
  margin-top: var(--space-1);
}

/* ==========================================================================
   Patient portal — redesigned surface.
   The first version was a plain stack of generic cards: correct, but it read like a
   database dump. This layer follows what consumer health apps actually do — one
   dominant "where do I stand" hero, a progress ring instead of a flat bar, icon
   tiles for the two or three things a patient came to do, and a real timeline for
   what happens next. Calm colour, generous spacing, one idea per block.
   ========================================================================== */

/* Greeting sits below the nav rather than in the header bar, so the bar stays a thin
   utility strip and the greeting can breathe at the top of the content column.
   It reuses .portal__content for the same width/padding, so it must opt OUT of that
   rule's `flex: 1` — otherwise it stretches and pushes the content far down the page. */
.portal__welcome {
  flex: 0 0 auto;
  /* Give the greeting the same breathing room the cards below it get; at padding-bottom:0 it
     sat clamped against the first card like a stray caption. */
  padding-bottom: var(--space-2);
  padding-top: var(--space-6);
}

/* ---- Header: avatar + greeting ---- */
.portal__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--color-brand);
  color: var(--color-brand-contrast);
  font-weight: var(--weight-bold);
  font-size: var(--text-sm);
  flex: 0 0 auto;
  letter-spacing: 0.02em;
}

/* ---- Section heading with an icon ---- */
.psection__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5) 0;
  margin-bottom: var(--space-3);
}

/* `.card` carries no padding of its own (that is `.card__body`'s job), and the portal's
   sections were dropping their content straight into it — so every heading looked inset while
   its text ran hard against the border. That one omission is most of why this page did not
   look like the rest of the product. */
.psection__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: 0 var(--space-5) var(--space-5);
}

/* The healing ring, with its words UNDER it. A stage is two or three words and they were being
   clipped by the ring's own edge at every viewport (Phase 10 §13). */
.pringwrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}
.pringwrap__label {
  text-align: center;
  line-height: 1.2;
}
.psection__icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--color-brand-weak);
  color: var(--color-brand-hover);
}
.psection__title {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  margin: 0;
}

/* ---- The hero status card ---- */
.phero {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  background: linear-gradient(135deg, var(--color-brand-hover) 0%, var(--color-brand) 100%);
  color: var(--color-brand-contrast);
  box-shadow: var(--shadow-md);
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
}
/* A soft radial highlight so the block does not read as a flat slab of colour. */
.phero::after {
  content: "";
  position: absolute;
  top: -60%;
  right: -10%;
  width: 320px;
  height: 320px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  pointer-events: none;
}
.phero--safe {
  background: linear-gradient(135deg, #15803d 0%, #16a34a 100%);
}
.phero__body {
  flex: 1 1 260px;
  position: relative;
  z-index: 1;
}
.phero__eyebrow {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.85;
  margin-bottom: var(--space-1);
}
.phero__title {
  font-size: var(--text-2xl, 1.5rem);
  font-weight: var(--weight-bold);
  line-height: 1.25;
  margin: 0 0 var(--space-2);
}
.phero__meta {
  font-size: var(--text-sm);
  opacity: 0.9;
}
/* The trust line — "a human reviewed this" is the single most reassuring fact here. */
.phero__trust {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--space-3);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.28);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}

/* ---- Circular progress ring ---- */
.pring {
  position: relative;
  z-index: 1;
  width: 128px;
  height: 128px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
}
.pring svg {
  position: absolute;
  inset: 0;
  transform: rotate(-90deg);
}
.pring__track {
  fill: none;
  stroke: rgba(255, 255, 255, 0.25);
  stroke-width: 9;
}
.pring__bar {
  fill: none;
  stroke: #fff;
  stroke-width: 9;
  stroke-linecap: round;
  transition: stroke-dashoffset 900ms cubic-bezier(0.22, 1, 0.36, 1);
}
.pring__label {
  position: relative;
  text-align: center;
  line-height: 1.1;
}
/* The ring's centre carries the healing STAGE in words, not "85 %" (Phase 10 §6). Words need
   more room and less weight than a two-digit number did. */
.pring__stage {
  display: block;
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
}
.pring__cap {
  display: block;
  font-size: var(--text-xs);
  opacity: 0.85;
}

/* ---- Quick-action tiles ---- */
.ptiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
}
.ptile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition:
    transform var(--transition-fast),
    box-shadow var(--transition-fast),
    border-color var(--transition-fast);
}
.ptile:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: var(--color-brand);
}
.ptile:disabled {
  opacity: 0.55;
  cursor: default;
}
.ptile__icon {
  display: grid;
  place-items: center;
  color: var(--color-brand);
}
.ptile__label {
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}
.ptile__hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ---- Care plan rows with icons ---- */
.pplan {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.pplan__row {
  display: grid;
  grid-template-columns: 26px 1fr;
  gap: var(--space-3);
  align-items: start;
}
.pplan__ico {
  display: grid;
  place-items: center;
  padding-top: 2px;
  color: var(--color-brand);
}
.pplan__key {
  display: block;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  margin-bottom: 2px;
}
.pplan__val {
  font-size: var(--text-sm);
  line-height: 1.65;
}

/* ==========================================================================
   Admin console — Overview tab. Stat tiles, proportion bars, a status list and
   an activity feed. Numbers that are themselves a signal (a non-empty review
   queue, an open healing alert) get a tone so they read before they are parsed.
   ========================================================================== */
.ovgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.ovtile {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-sm);
}
.ovtile--safe {
  border-left-color: var(--color-safe);
}
.ovtile--warn {
  border-left-color: var(--color-warn);
}
.ovtile--danger {
  border-left-color: var(--color-danger);
}
.ovtile__top {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 2px;
}
.ovtile__icon {
  font-size: 0.95rem;
  line-height: 1;
}
.ovtile__label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}
.ovtile__value {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: 1.15;
}
.ovtile__hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: 2px;
}

.ovbar {
  margin-bottom: var(--space-3);
}
.ovbar:last-child {
  margin-bottom: 0;
}
.ovbar__head {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-sm);
  margin-bottom: 4px;
}
.ovbar__track {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-alt);
  overflow: hidden;
}
.ovbar__fill {
  height: 100%;
  border-radius: inherit;
  transition: width 600ms cubic-bezier(0.22, 1, 0.36, 1);
}
.ovbar__fill--safe {
  background: var(--color-safe);
}
.ovbar__fill--warn {
  background: var(--color-warn);
}
.ovbar__fill--info {
  background: var(--color-info);
}
.ovbar__fill--danger {
  background: var(--color-danger);
}

.ovrow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}
.ovrow:last-child {
  border-bottom: 0;
}
.ovrow__key {
  color: var(--color-text-muted);
}

.ovfeed {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ovfeed li {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.ovfeed li:last-child {
  border-bottom: 0;
}
.ovfeed__action {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--color-surface-alt);
  border-radius: var(--radius-sm);
  padding: 2px 7px;
  white-space: nowrap;
}
.ovfeed__when {
  margin-left: auto;
  white-space: nowrap;
}

/* ---- Plain-language glossary under the care plan ---- */
.pgloss-wrap {
  margin-top: var(--space-3);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}
.pgloss-wrap > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-brand);
  list-style: none;
}
.pgloss-wrap > summary::-webkit-details-marker {
  display: none;
}
.pgloss-wrap > summary::before {
  content: "💬 ";
}
.pgloss {
  margin: var(--space-3) 0 var(--space-1);
  display: grid;
  gap: var(--space-2);
}
.pgloss dt {
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}
.pgloss dd {
  margin: 2px 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* ---- Vertical timeline (next steps / healing history) ---- */
.ptimeline {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ptimeline > li {
  position: relative;
  padding-left: var(--space-6);
  padding-bottom: var(--space-4);
}
.ptimeline > li:last-child {
  padding-bottom: 0;
}
/* The connecting rail, drawn per-item so the last node has no tail. */
.ptimeline > li::before {
  content: "";
  position: absolute;
  left: 7px;
  top: 18px;
  bottom: 0;
  width: 2px;
  background: var(--color-border);
}
.ptimeline > li:last-child::before {
  display: none;
}
.ptimeline > li::after {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-surface);
  border: 2px solid var(--color-border);
}
.ptimeline > li.is-done::after {
  background: var(--color-safe, #16a34a);
  border-color: var(--color-safe, #16a34a);
}
.ptimeline > li.is-now::after {
  background: var(--color-brand);
  border-color: var(--color-brand);
  box-shadow: 0 0 0 4px var(--color-brand-weak);
}
.ptl__when {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}

/* "in 9 days" — the part of a follow-up date a patient actually reads (Phase 10 §6.4). */
.ptl__rel {
  font-weight: var(--weight-regular, 400);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.ptl__what {
  font-size: var(--text-sm);
}
.ptl__why {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ---- Case list card ---- */
.pcase {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) var(--space-5);
  padding-left: calc(var(--space-5) + 4px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  transition:
    transform var(--transition-fast),
    box-shadow var(--transition-fast);
}
.pcase:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}
/* Status stripe: the case's headline verdict, readable before any text is parsed. */
.pcase::before {
  content: "";
  position: absolute;
  left: 0;
  top: 12px;
  bottom: 12px;
  width: 4px;
  border-radius: 0 4px 4px 0;
  background: var(--color-warn, #d97706);
}
.pcase--safe::before {
  background: var(--color-safe, #16a34a);
}
.pcase__ico {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  font-size: 1.25rem;
}
.pcase__body {
  flex: 1;
  min-width: 0;
}
.pcase__title {
  font-weight: var(--weight-semibold);
  margin-bottom: 2px;
}
.pcase__meta {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.pcase__side {
  flex: 0 0 auto;
  text-align: right;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.pcase__chev {
  flex: 0 0 auto;
  color: var(--color-text-muted);
  font-size: 1.2rem;
}

/* ---- Slim inline progress (used on list cards) ---- */
.pmini {
  margin-top: 8px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-surface-alt);
  overflow: hidden;
}
.pmini__fill {
  height: 100%;
  border-radius: inherit;
  background: var(--color-brand);
  transition: width 700ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* ---- Empty state ---- */
.pempty {
  text-align: center;
  padding: var(--space-8) var(--space-5);
}
.pempty__ico {
  font-size: 2.4rem;
  margin-bottom: var(--space-3);
  opacity: 0.6;
}

/* ---- X-ray frame ---- */
.pxray {
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #0b1220;
  display: grid;
  place-items: center;
  min-height: 180px;
  /* Sized to the film, not to the column. A portrait radiograph in a full-width grid became a
     small picture marooned in a wide black slab (Phase 10 §13). */
  max-width: max-content;
  margin-inline: auto;
}
.pxray img {
  max-width: 100%;
  max-height: 460px;
  display: block;
}

@media (max-width: 640px) {
  .phero {
    padding: var(--space-5) var(--space-4);
    gap: var(--space-4);
  }
  .phero__title {
    font-size: var(--text-xl, 1.25rem);
  }
  .pring {
    width: 104px;
    height: 104px;
  }
  .pring__stage {
    font-size: var(--text-sm);
  }
}

/* The portal assistant reuses .assistant; give it a height that fits the portal chrome. */
.assistant--portal {
  height: calc(100vh - 260px);
  min-height: 420px;
}

.text-xs {
  font-size: var(--text-xs);
}

@media (max-width: 640px) {
  .plan-row {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }
  .assistant--portal {
    grid-template-columns: 1fr;
    height: auto;
  }
  .portal__greeting {
    display: none;
  }
}

/* ==========================================================================
   Report HTML viewer (Phase 6c) — the reliable in-app report display, shared by
   staff (case page / full viewer / chat card) and the patient portal.
   ========================================================================== */
.report-view {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 720px;
}
.rv__letterhead {
  border-bottom: 1px solid var(--color-border);
  padding-bottom: var(--space-2);
}
.rv__center {
  font-weight: var(--weight-semibold);
}
.rv__titlerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.rv__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  margin: 0;
}
.rv__h3 {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  color: var(--color-brand-hover);
  margin: var(--space-2) 0 0;
}
.report-view p {
  margin: 0;
  line-height: var(--leading-normal);
  white-space: pre-wrap;
}
.rv__list {
  margin: 0;
  padding-left: var(--space-5);
}
.rv__byline {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2);
}
.rv__disclaimer {
  font-style: italic;
}

/* Patient portal: notifications bell + list */
.portal__bell {
  position: relative;
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: var(--text-lg);
  line-height: 1;
  padding: var(--space-1);
}
.portal__bellcount {
  position: absolute;
  top: -2px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--color-danger);
  color: #fff;
  font-size: 10px;
  font-weight: var(--weight-bold);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.portal-notif {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  text-align: left;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  cursor: pointer;
}
.portal-notif:hover {
  background: var(--color-surface-alt);
}
.portal-notif.is-unread {
  border-left: 3px solid var(--color-brand);
}
.portal-notif__title {
  font-weight: var(--weight-medium);
}

/* ==========================================================================
   Quality & analytics dashboard (Phase 7.3) — the AI-vs-expert dashboard.
   ========================================================================== */
.quality-headline {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: var(--space-1) 0;
}

/* Accuracy-over-time line chart (inline SVG, no chart library). */
.quality-chart__wrap {
  overflow-x: auto;
}
.quality-chart {
  width: 100%;
  max-width: 720px;
  height: auto;
}
.quality-chart__axis {
  stroke: var(--color-border-strong);
  stroke-width: 1;
}
.quality-chart__grid {
  stroke: var(--color-border);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.5;
}
.quality-chart__tick {
  fill: var(--color-text-faint);
  font-size: 9px;
  text-anchor: middle;
}
.quality-chart__tick--y {
  text-anchor: end;
}
.quality-chart__line {
  fill: none;
  stroke: var(--color-brand);
  stroke-width: 2;
}
.quality-chart__dot {
  fill: var(--color-brand);
  stroke: var(--color-surface);
  stroke-width: 1;
}

/* Two side-by-side breakdown columns (region / confidence). */
.quality-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-3);
}

/* Horizontal accuracy bars. */
.qbar {
  margin-bottom: var(--space-3);
}
.qbar__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
}
.qbar__key {
  color: var(--color-text);
}
.qbar__val {
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}
.qbar__track {
  height: 8px;
  background: var(--color-surface-muted, var(--color-border));
  border-radius: var(--radius-pill, 999px);
  overflow: hidden;
}
.qbar__fill {
  height: 100%;
  border-radius: inherit;
}
.qbar__fill--safe {
  background: var(--color-safe);
}
.qbar__fill--warn {
  background: var(--color-warn);
}
.qbar__fill--danger {
  background: var(--color-danger);
}

/* --------------------------------------------------------------------------
   Live indicator (Phase 9.0) — "this list keeps itself current".
   Sits in a card header next to the title. A pulsing dot while idle, an
   explicit "Updating…" during a refresh, then a timestamp — so a list that
   rewrites itself never leaves the reader guessing whether it is stale.
   -------------------------------------------------------------------------- */
.live {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.live__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-safe);
  box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.45);
  animation: live-pulse 2.4s ease-out infinite;
}

.live.is-busy .live__dot {
  background: var(--color-brand);
  animation-duration: 0.9s;
}

@keyframes live-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.45); }
  70%  { box-shadow: 0 0 0 6px rgba(22, 163, 74, 0); }
  100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .live__dot {
    animation: none;
  }
}

/* Loading pane (Phase 9.0) — spinner + label for a single loading surface
   (image, preview, document). Lists use .skeleton rows instead. */
.loading-block {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4);
}

.spinner--inline {
  display: inline-block;
  flex: none;
}

/* Triage severity dot (Phase 9.1) — replaces a coloured emoji circle, which arrived at a
   different size per platform and could not take the badge's own colour. */
.triage-badge__dot {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
}

/* ==========================================================================
   Case header (Phase 9.3) — the block that answers, before any scrolling:
   whose case is this, what was found, on whose authority, and what should I do.
   Replaces a bare status alert that printed an internal decision code twice and
   never named the patient.
   ========================================================================== */
.casehead {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.casehead__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.casehead__who {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.casehead__patient {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--color-text);
  letter-spacing: -0.01em;
  text-decoration: none;
}

.casehead__patient:hover {
  color: var(--color-brand-hover);
  text-decoration: underline;
}

/* A case with no patient attached is a data problem, not a styling choice — say so quietly
   rather than rendering an empty heading. */
.casehead__patient--unknown {
  color: var(--color-text-faint);
  font-style: italic;
}

.casehead__meta,
.casehead__facts {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.casehead__facts {
  margin-top: var(--space-1);
}

.casehead__badges {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.casehead__body {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: var(--space-5);
  padding: var(--space-5);
  align-items: start;
}

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

/* The finding, with a colour bar carrying its tone. The tone says "result", never "error":
   a fracture is amber because it needs action, not because something went wrong. */
.casehead__finding {
  border-left: 4px solid var(--color-neutral);
  padding-left: var(--space-4);
}

.casehead__finding--safe {
  border-left-color: var(--color-safe);
}
.casehead__finding--warn {
  border-left-color: var(--color-warn);
}
.casehead__finding--danger {
  border-left-color: var(--color-danger);
}

/* Whose judgement this is — the distinction the old page erased by rendering an AI
   auto-approval and a specialist's confirmation identically. */
.casehead__authority {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.casehead__headline {
  margin-top: var(--space-1);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
}

.casehead__detail {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.casehead__action {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}

.casehead__hint {
  font-size: var(--text-sm);
  color: var(--color-text);
  line-height: var(--leading-normal);
}

/* Collapsible reference card (Phase 9.3): open one click away, but not competing for
   attention with the finding. <details> so it needs no JS state of its own. */
.card--collapsible > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.card--collapsible > summary::-webkit-details-marker {
  display: none;
}

.card--collapsible > summary:hover .card__title {
  color: var(--color-brand-hover);
}

.card--collapsible__chev {
  display: grid;
  place-items: center;
  color: var(--color-text-faint);
  transition: transform var(--transition-fast);
}

.card--collapsible[open] .card--collapsible__chev {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .card--collapsible__chev {
    transition: none;
  }
}

/* ==========================================================================
   Review cockpit (Phase 9.4)
   ========================================================================== */

/* "Here is what the AI concluded, and here is why that was not enough." The two facts a
   reviewer's decision actually turns on, previously spread across a routing enum and an
   agreement ratio they had to interpret themselves. */
.askbox {
  padding: var(--space-3) var(--space-4);
  border-left: 4px solid var(--color-brand);
  background: var(--color-brand-weak);
  border-radius: var(--radius-md);
}

.askbox__reading {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
}

.askbox__why {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* The decision bar sticks to the bottom of the evidence pane: a reviewer should never have to
   scroll back past everything they just read in order to act on it. */
.decision {
  position: sticky;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-2);
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* Collapsed by default (Phase 10 §12). Sticky is right — the decision must stay reachable while
   the reviewer looks at the images — but sticky AND 240px tall put the decision physically in
   front of the evidence it is supposed to follow. One line until they are ready. */
.decision__summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}

.decision__chev {
  margin-left: auto;
  display: inline-flex;
  transition: transform var(--transition-base);
}
.decision.is-open .decision__chev {
  transform: rotate(180deg);
}

.decision__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.decision__body[hidden] {
  display: none;
}

.decision__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.decision__title {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}

.decision__hint {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
}

.decision__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Unclaimed (Phase 10 §1.3): the bar is inert and reads as inert. The disabled controls stay
   VISIBLE rather than hidden, so the reviewer can see what deciding will involve before they
   take the lock — hiding them would make claiming a step into the unknown. */
.decision--locked .decision__actions,
.decision--locked .field {
  opacity: 0.55;
}

.decision__gate {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--color-surface-alt);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius);
}

.decision__gate-text {
  flex: 1 1 18rem;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Held: the deadline is part of the bar, not a toast that has already gone. */
.decision__hold {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.decision__hold-text {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.decision__hold-text.is-warning {
  color: var(--color-warn-text);
  font-weight: var(--weight-semibold);
}

/* Shortcut chips. The hint used to be part of the button's label ("Confirm CNN label (c)"),
   which made the label read like a sentence with debris in it. */
.kbd {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  padding: 1px 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.6;
  color: var(--color-text-muted);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
}

.btn .kbd {
  margin-left: var(--space-1);
  color: inherit;
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.35);
}

.btn--secondary .kbd {
  background: var(--color-surface-alt);
  border-color: var(--color-border-strong);
}

/* The evidence radiographs are the reviewer's primary object — give them room. */
.evidence-images .viewer__img {
  max-height: 420px;
}

/* The sticky decision bar sits over the pane's own scroll, so the pane needs room underneath
   for its last content to clear it — otherwise the bar covers the bottom of the radiographs.
   A class rather than :has(), so the rule does not depend on the DOM nesting staying put. */
/* Room for the sticky bar to float over. Much less than before: the bar is one line until the
   reviewer opens it (Phase 10 §12), so reserving two full spacing steps left a dead band under
   the radiographs on every case. */
.evidence--deciding {
  padding-bottom: var(--space-8);
}

/* Worklist filters (Phase 9.5). Search and sort stay in the header; the rest live in a drawer
   whose badge counts what is active, because a filtered worklist that looks unfiltered is how
   a clinician concludes there is nothing to do. */
.filter-more {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-alt);
  border-bottom: 1px solid var(--color-border);
}

/* The headline counts are buttons: clicking one filters the list below it (Phase 9.5). They
   were inert numbers, and clicking a number that does nothing is a dead end you have to learn. */
.stat {
  width: 100%;
  text-align: left;
  font: inherit;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.stat:hover {
  box-shadow: var(--shadow-md);
}

.stat:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.stat.is-active {
  border-color: var(--color-brand);
  box-shadow: inset 0 0 0 1px var(--color-brand);
}

/* The server's full-sentence headline, under the short finding that titles the card. */
.pcase__line {
  margin-top: 2px;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

/* ---- Admin: organisation identity (Phase 10 §8) ---- */
.brand-preview {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.brand-preview__mark {
  display: inline-flex;
  color: var(--color-brand);
}
.brand-preview__name {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}
.brand-preview .brandmark {
  height: 32px;
}
.brand-swatch {
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius);
}

/* ---- Report: the radiographs (Phase 10) ----
   The PDF always carried these; the in-app view did not, which made the thing a clinician
   reviewed before publishing a different document from the one the patient received. */
.rv__images {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
}
.rv__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.rv__imgslot {
  display: grid;
  place-items: center;
  min-height: 140px;
  background: #0b1220;
  border-radius: var(--radius);
  overflow: hidden;
}
.rv__img {
  width: 100%;
  height: auto;
  max-height: 320px;
  object-fit: contain;
  display: block;
}

/* Three action tiles wrap 2 + 1 on a phone; the orphan takes the full row rather than sitting
   at half width next to nothing (Phase 10 §13). */
@media (max-width: 640px) {
  .ptiles > .ptile:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}

/* ── Assistant: starters, the surviving trace, proposals, queue rows (Phase 11) ───────────── */

/* The empty state is the persona's own: its greeting plus the questions it can actually answer.
   A shared hardcoded paragraph was wrong for four of the five audiences. */
.chat__empty {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.chat__starters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.chat__starter {
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-pill, 999px);
  padding: 6px var(--space-3);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}

.chat__starter:hover {
  border-color: var(--color-brand);
  background: var(--color-surface-alt);
}

/* Stop is only visible mid-turn; a long answer must be interruptible. */
.chat__stop {
  flex: 0 0 auto;
}

/* "How I did this" — collapsed by default, rebuilt from the persisted trace on reload. It used to
   be deleted the moment the answer began, and the stored trace was never rendered at all. */
.tool-trace {
  align-self: flex-start;
  max-width: 88%;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  padding: 4px var(--space-2);
}

.tool-trace__summary {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  list-style: none;
}

.tool-trace__summary::-webkit-details-marker {
  display: none;
}

.tool-trace__list {
  margin: var(--space-2) 0 var(--space-1);
  padding-left: 1.3em;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.tool-trace__list li {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tool-trace__label {
  color: var(--color-text);
}

/* A proposed write (D3): the assistant asks, the human acts. Danger = it replaces or revokes
   something that already exists. */
.mini-card--action {
  border-color: var(--color-brand);
}

.mini-card--action.is-danger {
  border-color: var(--color-danger);
}

.mini-card__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-2);
}

/* A value shown ONCE and never persisted (a handed-over temporary password). */
.mini-card__once {
  margin-top: var(--space-2);
  padding: var(--space-2);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
}

.mini-card__secret {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: var(--text-md);
  letter-spacing: 0.04em;
  user-select: all;
}

.queue-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: 3px 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

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

.queue-row__case {
  font-weight: var(--weight-semibold, 600);
  color: var(--color-brand);
  text-decoration: none;
}

.queue-row__reason {
  flex: 1 1 auto;
  min-width: 0;
}

/* ── A reply's own controls, and the sources behind it (Phase 12.2) ─────────────────────────
   Both sit under the bubble, aligned with it, in the same quiet register as the tool trace: they
   are affordances for a reader who wants to act on the answer, and must not compete with it. */
.msg-actions {
  align-self: flex-start;
  display: flex;
  gap: var(--space-1);
  margin-top: -2px;
}

.msg-actions__btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.msg-actions__btn:hover {
  color: var(--color-text);
  border-color: var(--color-border);
  background: var(--color-surface-alt);
}

.sources {
  align-self: flex-start;
  max-width: 88%;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  padding: 4px var(--space-2);
}

.sources__summary {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  list-style: none;
}

.sources__summary::-webkit-details-marker {
  display: none;
}

.sources__list {
  margin: var(--space-2) 0 var(--space-1);
  padding-left: 1.4em;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.sources__list li {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.sources__title {
  color: var(--color-text);
}

/* The origin can be a long identifier; let it wrap rather than widen the transcript. */
.sources__where {
  overflow-wrap: anywhere;
}

/* ── Overdue panel (Phase 13.2) ─────────────────────────────────────────────
   Sits under the admin pipeline bars. Those say WHERE the work is; this says
   where it has been sitting too long, split by state so an operator can see
   whom to ask rather than only that something is late. */
.ov-sla {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.ov-sla__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-weight: 600;
}
.ov-sla__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.ov-sla__list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
