/* ═══════════════════════════════════════════════════════════════
   Relay Me — dashboard stylesheet
   Ported from design/website-3 (slate gradient canvas, translucent
   surfaces, blue-gradient accent, rounded geometry) for the embedded
   SPA shell.

   The class vocabulary is unchanged from the previous (v1) stylesheet —
   the JS in assets/js emits these names and was not rewritten. Only the
   presentation moved. If you are looking for a class here, it exists
   because something in assets/js asks for it.

   Fonts are the system stack on purpose: the dashboard runs under
   `default-src 'self'` (see packages/web/security.go), which blocks
   webfont CDNs. website-3's own fallback chain is what we use.
   ═══════════════════════════════════════════════════════════════ */

/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
  min-height: 100vh;
}
a { color: inherit; text-decoration: none; cursor: pointer; }
img, svg { display: block; max-width: 100%; }
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; }

/* ═══════════════════════════════════════════════════════════════
   Theme tokens
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* Canonical cross-surface foundation. Component-specific aliases below keep
     this stylesheet readable while sharing the product-level vocabulary. */
  --rm-canvas: #1f2733;
  --rm-canvas-raised: #27303c;
  --rm-action: #2f67ff;
  --rm-action-strong: #1f50f2;
  --rm-success: #6ee7a0;
  --rm-warning: #f5b86a;
  --rm-danger: #ff7a7a;
  --rm-text: #e6eaf2;
  --rm-text-secondary: #b6bfce;
  /* canvas */
  --bg:         var(--rm-canvas);
  --page-grad:  linear-gradient(135deg, var(--rm-canvas-raised) 0%, var(--rm-canvas) 100%);

  /* surfaces sit *on* the canvas as translucent white films, so a panel
     inside a panel keeps reading as a distinct layer without new colours */
  --surface:    rgba(255, 255, 255, 0.03);
  --surface-2:  rgba(255, 255, 255, 0.05);
  --surface-3:  rgba(0, 0, 0, 0.15);
  /* floating things (menus, modals) need an opaque fill — a translucent
     film over arbitrary page content turns to mud */
  --elevated:   #2a3340;
  --modal-grad: linear-gradient(180deg, #2a3340 0%, #232b37 100%);

  --border:     rgba(255, 255, 255, 0.06);
  --border-2:   rgba(255, 255, 255, 0.10);

  --fg:         var(--rm-text);
  --fg-dim:     #c8d0dd;
  --fg-faint:   #aab3c2;
  --fg-muted:   #8792a3;

  --accent:        var(--rm-action);
  --accent-2:      var(--rm-action-strong);
  --accent-grad:   linear-gradient(45deg, #2f67ff 0%, #1f50f2 100%);
  --accent-soft:   rgba(47, 103, 255, 0.18);
  --accent-softer: rgba(47, 103, 255, 0.10);
  --accent-border: rgba(47, 103, 255, 0.30);
  --accent-fg:     #8eb1ff;
  --accent-glow:   0 8px 22px -8px rgba(31, 80, 242, 0.6);
  --accent-glow-2: 0 12px 28px -8px rgba(31, 80, 242, 0.7);

  --header-bg:  rgba(31, 39, 51, 0.72);
  --shadow:     0 20px 40px -10px rgba(0, 0, 0, 0.5);
  --shadow-lg:  0 40px 80px -20px rgba(0, 0, 0, 0.6);
  --selection:  rgba(47, 103, 255, 0.35);

  --terminal-bg:  rgba(0, 0, 0, 0.28);
  --terminal-bar: rgba(0, 0, 0, 0.35);
  --terminal-dot: rgba(255, 255, 255, 0.18);

  --danger:        var(--rm-danger);
  --danger-bg:     rgba(255, 122, 122, 0.12);
  --danger-border: rgba(255, 122, 122, 0.28);
  --success:        var(--rm-success);
  --success-bg:     rgba(110, 231, 160, 0.12);
  --success-border: rgba(110, 231, 160, 0.25);
  --warning:        var(--rm-warning);
  --warning-bg:     rgba(245, 184, 106, 0.12);
  --warning-border: rgba(245, 184, 106, 0.25);
  --info:        #8eb1ff;
  --info-bg:     rgba(142, 177, 255, 0.12);
  --info-border: rgba(142, 177, 255, 0.28);

  --focus-ring: rgba(47, 103, 255, 0.30);

  /* radii — the single biggest departure from v1, which was flat 0 */
  --r-sm:   7px;
  --r:      10px;
  --r-md:   12px;
  --r-lg:   14px;
  --r-xl:   16px;
  --r-pill: 999px;

  --sidebar-w: 260px;
  --header-h:  64px;

  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

/* Light is derived rather than lifted: website-3 ships dark only, so the
   mapping keeps every variable name and swaps the values. Films invert —
   on a light canvas a raised card wants to be *whiter* than its
   background, not darker — and the status hues darken for contrast. */
html.light {
  --bg:         #f7f9fc;
  --page-grad:  linear-gradient(135deg, #ffffff 0%, #eef2f8 100%);

  --surface:    rgba(255, 255, 255, 0.75);
  --surface-2:  #ffffff;
  --surface-3:  rgba(15, 23, 42, 0.04);
  --elevated:   #ffffff;
  --modal-grad: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);

  --border:     rgba(15, 23, 42, 0.09);
  --border-2:   rgba(15, 23, 42, 0.16);

  --fg:         #111827;
  --fg-dim:     #334155;
  --fg-faint:   #64748b;
  --fg-muted:   #718096;

  --accent-soft:   rgba(47, 103, 255, 0.10);
  --accent-softer: rgba(47, 103, 255, 0.06);
  --accent-border: rgba(47, 103, 255, 0.28);
  --accent-fg:     #1f50f2;
  --accent-glow:   0 8px 22px -10px rgba(31, 80, 242, 0.45);
  --accent-glow-2: 0 12px 28px -10px rgba(31, 80, 242, 0.55);

  --header-bg:  rgba(255, 255, 255, 0.78);
  --shadow:     0 20px 40px -12px rgba(15, 23, 42, 0.18);
  --shadow-lg:  0 40px 80px -24px rgba(15, 23, 42, 0.22);
  --selection:  rgba(47, 103, 255, 0.20);

  --terminal-bg:  #0f1520;
  --terminal-bar: #1a2130;
  --terminal-dot: rgba(255, 255, 255, 0.22);

  --danger:        #dc2626;
  --danger-bg:     rgba(220, 38, 38, 0.08);
  --danger-border: rgba(220, 38, 38, 0.25);
  --success:        #16a34a;
  --success-bg:     rgba(22, 163, 74, 0.09);
  --success-border: rgba(22, 163, 74, 0.25);
  --warning:        #d97706;
  --warning-bg:     rgba(217, 119, 6, 0.09);
  --warning-border: rgba(217, 119, 6, 0.25);
  --info:        #2563eb;
  --info-bg:     rgba(37, 99, 235, 0.08);
  --info-border: rgba(37, 99, 235, 0.25);
}

html {
  background: var(--page-grad);
  /* fixed so the gradient belongs to the viewport, not the document —
     otherwise long pages stretch it into visible banding */
  background-attachment: fixed;
  color: var(--fg);
}
/* The terminal keeps its dark chrome in light mode, so text inside it
   must not follow the light foreground ramp. */
html.light .terminal { color: #e6eaf2; }
::selection { background: var(--selection); }

/* ═══════════════════════════════════════════════════════════════
   Utilities
   ═══════════════════════════════════════════════════════════════ */

.mt-6  { margin-top: 1.5rem; }
.mt-8  { margin-top: 2rem; }
.mb-6  { margin-bottom: 1.5rem; }
.mb-8  { margin-bottom: 2rem; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }

.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-between { justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1; }
.grid { display: grid; }
.hidden { display: none; }

.text-center { text-align: center; }
.text-right { text-align: right; }
.text-mono { font-family: var(--mono); }
.text-faint { color: var(--fg-faint); }
.text-dim { color: var(--fg-dim); }
.text-danger { color: var(--danger); }
.text-success { color: var(--success); }
.text-sm { font-size: 0.84rem; }
.text-xs { font-size: 0.74rem; }
.text-lg { font-size: 1.1rem; }

.loading { padding: 4rem; text-align: center; color: var(--fg-muted); font-size: 0.9rem; }

/* ═══════════════════════════════════════════════════════════════
   Section label
   ═══════════════════════════════════════════════════════════════ */

.section-label {
  display: flex; align-items: baseline; gap: 0.75rem;
}
.section-num {
  font-family: var(--mono);
  font-size: 0.68rem; letter-spacing: 0.18em; color: var(--accent-fg);
  font-variant-numeric: tabular-nums;
}
.section-tag {
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--fg-muted);
}
.section-rule { flex: 1; height: 1px; background: var(--border); transform: translateY(1px); }
.section-heading { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }

/* ═══════════════════════════════════════════════════════════════
   Top header
   ═══════════════════════════════════════════════════════════════ */

.app-header {
  position: sticky; top: 0; z-index: 40;
  height: var(--header-h);
  background: var(--header-bg);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  padding: 0 1.5rem;
}
/* The header spans the viewport but its contents track .app-shell's centred
   column, so the wordmark sits directly above the sidebar. */
.app-header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  height: 100%;
  width: 100%; max-width: 1440px;
  margin: 0 auto;
}
.logo {
  display: flex; align-items: center; gap: 0.6rem;
  font-weight: 800; font-size: 1.15rem; letter-spacing: -0.02em;
  color: var(--fg);
}
/* The wordmark is one flex item, not two: as bare text + <span> it would be
   an anonymous item plus a real one, and .logo's gap would split "relayme"
   from "this". */
.logo-text { white-space: nowrap; }
/* v1 tinted the second half of the wordmark; the gradient tile carries
   the brand now, so the text stays one colour. */
.logo-text span { color: inherit; }
.logo-mark {
  width: 32px; height: 32px;
  border-radius: 9px;
  background: var(--accent-grad);
  display: grid; place-items: center;
  box-shadow: 0 6px 18px -6px rgba(31, 80, 242, 0.6);
  flex-shrink: 0;
}
.logo-mark svg { width: 18px; height: 18px; color: #fff; }
.header-actions { display: flex; align-items: center; gap: 0.6rem; }

/* ═══════════════════════════════════════════════════════════════
   Theme toggle
   ═══════════════════════════════════════════════════════════════ */

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0;
  color: var(--fg-dim); background: var(--surface);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.theme-toggle:hover { background: var(--surface-2); color: var(--fg); }
.icon { display: block; flex-shrink: 0; }

/* ═══════════════════════════════════════════════════════════════
   Buttons
   ═══════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  background: var(--accent-grad); color: #fff;
  font-weight: 600; font-size: 0.9rem;
  padding: 0.65rem 1.1rem;
  border: 1px solid transparent;
  border-radius: var(--r);
  box-shadow: var(--accent-glow);
  transition: transform 0.15s, box-shadow 0.15s, background 0.15s, opacity 0.15s;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); box-shadow: var(--accent-glow-2); }
.btn:active { transform: translateY(0); }

.btn-ghost {
  background: var(--surface); color: var(--fg);
  border-color: var(--border-2);
  box-shadow: none;
}
.btn-ghost:hover { background: var(--surface-2); transform: none; box-shadow: none; }

.btn-sm { padding: 0.45rem 0.8rem; font-size: 0.82rem; border-radius: var(--r-sm); }
.btn-lg { padding: 0.85rem 1.6rem; font-size: 0.98rem; border-radius: var(--r-md); }

.btn-danger {
  background: var(--danger); color: #fff; border-color: var(--danger);
  box-shadow: 0 8px 22px -8px rgba(220, 38, 38, 0.5);
}
.btn-danger:hover { filter: brightness(1.08); }

.btn-success {
  background: var(--success); color: #0f1520; border-color: var(--success);
  box-shadow: 0 8px 22px -8px rgba(22, 163, 74, 0.45);
}
.btn-success:hover { filter: brightness(1.08); }

.btn:disabled, .btn[disabled] {
  opacity: 0.45; cursor: not-allowed; pointer-events: none;
  transform: none; box-shadow: none;
}

.btn-row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; }

.btn-link {
  background: none; border: none; color: var(--accent-fg);
  cursor: pointer; font: inherit; padding: 0;
  box-shadow: none; border-radius: 0;
}
.btn-link:hover { text-decoration: underline; transform: none; box-shadow: none; background: none; }
.btn-link.danger { color: var(--danger); }

/* icon button */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--fg-dim);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.icon-btn:hover { background: var(--surface-2); color: var(--fg); }
.icon-btn.sm { width: 30px; height: 30px; border-radius: 8px; }
.icon-btn svg.icon { width: 16px; height: 16px; }
.icon-btn.sm svg.icon { width: 14px; height: 14px; }

/* ═══════════════════════════════════════════════════════════════
   Badges / chips
   ═══════════════════════════════════════════════════════════════ */

.badge {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.76rem; font-weight: 500;
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--fg-faint);
  white-space: nowrap;
}
.badge.badge-success { color: var(--success); border-color: var(--success-border); background: var(--success-bg); }
.badge.badge-danger  { color: var(--danger);  border-color: var(--danger-border);  background: var(--danger-bg); }
.badge.badge-warning { color: var(--warning); border-color: var(--warning-border); background: var(--warning-bg); }
.badge.badge-info    { color: var(--info);    border-color: var(--info-border);    background: var(--info-bg); }
.badge.badge-dot::before {
  content: ""; display: inline-block;
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}

