/* SDRCenter — one stylesheet for the entry screens and the app.
 *
 * The server-rendered sign-in / invite pages (src/authpage.ts) and the SPA
 * both link this file, so the thing you sign in to and the thing you sign in
 * from cannot drift into looking like two different products.
 *
 * Desktop-first, Inter, Wishpond blue accent — spec §9. */

/* ── Tokens ─────────────────────────────────────────────────────────────── */

:root {
  /* The dark rail. Two stops so it reads as a surface, not a flat block. */
  --rail-bg: #141b2d;
  --rail-bg-2: #0e1420;
  --rail-fg: #e9edf6;
  --rail-muted: #838ea6;
  --rail-hover: #1c2539;
  --rail-active: #222d46;
  --rail-line: #232d44;

  --canvas: #f4f7fc;
  --surface: #ffffff;
  --surface-2: #f9fbfe;

  --ink: #131a2a;
  --ink-2: #46506a;
  --muted: #79849c;
  --faint: #9aa4ba;

  --line: #e4e9f2;
  --line-2: #eef2f8;

  --accent: #2f6fed;
  --accent-ink: #1c4fc0;
  --accent-wash: #eaf1ff;
  --accent-ring: rgba(47, 111, 237, 0.28);

  /* Three status treatments, and only three — see PILLS in app.js. */
  --ok-bg: #e8f7ef;
  --ok-fg: #0b7a43;
  --ok-line: #c6e9d6;
  --warn-bg: #fdf1e0;
  --warn-fg: #9c5c07;
  --warn-line: #f4ddb9;
  --dead-bg: #f4eeee;
  --dead-fg: #975252;
  --dead-line: #e8d7d7;

  --r-xs: 5px;
  --r-sm: 7px;
  --r: 10px;
  --r-lg: 14px;
  --r-pill: 999px;

  --shadow-xs: 0 1px 2px rgba(19, 26, 42, 0.05);
  --shadow-sm: 0 1px 3px rgba(19, 26, 42, 0.07), 0 1px 2px rgba(19, 26, 42, 0.04);
  --shadow: 0 8px 24px rgba(19, 26, 42, 0.08);
  --shadow-card: 0 24px 60px rgba(6, 11, 22, 0.38);

  --rail-w: 244px;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* ── Reset ──────────────────────────────────────────────────────────────── */

*,
*::before,
*::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, dl, dd, ul { margin: 0; }
ul { list-style: none; padding: 0; }
button { font: inherit; color: inherit; }

/* One focus treatment everywhere. UX-SHELL-002 requires the sign-out control
 * to be keyboard-reachable, which is worth nothing if focus is invisible. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

/* ═══════════════════════════════════════════════════════════════════════════
   ENTRY SCREENS — mockup B. Rendered by the Worker, not the SPA.
   ═══════════════════════════════════════════════════════════════════════════ */

.auth-body {
  background:
    radial-gradient(1100px 620px at 14% -12%, rgba(47, 111, 237, 0.20) 0%, transparent 62%),
    linear-gradient(145deg, #172136 0%, #0d1220 55%, #0a0e1a 100%);
  background-color: #0b1120;
  min-height: 100%;
}

.auth-page {
  min-height: 100vh;
  max-width: 1120px;
  margin: 0 auto;
  padding: 64px 40px;
  display: grid;
  grid-template-columns: 1fr 440px;
  gap: 72px;
  align-items: center;
}

/* Marketing column */
.auth-pitch { color: #fff; max-width: 460px; }

.auth-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 48px;
}

.auth-logo,
.rail-logo {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: linear-gradient(140deg, var(--accent) 0%, #5a92ff 100%);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 14px rgba(47, 111, 237, 0.45);
}

.auth-wordmark,
.rail-wordmark {
  display: flex;
  flex-direction: column;
  font-size: 15px;
  font-weight: 650;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

.auth-wordmark small,
.rail-wordmark small {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--rail-muted);
}

.auth-pitch h1 {
  font-size: 44px;
  line-height: 1.1;
  letter-spacing: -0.028em;
  font-weight: 680;
  margin-bottom: 32px;
}

.auth-ticks { display: grid; gap: 14px; }

.auth-ticks li {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #aeb9cf;
  font-size: 15px;
}

.tick {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgba(47, 111, 237, 0.16);
  border: 1px solid rgba(90, 146, 255, 0.4);
  position: relative;
}

.tick::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 4px;
  width: 5px;
  height: 9px;
  border: solid #7ea8ff;
  border-width: 0 1.8px 1.8px 0;
  transform: rotate(45deg);
}

/* The card */
.auth-cardwrap { display: flex; justify-content: center; }

.auth-card {
  width: 100%;
  background: var(--surface);
  border-radius: 16px;
  padding: 36px 34px;
  box-shadow: var(--shadow-card);
}

.auth-card h2 {
  font-size: 22px;
  font-weight: 660;
  letter-spacing: -0.018em;
  margin-bottom: 8px;
}

.auth-sub {
  color: var(--ink-2);
  font-size: 14.5px;
  margin-bottom: 24px;
}

.auth-badge {
  display: inline-block;
  margin-bottom: 14px;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  background: var(--accent-wash);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.auth-badge.is-dead {
  background: var(--dead-bg);
  color: var(--dead-fg);
}

.auth-error {
  display: flex;
  gap: 10px;
  padding: 12px 14px;
  margin-bottom: 20px;
  border-radius: var(--r);
  background: #fdf2f2;
  border: 1px solid #f1d9d9;
  color: #8f3c3c;
  font-size: 13.5px;
}

.auth-error::before {
  content: "!";
  flex: none;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #d98686;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
}

/* The bordered detail box — invited address, client, role, expiry. */
.auth-details {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface-2);
  padding: 4px 16px;
  margin-bottom: 24px;
}

.kv {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-2);
}

.kv:last-child { border-bottom: 0; }

.kv dt {
  color: var(--muted);
  font-size: 12.5px;
  flex: none;
}

.kv dd {
  font-weight: 560;
  font-size: 13.5px;
  text-align: right;
  word-break: break-word;
}

.kv dd.muted { color: var(--muted); font-weight: 500; }

.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 11px;
  width: 100%;
  padding: 12px 16px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  color: #202a44;
  font-size: 15px;
  font-weight: 560;
  text-decoration: none;
  box-shadow: var(--shadow-xs);
  transition: background 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
}

