/*
 * Phone companion page styling — the IDEXX Spot palette expressed as CSS
 * custom properties (mirrors lib/theme/agent_idexx_theme.dart, docs/design/
 * spot.md) so the page reads as part of Agent IDEXX rather than a stray web
 * form.
 *
 * Two deliberate departures from the Flutter app's Spot usage:
 *
 *  1. **System fonts, not Sora/Roboto.** The page must be scannable-to-typing
 *     inside a second on clinic cellular (design §3); a Google Fonts round
 *     trip plus FOUT costs more than the brand match is worth, and the CSP in
 *     index.html forbids third-party origins outright.
 *  2. **Both schemes via `prefers-color-scheme`.** Staff phones are set to
 *     whatever the staff member likes, so there is no in-page theme toggle to
 *     mirror — the OS decides.
 */

:root {
  /* Spot dark tokens (neutral-900 … feedback-*-on-dark). */
  --bg: #0f1114;
  --surface: #25282e;
  --surface-elevated: #31353d;
  --primary: #0b76f0;
  --primary-hover: #3e9af5;
  --primary-subtle: #224569;
  --text-primary: #e8e8e8;
  --text-secondary: #bcbdbc;
  --text-muted: #717479;
  --border: #5d6066;
  --green: #81c784;
  --yellow: #f8ed81;
  --red: #f5767d;

  /* Spot spacing + radius scales. */
  --space-xs: 4px;
  --space-s: 8px;
  --space-m: 12px;
  --space-l: 16px;
  --space-xl: 24px;
  --radius-sm: 4px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "JetBrains Mono", Menlo, monospace;
}