/* status → badge variant mapping (send/token statuses) */
.badge-sent, .badge-active { color: var(--success); border-color: var(--success-border); background: var(--success-bg); }
.badge-failed, .badge-revoked { color: var(--danger); border-color: var(--danger-border); background: var(--danger-bg); }
.badge-partial, .badge-sending, .badge-queued, .badge-accepted, .badge-pending { color: var(--warning); border-color: var(--warning-border); background: var(--warning-bg); }
/* A turned-off mailing list. Muted rather than red: it is a deliberate state
   the owner chose, not a failure. */
.badge-disabled, .badge-archived { color: var(--fg-muted); border-color: var(--border-2); background: var(--surface-2); }

.chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.82rem; font-weight: 500;
  padding: 0.35rem 0.75rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--fg-faint);
  cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.chip:hover { border-color: var(--border-2); color: var(--fg); background: var(--surface-2); }
.chip.active {
  background: var(--accent-soft); color: var(--fg);
  border-color: var(--accent-border);
}

.tier-badge {
  font-size: 0.7rem; font-weight: 600;
  color: var(--accent-fg);
  background: var(--accent-softer);
  border: 1px solid var(--accent-border);
  border-radius: var(--r-pill);
  padding: 0.15rem 0.55rem;
}

/* ═══════════════════════════════════════════════════════════════
   Inputs
   ═══════════════════════════════════════════════════════════════ */