.btn-google:hover {
  background: var(--surface-2);
  border-color: #d3dbe8;
  box-shadow: var(--shadow-sm);
}

.g-mark { width: 18px; height: 18px; flex: none; }

.auth-foot {
  margin-top: 18px;
  color: var(--faint);
  font-size: 12.5px;
  line-height: 1.55;
}

/* ═══════════════════════════════════════════════════════════════════════════
   APP SHELL — the dark rail and the light content column.
   ═══════════════════════════════════════════════════════════════════════════ */

.app {
  display: grid;
  grid-template-columns: var(--rail-w) 1fr;
  min-height: 100vh;
}

/* ── Left rail ──────────────────────────────────────────────────────────── */

.rail {
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--rail-bg) 0%, var(--rail-bg-2) 100%);
  color: var(--rail-fg);
  padding: 22px 14px 14px;
  position: sticky;
  top: 0;
  height: 100vh;
}

.rail-brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 0 8px 22px;
}

.rail-group { margin-bottom: 22px; }

.rail-label {
  padding: 0 10px 8px;
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #6c768d;
}

.rail-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  color: #b6c0d4;
  font-size: 14px;
  font-weight: 520;
  text-decoration: none;
  cursor: pointer;
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  transition: background 0.13s ease, color 0.13s ease;
}

.rail-item:hover { background: var(--rail-hover); color: var(--rail-fg); }

.rail-item.is-active {
  background: var(--rail-active);
  color: #fff;
  font-weight: 580;
  box-shadow: inset 2px 0 0 var(--accent);
}

.rail-item .ico { flex: none; width: 17px; height: 17px; opacity: 0.85; }
.rail-item.is-active .ico { opacity: 1; }
.rail-item .lbl { flex: 1; }