@media (prefers-color-scheme: light) {
  :root {
    /* Spot light tokens (white … feedback-*-on-light). */
    --bg: #ffffff;
    --surface: #f5f5f5;
    --surface-elevated: #f0f0f0;
    --primary: #0969d9;
    --primary-hover: #075bc3;
    --primary-subtle: #e2f5ff;
    --text-primary: #25282e;
    --text-secondary: #5d6066;
    --text-muted: #a9aaaa;
    --border: #d0d1d0;
    --green: #388e3c;
    --yellow: #f6e962;
    --red: #d30e1f;
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html,
body {
  height: 100%;
  margin: 0;
  /* No rubber-banding or pull-to-refresh: this is an app surface, and an
     accidental refresh mid-turn drops the phone back through the connect
     handshake. */
  overscroll-behavior: none;
}

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 16px; /* iOS zooms any focused input below 16px. */
  line-height: 1.45;
}

/* ── Top bar ── */

.topbar {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  padding: max(var(--space-s), env(safe-area-inset-top)) var(--space-l) var(--space-s);
  padding-left: max(var(--space-l), env(safe-area-inset-left));
  padding-right: max(var(--space-l), env(safe-area-inset-right));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.title {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pill {
  flex: none;
  padding: 2px var(--space-s);
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  background: var(--surface-elevated);
  color: var(--text-secondary);
}

.pill.ok {
  color: var(--green);
}

.pill.warn {
  color: var(--yellow);
}

/* ── Transcript ── */

.scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.chat {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-l) max(var(--space-l), env(safe-area-inset-right)) var(--space-l)
    max(var(--space-l), env(safe-area-inset-left));
}

.bubble {
  max-width: 85%;
  padding: var(--space-s) var(--space-m);
  border-radius: var(--radius-lg);
  /* Streamed deltas arrive with real newlines and are set via textContent. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.bubble.user {
  align-self: flex-end;
  background: var(--primary);
  color: #ffffff;
}

.bubble.assistant {
  align-self: flex-start;
  background: var(--surface);
}

.bubble img {
  display: block;
  max-width: 100%;
  margin-top: var(--space-s);
  border-radius: var(--radius-sm);
}

.toolline {
  align-self: flex-start;
  max-width: 100%;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.notice {
  align-self: center;
  max-width: 90%;
  text-align: center;
  font-size: 13px;
  color: var(--text-secondary);
}

.notice.error {
  color: var(--red);
}

/* A rich surface the phone can't draw (design §16). Styled as a real object in
   the transcript rather than an error — nothing went wrong, it is just
   elsewhere. */
.notice.surface {
  padding: var(--space-s) var(--space-m);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  text-align: left;
}

.notice.surface strong {
  color: var(--text-primary);
  font-weight: 600;
}

/* ── Turn activity ("Thinking…") ── */

.activity {
  display: flex;
  gap: var(--space-s);
  align-items: center;
  padding: var(--space-xs) 0;
  font-size: 14px;
  color: var(--text-muted);
}

.activity-elapsed {
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  opacity: 0.8;
}

.spinner {
  flex: none;
  width: 14px;
  height: 14px;
  border: 2px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
}

@media (prefers-reduced-motion: no-preference) {
  .spinner {
    animation: spin 700ms linear infinite;
  }

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

/* Without motion the ring alone reads as a stray dot, so make it a filled,
   clearly-deliberate marker instead. */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    border-color: var(--primary);
    background: var(--primary-subtle);
  }
}

/* ── Markdown inside assistant bubbles ── */

.bubble.assistant > :first-child {
  margin-top: 0;
}

.bubble.assistant > :last-child {
  margin-bottom: 0;
}

.bubble.assistant p {
  margin: var(--space-s) 0;
}

.bubble.assistant h3,
.bubble.assistant h4,
.bubble.assistant h5,
.bubble.assistant h6 {
  margin: var(--space-m) 0 var(--space-s);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
}

.bubble.assistant ul,
.bubble.assistant ol {
  margin: var(--space-s) 0;
  padding-left: var(--space-xl);
}

.bubble.assistant li {
  margin: var(--space-xs) 0;
}

.bubble.assistant code {
  padding: 1px 4px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 0.9em;
  overflow-wrap: anywhere;
}

/* Code blocks scroll on their own rather than widening the bubble — a long
   command must never make the whole transcript pan sideways. */
.bubble.assistant pre {
  margin: var(--space-s) 0;
  padding: var(--space-s);
  overflow-x: auto;
  border-radius: var(--radius-sm);
  background: var(--bg);
}

.bubble.assistant pre code {
  padding: 0;
  background: none;
  white-space: pre;
  overflow-wrap: normal;
}

.bubble.assistant blockquote {
  margin: var(--space-s) 0;
  padding-left: var(--space-m);
  border-left: 3px solid var(--border);
  color: var(--text-secondary);
}

.bubble.assistant hr {
  margin: var(--space-m) 0;
  border: 0;
  border-top: 1px solid var(--border);
}

.bubble.assistant a {
  color: var(--primary);
  overflow-wrap: anywhere;
}

.bubble.assistant strong {
  font-weight: 600;
}

/* ── A2UI cards (design §12.1.2) ──
   The desktop draws these as wide two-column tables; at phone width they are
   re-laid-out as stacked blocks. Nothing in here is interactive: 4a ships the
   surfaces read-only, so a card must not look pressable. */

.a2ui {
  align-self: stretch;
  padding: var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  font-size: 13px;
  color: var(--text-secondary);
}

.a2ui-title {
  margin-bottom: var(--space-s);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.a2ui-entry + .a2ui-entry {
  margin-top: var(--space-s);
  padding-top: var(--space-s);
  border-top: 1px solid var(--border);
}

.a2ui-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-s);
}

.a2ui-entry-title {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

/* The desktop inbox card's unread dot, as a leading marker that costs no
   layout width. */
.a2ui-entry.unread .a2ui-entry-title::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--space-s);
  border-radius: 50%;
  background: var(--primary);
}