.label {
  display: block;
  font-size: 0.84rem; font-weight: 500;
  color: var(--fg-dim);
  margin-bottom: 0.4rem;
}
.label .opt { color: var(--fg-muted); font-weight: 400; margin-left: 0.4rem; }

.input, .select, .textarea {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  color: var(--fg);
  font-size: 0.92rem;
  padding: 0.65rem 0.85rem;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
  outline: none;
}
.input::placeholder, .textarea::placeholder { color: var(--fg-muted); }
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--accent);
  background: var(--surface-2);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.input:disabled, .select:disabled, .textarea:disabled { opacity: 0.5; cursor: not-allowed; }

.textarea { resize: vertical; min-height: 7rem; line-height: 1.55; font-family: inherit; }

.select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%236b7585' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2rem;
}

/* input with prefix/suffix */
.input-group {
  display: flex; align-items: stretch;
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.input-group:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus-ring); }
.input-group .input { border: none; background: none; border-radius: 0; }
.input-group .input:focus { box-shadow: none; background: none; }
.input-group .input-prefix, .input-group .input-suffix {
  display: flex; align-items: center;
  padding: 0 0.85rem;
  font-size: 0.84rem; color: var(--fg-faint);
  background: var(--surface-3);
  border-right: 1px solid var(--border);
  white-space: nowrap;
  cursor: pointer;
}
.input-group .input-suffix { border-right: none; border-left: 1px solid var(--border); }

/* checkbox & radio */
.checkbox, .radio {
  display: inline-flex; align-items: flex-start; gap: 0.6rem;
  cursor: pointer; font-size: 0.9rem; color: var(--fg-dim);
}
.checkbox input, .radio input { margin-top: 0.2rem; accent-color: var(--accent); }
.radio-row { display: flex; flex-wrap: wrap; gap: 1.25rem; }

/* search input */
.search { position: relative; flex: 1; max-width: 360px; }
.search .input { padding-left: 2.4rem; }
.search::before {
  content: "";
  position: absolute; left: 0.85rem; top: 50%;
  width: 15px; height: 15px;
  transform: translateY(-50%);
  background: no-repeat center/contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7585' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E");
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════
   Form layout
   ═══════════════════════════════════════════════════════════════ */

.form-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 640px) { .form-grid.cols-2 { grid-template-columns: repeat(2, 1fr); } }

.field { margin-bottom: 1.15rem; }
.field .hint { font-size: 0.82rem; color: var(--fg-muted); margin-top: 0.35rem; }
.field.has-error .input, .field.has-error .select, .field.has-error .textarea { border-color: var(--danger); }

.form-actions {
  display: flex; gap: 0.6rem; justify-content: flex-end;
  padding-top: 1.15rem; border-top: 1px solid var(--border);
  margin-top: 1.15rem; flex-wrap: wrap;
}

.row-form { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.row-form .input, .row-form .select { width: auto; flex: 1; min-width: 200px; }

/* dynamic identity rows */
.identity-row { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; align-items: center; }
.identity-row .input { flex: 1; }
.identity-row .idx {
  font-family: var(--mono); font-size: 0.8rem; color: var(--fg-muted);
  width: 1.25rem; text-align: center;
}

/* ═══════════════════════════════════════════════════════════════
   Cards & panels
   ═══════════════════════════════════════════════════════════════ */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  margin-bottom: 1.5rem;
  overflow: hidden;
}
.panel-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: 1.1rem 1.4rem;
  border-bottom: 1px solid var(--border);
}
.panel-title { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }
.panel-body { padding: 1.4rem; }
.panel.warn { border-color: var(--warning-border); background: var(--warning-bg); }

/* ── breadcrumb ──
   Nothing here matched .breadcrumb, so the trail inherited the reset's
   `svg { display: block }` and the separator chevron dropped onto its own
   line — the stray arrow above a list's title. */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.5rem;
  font-size: 0.82rem;
  color: var(--fg-muted);
}
.breadcrumb a { color: var(--fg-faint); }
.breadcrumb a:hover { color: var(--fg); }
.breadcrumb .icon {
  display: inline-block;
  flex-shrink: 0;
  color: var(--fg-muted);
  opacity: 0.7;
}
.breadcrumb .crumb-current {
  color: var(--fg-dim);
  /* A long list name must not push the trail into a second line. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── mailing-list cards ──
   The lists screen emits .lists-grid / .list-card and nothing here matched
   them, so the collection rendered as a column of unstyled anchors — the
   headline "improve the mailing list UI" problem. These follow .stat-tile's
   geometry so the two screens look like the same product. */
.lists-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .lists-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .lists-grid { grid-template-columns: repeat(3, 1fr); } }