/* The live conversation count — fed by the list endpoint's `total`. */
.rail-count {
  flex: none;
  min-width: 26px;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  background: #313d58;
  color: #d5deef;
  font-size: 11px;
  font-weight: 620;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.rail-item.is-active .rail-count { background: #3c4b6b; color: #fff; }

/* The badge exists from the first paint so the first list response has
 * somewhere to write, but an empty pill is just a smudge. */
.rail-count:empty { display: none; }

/* PEOPLE, drawn in five mockups with no screen and no endpoint behind it.
 * Deliberately inert, and it has to LOOK deliberate rather than broken
 * (UX-SHELL-003): dimmed, no hover response, default cursor. */
.rail-item.is-inert {
  color: #5b6478;
  cursor: default;
  opacity: 0.62;
}

.rail-item.is-inert:hover { background: transparent; color: #5b6478; }

.rail-soon {
  flex: none;
  font-size: 9.5px;
  font-weight: 620;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #5b6478;
  border: 1px solid #333d54;
  border-radius: var(--r-xs);
  padding: 1px 5px;
}

/* ── Rail footer: the user block, and the sign-out control ──────────────── */
/* No mockup draws sign-out; the spec defines the endpoint and UX-SHELL-002
 * puts the control here, on the user block, on every screen. */

.rail-foot {
  margin-top: auto;
  border-top: 1px solid var(--rail-line);
  padding-top: 12px;
}

.user-block {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 8px 8px 10px;
  border-radius: var(--r-sm);
}

.avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #33405c;
  color: #dbe4f5;
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.02em;
}

.user-meta { min-width: 0; flex: 1; }

.user-name {
  font-size: 13px;
  font-weight: 580;
  color: var(--rail-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-scope {
  font-size: 11.5px;
  color: var(--rail-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.btn-signout {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: #93a0b8;
  cursor: pointer;
  transition: background 0.13s ease, color 0.13s ease, border-color 0.13s ease;
}

.btn-signout:hover {
  background: #232e46;
  border-color: #33405c;
  color: #fff;
}

.btn-signout .ico { width: 16px; height: 16px; }

/* ── Content column ─────────────────────────────────────────────────────── */

.content {
  min-width: 0;
  padding: 30px 38px 56px;
  max-width: 1440px;
}

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 22px;
}

.page-title {
  font-size: 25px;
  font-weight: 670;
  letter-spacing: -0.024em;
}

.page-sub {
  margin-top: 3px;
  color: var(--muted);
  font-size: 13.5px;
}

/* Which tenant the rows belong to. For a member this is the answer to "whose
 * calls am I looking at", which is exactly what /api/me's client_name is for;
 * for an admin it reads "All clients".
 *
 * A LABEL BY DEFAULT, A CONTROL WHERE IT EARNS IT. GET /api/clients exists
 * now, so on the conversations list an admin with more than one tenant gets
 * the switcher mockups A and D draw (.scope-chip-live). Everywhere else --
 * every member, every single-client install, and /invites, whose content does
 * not narrow by client -- it stays the chip it was. See scopeChip() in
 * public/app.js for why each of those three conditions is there. */
.scope-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  font-size: 13.5px;
  font-weight: 560;
  white-space: nowrap;
}

.scope-chip .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

.scope-chip .scope-label {
  color: var(--muted);
  font-weight: 500;
  font-size: 12px;
}

/* The switcher sits INSIDE the chip rather than replacing it, so the admin's
 * eye lands in the same place whether or not the control is there. The chip
 * keeps its own border and the select drops hers, or the two nest visibly. */
.scope-chip-live { padding: 4px 6px 4px 13px; gap: 7px; }

.select-chip select {
  padding: 4px 28px 4px 7px;
  border: none;
  background: transparent;
  box-shadow: none;
  font-size: 13.5px;
  font-weight: 560;
}

.select-chip .ico { right: 8px; }

/* Focus has to stay visible even though the border is gone -- this is a
 * keyboard-reachable control, not decoration. */
.select-chip select:focus {
  border-radius: 7px;
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* ── Toolbar: search + sort ─────────────────────────────────────────────── */

.toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.search {
  position: relative;
  flex: 1;
  max-width: 560px;
}

.search .ico {
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--faint);
  pointer-events: none;
}

.search input {
  width: 100%;
  padding: 10px 36px 10px 38px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  font: inherit;
  color: var(--ink);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}

.search input::placeholder { color: var(--faint); }

.search input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.search-clear {
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: #e8edf6;
  color: var(--muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}

.search-clear:hover { background: #dde4f0; color: var(--ink-2); }

.select {
  position: relative;
  flex: none;
}

.select select {
  appearance: none;
  padding: 10px 34px 10px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  font: inherit;
  font-weight: 540;
  color: var(--ink);
  cursor: pointer;
}

.select select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.select .ico {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 13px;
  height: 13px;
  color: var(--muted);
  pointer-events: none;
}

/* The result / status line. Mockup C's "Searching 214 conversations…" and
 * mockup D's "214 conversations · showing 1–8" are the same element. */
.result-line {
  margin-bottom: 12px;
  color: var(--muted);
  font-size: 13px;
  min-height: 20px;
  font-variant-numeric: tabular-nums;
}

.result-line.is-searching { color: var(--accent-ink); }

.result-line .spin {
  display: inline-block;
  width: 11px;
  height: 11px;
  margin-right: 7px;
  vertical-align: -1px;
  border: 2px solid var(--accent-ring);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

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

/* ── The table ──────────────────────────────────────────────────────────── */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.tbl { width: 100%; border-collapse: collapse; }

.tbl th {
  padding: 11px 18px;
  text-align: left;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #8792a8;
  white-space: nowrap;
}

.tbl td {
  padding: 13px 18px;
  border-bottom: 1px solid var(--line-2);
  vertical-align: middle;
}

.tbl tbody tr:last-child td { border-bottom: 0; }

.tbl tbody tr { transition: background 0.11s ease; }
.tbl tbody tr.row:hover { background: #f7faff; cursor: pointer; }

.col-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Lead cell: name over email. GAP-4 — when no lead_name is stored yet the
 * EMAIL is promoted to the primary line and the sub-line says so, rather
 * than the row rendering with a blank where the person should be. */
.lead-name {
  font-weight: 580;
  color: var(--ink);
  letter-spacing: -0.006em;
}

.lead-sub {
  margin-top: 1px;
  font-size: 12.5px;
  color: var(--muted);
}

.lead-sub.is-pending { color: var(--faint); font-style: italic; }

.cell-muted { color: var(--faint); }
.cell-ae { color: var(--ink-2); }

/* ── Status pills ───────────────────────────────────────────────────────── */
/* THREE treatments for five statuses. The mapping lives in PILLS in app.js;
 * there is no fourth. */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3.5px 10px 3.5px 8px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.pill-ready { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-line); }
.pill-pending { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-line); }
.pill-dead { background: var(--dead-bg); color: var(--dead-fg); border-color: var(--dead-line); }

/* ── Skeletons — mockup C ───────────────────────────────────────────────── */
/* These hold the table's height so the page does not jump when results land,
 * which is the whole point of the state (UX-C-001). */

.sk {
  display: block;
  height: 10px;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, #eef1f7 25%, #e3e8f1 37%, #eef1f7 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease-in-out infinite;
}

.sk + .sk { margin-top: 7px; }
.sk-pill { height: 20px; width: 108px; border-radius: var(--r-pill); }

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

@media (prefers-reduced-motion: reduce) {
  .sk, .result-line .spin { animation: none; }
}

/* ── Empty state — mockup A ─────────────────────────────────────────────── */

.empty {
  padding: 58px 32px 64px;
  text-align: center;
}

.empty-ico {
  width: 62px;
  height: 62px;
  margin: 0 auto 20px;
  color: #c2d7fb;
}

.empty h3 {
  font-size: 19px;
  font-weight: 640;
  letter-spacing: -0.018em;
  margin-bottom: 7px;
}

.empty p {
  color: var(--muted);
  font-size: 14px;
  max-width: 460px;
  margin: 0 auto;
}

/* A demo call ends → The transcript lands → It shows up here */
.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
  max-width: 720px;
  margin: 32px auto 0;
  text-align: left;
}

.step {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 15px 16px;
}

.step-n {
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  margin-bottom: 9px;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 680;
}

.step h4 { font-size: 13.5px; font-weight: 600; margin-bottom: 3px; }
.step p { font-size: 12.5px; color: var(--muted); max-width: none; }

/* ── Pagination ─────────────────────────────────────────────────────────── */

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

.pager-info { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }

.pager-controls { display: flex; align-items: center; gap: 6px; }

.pg {
  min-width: 34px;
  height: 34px;
  padding: 0 11px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 560;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  transition: background 0.13s ease, border-color 0.13s ease, color 0.13s ease;
}

.pg:hover:not(:disabled):not(.is-current) {
  background: var(--surface-2);
  border-color: #d3dbe8;
  color: var(--ink);
}

.pg.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  cursor: default;
}

.pg:disabled { opacity: 0.42; cursor: default; }

.pg-gap { color: var(--faint); padding: 0 2px; user-select: none; }

/* ── Misc ───────────────────────────────────────────────────────────────── */

.banner {
  display: flex;
  gap: 10px;
  padding: 13px 16px;
  margin-bottom: 16px;
  border-radius: var(--r);
  background: #fdf2f2;
  border: 1px solid #f1d9d9;
  color: #8f3c3c;
  font-size: 13.5px;
}

.stub {
  padding: 56px 32px;
  text-align: center;
  color: var(--muted);
}

.stub h3 { font-size: 17px; font-weight: 620; color: var(--ink); margin-bottom: 6px; }

.boot {
  display: grid;
  place-items: center;
  min-height: 100vh;
  color: var(--faint);
}

@media (max-width: 1080px) {
  .content { padding: 24px 22px 44px; }
  .steps { grid-template-columns: 1fr; }
  .auth-page { grid-template-columns: 1fr; gap: 44px; padding: 48px 24px; }
  .auth-cardwrap { justify-content: flex-start; max-width: 440px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   CONVERSATION DETAIL — mockup E, and mockup G's four degraded states.
   ══════════════════════════════════════════════════════════════════════════ */

.det-head { align-items: flex-start; }

.crumb {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-bottom: 7px;
  margin-left: -4px;
  padding: 2px 7px 2px 3px;
  border-radius: var(--r-sm);
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 560;
  text-decoration: none;
}

.crumb:hover { background: #e9eef8; color: var(--ink-2); }
.crumb .ico { width: 14px; height: 14px; }

.head-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

/* Mockup E: summary over transcript on the left, recording over call details
   on the right. The side column is fixed so the transcript — the thing people
   came to read — takes every pixel the window adds. */
/* `stretch`, NOT `start`. The call-details rail is a fixed ten fields tall
   whatever the call was, while the main column swings from a 20-minute
   transcript down to two degraded boxes — so with `start` the left column
   ended up to 390px shorter than the right on exactly the pages mockup G
   draws. The transcript panel absorbs the difference instead (see
   `.panel-transcript` below), which is also what it should do: the reading
   pane wants every pixel going spare. */
.det-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 352px;
  gap: 18px;
  align-items: stretch;
}

.det-main,
.det-side {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--line-2);
  background: var(--surface-2);
}

.panel-label {
  font-size: 11px;
  font-weight: 680;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}

.panel-note {
  font-size: 12px;
  color: var(--faint);
  font-weight: 500;
  white-space: nowrap;
}

/* Segmented filter in a panel head — the invitations list's All / Outstanding
 * / Accepted / Revoked. Real <button>s with aria-pressed rather than a
 * <select>: there are four values, they are the primary way this panel is
 * navigated, and one click should not cost a menu.
 *
 * `margin-right: auto` pushes the count note to the far edge, so the head
 * reads label — filter ......... count. */
.seg {
  display: inline-flex;
  margin-right: auto;
  padding: 2px;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--surface);
}

.seg-btn {
  appearance: none;
  border: none;
  background: transparent;
  border-radius: 7px;
  padding: 4px 11px;
  font: inherit;
  font-size: 12px;
  font-weight: 580;
  color: var(--muted);
  cursor: pointer;
}

.seg-btn:hover { color: var(--ink); }

.seg-btn[aria-pressed="true"] {
  background: var(--accent);
  color: #fff;
  box-shadow: var(--shadow-xs);
}

.seg-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.panel-body { padding: 18px; }

.summary-text {
  font-size: 14px;
  line-height: 1.68;
  color: var(--ink-2);
}

/* ── The degraded treatment — mockup G ─────────────────────────────────────
   ONE box for all four states, so a page showing every one of them reads as
   four deliberate absences and not as four different things going wrong
   (E2E-036, UX-G-002). Dashed, because a dashed border is the established
   "nothing here yet" idiom and a solid one would read as a real surface.
   It deliberately contains no control: nothing here can be actioned. */

.degraded {
  border: 1px dashed #d3dbe9;
  border-radius: var(--r);
  background: #fbfcfe;
  padding: 18px 18px 19px;
  text-align: center;
}

.degraded-ico {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  margin: 0 auto 10px;
  border-radius: 50%;
  background: #eef2f9;
  color: var(--faint);
}

.degraded-title {
  font-size: 13.5px;
  font-weight: 620;
  color: var(--ink-2);
  margin-bottom: 5px;
}

/* Mockup G draws the transcript's empty state as a FULL-SIZE panel, not a
   notice. That is not decoration: the transcript panel is the tall one on
   this page, and letting it collapse to three lines leaves the main column
   ~300px shorter than the call-details rail beside it — which is what the
   all-four-degraded page (E2E-036) actually looked like before this rule. */
.degraded-tall {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 300px;
}

.degraded-body {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
  max-width: 46ch;
  margin: 0 auto;
}

/* The side column is narrow; its boxes get less air so the rail does not
   grow taller than the summary it sits beside. */
.degraded-quiet { padding: 15px 14px 16px; }

/* Terminal, not pending. Tinted with the same desaturated red the
   `transcript_unavailable` pill uses, so the panel and the pill in the header
   are visibly the same fact. */
.degraded-dead {
  border-color: var(--dead-line);
  background: #fdf8f8;
}

.degraded-dead .degraded-title { color: var(--dead-fg); }
.degraded-dead .degraded-ico { background: #f8ebeb; color: #bf8080; }

/* ── The top alert — mockup G ──────────────────────────────────────────── */

.alert {
  display: flex;
  gap: 13px;
  padding: 15px 18px 16px;
  margin-bottom: 18px;
  border-radius: var(--r);
  background: #fdf3f3;
  border: 1px solid var(--dead-line);
  color: var(--dead-fg);
}

.alert-ico { flex: none; margin-top: 1px; }

.alert-title {
  font-size: 13.5px;
  font-weight: 640;
  margin-bottom: 3px;
}

.alert-body {
  font-size: 12.5px;
  line-height: 1.62;
  color: #8a5a5a;
  max-width: 88ch;
}

/* An AI-only call is an OUTCOME, not a hole in the data, so it is badged
   rather than muted like the fields that are genuinely absent (E2E-009). */
.badge-ai {
  display: inline-block;
  padding: 2.5px 9px;
  border-radius: var(--r-pill);
  background: #f0ecfb;
  border: 1px solid #ddd4f4;
  color: #5b46a8;
  font-size: 11.5px;
  font-weight: 600;
}

/* ── The player ────────────────────────────────────────────────────────────
   Real playback over a real <audio> element; this is only its chrome. */

.player {
  display: flex;
  align-items: center;
  gap: 13px;
}

.play-btn {
  flex: none;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 8px var(--accent-ring);
  transition: background 0.13s ease, transform 0.13s ease;
}

.play-btn:hover { background: var(--accent-ink); }
.play-btn:active { transform: scale(0.95); }
.play-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The waveform is the seek bar. Clicking it seeks, which is what exercises
   §4.6's range support rather than merely asserting it exists. */
.wave {
  position: relative;
  flex: 1;
  height: 38px;
  min-width: 0;
  cursor: pointer;
  border-radius: var(--r-xs);
}

.wave:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.wave-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 2px;
}

.wave-bar {
  flex: 1;
  min-width: 2px;
  border-radius: 1px;
  background: #d5dded;
}

/* The played portion: a full-width copy of the same bars, revealed by
   clip-path rather than by width.
   CLIP, NOT WIDTH. An overlay narrowed with `width` takes its bars with it,
   so the accented copy compresses instead of unveiling and the two layers
   stop lining up the moment playback starts. clip-path leaves the geometry
   alone and hides part of it. */
.wave-played {
  position: absolute;
  inset: 0;
  clip-path: inset(0 100% 0 0);
}

.wave-played .wave-bar { background: var(--accent); }

.player-times {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  font-weight: 560;
}

.player-times .t-sep { color: var(--faint); }
#t-tot { color: var(--muted); font-weight: 500; }

audio { display: none; }

/* ── Call details ──────────────────────────────────────────────────────── */

.kv-list .kv:first-child { padding-top: 0; }
.kv-list .kv dt { font-size: 12px; }
.kv-list .kv dd { font-size: 13px; }

/* ── The transcript ────────────────────────────────────────────────────────
   Scrolls under a header that stays put (UX-E-001). The header is pinned
   STRUCTURALLY — the scroll container is `.tr-body`, and the head sits
   outside it — rather than with `position: sticky`, which inside `.panel`'s
   `overflow: hidden` would have been a no-op dressed up as an effect. */

/* The panel that takes up the slack in the column. Its inner body and the
   scroller inside that have to flex too, or the growth stops at the panel
   border and leaves the gap where it was. */
.det-main > .panel-transcript {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.panel-transcript > .panel-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 0;
}

/* Degraded, the box stretches too rather than sitting in a tall empty panel. */
.panel-transcript .degraded { flex: 1 1 auto; }

/* The slot the transcript is painted into sits BETWEEN the panel body and
   the scroller, so it has to carry the flex chain or the growth stops dead
   at it — which showed up as ~390px of blank white inside the panel on a
   short transcript, with the footer stranded halfway up. */
.tr-slot {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.tr-body {
  position: relative;
  padding: 18px;
  /* A DEFINITE height, then `flex-grow` on top of it — not `max-height`.
     The definite height is what the grid row measures, so a 20-minute
     transcript cannot stretch the page to its own length; the grow factor is
     what lets the very same box expand past it when the call-details rail
     beside it is taller. `max-height` caps growth as well as measurement, so
     it fixes the page height and leaves the column gap exactly where it was. */
  height: 420px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 13.5px;
  line-height: 1.72;
  color: var(--ink-2);
}

/* Expanded, the body gets more room before it starts scrolling — the reader
   asked for the whole thing. */
.tr-body.is-full { height: 560px; }

/* Too short to collapse: measured at its own height so the panel does not
   open a 560px box around three lines, but still allowed to grow into
   whatever slack the column has. */
.tr-body.is-short { height: auto; min-height: 120px; }

/* The collapsed body does not end at a hard edge: a fade says "this
   continues" in a way a flat cut does not. Non-interactive, so it cannot
   swallow a click meant for the text underneath. */
.tr-fade {
  position: sticky;
  bottom: -18px;
  display: block;
  height: 54px;
  margin: -54px -18px -18px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff 88%);
}

.tr-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 18px;
  border-top: 1px solid var(--line-2);
  background: var(--surface-2);
}

/* COUNTS CHARACTERS. Never minutes — see COLLAPSE_CHARS in detail.js. */
.tr-count {
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.btn-link {
  border: 0;
  background: none;
  padding: 2px 4px;
  margin: -2px -4px;
  border-radius: var(--r-xs);
  color: var(--accent);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.btn-link:hover { color: var(--accent-ink); text-decoration: underline; }
.btn-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Below this the fixed 352px side column stops paying for itself: the
   transcript gets too narrow to read comfortably, so the two columns stack
   and the call details fall under what they describe. */
@media (max-width: 1180px) {
  .det-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── The admin invites screen — mockup F ─────────────────────────────────── */
/* Same two-column grammar as the detail page, mirrored: the form is the fixed
   column and the table takes the remaining width, because a table of email
   addresses is what grows and a form of four fields is not. */

.inv-grid {
  display: grid;
  grid-template-columns: 394px minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}

.inv-right {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.inv-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.fld { display: flex; flex-direction: column; gap: 7px; }

.fld label,
.fld-label {
  font-size: 12.5px;
  font-weight: 620;
  color: var(--ink-2);
  letter-spacing: -0.003em;
}

/* Matches .search input so a field is a field wherever it appears. */
.fld input[type="email"],
.fld input[type="number"] {
  width: 100%;
  padding: 10px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  font: inherit;
  color: var(--ink);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}

.fld input::placeholder { color: var(--faint); }

.fld input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.fld-help,
.fld-foot {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.45;
}

.fld-foot { text-align: center; margin-top: -8px; }

/* The picker fills its column here, unlike the toolbar's sort control. */
.select-block { display: block; width: 100%; }
.select-block select { width: 100%; font-weight: 500; }

#inv-client-other { margin-top: 7px; }

/* ── Role cards ──────────────────────────────────────────────────────────── */
/* Radios, drawn as cards. The DESCRIPTION is the point: "Member" and "Admin"
   alone do not tell an admin that one of the two can invite people and see
   every client, which is the decision they are actually making (UX-F-001). */

.role-cards { display: flex; flex-direction: column; gap: 8px; }

.role-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.13s ease, background 0.13s ease;
}

.role-card:hover { border-color: #cdd8ea; background: var(--surface-2); }

.role-card input {
  appearance: none;
  flex: none;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  border: 1.5px solid #c3cddf;
  border-radius: 50%;
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.13s ease, box-shadow 0.13s ease;
}

.role-card input:checked {
  border-color: var(--accent);
  border-width: 5px;
}

.role-card input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.role-card:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.role-body { display: flex; flex-direction: column; gap: 2px; }

.role-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}

.role-desc {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.4;
}

/* ── The send button ─────────────────────────────────────────────────────── */

.btn-primary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 11px 16px;
  border: 0;
  border-radius: var(--r);
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(28, 79, 192, 0.24);
  transition: background 0.13s ease, box-shadow 0.13s ease;
}

.btn-primary:hover { background: var(--accent-ink); }
.btn-primary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* In flight. It stays readable rather than fading to nothing: the admin has
   to be able to tell a busy button from a broken one. */
.btn-primary:disabled {
  background: #9dbaf3;
  box-shadow: none;
  cursor: progress;
}

.btn-primary .ico { width: 16px; height: 16px; }

/* A 400 belongs AT the form, under the fields it is about — not in the banner
   across the page, which is where decisions go. */
.inv-error {
  padding: 10px 12px;
  border: 1px solid var(--dead-line);
  border-radius: var(--r-sm);
  background: var(--dead-bg);
  color: var(--dead-fg);
  font-size: 12.5px;
  line-height: 1.5;
}

/* ── The banner ──────────────────────────────────────────────────────────── */
/* Mockup F's green confirmation, and the failure that shares its slot. The
   two are the SAME element in two treatments so a failure lands exactly where
   the admin just read a success — a red box somewhere else on the page is a
   box that gets missed. */

.notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid;
  border-radius: var(--r);
  font-size: 13px;
  line-height: 1.55;
  box-shadow: var(--shadow-xs);
}

.notice-ok {
  background: var(--ok-bg);
  border-color: var(--ok-line);
  color: #0a6a3b;
}

.notice-bad {
  background: var(--dead-bg);
  border-color: var(--dead-line);
  color: #8a4a4a;
}

/* THREE treatments, not two, and the third is load bearing. A 502 carrying
   `held: true` is a failed send in which NOBODY LOST ANYTHING -- the link
   already in the recipient's inbox still works. Painting that the same red as
   `held: false`, where the recipient has nothing at all, hides the one
   distinction the flag exists to draw behind a sentence the admin has to read
   to the end of. During a Mailgun outage they will be reading a lot of these. */
.notice-warn {
  background: var(--warn-bg);
  border-color: var(--warn-line);
  color: var(--warn-fg);
}

.notice-ico { flex: none; margin-top: 1px; }

.notice strong { font-weight: 660; }

/* The mail service's own words, kept but subordinated: it is diagnostic, and
   it must not compete with the sentence that says whether anyone is locked
   out. */
.notice-detail {
  display: block;
  margin-top: 5px;
  font-size: 11.5px;
  font-family: var(--mono);
  opacity: 0.72;
}

/* ── The invitations table ───────────────────────────────────────────────── */

.tbl-inv td { vertical-align: middle; }

.col-act { text-align: right; white-space: nowrap; width: 1%; }

.btn-danger { color: var(--dead-fg); }
.btn-danger:hover { color: #7d3b3b; }

.btn-link:disabled {
  color: var(--faint);
  cursor: progress;
  text-decoration: none;
}

.role-tag {
  display: inline-block;
  padding: 2.5px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-2);
}

/* A FOURTH pill treatment, and it is not a conversation status — those are
   the three in PILLS. An expired invitation is not a failure and not a
   warning: it is simply past, and it reads that way (mockup F draws it grey
   beside the amber Pending). */
.pill-muted {
  background: #eef1f6;
  color: #77839b;
  border-color: #e0e5ee;
}

/* The table's empty state sits inside a panel beside a form, not alone on the
   page, so it does not get the list's full-height treatment. */
.empty-sm { padding: 40px 28px 44px; }
.empty-sm .empty-ico { width: 46px; height: 46px; margin-bottom: 14px; }
.empty-sm h3 { font-size: 16px; }
.empty-sm p { font-size: 13px; }

/* The form column stops being worth its fixed width here — below this the
   role cards and the table columns are both squeezed, so they stack. */
@media (max-width: 1180px) {
  .inv-grid { grid-template-columns: minmax(0, 1fr); }
}