.a2ui-trailing {
  flex: none;
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.a2ui-badge {
  flex: none;
  padding: 0 var(--space-xs);
  border: 1px solid var(--green);
  border-radius: var(--radius-sm);
  color: var(--green);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

.a2ui-line {
  margin: var(--space-xs) 0 0;
  overflow-wrap: anywhere;
}

.a2ui-empty,
.a2ui-footnote {
  margin: var(--space-xs) 0 0;
  color: var(--text-muted);
}

.a2ui-footnote {
  margin-top: var(--space-s);
  font-size: 12px;
  font-style: italic;
}

/* Label above value rather than beside it: a reference range plus a unit does
   not fit next to an analyte name on a phone, and wrapping a two-column grid
   reads worse than stacking it. */
.a2ui-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-xs) var(--space-s);
  margin: var(--space-xs) 0 0;
}

.a2ui-fields dt {
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.a2ui-fields dd {
  margin: 0;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.a2ui-fields dd.alert {
  color: var(--red);
  font-weight: 600;
}

.a2ui-note {
  display: block;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 400;
}

.a2ui-pill {
  display: inline-block;
  margin-top: var(--space-s);
  padding: 1px var(--space-s);
  border: 1px solid;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 500;
}

.a2ui-pill.info {
  border-color: var(--primary);
  color: var(--primary);
}

.a2ui-pill.success {
  border-color: var(--green);
  color: var(--green);
}

.a2ui-pill.error {
  border-color: var(--red);
  color: var(--red);
}

/* ── Canvas cards (design §12.1.3) ──
   Closed until tapped: opening is what pulls the bytes over the channel, so a
   transcript full of canvases costs nothing until one is read. */

.canvas {
  align-self: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.canvas-head {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
}

.canvas-kind {
  flex: none;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.canvas-name {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.canvas-action {
  flex: none;
  /* 44px is the smallest reliably tappable target on a phone. */
  min-height: 44px;
  padding: 0 var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--primary);
  font: inherit;
  font-size: 13px;
}

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

.canvas-body:empty {
  display: none;
}

/* Fixed aspect, never auto-height: the frame is an opaque origin and cannot be
   measured from the parent, and the postMessage workaround needs script inside
   the untrusted document (design §12.1.3). The MCP-UI frame shares this for the
   same reason — on the desktop a panel sizes itself through `ui/sizeChanged`
   over the webview bridge, which is exactly the channel that does not exist
   here. */
.canvas-frame,
.canvas-image,
.canvas-text,
.mcpui-frame {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 0;
  border-top: 1px solid var(--border);
  /* The frame is an untrusted document; a white base keeps an unstyled HTML
     report legible instead of black-on-black under the dark theme. */
  background: #fff;
}

.canvas-image {
  aspect-ratio: auto;
  max-height: 60vh;
  object-fit: contain;
  background: var(--surface-elevated);
}

.canvas-text {
  margin: 0;
  padding: var(--space-m);
  aspect-ratio: auto;
  max-height: 50vh;
  overflow: auto;
  background: var(--surface);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 12px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.canvas-loading,
.canvas-error {
  margin: 0;
  padding: 0 var(--space-m) var(--space-s);
  font-size: 13px;
  color: var(--text-muted);
}

.canvas-error {
  color: var(--red);
}

/* Expand toggles a class on the card rather than moving the frame, so the
   content is never reloaded.

   The stacking order below the sheet and the overlay is deliberate and is the
   reason those two gained explicit z-indexes: an expanded canvas lives inside
   #chat, which precedes both in the DOM, so without them a "Connection lost"
   overlay would paint *behind* a fullscreen canvas. */
.canvas.expanded,
.mcpui.expanded {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  border: 0;
  border-radius: 0;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

.canvas.expanded .canvas-body,
.mcpui.expanded .mcpui-body {
  flex: 1;
  min-height: 0;
}

.canvas.expanded .canvas-frame,
.canvas.expanded .canvas-image,
.canvas.expanded .canvas-text,
.mcpui.expanded .mcpui-frame {
  height: 100%;
  max-height: none;
  aspect-ratio: auto;
}

/* ── MCP-UI cards (design §12.1.4) ──
   Open on arrival, unlike a canvas card: the panel content rode in on the tool
   result, so there is nothing to fetch and nothing a tap would save. Shares the
   canvas card's shell and the fixed-aspect frame rules above. */

.mcpui {
  align-self: stretch;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}

.mcpui-head {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
}

.mcpui-kind {
  flex: none;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* Server + `ui://` resource. Monospace and clipped to one line: it is
   provenance, not prose, and it can be long. */
.mcpui-name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mcpui-action {
  flex: none;
  min-height: 44px;
  padding: 0 var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--primary);
  font: inherit;
  font-size: 13px;
}

.mcpui-error {
  margin: 0;
  padding: 0 var(--space-m) var(--space-s);
  font-size: 13px;
  color: var(--red);
}

/* The panel's buttons are inert here (the frame runs no script), so the note
   below it is the only thing that says why. Not muted into invisibility. */
.mcpui-note {
  margin: 0;
  padding: var(--space-s) var(--space-m);
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}

.mcpui.expanded .mcpui-note {
  flex: none;
}

/* A `text/uri-list` panel: the destination as a tap target, since the page
   cannot frame a remote origin (design §12.1.4). */
.mcpui-link {
  display: block;
  min-height: 44px;
  padding: var(--space-m);
  border-top: 1px solid var(--border);
  color: var(--primary);
  font-size: 15px;
  overflow-wrap: anywhere;
}

/* ── Approval / turn-limit cards ── */

.cards:empty {
  display: none;
}

.cards {
  /* Several prompts can queue up (a turn approving tool after tool). Cap the
     stack so it can never grow over the composer and strand the user. */
  max-height: 60vh;
  overflow-y: auto;
  padding: var(--space-s) max(var(--space-l), env(safe-area-inset-right)) 0
    max(var(--space-l), env(safe-area-inset-left));
}

.card {
  padding: var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-elevated);
  box-shadow: 0 6px 16px rgb(0 0 0 / 16%);
}

/* A guard escalation (rm -rf and the like). Approve must not read as the safe
   default here the way a primary-filled button does. */
.card.danger {
  border-color: var(--red);
  box-shadow: 0 6px 16px rgb(0 0 0 / 16%), inset 3px 0 0 var(--red);
}

.card.settled,
.card.busy {
  opacity: 0.72;
}

.card + .card {
  margin-top: var(--space-s);
}

.card .guard {
  margin: var(--space-s) 0 0;
  font-size: 13px;
  color: var(--yellow);
}

.card.danger .guard {
  color: var(--red);
}

/* The one-line "what this actually does". Deliberately the most legible thing
   on the card — it is what the decision is made on. */
.card .summary {
  margin: var(--space-s) 0 0;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text);
  overflow-wrap: anywhere;
}

.card .args {
  margin-top: var(--space-s);
}

.card .args summary {
  padding: var(--space-xs) 0;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
}

.card pre {
  margin: var(--space-s) 0 0;
  padding: var(--space-s);
  max-height: 30vh;
  overflow: auto;
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-secondary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.card .waiting {
  margin: var(--space-m) 0 0;
  font-size: 13px;
  font-style: italic;
  color: var(--text-muted);
}

.card .outcome {
  margin: var(--space-m) 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-secondary);
}

.card .failure {
  margin: var(--space-s) 0 0;
  font-size: 13px;
  color: var(--red);
}

.card .actions {
  display: flex;
  gap: var(--space-s);
  margin-top: var(--space-m);
}

.card .actions button {
  flex: 1;
  min-height: 44px; /* Comfortable target for a gloved clinic hand. */
  padding: 0 var(--space-m);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}

.card .actions .approve {
  border: 1px solid var(--primary);
  background: var(--primary);
  color: #ffffff;
}

/* "Run anyway" on a guard-flagged call. Same position as Approve so muscle
   memory still lands on the right button, but it stops looking like the
   recommended one. */
.card .actions .approve.danger {
  border-color: var(--red);
  background: transparent;
  color: var(--red);
}

.card .actions .deny {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--red);
}

.card .actions button:disabled {
  cursor: default;
  opacity: 0.5;
}

/* ── Composer ── */

.composer {
  padding: var(--space-s) max(var(--space-l), env(safe-area-inset-right))
    max(var(--space-s), env(safe-area-inset-bottom)) max(var(--space-l), env(safe-area-inset-left));
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.chips:empty {
  display: none;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-bottom: var(--space-s);
}

.chip {
  padding: var(--space-xs) var(--space-s);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg);
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
}

.composer-row {
  display: flex;
  align-items: flex-end;
  gap: var(--space-s);
}

.input {
  flex: 1;
  min-width: 0;
  /* Grows with the message, then scrolls — a 40-line dictation dump must not
     push the send button off screen. */
  max-height: 30vh;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg);
  color: var(--text-primary);
  font-family: inherit;
  font-size: 16px;
  line-height: 1.45;
  resize: none;
  field-sizing: content;
}

.input:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}

.iconbutton {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--bg);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 18px;
  cursor: pointer;
  user-select: none;
}