.list-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.15rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  transition: border-color 0.15s, background 0.15s, transform 0.15s;
}
.list-card:hover {
  border-color: var(--border-2);
  background: var(--surface-2);
  transform: translateY(-2px);
}
.list-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.list-card .list-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--accent-fg);
  flex-shrink: 0;
}
.list-card .list-name {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.list-card .list-desc {
  font-size: 0.85rem;
  color: var(--fg-muted);
  line-height: 1.45;
  /* Two lines, then ellipsis: a long description must not make one card in a
     row twice the height of its neighbours. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.list-card .list-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.5rem;
}

/* stat tile */
.stat-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 1.15rem;
}
.stat-tile .stat-label { display: flex; align-items: center; gap: 0.45rem; font-size: 0.8rem; color: var(--fg-muted); }
.stat-tile .stat-label .icon { color: var(--accent-fg); }
.stat-tile .stat-value {
  font-size: 1.6rem; font-weight: 700; line-height: 1.1; color: var(--fg);
  margin-top: 0.4rem; letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.stat-tile .stat-sub { font-size: 0.8rem; color: var(--fg-muted); margin-top: 0.35rem; }

/* A copy button that has just fired. core.js swaps the label; this is the
   colour that confirms it without needing a toast. */
.btn.is-copied, .btn-link.is-copied { color: var(--success); }

.stat-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
.attention-panel {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  margin-bottom: 1.5rem; padding: 1.1rem 1.2rem;
  border: 1px solid var(--warning-border); border-radius: var(--r-md);
  background: var(--warning-bg);
}
.attention-panel h2 { margin: 0.15rem 0 0.25rem; font-size: 1rem; color: var(--fg); }
.attention-panel p { margin: 0; color: var(--fg-faint); font-size: 0.88rem; }
.attention-kicker { color: var(--warning); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; }
.coming-soon-panel {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 1rem;
  align-items: center; padding: 1.25rem; border: 1px solid var(--border);
  border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--shadow-sm);
}
.coming-soon-icon {
  display: grid; width: 3rem; height: 3rem; place-items: center;
  border-radius: 0.85rem; color: var(--accent-fg); background: var(--accent-soft);
}
.coming-soon-panel h2 { margin: 0 0 0.25rem; font-size: 1rem; }
.coming-soon-panel p { margin: 0; max-width: 48rem; color: var(--fg-faint); font-size: 0.86rem; }
@media (max-width: 640px) {
  .attention-panel { align-items: stretch; flex-direction: column; }
  .attention-panel .btn { justify-content: center; }
  .coming-soon-panel { grid-template-columns: auto 1fr; align-items: start; }
  .coming-soon-panel .btn { grid-column: 2; justify-self: start; }
}
@media (min-width: 640px) { .stat-grid.sm-2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .stat-grid.lg-3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .stat-grid.lg-4 { grid-template-columns: repeat(4, 1fr); } }

/* ═══════════════════════════════════════════════════════════════
   App shell — sidebar + main
   ═══════════════════════════════════════════════════════════════ */

.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  gap: 2rem;
  width: 100%; max-width: 1440px;
  margin: 0 auto;
  padding: 1.75rem 1.5rem 4rem;
  align-items: start;
}
@media (max-width: 900px) {
  .app-shell { grid-template-columns: 1fr; gap: 0; }
  /* Scoped to .app-shell on purpose: the bare `.app-sidebar` rule below sets
     display:flex, and a media query adds no specificity of its own, so an
     unscoped `display:none` here would simply lose to it. */
  .app-shell .app-sidebar { display: none; }
  .app-shell.sidebar-open .app-sidebar {
    display: flex;
    position: fixed; inset: var(--header-h) 0 0 0;
    z-index: 30;
    background: var(--bg);
    padding: 1.5rem;
    overflow-y: auto;
    max-height: none;
  }
}

.app-sidebar {
  display: flex; flex-direction: column; gap: 1.25rem;
  position: sticky; top: calc(var(--header-h) + 1.75rem);
  max-height: calc(100vh - var(--header-h) - 3rem);
  overflow-y: auto;
  /* the scrollbar sits over the sticky column; keep it unobtrusive */
  scrollbar-width: thin;
}
.app-sidebar::-webkit-scrollbar { width: 6px; }
.app-sidebar::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 3px; }

.sidebar-section { display: flex; flex-direction: column; gap: 0.15rem; }
.sidebar-heading {
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--fg-muted); padding: 0.25rem 0.75rem 0.35rem;
}
.sidebar-link {
  display: flex; align-items: center; gap: 0.65rem;
  padding: 0.5rem 0.75rem;
  font-size: 0.9rem; color: var(--fg-faint);
  border: 1px solid transparent;
  border-radius: 9px;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.sidebar-marker {
  margin-left: auto; padding: 0.08rem 0.35rem; border: 1px solid var(--border);
  border-radius: 999px; color: var(--fg-muted); font-size: 0.58rem;
  font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
}
.sidebar-link.active .sidebar-marker { border-color: rgba(255,255,255,.16); color: var(--accent-fg); }
.sidebar-link:hover { color: var(--fg); background: var(--surface-2); }
.sidebar-link.active {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--fg);
  font-weight: 500;
}
.sidebar-link .sidebar-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.1rem; flex-shrink: 0;
}
.sidebar-link .sidebar-icon svg.icon { width: 16px; height: 16px; }
.sidebar-link .badge { margin-left: auto; }

.app-main { min-width: 0; }

/* page header (within main) */
.page-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin-bottom: 1.75rem;
}
.page-title { font-size: 1.6rem; font-weight: 700; letter-spacing: -0.02em; }
.page-sub { font-size: 0.95rem; color: var(--fg-faint); margin-top: 0.35rem; }
.page-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; }

/* ═══════════════════════════════════════════════════════════════
   Tabs / segmented
   ═══════════════════════════════════════════════════════════════ */

.tabs {
  display: flex; gap: 0.25rem;
  border-bottom: 1px solid var(--border);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.tab {
  padding: 0.65rem 0.9rem;
  font-size: 0.9rem; color: var(--fg-faint);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s;
  background: none; border-top: none; border-left: none; border-right: none;
}
.tab:hover { color: var(--fg); }
.tab.active { color: var(--fg); border-bottom-color: var(--accent); font-weight: 500; }

.segmented {
  display: inline-flex; gap: 0.25rem;
  padding: 0.2rem;
  background: var(--surface-3);
  border-radius: 9px;
}
.segmented button {
  padding: 0.4rem 0.85rem;
  font-size: 0.84rem; font-weight: 500;
  background: none; color: var(--fg-faint);
  border: 1px solid transparent; border-radius: var(--r-sm);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.segmented button:hover { color: var(--fg); }
.segmented button.active {
  background: var(--accent-soft); color: var(--fg);
  border-color: var(--accent-border);
}

/* ═══════════════════════════════════════════════════════════════
   Tables
   ═══════════════════════════════════════════════════════════════ */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
}
/* A .table-wrap inside a .panel would double the frame; the panel owns it. */
.panel > .table-wrap, .panel-body > .table-wrap { border: none; border-radius: 0; background: none; }
table.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table thead th {
  text-align: left;
  font-size: 0.74rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--fg-muted);
  padding: 0.75rem 1.1rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-3);
  white-space: nowrap;
}
.table tbody td {
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--border);
  color: var(--fg-dim);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover td { background: var(--surface-2); }
.table .cell-mono { font-family: var(--mono); font-size: 0.84rem; color: var(--fg-dim); }
.table .cell-faint { color: var(--fg-muted); }
.table .cell-strong { color: var(--fg); font-weight: 500; }
.table .row-actions { display: flex; gap: 0.4rem; justify-content: flex-end; align-items: center; }

th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--accent-fg); }
th.sort-asc::after  { content: " ↑"; font-size: 0.72rem; }
th.sort-desc::after { content: " ↓"; font-size: 0.72rem; }

/* ═══════════════════════════════════════════════════════════════
   Avatar
   ═══════════════════════════════════════════════════════════════ */

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem;
  border-radius: 50%;
  font-size: 0.8rem; font-weight: 700; text-transform: uppercase;
  background: linear-gradient(45deg, #6e8aff 0%, #b66eff 100%);
  color: #fff;
  flex-shrink: 0;
}
.avatar.lg { width: 3rem; height: 3rem; font-size: 1rem; }
.avatar.sm { width: 1.6rem; height: 1.6rem; font-size: 0.62rem; }
.avatar-meta { margin-left: 0.65rem; }
.avatar-meta .avatar-name { font-size: 0.9rem; font-weight: 500; color: var(--fg); }
.avatar-row { display: flex; align-items: center; }

/* ═══════════════════════════════════════════════════════════════
   Lists / keyval
   ═══════════════════════════════════════════════════════════════ */

.list {
  list-style: none;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
}
.list > li {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--border);
}
.list > li:last-child { border-bottom: none; }
.list > li:hover { background: var(--surface-2); }

.keyval { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.6rem 1rem; }
.keyval .kv-key { font-size: 0.84rem; color: var(--fg-muted); }
.keyval .kv-val { font-size: 0.9rem; color: var(--fg-dim); text-align: right; }
.keyval .kv-val.mono { font-family: var(--mono); font-size: 0.84rem; }

/* ═══════════════════════════════════════════════════════════════
   Modal / dialog
   ═══════════════════════════════════════════════════════════════ */

.modal-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(11, 16, 24, 0.6);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: none; align-items: center; justify-content: center;
  padding: 1.5rem;
  animation: fade-in 0.15s ease;
}
.modal-overlay.open { display: flex; }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes slide-up { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.modal {
  background: var(--modal-grad);
  border: 1px solid var(--border-2);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  width: 100%; max-width: 480px; max-height: 90vh; overflow-y: auto;
  animation: slide-up 0.2s ease;
}
.modal.lg { max-width: 680px; }
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.1rem 1.4rem; border-bottom: 1px solid var(--border);
}
.modal-title { font-size: 1.05rem; font-weight: 600; letter-spacing: -0.01em; }
.modal-body { padding: 1.4rem; }
.modal-footer {
  display: flex; gap: 0.6rem; justify-content: flex-end;
  padding: 1rem 1.4rem; border-top: 1px solid var(--border);
  background: var(--surface-3);
}
.modal-close {
  display: grid; place-items: center;
  width: 30px; height: 30px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--fg-faint); font-size: 1.15rem; line-height: 1; padding: 0;
  transition: background 0.15s, color 0.15s;
}
.modal-close:hover { background: var(--surface-2); color: var(--fg); }

/* ═══════════════════════════════════════════════════════════════
   Alert / toast
   ═══════════════════════════════════════════════════════════════ */

.alert {
  display: flex; gap: 0.6rem; align-items: flex-start;
  padding: 0.8rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  font-size: 0.9rem; color: var(--fg-dim);
}
.alert.alert-danger  { border-color: var(--danger-border);  background: var(--danger-bg);  color: var(--fg); }
.alert.alert-success { border-color: var(--success-border); background: var(--success-bg); }
.alert.alert-warning { border-color: var(--warning-border); background: var(--warning-bg); }
.alert.alert-info    { border-color: var(--info-border);    background: var(--info-bg); }
.alert .alert-icon { flex-shrink: 0; display: inline-flex; }
.alert.alert-danger  .alert-icon { color: var(--danger); }
.alert.alert-success .alert-icon { color: var(--success); }
.alert.alert-warning .alert-icon { color: var(--warning); }
.alert.alert-info    .alert-icon { color: var(--info); }
.alert .alert-icon svg.icon { width: 16px; height: 16px; margin-top: 0.15rem; }

.toast-stack {
  position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 200;
  display: flex; flex-direction: column; gap: 0.5rem;
  max-width: 360px;
}
.toast {
  display: flex; gap: 0.6rem; align-items: flex-start;
  padding: 0.8rem 1rem;
  background: var(--elevated);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  font-size: 0.9rem; color: var(--fg-dim);
  animation: toast-in 0.2s ease;
}
@keyframes toast-in { from { opacity: 0; transform: translateY(0.75rem); } to { opacity: 1; transform: none; } }
.toast.toast-success { border-left: 3px solid var(--success); }
.toast.toast-danger  { border-left: 3px solid var(--danger); }
.toast.toast-info    { border-left: 3px solid var(--info); }