.iconbutton.primary {
  border-color: var(--primary);
  background: var(--primary);
  color: #ffffff;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── Session picker sheet ── */

.sheet {
  position: fixed;
  inset: 0;
  /* Above an expanded canvas card (z-index 5), which sits earlier in the DOM. */
  z-index: 10;
  display: flex;
  align-items: flex-end;
  background: rgb(0 0 0 / 45%);
}

.sheet[hidden] {
  display: none;
}

.sheet-panel {
  width: 100%;
  max-height: 70dvh;
  display: flex;
  flex-direction: column;
  padding: var(--space-l) max(var(--space-l), env(safe-area-inset-right))
    max(var(--space-l), env(safe-area-inset-bottom))
    max(var(--space-l), env(safe-area-inset-left));
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.sheet-head {
  display: flex;
  align-items: center;
  gap: var(--space-m);
  margin-bottom: var(--space-m);
}

.sheet-title {
  flex: 1;
  margin: 0;
  font-size: 17px;
  font-weight: 600;
}

.sheet-empty {
  margin: 0;
  color: var(--text-muted);
}

/* ── Share sheet ── */

.sheet-body {
  margin: 0 0 var(--space-m);
  font-size: 14px;
  color: var(--text-secondary);
}

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

.sheet-body:empty {
  display: none;
}

.sheet-actions {
  display: flex;
  gap: var(--space-s);
}

.sheet-actions button {
  flex: 1;
  min-height: 44px; /* Comfortable target for a gloved clinic hand. */
  padding: 0 var(--space-m);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}

.sheet-actions .primary {
  border: 1px solid var(--primary);
  background: var(--primary);
  color: #ffffff;
}

.sheet-actions .secondary {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-primary);
}

.session-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.session-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Comfortable target for a gloved clinic hand, same as the approval buttons. */
  min-height: 44px;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-primary);
  font-family: inherit;
  font-size: 15px;
  text-align: left;
  cursor: pointer;
}

.session-row.current {
  border-color: var(--primary);
  background: var(--primary-subtle);
}

.session-title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.session-meta {
  font-size: 12px;
  color: var(--text-muted);
}

.iconbutton.small {
  width: 32px;
  height: 32px;
  font-size: 15px;
}

.iconbutton.small[hidden] {
  display: none;
}

/* ── Blocking overlay (connecting / pending admit / unrecoverable) ── */

.overlay {
  position: fixed;
  inset: 0;
  /* Top of the stack: a connection-lost overlay must never be hidden behind an
     expanded canvas card. */
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl);
  background: var(--bg);
  /* `hidden` must win over the flex display above — App toggles .hidden. */
}

.overlay[hidden] {
  display: none;
}

.overlay-card {
  max-width: 22rem;
  text-align: center;
}

.overlay-title {
  margin: 0;
  font-size: 20px;
  font-weight: 600;
}

.overlay-detail {
  margin: var(--space-m) 0 0;
  color: var(--text-secondary);
}

@media (prefers-reduced-motion: no-preference) {
  .bubble,
  .card {
    animation: rise 120ms ease-out;
  }

  @keyframes rise {
    from {
      opacity: 0;
      transform: translateY(4px);
    }
  }
}