/* ═══════════════════════════════════════════════════════════════
   Progress / quota bars
   ═══════════════════════════════════════════════════════════════ */

.progress {
  height: 7px;
  background: var(--surface-3);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  background: var(--accent-grad);
  border-radius: var(--r-pill);
  transition: width 0.4s ease;
}
.progress-bar.danger  { background: var(--danger); }
.progress-bar.warning { background: var(--warning); }
.progress-bar.success { background: var(--success); }

.quota-row {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 0.84rem; margin-bottom: 0.4rem;
}
.quota-row .quota-label { color: var(--fg-muted); }
.quota-row .quota-val {
  font-family: var(--mono); color: var(--fg-dim); font-variant-numeric: tabular-nums;
}

/* ═══════════════════════════════════════════════════════════════
   Terminal / code
   ═══════════════════════════════════════════════════════════════ */

.terminal {
  background: var(--terminal-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  font-family: var(--mono);
  font-size: clamp(0.78rem, 0.72rem + 0.2vw, 0.88rem); line-height: 1.7;
}
.terminal-bar {
  background: var(--terminal-bar);
  padding: 0.5rem 0.85rem;
  display: flex; gap: 0.35rem;
}
.terminal-bar span { width: 9px; height: 9px; border-radius: 50%; background: var(--terminal-dot); }
.terminal-body { padding: 1.1rem 1.25rem; }
.terminal .prompt { color: var(--fg-muted); user-select: none; }
.terminal .cmd   { color: var(--accent-fg); font-weight: 600; }
.terminal .flag  { color: #c8d0dd; }
.terminal .arg   { color: #c8d0dd; }
.terminal .out   { color: var(--fg-muted); margin-top: 0.5rem; }

code, pre { font-family: var(--mono); }
code.inline {
  font-size: 0.85em;
  background: var(--surface-3);
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--accent-fg);
}
pre.code-block {
  background: var(--terminal-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 1.1rem 1.25rem; overflow-x: auto;
  font-size: 0.82rem; line-height: 1.65; color: var(--fg-dim);
}
html.light pre.code-block { color: #c8d0dd; }
.token-secret { color: var(--warning); font-weight: 600; word-break: break-all; }

/* ═══════════════════════════════════════════════════════════════
   Empty state
   ═══════════════════════════════════════════════════════════════ */

.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 3rem 1.5rem; gap: 0.6rem;
  border: 1px dashed var(--border-2);
  border-radius: var(--r-lg);
}
.empty-state .empty-title { font-size: 1rem; font-weight: 600; color: var(--fg-dim); }
.empty-state .empty-desc { font-size: 0.9rem; color: var(--fg-muted); max-width: 44ch; }

/* ═══════════════════════════════════════════════════════════════
   Dropdown menu (header user)
   ═══════════════════════════════════════════════════════════════ */

.dropdown { position: relative; display: inline-block; }
.dropdown-menu {
  position: absolute; right: 0; top: calc(100% + 0.4rem);
  min-width: 14rem;
  padding: 0.3rem;
  background: var(--elevated);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  display: none; z-index: 50;
  animation: menu-in 0.15s ease;
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.dropdown.open .dropdown-menu { display: block; }
.dropdown-menu .dm-item {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.55rem 0.65rem;
  border-radius: var(--r-sm);
  font-size: 0.88rem; color: var(--fg-dim);
  width: 100%; text-align: left;
  background: none; border: none;
  transition: background 0.1s, color 0.1s;
}
.dropdown-menu .dm-item:hover { background: var(--surface-2); color: var(--fg); }
.dropdown-menu .dm-item.danger { color: var(--danger); }
.dropdown-menu .dm-item.danger:hover { background: var(--danger-bg); color: var(--danger); }
.dropdown-menu .dm-divider { height: 1px; background: var(--border); margin: 0.3rem 0.15rem; }

/* ═══════════════════════════════════════════════════════════════
   Auth pages (login / signup / verify)
   ═══════════════════════════════════════════════════════════════ */

.auth-wrap { min-height: 100vh; display: flex; flex-direction: column; }
.auth-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.25rem 1.75rem;
}
.auth-main {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 2rem 1.25rem 4rem;
}
/* The card is the one place the login screen has any weight to give, so
   unlike the in-app panels it gets a real surface and shadow. */
.auth-card {
  width: 100%; max-width: 420px;
  padding: 2rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow);
}
.auth-card .auth-title { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 0.4rem; }
.auth-card .auth-sub { font-size: 0.92rem; color: var(--fg-faint); margin-bottom: 1.6rem; }
.auth-foot { text-align: center; font-size: 0.88rem; color: var(--fg-muted); margin-top: 1.4rem; }
.auth-foot a { color: var(--accent-fg); font-weight: 500; }
.auth-foot a:hover { text-decoration: underline; }
.auth-tabs {
  display: flex; gap: 0.25rem;
  margin-bottom: 1.6rem;
  padding: 0.25rem;
  background: var(--surface-3);
  border-radius: var(--r);
}
.auth-tabs button {
  flex: 1; padding: 0.6rem;
  font-size: 0.88rem; font-weight: 500;
  background: none; color: var(--fg-faint);
  border: 1px solid transparent; border-radius: var(--r-sm);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.auth-tabs button:hover { color: var(--fg); }
.auth-tabs button.active {
  background: var(--accent-soft); color: var(--fg);
  border-color: var(--accent-border);
}
.auth-foot-bar { padding: 1.5rem; border-top: 1px solid var(--border); text-align: center; }
.footer-copy { font-size: 0.8rem; color: var(--fg-muted); }

/* ═══════════════════════════════════════════════════════════════
   Responsive
   ═══════════════════════════════════════════════════════════════ */

@media (max-width: 640px) {
  .app-header { padding: 0 1rem; }
  .app-shell { padding: 1.25rem 1rem 3rem; }
  .auth-header { padding: 1rem; }
  .auth-card { padding: 1.5rem; }
  .page-title { font-size: 1.35rem; }

  /* The workspace label is the first thing to go: the wordmark already
     identifies the app and there is only one workspace to be in. */
  .logo-divider, .logo-workspace { display: none; }

  /* Actions stack and fill the row rather than crowding into a corner. */
  .page-actions { width: 100%; }
  .page-actions .btn { flex: 1; justify-content: center; }
  .form-actions { flex-direction: column-reverse; }
  .form-actions .btn { width: 100%; }
  .modal-footer { flex-direction: column-reverse; }
  .modal-footer .btn { width: 100%; }

  .stat-grid { gap: 0.75rem; }
  .panel-body { padding: 1.1rem; }
  .panel-header { padding: 1rem 1.1rem; }
  .toast-stack { left: 1rem; right: 1rem; bottom: 1rem; max-width: none; }
}

/* ── Touch and small-screen safety ──────────────────────────────
   Applies to every handheld width, not just the narrowest: a 44px target
   is the smallest a finger hits reliably, and a table or a token string
   must scroll inside its own box rather than widening the document. */
@media (max-width: 900px) {
  .sidebar-link, .dm-item, .pp-opt, .tab {
    min-height: 44px;
  }
  .icon-btn, .theme-toggle { width: 40px; height: 40px; }
  .icon-btn.sm { width: 40px; height: 40px; }
  /* iOS zooms the page in when a focused input is under 16px. */
  .input, .select, .textarea { font-size: 16px; }
  .table-wrap { -webkit-overflow-scrolling: touch; }
  .cell-mono, .token-secret, code.inline { word-break: break-word; }
  .keyval { grid-template-columns: 1fr; gap: 0.25rem 0; }
  .keyval .kv-val { text-align: left; margin-bottom: 0.5rem; }
}

/* The sidebar drawer must sit above the page and scroll on its own. */
@media (max-width: 900px) {
  body.sidebar-open { overflow: hidden; }
  .app-shell.sidebar-open { overflow: hidden; }
  .app-shell.sidebar-open .app-sidebar {
    box-shadow: var(--shadow-lg);
    -webkit-overflow-scrolling: touch;
  }
}

/* ── verification gate ──────────────────────────────────────────
   Replaces the old dismissible "confirm your email" banner. An unverified
   account is refused every write by the server, so a banner over a fully
   interactive dashboard meant the rule was discovered by filling in a form
   and having it rejected. This blocks instead.

   There is no close button and no click-outside handler, by design: the only
   ways past are the link in the email and signing out. */
.gate-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: rgba(10, 14, 22, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
html.light .gate-overlay { background: rgba(15, 23, 42, 0.45); }

/* The dashboard behind the gate must not scroll under it. */
body.gated { overflow: hidden; }

.gate {
  width: min(430px, 100%);
  padding: 2rem;
  border-radius: var(--r-xl);
  background: var(--modal-grad);
  border: 1px solid var(--border-2);
  box-shadow: var(--shadow-lg);
  text-align: center;
}
.gate-icon {
  width: 52px; height: 52px; margin: 0 auto 1.1rem;
  border-radius: var(--r-lg);
  display: grid; place-items: center;
  background: var(--accent-soft);
  color: var(--accent-fg);
}
.gate-title { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 0.5rem; }
.gate-text {
  color: var(--fg-faint);
  font-size: 0.92rem;
  line-height: 1.55;
  margin-bottom: 1.4rem;
  /* A long address must wrap rather than widen the dialog. */
  overflow-wrap: anywhere;
}
.gate-text strong { color: var(--fg); font-weight: 600; }
.gate-actions { display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap; }
.gate-msg { margin-top: 1rem; text-align: left; }
.gate-msg:empty { display: none; }
.gate-foot { margin-top: 1.2rem; font-size: 0.78rem; color: var(--fg-muted); }

@media (max-width: 520px) {
  .gate { padding: 1.5rem 1.25rem; }
}

/* ═══════════════════════════════════════════════════════════════
   Billing — plan picker
   ═══════════════════════════════════════════════════════════════ */

/* Plans are compared side by side, so they are cards in a grid rather than
   rows in a list: the eye needs to scan across three allowances, not down. */
.plans-hero {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 2rem;
  margin-bottom: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border: 1px solid var(--accent-border);
  border-radius: var(--r-xl);
  background:
    radial-gradient(circle at 85% 15%, rgba(142, 177, 255, 0.18), transparent 34%),
    linear-gradient(135deg, rgba(47, 103, 255, 0.18), rgba(255, 255, 255, 0.025) 62%);
}
.plans-hero::after {
  content: "";
  position: absolute;
  width: 230px; height: 230px; right: -95px; bottom: -145px;
  border: 1px solid rgba(142, 177, 255, 0.22);
  border-radius: 50%;
}
.plans-hero-copy { position: relative; z-index: 1; max-width: 620px; }
.plans-eyebrow {
  display: block;
  margin-bottom: 0.65rem;
  color: var(--accent-fg);
  font-size: 0.72rem;
  font-weight: 750;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.plans-hero h2 { font-size: clamp(1.55rem, 3vw, 2.25rem); line-height: 1.08; letter-spacing: -0.035em; }
.plans-hero p { max-width: 570px; margin-top: 0.8rem; color: var(--fg-faint); font-size: 0.94rem; }
.plans-current {
  position: relative;
  z-index: 1;
  min-width: 180px;
  padding: 1rem 1.1rem;
  border: 1px solid var(--border-2);
  border-radius: var(--r-lg);
  background: rgba(15, 21, 31, 0.26);
  box-shadow: 0 16px 36px -28px rgba(0, 0, 0, 0.8);
}
html.light .plans-current { background: rgba(255, 255, 255, 0.7); }
.plans-current span, .plans-current small { display: block; color: var(--fg-faint); font-size: 0.76rem; }
.plans-current strong { display: block; margin: 0.22rem 0; font-size: 1.2rem; }

.plans-compare {
  padding: clamp(1rem, 2.5vw, 1.5rem);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: rgba(255, 255, 255, 0.018);
}
.plans-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.plans-toolbar h2 { font-size: 1.05rem; letter-spacing: -0.015em; }
.plans-toolbar p { margin-top: 0.2rem; color: var(--fg-faint); font-size: 0.78rem; }
.plans-footnote {
  display: flex;
  align-items: baseline;
  gap: 0.45rem 0.8rem;
  flex-wrap: wrap;
  margin-top: 1rem;
  padding: 0 0.2rem;
  color: var(--fg-faint);
  font-size: 0.78rem;
}
.plans-footnote strong { color: var(--fg-dim); }

.plan-cards {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: 1fr;
}
@media (min-width: 700px)  { .plan-cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1180px) { .plan-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.plan-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.9rem;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  transition: transform 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
}
.plan-card:hover { transform: translateY(-2px); border-color: var(--border-2); box-shadow: 0 18px 35px -30px rgba(0, 0, 0, 0.9); }
/* The plan you are on is marked by the accent border and its own badge, not by
   being moved or sized differently — its position in the ladder is information
   too, and reordering the list to float it would destroy that. */
.plan-card.is-current {
  border-color: var(--accent-border);
  background: linear-gradient(180deg, rgba(47, 103, 255, 0.14), rgba(47, 103, 255, 0.05));
  box-shadow: inset 0 1px 0 rgba(142, 177, 255, 0.08);
}
.plan-card-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 0.75rem;
}
.plan-card-name { font-size: 1.05rem; font-weight: 700; letter-spacing: -0.02em; }
.plan-current-badge {
  flex: none;
  padding: 0.22rem 0.5rem;
  border: 1px solid var(--accent-border);
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-size: 0.67rem;
  font-weight: 700;
}
.plan-card-price {
  display: flex;
  align-items: baseline;
  gap: 0.38rem;
  min-height: 2.2rem;
  color: var(--fg-faint);
  font-size: 0.76rem;
}
.plan-card-price strong { color: var(--fg); font-size: clamp(1.5rem, 2.4vw, 1.85rem); line-height: 1; letter-spacing: -0.045em; }
.plan-price-note { min-height: 1.15rem; margin-top: -0.65rem; color: var(--fg-muted); font-size: 0.7rem; }
.plan-card-desc { margin-top: 0.22rem; color: var(--fg-faint); font-size: 0.78rem; line-height: 1.42; }
.plan-card-rule { height: 1px; background: var(--border); }
.plan-card-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 0.65rem; }
.plan-metric { min-width: 0; }
.plan-metric-label, .plan-metric span { display: block; color: var(--fg-muted); font-size: 0.67rem; }
.plan-metric .plan-metric-label { margin-bottom: 0.14rem; color: var(--fg-faint); }
.plan-metric strong { display: block; overflow-wrap: anywhere; font-size: 0.92rem; line-height: 1.2; }
/* margin-top:auto pins every card's button to the bottom, so a longer
   description in one card does not leave its button floating mid-row. */
.plan-card-action { margin-top: auto; padding-top: 0.5rem; }
.plan-card-action .btn { width: 100%; justify-content: center; text-align: center; }

/* ── Usage meter ──────────────────────────────────────────────────────────
   How much of the plan allowance is gone, and when it comes back. The bar
   exists because three numbers in three tiles do not answer "am I close?" at a
   glance — the proportion does. It is only rendered for a capped plan; an
   unlimited one has no proportion to show. */
.usage-meter { display: flex; flex-direction: column; gap: 0.4rem; }
.usage-meter-track {
  height: 0.5rem;
  border-radius: 999px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  overflow: hidden;
}
.usage-meter-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent-grad);
  /* Width is the only animated property, so a refresh that moves the bar reads
     as movement rather than as a repaint. */
  transition: width 240ms ease-out;
}
/* Near the cap the bar stops being decorative and becomes a warning. */
.usage-meter-fill.is-high { background: var(--warning); }
.usage-meter-caption {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  justify-content: space-between;
  font-size: 0.8rem;
  /* --fg-faint, not --fg-muted: in the dark theme "muted" is the darker of the
     two and drops this caption below the contrast of the text around it. */
  color: var(--fg-faint);
  font-variant-numeric: tabular-nums;
}
.usage-chart-panel { overflow: visible; }
.usage-chart {
  width: 100%; min-height: 240px; color: var(--fg-muted);
  --ts-chart-1: var(--accent);
  --ts-chart-focus-fill: var(--accent-soft);
  --ts-chart-tooltip-background: var(--surface-3);
  --ts-chart-tooltip-border: 1px solid var(--border-strong);
  --ts-chart-tooltip-border-radius: 8px;
  --ts-chart-tooltip-color: var(--fg);
  --ts-chart-tooltip-shadow: var(--shadow-md);
}


/* ── Billing interval toggle ─────────────────────────────────────────────
   Monthly/yearly is a view of the same plan list, not a different page, so it
   sits directly above the cards and swaps their prices in place. It only
   renders when both intervals are actually buyable on this deployment. */
.interval-toggle {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0;
  flex-wrap: wrap;
}
.interval-option {
  appearance: none;
  cursor: pointer;
  padding: 0.35rem 0.85rem;
  font: inherit;
  font-size: 0.82rem;
  color: var(--fg-faint);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: color 120ms ease-out, background 120ms ease-out;
}
.interval-option:hover { color: var(--fg); }
.interval-option.is-active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.interval-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The saving is computed from the two prices (see pricing.YearlySavingText), so
   it cannot drift out of agreement with the numbers beside it. */
.interval-saving {
  margin-left: 0.35rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--success, var(--accent-fg));
}
/* The starter grant backs up the allowance rather than adding to it, so it is
   phrased and styled as a footnote to the headline number above it. */
.plan-card-starter {
  padding: 0.55rem 0.65rem;
  border-radius: var(--r-sm);
  background: var(--success-bg);
  color: var(--success);
  font-size: 0.72rem;
  font-weight: 600;
}
.plan-card-starter span { font-weight: 800; }

@media (max-width: 760px) {
  .plans-hero { grid-template-columns: 1fr; gap: 1.25rem; }
  .plans-current { min-width: 0; }
  .plans-toolbar { align-items: flex-start; flex-direction: column; }
}

/* A panel for the one action on a screen that cannot be undone. It is bordered
   rather than filled: the destructive weight belongs on the button, and a whole
   red panel would make the rest of the settings page read as an alarm. */
.panel-danger { border-color: var(--danger-border, rgba(255, 107, 107, 0.28)); }
.panel-danger .panel-title { color: var(--danger, #ff6b6b); }

/* The plan in force, on the billing page. It states one fact and offers one
   way out; the comparison of plans lives on /dashboard/plans, because putting
   both here made one page answer "what am I on" and "what could I be on" in a
   single scroll. */
.current-plan {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.current-plan-name {
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

/* A short list of facts about something before an irreversible action. The
   numbers are the point: "this cannot be undone" tells someone nothing they had
   not assumed, whereas "3 mailing lists, 47 recipients" is a fact they can check
   against what they expect to lose. */
.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 1rem;
  margin: 1rem 0;
  font-size: 0.88rem;
}
.facts dt { color: var(--fg-faint); }
.facts dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
