/* Prompt Skiller — minimal but polished dark theme */

:root {
  --bg: #0e1116;
  --bg-2: #161b22;
  --bg-3: #1f2530;
  --border: #2c3340;
  --text: #e6edf3;
  --text-dim: #8b949e;
  --muted: #8b949e;
  --panel: #161b22;
  --accent: #7c5cff;
  --accent-2: #5d3fd3;
  /* Lighter accent used for accent-coloured *text* on dark surfaces.
     ``--accent`` on ``--bg-2`` is ~3.98:1 (below WCAG AA 4.5:1); this
     value is ~5.1:1. Fill/background uses keep ``--accent`` (FE-103). */
  --accent-text: #8b78ff;
  --ok: #3fb950;
  --warn: #d29922;
  --err: #f85149;
  --shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  --surface-1: rgba(255, 255, 255, 0.03);
  --surface-2: rgba(255, 255, 255, 0.06);
  --surface-3: rgba(255, 255, 255, 0.10);
  --surface-4: rgba(255, 255, 255, 0.16);
  --card-bg: rgba(255, 255, 255, 0.03);
  --glass-1: rgba(10, 12, 22, 0.78);
  --glass-2: rgba(10, 12, 22, 0.55);
  --glass-flat: rgba(10, 12, 22, 0.45);
  --glass-panel: rgba(22, 27, 34, 0.78);
  --glass-panel-strong: rgba(22, 27, 34, 0.92);
  --text-muted: #8b949e;
  --focus-ring: #8b78ff;
  color-scheme: dark;
  --radius: 8px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* ---- Canonical alias layer (DESIGN_SYSTEM.md §7.1) ---------------------
     Maps the client SPA's canonical token vocabulary (the client owns the
     names) onto the parent's existing values. Purely additive: no rule above
     or below references these names yet, so nothing renders differently.
     Component-by-component adoption can now swap rules onto the canonical
     names. Landed on `release/v0.3.0` (v0.3.0).
     Names already defined verbatim above (--bg, --border, --accent, --warn)
     are intentionally not repeated. */
  /* Surfaces + text (aliases onto existing parent names) */
  --surface: var(--bg-2);
  --surface-elevated: var(--bg-3);
  --fg: var(--text);
  --fg-muted: var(--text-dim);
  --fg-subtle: var(--muted);
  /* Accent family (absent in the parent) */
  --accent-fg: #0e1116;
  --accent-soft: rgba(124, 92, 255, 0.12);
  /* --focus-ring is already defined verbatim above (#8b78ff); repeating it here
     would shadow the existing dark focus ring with a 45%-alpha value and change
     rendered focus outlines (DS-117). Intentionally omitted. */
  /* Brand (aliases onto the existing accent names) */
  --brand-from: var(--accent);
  --brand-to: var(--accent-2);
  --brand-glow: rgba(124, 92, 255, 0.4);
  /* Borders */
  --border-strong: #3d4757;
  /* Status (aliases onto ok/err + absent backgrounds) */
  --danger: var(--err);
  --danger-bg: rgba(248, 81, 73, 0.12);
  --success: var(--ok);
  --success-bg: rgba(63, 185, 80, 0.12);
  --warn-bg: rgba(210, 153, 34, 0.12);
  /* Overlay + touch target */
  --code-bg: rgba(127, 127, 127, 0.12);
  --tap: 44px;
  /* Spacing scale (4/8/12/16/24/32/48px) */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  /* Radius scale (4/8/12px) */
  --radius-1: 4px;
  --radius-2: var(--radius);
  --radius-3: 12px;
  /* Shadow scale */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-2: var(--shadow);
  --shadow-3: 0 12px 28px rgba(0, 0, 0, 0.5);
  /* Type scale */
  --fs-xs: 0.75rem;
  --fs-sm: 0.85rem;
  --fs-base: 0.875rem;
  --fs-md: 1.05rem;
  --fs-lg: 1.3rem;
  --fs-xl: 1.6rem;
  /* Z-index scale */
  --z-nav: 10;
  --z-toast: 50;
  --z-modal: 100;
}

/* Light theme (dark stays the :root default). Only the colour-bearing
   tokens are overridden; brand accent stays #7c5cff and --accent-text is
   re-derived for AA contrast on light surfaces. */
html[data-theme="light"] {
  color-scheme: light;
  --bg: #f5f6f8;
  --bg-2: #ffffff;
  --bg-3: #eceef2;
  --border: #d7dce3;
  --text: #1b2027;
  --text-dim: #5a6472;
  --muted: #5a6472;
  --panel: #ffffff;
  --accent-text: #5b3fd6;
  --ok: #1a7f37;
  --warn: #9a6700;
  --err: #cf222e;
  --shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  --surface-1: rgba(0, 0, 0, 0.02);
  --surface-2: rgba(0, 0, 0, 0.05);
  --surface-3: rgba(0, 0, 0, 0.08);
  --surface-4: rgba(0, 0, 0, 0.12);
  --card-bg: #ffffff;
  --glass-1: rgba(255, 255, 255, 0.85);
  --glass-2: rgba(255, 255, 255, 0.65);
  --glass-flat: rgba(255, 255, 255, 0.65);
  --glass-panel: rgba(255, 255, 255, 0.82);
  --glass-panel-strong: rgba(255, 255, 255, 0.96);
  --text-muted: #5a6472;
  --focus-ring: #6b4fe0;
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  min-height: 100vh;
  min-height: 100dvh;
}

a { color: var(--accent-text); text-decoration: none; cursor: pointer; }
a:hover { text-decoration: underline; }

/* FE-112: skip-to-content link. Positioned off-screen until it receives
   keyboard focus, then pinned to the top-left above the topbar. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: 10px 16px;
  background: var(--accent);
  color: var(--text);
  font-weight: 600;
  border-radius: 0 0 var(--radius) 0;
  text-decoration: none;
}
.skip-link:focus {
  left: 0;
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* Touch optimizations: drop the legacy 300ms tap delay and remove
   the OS-default grey tap rectangle. */
button,
a,
input,
textarea,
select,
label,
.nav-link,
.pill {
  -webkit-tap-highlight-color: transparent;
}

button,
a,
.nav-link {
  touch-action: manipulation;
}

/* Keep inner scroll contained inside modals/drawers/wizards so
   over-scrolling at the edges doesn't drag the body around. */
.wizard,
.wizard-card,
.wizard-step,
.drawer,
.drawer-panel,
.sheet,
.modal {
  overscroll-behavior: contain;
}

/* Alpine 3 uses `x-cloak` (the x- prefix). The Vue-era `v-cloak`
   selector is also accepted as a fallback for any leftover markup. */
[x-cloak], [v-cloak] { display: none !important; }

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14px 24px;
  /* Honor the iPhone safe-area insets when installed as a home-screen
     app. The home-indicator gutter on modern iPhones otherwise overlaps
     the touch target. */
  padding-top: max(14px, env(safe-area-inset-top));
  padding-inline-start: max(24px, env(safe-area-inset-left));
  padding-inline-end: max(24px, env(safe-area-inset-right));
  /* Translucent glass header — matches the landing page `.landing-nav`
     treatment so the SPA topbar feels like the same brand chrome. The
     gradient + backdrop-filter blurs the page content behind the bar
     as the user scrolls, giving the same depth the landing page has. */
  background: linear-gradient(180deg, var(--glass-1), var(--glass-2));
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--surface-2);
  position: sticky; top: 0; z-index: 100;
}

@media (max-width: 640px) {
  .topbar { padding: 10px 16px; gap: 8px; }
}

/* Mobile-only topbar collapse ----------------------------------------
   On screens <=768px the dense nav (Dashboard / Sessions / Library /
   Account / Admin) and the auth user/signout pill don't fit cleanly
   alongside the brand. We collapse the topbar to a single row —
   brand on the left, hamburger on the right — and let the
   `.mobile-drawer` carry the full nav. This matches the landing page
   (which hides non-CTA links on mobile) and the Vue client (which
   already uses 719px for the same purpose). 768px was chosen because
   at that width even an iPad in portrait is too narrow to comfortably
   hold brand + 5 nav links + auth pill + hamburger. */
@media (max-width: 768px) {
  .topbar-nav { display: none !important; }
  .topbar .auth-user,
  .topbar .auth-signout { display: none !important; }
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  border-radius: 8px;
  padding: 4px 6px;
  margin: -4px -6px;
  transition: background 120ms ease;
}
.brand-link:hover { background: var(--surface-2); }
.brand-link:active { background: var(--surface-4); }

/* A11Y-109 / DS-109: topbar + drawer navigation relied on the UA default
   focus ring, which is inconsistent or invisible against the themed
   surfaces. Give every nav/drawer link a shared, theme-aware ring. */
.brand-link:focus-visible,
.nav-link:focus-visible,
.mobile-drawer .drawer-link:focus-visible,
.mobile-drawer .drawer-link-signin:focus-visible,
.mobile-drawer .drawer-link-signout:focus-visible {
  outline: 2px solid var(--focus-ring, var(--accent));
  outline-offset: 2px;
}
.logo {
  /* Gradient logo square — matches the landing page brand mark so the
     SPA and the landing page read as the same product. 32×32 with
     a violet gradient and a soft accent-tinted shadow. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 0 4px 18px rgba(124, 92, 255, 0.4);
  font-size: 18px;
  line-height: 1;
}
.brand-name {
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.2px;
  color: var(--text);
  /* Keep the brand on a single line even on narrow screens. */
  white-space: nowrap;
}

/* Topbar nav (Sessions | Library) */
.topbar-nav {
  display: flex;
  gap: 6px;
  margin-left: 18px;
  flex: 1 1 auto;
  align-items: center;
}
.nav-link {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  font-size: 13px;
  padding: 6px 12px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  white-space: nowrap;
}
.nav-link:hover { color: var(--text); background: var(--surface-2); }
.nav-link.active {
  color: var(--text);
  background: rgba(124, 92, 255, 0.14);
  border-color: rgba(124, 92, 255, 0.4);
}
.count-pill {
  display: inline-block;
  min-width: 18px;
  padding: 0 6px;
  height: 18px;
  border-radius: 999px;
  background: rgba(124, 92, 255, 0.25);
  color: var(--accent-text);
  font-size: 11px;
  line-height: 18px;
  text-align: center;
  font-weight: 600;
}
.title { font-weight: 700; font-size: 18px; letter-spacing: 0.3px; }
.tagline { color: var(--text-dim); font-size: 12px; margin-left: 4px; }

.status { display: flex; gap: 8px; align-items: center; }
.pill {
  font-size: 11px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.pill.ok    { color: var(--ok);   border-color: rgba(63, 185, 80, 0.3); }
.pill.warn  { color: var(--warn); border-color: rgba(210, 153, 34, 0.4); }
.pill.err   { color: var(--err);  border-color: rgba(248, 81, 73, 0.4); }
.pill.muted { color: var(--text-dim); }

/* Theme toggle: sun in dark (switch to light), moon in light. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.theme-toggle:hover { color: var(--text); background: var(--surface-2); }
.theme-toggle .theme-icon-light { display: inline; }
.theme-toggle .theme-icon-dark { display: none; }
html[data-theme="light"] .theme-toggle .theme-icon-light { display: none; }
html[data-theme="light"] .theme-toggle .theme-icon-dark { display: inline; }
.landing-theme-toggle { margin-left: 8px; }

.container {
  max-width: 1100px;
  margin: 24px auto;
  padding: 0 24px 80px;
  padding-inline-start: max(24px, env(safe-area-inset-left));
  padding-inline-end: max(24px, env(safe-area-inset-right));
  /* Add a bottom safe-area buffer so the home-indicator on notched
     iPhones doesn't overlap the last card. */
  padding-bottom: max(80px, calc(80px + env(safe-area-inset-bottom)));
}
@media (max-width: 640px) {
  .container {
    margin: 16px auto;
    padding: 0 16px;
    padding-inline-start: max(16px, env(safe-area-inset-left));
    padding-inline-end: max(16px, env(safe-area-inset-right));
    padding-bottom: max(24px, calc(24px + env(safe-area-inset-bottom)));
  }
}

h2 { margin: 4px 0 18px; font-size: 22px; }
h3 { margin: 6px 0 8px; font-size: 16px; }

.row { display: flex; align-items: center; gap: 10px; }
.row.spread { justify-content: space-between; }

button {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 14px;
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.04s;
}
button:hover:not(:disabled) { background: var(--bg-3); border-color: var(--border); }
button:active:not(:disabled) { transform: translateY(1px); }
button:disabled { opacity: 0.45; cursor: not-allowed; }

button.primary {
  background: var(--accent);
  color: white;
  border-color: var(--accent-2);
}
button.primary:hover:not(:disabled) { background: var(--accent-2); }

button:focus-visible,
button.primary:focus-visible {
  outline: 2px solid var(--focus-ring, var(--accent));
  outline-offset: 2px;
}

button.ghost { background: transparent; }
button.danger { color: var(--err); border-color: rgba(248, 81, 73, 0.4); }
button.danger:hover:not(:disabled) { background: rgba(248, 81, 73, 0.1); }

input[type="text"], input[type="number"], input[type="password"],
input[type="email"], input[type="url"], input[type="search"],
input[type="tel"], textarea, select {
  width: 100%;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  color: var(--text);
  font-family: inherit;
  font-size: max(16px, 14px);
  outline: none;
  transition: border-color 0.12s;
}
input[type="text"]:focus, input[type="number"]:focus,
textarea:focus, select:focus { border-color: var(--accent); }

textarea { resize: vertical; min-height: 80px; font-family: var(--mono); font-size: 13px; line-height: 1.55; }
input.mono, textarea.mono { font-family: var(--mono); }

.empty {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-dim);
  background: var(--bg-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

/* Session/Library row-grid table.
   ``position: relative`` (NOT ``overflow: hidden``) keeps the table a
   positioning context so the per-row kebab menus stack correctly,
   while still letting the menu drop down past the table's bottom
   edge. Previously ``overflow: hidden`` was used to clip the rounded
   ``border-radius`` corners around the cell contents, but it also
   clipped the kebab dropdown — with only one session the menu had
   nowhere to drop and got invisibly cut off. The rounded-corner
   look is preserved by the per-cell ``border`` + the table's
   ``border-radius`` painting the outline. */
.grid {
  width: 100%;
  /* ``separate`` border-collapse so each tbody row can paint its own
     rounded card outline via ``box-shadow`` (works reliably with
     border-radius, unlike ``border`` on table rows in ``collapse``
     mode). ``border-spacing: 0 8px`` gives an 8px gap between rows. */
  border-collapse: separate;
  border-spacing: 0 8px;
  background: transparent;
  border: none;
  border-radius: 0;
  position: relative;
}
.grid th, .grid td {
  text-align: left;
  padding: 12px 14px;
  /* No per-cell border-bottom: each row is now a self-contained card,
     so dividers between rows would split the card visually. The
     header keeps its separator via the ``thead th`` rule below. */
  border-bottom: none;
}
.grid thead th { border-bottom: 1px solid var(--border); }
.grid th { background: var(--bg-3); font-weight: 600; color: var(--text-dim); font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
/* Each tbody row is rendered as a self-contained card: rounded
   corners, a 1px outline via box-shadow (which respects border-radius
   reliably across browsers, unlike ``border`` on table rows), and
   the standard surface background. The hover rule further down paints
   the whole card violet with a matching border. */
.grid tbody tr {
  background: var(--bg-2);
  border-radius: var(--radius);
  box-shadow: 0 0 0 1px var(--border);
  transition: background 0.15s ease;
}
/* Kebab menus inside the grid need to float above sibling rows. The
   table is now ``position: relative`` (so its children stack above
   each other within the table), and the open menu gets a high
   z-index so it visually lifts off the row. */
.grid .kebab-wrap { position: relative; }
.grid .kebab-menu.open { z-index: 5; }

/* Mobile (horizontal-scrolling tables with comfortable row height,
   per UX direction). The Sessions / Library tables aren't currently
   wrapped in a ``.table-wrap`` div — apply the same scroll behaviour
   on the table itself so we don't have to touch every template. */
@media (max-width: 640px) {
  .grid { display: block; }
  /* Card-based mobile layout: each table row becomes a card with
     rounded corners, a subtle status-tinted background, a colored
     top accent strip, and compact vertical spacing. Fields stack
     top-to-bottom for easy thumb scanning. The header is hidden so
     the card visuals carry the meaning. */
  .grid thead { display: none; }
  .grid tbody { display: block; }
  .grid tr {
    display: grid;
    /* Three-column grid so id-chip and title share row 1:
         col 1 (auto) = id-chip, col 2 (1fr) = title, col 3 (auto) = unused */
    grid-template-columns: auto 1fr auto;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    margin-bottom: 6px;
    padding: 8px 10px 10px;
    position: relative;
    column-gap: 8px;
    row-gap: 2px;
    /* Consistent minimum height so Session and Library cards feel the
       same on mobile. Library cards tend to be taller because their
       multi-button actions row adds vertical space; this keeps Session
       cards from looking squished in comparison. */
    min-height: 120px;
    /* Colored top accent strip: shows the session's status colour
       as a 3px line at the top of each card. Matches the
       ``status-badge`` palette. */
    overflow: hidden;
  }
  .grid tr::before {
    content: "";
    /* CSS Grid item (NOT absolutely positioned): placed in row 1,
       spanning all three columns. The actual id / title / kebab
       cells also live in row 1 but appear AFTER the pseudo-element
       in DOM order, so they naturally stack on top of this header
       bar without needing z-index hacks. */
    grid-column: 1 / -1;
    grid-row: 1;
    /* 30px tall: a 3px status-colored strip at the very top, then
       a soft violet wash for the rest. Creates a permanent "header
       bar" behind the id + title + kebab row so each card visually
       echoes the desktop ``<thead>`` look. */
    height: 30px;
    background: linear-gradient(to bottom,
                                var(--text-dim) 3px,
                                rgba(124, 92, 255, 0.12) 3px);
  }
  .grid tr[data-status="ready"]::before,
  .grid tr.s-ready::before      { background: linear-gradient(to bottom, var(--ok) 3px, rgba(124, 92, 255, 0.12) 3px); }
  .grid tr[data-status="in_progress"]::before,
  .grid tr.s-in_progress::before { background: linear-gradient(to bottom, var(--warn) 3px, rgba(124, 92, 255, 0.12) 3px); }
  .grid tr[data-status="finalized"]::before,
  .grid tr.s-finalized::before  { background: linear-gradient(to bottom, var(--accent) 3px, rgba(124, 92, 255, 0.12) 3px); }
  /* Status-tinted card backgrounds: a faint wash of the status color
     so rows are visually distinct at a glance without overwhelming
     the text. The alphas (0.06–0.08) are light enough to keep the
     card readable on both light and dark themes. */
  .grid tr[data-status="ready"] { background: rgba(34, 197, 94, 0.06); }
  .grid tr[data-status="in_progress"] { background: rgba(245, 158, 11, 0.06); }
  .grid tr[data-status="finalized"] { background: rgba(124, 92, 255, 0.06); }
  .grid tr > * { border: none; padding: 0; background: transparent; min-height: 0; }

  /* ID chip cell (1st column) — sits on row 1 column 1, vertically
     centered, sharing the title row with the kebab. No more two-row
     id field. Renders on top of the header bar painted by
     ``::before`` because it comes after the pseudo-element in DOM
     order (later grid siblings stack on top of earlier ones). */
  .grid tr td:nth-child(1) {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    justify-self: center;
    text-align: center;
  }
  /* Title cell (2nd column) — large, bold, takes the middle column
     of row 1. No ``padding-right`` reservation needed because the
     kebab now has its own column on the right. */
  .grid tr td:nth-child(2) {
    grid-column: 2;
    grid-row: 1;
    font-weight: 700;
    font-size: 13.5px;
    color: var(--text);
    line-height: 1.2;
    min-width: 0;
    align-self: center;
    justify-self: center;
    text-align: center;
  }

  /* Kebab / actions cell (last column) — floats top-right of row 1,
     in its own grid column so it doesn't overlap the title. */
  .grid tr > *:last-child {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    align-self: start;
  }

  /* Actions cell with multiple buttons (Session & Library tables):
     the actions column needs to span the full width and wrap its
     buttons so they don't cram into the narrow unused column 3 and
     visually overlap the id + title in the header bar. This is the
     rule that unifies Session and Library mobile cards: both now
     render their action buttons as a full-width wrapping row at
     the bottom of the card. */
  .grid tr td:last-child:has(button + button) {
    grid-column: 1 / -1;
    grid-row: auto;
    justify-self: start;
    align-self: start;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
  }

  /* Topic + Status chips (columns 3–4) — inline on a single flow line
     below the title. ``:not(:last-child)`` keeps the actions cell out
     of this rule so it works for both Session (which has columns 3-4)
     and Library (which doesn't, but the rule is a no-op there). */
  .grid tr td:nth-child(3):not(:last-child),
  .grid tr td:nth-child(4):not(:last-child) {
    grid-column: 1 / -1;
    display: inline;
    font-size: 11px;
    color: var(--text-dim);
    line-height: 1.25;
  }
  /* Put a small spacing between inline chips. */
  .grid tr td:nth-child(3):not(:last-child) { margin-right: 6px; }

  /* Updated (column 5) — small muted text on its own line, right-aligned
     so the relative timestamps ("5 min ago") sit close to the trailing
     edge of the card. */
  .grid tr td:nth-child(5):not(:last-child) {
    grid-column: 1 / -1;
    font-size: 11px;
    color: var(--text-dim);
    line-height: 1.25;
    text-align: right;
  }

  /* Hide the dedicated "library-cell" column on mobile. The "📚 Library"
     action is now available as an inline button in the actions row
     (conditional on a library entry existing), so this column would
     just duplicate that affordance and make Session cards taller than
     Library cards. Hiding it keeps the two card layouts identical. */
  .grid td.library-cell { display: none; }
}

.muted { color: var(--text-dim); }
.small { font-size: 12px; }
.mono  { font-family: var(--mono); }

.chip {
  display: inline-block;
  padding: 2px 8px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 11px;
  color: var(--text-dim);
}
/* Tiny interactive chips used by the per-row +1 / −1 quick-grant
   controls on the admin page (D-108c). Smaller padding/font than
   .chip so a row of four (sessions +1/-1, library +1/-1) fits on
   one line next to the Legacy cap input. */
.chip-mini {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  padding: 1px 6px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  line-height: 1.2;
  transition: background 80ms ease, border-color 80ms ease;
}
.chip-mini:hover:not(:disabled) {
  background: var(--bg-2);
  border-color: var(--accent);
}
.chip-mini:active:not(:disabled) {
  transform: translateY(1px);
}
.chip-mini:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.chip-mini-positive { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--border)); }
.chip-mini-negative { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, var(--border)); }
.admin-edit-row {
  display: flex;
  gap: 6px;
  align-items: center;
}
.admin-edit-row input { flex: 1 1 auto; min-width: 0; }

.status-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: middle;
}
.status-dot.s-in_progress  { background: var(--warn); }
.status-dot.s-ready       { background: var(--ok); }
.status-dot.s-finalized   { background: var(--accent); }

/* Colored status badges: replace the dot + text pattern with a
   single pill. Used in the Sessions table (and anywhere a session
   status needs to be shown as a badge rather than a dot). */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: capitalize;
  line-height: 1;
}
.status-badge.s-in_progress { background: rgba(210, 153, 34, 0.15); color: var(--warn); }
.status-badge.s-ready      { background: rgba(63, 185, 80, 0.15);  color: var(--ok); }
.status-badge.s-finalized  { background: rgba(124, 92, 255, 0.15); color: var(--accent-text); }
/* Session lifecycle statuses (History table). ``draft`` is the free
   pre-finalize state, ``review`` sits between draft and finalize, and
   ``finalized`` matches the library/agent badge above. */
.status-badge.s-awaiting_intent,
.status-badge.s-draft      { background: rgba(139, 148, 158, 0.18); color: var(--muted); }
.status-badge.s-review     { background: rgba(88, 166, 255, 0.15); color: var(--accent-text); }
.status-badge.s-complete   { background: rgba(124, 92, 255, 0.15); color: var(--accent-text); }
.status-badge.s-archived   { background: rgba(139, 148, 158, 0.12); color: var(--muted); }

/* Slightly smaller on mobile so the chips row stays tight. */
@media (max-width: 640px) {
  .status-badge {
    padding: 2px 7px;
    font-size: 10.5px;
  }
}

.actions { display: flex; gap: 6px; }

.card {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-top: 16px;
  box-shadow: var(--shadow);
}

.step-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
}
.step-key {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 8px;
  background: rgba(124, 92, 255, 0.15);
  color: var(--accent-text);
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
}
.question { margin-top: 4px; }
.hint {
  color: var(--text-dim);
  font-size: 13px;
  background: rgba(124, 92, 255, 0.06);
  border-left: 3px solid var(--accent);
  padding: 8px 12px;
  border-radius: 0 6px 6px 0;
  margin: 12px 0;
}

.progress-wrap { width: 280px; }
.progress-bar {
  width: 100%;
  height: 8px;
  background: var(--bg-3);
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.progress-fill {
  height: 100%;
  width: 100%;
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: transform 0.3s ease;
}

.nav { margin-top: 16px; }

.error {
  background: rgba(248, 81, 73, 0.1);
  border: 1px solid rgba(248, 81, 73, 0.4);
  color: #ffb4ad;
  padding: 8px 12px;
  border-radius: var(--radius);
  margin-top: 12px;
  font-size: 13px;
}
.success {
  background: rgba(63, 185, 80, 0.1);
  border: 1px solid rgba(63, 185, 80, 0.4);
  color: #7ed48d;
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-top: 12px;
  font-size: 13px;
}
.success code {
  background: var(--bg-3);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: var(--mono);
}

.editor-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 18px;
}
@media (max-width: 800px) {
  .editor-grid { grid-template-columns: 1fr; }
}
.pane {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}
.pane-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-dim);
  margin-bottom: 8px;
}
.rendered {
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  margin-top: 8px;
  max-height: 520px;
  overflow: auto;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 200;
}
.modal {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 22px;
  width: 460px;
  max-width: 90vw;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
}
.modal h3 { margin-top: 0; }
.modal label {
  display: block;
  margin-top: 12px;
  font-size: 12px;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.modal-actions { margin-top: 20px; }

/* ---- Modal card (Review Wizard) -----------------------------------
   The library's "Review Wizard" modal needs a larger surface than the
   default .modal (a single small dialog) so we add a card variant
   with header / body / footer rows and a scrollable middle. The
   backdrop click + Esc close behaviour is wired in the HTML, not here. */
.modal-card {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 560px;
  max-width: 90vw;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
.modal-card-large {
  width: 760px;
}

/* Smaller variant for compact single-purpose modals (e.g. the
   Change-password form). Keeps the form focused without dominating
   the page. */
.modal-card-small {
  width: 420px;
}

/* Inline success text inside an info/error pill — used by the
   Change-password modal to confirm "Password updated, signing you
   out…" right before the signOut() call runs. */
.success-text {
  background: rgba(63, 185, 80, 0.12);
  border: 1px solid rgba(63, 185, 80, 0.35);
  color: #6ec36e;
}
.modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
}
.modal-body {
  padding: 18px 20px;
  overflow-y: auto;
  flex: 1 1 auto;
}
.modal-footer {
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  background: var(--bg-2);
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

/* ---- Review Wizard step cards --------------------------------------
   Each interview step is rendered as a small card with the step
   number, fact_key chip, question text, the selected answer values
   (joined with commas), and the free-text "Other" reply (when the
   user picked __other__ and typed something). Matches the dark
   theme used elsewhere in the SPA. */
.wizard-step {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 10px;
  background: var(--bg-3);
}
.wizard-step:last-child { margin-bottom: 0; }
.wizard-step-header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}
.wizard-step-question {
  font-size: 14px;
  margin: 4px 0 8px 0;
  color: var(--text);
}
.wizard-step-answer,
.wizard-step-other {
  font-size: 13px;
  color: var(--text-dim);
  margin-top: 4px;
}
.wizard-step-answer span:not(.muted),
.wizard-step-other span:not(.muted) {
  color: var(--text);
  margin-left: 4px;
}

.back { color: var(--text-dim); font-size: 13px; }

.footer {
  text-align: center;
  padding: 20px;
  color: var(--text-dim);
  font-size: 12px;
  border-top: 1px solid var(--border);
  margin-top: 40px;
}

/* ---- Debug panel (AI call log) -----------------------------------------
   A slide-up drawer that appears above the footer when the user toggles
   the "Debug log" button. Each row is color-coded: green for clean OK,
   yellow for retried (recovered from an empty response), red for hard
   failures. The columns are: timestamp, op label, model, HTTP status,
   duration, prompt/system/num_predict/timeout/response_len payload
   summary, and a flag for retried / failed. */
.footer-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
}
.debug-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  padding: 4px 10px;
}
.debug-count {
  display: inline-block;
  min-width: 18px;
  padding: 0 6px;
  height: 16px;
  line-height: 16px;
  border-radius: 999px;
  background: rgba(124, 92, 255, 0.3);
  color: var(--accent-text);
  font-size: 10px;
  font-weight: 700;
  text-align: center;
}
.debug-panel {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  max-height: 45vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.5);
  z-index: 150;
  animation: debug-slide-up 0.18s ease-out;
}
@keyframes debug-slide-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
.debug-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  background: var(--bg-3);
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
}
.debug-title {
  font-size: 13px;
  font-weight: 600;
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.debug-actions {
  display: flex;
  gap: 6px;
}
.debug-actions button {
  font-size: 11px;
  padding: 4px 9px;
}
.debug-body {
  overflow: auto;
  flex: 1 1 auto;
  padding: 4px 0;
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.45;
}
.debug-row {
  padding: 6px 16px;
  border-bottom: 1px solid rgba(44, 51, 64, 0.5);
}
.debug-row:last-child { border-bottom: none; }
.debug-row-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.debug-row.debug-ok    { border-left: 3px solid var(--ok); }
.debug-row.debug-warn  { border-left: 3px solid var(--warn); background: rgba(210, 153, 34, 0.05); }
.debug-row.debug-err   { border-left: 3px solid var(--err);  background: rgba(248, 81, 73, 0.06); }

.debug-time   { color: var(--text-dim); font-size: 11px; }
.debug-op {
  font-weight: 600;
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(124, 92, 255, 0.15);
  color: var(--accent-text);
  text-transform: lowercase;
  letter-spacing: 0.3px;
}
.debug-model  { color: var(--text); font-size: 11px; }
.debug-status { color: var(--text-dim); font-size: 11px; }
.debug-dur    { color: var(--text-dim); font-size: 11px; }
.debug-len    {
  color: var(--text-dim);
  font-size: 10.5px;
  flex: 1 1 auto;
  min-width: 200px;
  word-break: break-word;
}
.debug-flag {
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(210, 153, 34, 0.2);
  color: var(--warn);
  border: 1px solid rgba(210, 153, 34, 0.4);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.debug-flag-err {
  background: rgba(248, 81, 73, 0.2);
  color: var(--err);
  border-color: rgba(248, 81, 73, 0.4);
}
.debug-err-text {
  color: #ffb4ad;
  font-size: 11px;
  margin-top: 4px;
  word-break: break-word;
  white-space: pre-wrap;
  font-family: var(--mono);
}
.debug-empty {
  padding: 24px;
  text-align: center;
  color: var(--text-dim);
  font-size: 13px;
}

/* ---- Session header topic pill + meta row -------------------------
   Used by the library detail page to display the topic + last-saved
   timestamp. Kept after the v1 dead-code strip so the pill remains
   styled the same. */
.session-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}
.topic-pill {
  display: inline-block;
  padding: 4px 12px;
  background: rgba(124, 92, 255, 0.15);
  border: 1px solid rgba(124, 92, 255, 0.4);
  color: var(--accent-text);
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3px;
}

/* ---- Full editor (single-pane preview, library detail) ------------ */
.full-editor {
  width: 100%;
  min-height: 500px;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.55;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  color: var(--text);
  resize: vertical;
}
.full-editor:focus {
  border-color: var(--accent);
  outline: none;
}

/* ---- Inline rename (sessions table + library detail heading) ----------- */
.inline-rename {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}
.inline-rename input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 9px;
  font-size: 13px;
  /* Don't expand to fill the whole cell; keep inline-rename compact. */
  width: auto;
}
.inline-rename button.small {
  padding: 4px 9px;
  font-size: 12px;
  line-height: 1;
  flex: 0 0 auto;
}
/* Library detail: heading-sized rename. The input takes the full heading
   width so the user can edit long names. */
.library-name-rename {
  width: 100%;
  max-width: 540px;
}
.library-name-rename input[type="text"] {
  font-size: 18px;
  font-weight: 600;
  padding: 7px 11px;
}
.library-detail-title {
  margin: 4px 0 8px;
}
/* Long agent names can otherwise overflow the flex container or force
   horizontal scroll on narrow viewports. ``overflow-wrap: anywhere``
   lets the title break inside a long word (e.g. a URL slug) instead
   of breaking the layout. */
.library-detail-title h2 {
  overflow-wrap: anywhere;
  word-break: break-word;
}
/* Inline rename pencil (✏️): upgrade from a tiny text button to a
   proper circular 44px minimum tap target so it's comfortable on
   touch devices. Applies to both desktop and mobile. */
.library-detail-title button.ghost.small {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  flex-shrink: 0;
}

/* =====================================================================
   V2 WIZARD (embedded in the SPA)
   =====================================================================
   Originally lived in v2.css as a standalone page. Now ported here so
   the wizard lives inside the same shell (topbar, footer, debug panel)
   as the dashboard and library. Visual language matches v1 (purple
   --accent, dark theme) rather than the original blue/light mode. */

/* Wizard container: slightly narrower than the dashboard's 1100px so the
   multiple-choice cards feel like a focused interview, not a data table. */
.v2-wizard {
  max-width: 820px;
  margin: 0 auto;
  position: relative;
}

/* ---- Wizard "thinking" overlay -----------------------------------
   Shown by ``v2Thinking`` while a model-backed action is in flight
   (start interview, answer, skip, generate draft, finalize). The screen
   body is hidden and a clock + question-card skeleton takes its place;
   the stepper stays visible above to preserve progress context. JS
   reveals it only after a short delay so fast/offline answers never
   flash it. */
.v2-wizard.is-thinking > div:not(.wizard-stepper):not(.v2-thinking-overlay) {
  visibility: hidden;
}
.v2-wizard .wizard-stepper {
  position: relative;
  z-index: 2;
}
.v2-thinking-overlay {
  display: flex;
  position: absolute;
  inset: 0;
  z-index: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 40px 20px;
  text-align: center;
  background: var(--glass-panel-strong);
  border-radius: var(--radius);
}
/* The clock + skeleton are anchored to the viewport, not the section.
   The overlay backdrop still covers the whole (possibly very tall)
   wizard section, but on long screens like the Draft editor a
   viewport-centred card stays visible wherever the user has scrolled.
   ``position: fixed`` resolves against the viewport because no ancestor
   of the wizard sets a transform / filter / backdrop-filter. */
.v2-thinking-card {
  display: flex;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: min(420px, 90vw);
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
}
.v2-thinking-clock {
  position: relative;
  width: 44px;
  height: 44px;
  border: 2px solid var(--border);
  border-radius: 50%;
}
.v2-clock-hand {
  position: absolute;
  left: 50%;
  bottom: 50%;
  transform-origin: 50% 100%;
  background: var(--accent);
  border-radius: 2px;
}
.v2-clock-hour {
  width: 3px;
  height: 12px;
  margin-left: -1.5px;
  animation: v2-clock-spin 3.2s linear infinite;
}
.v2-clock-minute {
  width: 2px;
  height: 16px;
  margin-left: -1px;
  animation: v2-clock-spin 1.6s linear infinite;
}
.v2-thinking-label { margin: 0; font-weight: 600; }
.v2-thinking-sub { margin: 0; }
.v2-thinking-skeleton {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(360px, 80%);
  margin-top: 6px;
}
.v2-thinking-skeleton span {
  height: 12px;
  border-radius: 6px;
  background: var(--surface-3);
  animation: v2-skeleton-pulse 1.4s ease-in-out infinite;
}
.v2-thinking-skeleton span:nth-child(2) { width: 85%; }
.v2-thinking-skeleton span:nth-child(3) { width: 60%; }

@keyframes v2-clock-spin {
  to { transform: rotate(360deg); }
}
@keyframes v2-skeleton-pulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 0.9; }
}

.v2-header {
  margin-bottom: 18px;
}
.v2-header h1 {
  margin: 4px 0 6px;
  font-size: 24px;
}
.v2-header .subtitle {
  color: var(--text-dim);
  margin: 0;
}

/* ---- Topic picker ---- */
.topic-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-top: 18px;
}
.topic-card {
  display: block;
  text-align: left;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  cursor: pointer;
  color: var(--text);
  transition: border-color 0.15s ease, transform 0.15s ease, background 0.15s ease;
  font-family: inherit;
  width: 100%;
}
.topic-card:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
  background: var(--bg-3);
}
.topic-card h2 { margin: 0 0 4px; font-size: 16px; }
.topic-card p { margin: 0; color: var(--text-dim); font-size: 12px; line-height: 1.45; }

/* ---- Intent step ---- */
.intent-form textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
}
.intent-form textarea:focus {
  border-color: var(--accent);
}

/* ---- Wizard back link (sits below the header, above content) ---- */
.wizard-back {
  color: var(--text-dim);
  background: transparent;
  border: none;
  padding: 0;
  font-size: 13px;
  cursor: pointer;
  margin-bottom: 6px;
}
.wizard-back:hover {
  color: var(--accent-text);
  background: transparent;
}

/* ---- Question card (multiple-choice interview) ---- */
.question-card {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-top: 16px;
  box-shadow: var(--shadow);
}
.question-card h2 { margin: 0 0 6px; font-size: 18px; }
.question-card .hint {
  color: var(--text-dim);
  margin: 0 0 14px;
  font-size: 13px;
  font-style: italic;
  background: transparent;
  border: none;
  padding: 0;
}

/* Inline Next / Skip / Finish row on the interview card. Hidden on
   phones (see the max-width:640px block) where the sticky
   ``.v2-mobile-bar`` is the single action surface. */
.wizard-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* Desktop-only Cancel link, pushed to the far right of the action row so
   it is visually separated from the primary Next. */
.cancel-interview-inline {
  margin-left: auto;
  color: var(--err);
}

/* Multi-step Back hint: shown only after the user steps back, naming how
   many later answers a re-answer will discard. */
.wizard-revise-hint { margin: 12px 0 0; }

.choices { display: flex; flex-direction: column; gap: 8px; }
.choice {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
  font-size: 13px;
}
.choice:hover { border-color: var(--accent); }
.choice.selected {
  border-color: var(--accent);
  background: rgba(124, 92, 255, 0.10);
}
.choice input[type="radio"],
.choice input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
  accent-color: var(--accent);
  /* Larger tap target on mobile without affecting layout: the
     native input keeps its 18-20px visual size but the clickable
     box extends to 44px via negative margin + offset. */
  flex-shrink: 0;
  min-width: 20px;
  min-height: 20px;
}
/* Multi-select marker: a custom square checkbox. Native checkboxes
   render rounded on some mobile browsers (notably iOS Safari), which
   reads as a radio and blurs the single- vs multi-select distinction.
   Radios keep the native round control; checkboxes are squared off. */
.choice input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  width: 20px;
  height: 20px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.choice input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
.choice input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 2px;
  width: 5px;
  height: 10px;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.choice input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.choice-label { font-weight: 500; }
.choice-desc {
  color: var(--text-dim);
  font-size: 12px;
  margin-left: auto;
  text-align: right;
}

.other-box { margin-top: 14px; }
.other-box label {
  display: block;
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 4px;
}

/* ---- Source badge (shows whether the question came from AI or offline) ---- */
.source-badge {
  text-transform: uppercase;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.source-badge.ollama {
  color: var(--accent-text);
  border-color: rgba(124, 92, 255, 0.5);
  background: rgba(124, 92, 255, 0.1);
}
.source-badge.offline { color: var(--text-dim); }
.source-badge.cap {
  color: var(--err);
  border-color: rgba(248, 81, 73, 0.4);
  background: rgba(248, 81, 73, 0.08);
}
.source-badge.essential {
  color: var(--ok);
  border-color: rgba(63, 185, 80, 0.4);
  background: rgba(63, 185, 80, 0.08);
}

/* ---- Review screen (fact grid) ---- */
.review-grid { display: flex; flex-direction: column; gap: 10px; }
.fact-row {
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: start;
}
.fact-meta {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
}
.fact-key {
  font-weight: 600;
  color: var(--accent-text);
  font-family: var(--mono);
  font-size: 12px;
}
.fact-source {
  color: var(--text-dim);
  background: var(--bg-3);
  border: 1px solid var(--border);
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.fact-row textarea {
  width: 100%;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 8px;
  font-family: var(--sans);
  font-size: 13px;
  resize: vertical;
  min-height: 38px;
}

.add-fact {
  margin-top: 16px;
  background: var(--bg-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
}
.add-fact h3 { margin: 0 0 8px; font-size: 14px; }
.add-fact-row { display: flex; gap: 8px; }
.add-fact-row input {
  flex: 1;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 6px 10px;
  font-family: var(--sans);
  font-size: 13px;
}

.missing {
  margin-top: 12px;
  padding: 8px 12px;
  background: rgba(210, 153, 34, 0.08);
  border: 1px solid rgba(210, 153, 34, 0.35);
  border-radius: var(--radius);
  color: var(--warn);
  font-size: 13px;
}
.missing strong { font-weight: 600; }
.missing p { margin: 6px 0 0; }

/* ---- "Resume where you left off" banner (Review screen) ---- */
.resume-banner {
  margin: 14px 0 18px;
  padding: 14px 16px;
  background: rgba(56, 132, 255, 0.08);
  border: 1px solid rgba(56, 132, 255, 0.35);
  border-radius: var(--radius);
}
.resume-banner-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.resume-banner-icon {
  font-size: 14px;
  line-height: 1.4;
  color: var(--accent-text);
}
.resume-banner-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}
.resume-steps {
  margin-top: 12px;
  border-top: 1px solid var(--border);
  padding-top: 10px;
  max-height: 260px;
  overflow-y: auto;
}
.resume-step {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 0;
  font-size: 13px;
}
.resume-step + .resume-step { border-top: 1px dashed var(--border); }
.resume-step-q { color: var(--text-dim); }
.resume-step-a { color: var(--text); font-weight: 500; }

/* ---- Draft + final screens ---- */
.title-edit { margin-bottom: 10px; }
.title-edit label {
  display: block;
  font-size: 12px;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.title-edit input {
  width: 100%;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  font-family: var(--sans);
  font-size: 14px;
}

.draft-area {
  width: 100%;
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.55;
  resize: vertical;
  min-height: 320px;
}

.final-md {
  margin-top: 16px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.55;
  white-space: pre-wrap;
  max-height: 60vh;
  overflow-y: auto;
}

/* Wizard-mode adjustments: when the user is in the v2 wizard, the topbar
   nav links (Sessions | Library) become "exit" affordances rather than
   primary navigation. We visually mute them and let the wizard header
   take over as the main context. */
.v2-mode .topbar-nav .nav-link {
  color: var(--text-dim);
  opacity: 0.7;
}
.v2-mode .topbar-nav .nav-link:hover {
  opacity: 1.0;
}
.v2-mode .topbar-nav .nav-link.active {
  /* In v2 mode, no nav link is "active" — the wizard is. */
  background: transparent;
  border-color: transparent;
}

/* Stale-session toast (v2 wizard topic picker): shown when the user
   lands on the picker because v2ResumeSession could not find the
   session id (e.g. a deep-link to a deleted session). Auto-dismisses
   after 6 s in JS; the styling is deliberately warning-toned so it
   reads as informational, not as a hard error. */
.v2-resume-toast {
  margin: 12px 0;
  padding: 10px 14px;
  background: rgba(210, 153, 34, 0.08);
  border: 1px solid rgba(210, 153, 34, 0.35);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius);
  color: var(--warn);
  font-size: 13px;
  line-height: 1.45;
}
.v2-resume-toast::before {
  content: "ℹ️  ";
  margin-right: 2px;
}

/* ===================================================================
   AUTH GATE
   -------------------------------------------------------------------
   When the user is not authenticated we render a single full-page
   login/register card instead of the regular SPA shell. Everything
   inside the body is hidden (display: none) and a dedicated
   .auth-gate element becomes the only visible thing. The user picks
   between "Sign in" and "Create account" via two tab buttons; the
   form is rendered by inline JSX-style x-show blocks in the
   template. The header at the top of the card shows the brand
   identity. We deliberately keep all the styles here rather than
   scattering them so the gate can be tweaked in one place. */

.auth-gate {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 20% 20%, rgba(108, 99, 255, 0.15), transparent 40%),
    radial-gradient(circle at 80% 80%, rgba(0, 200, 180, 0.10), transparent 45%),
    var(--bg);
  padding: 20px;
  z-index: 1000;
  overflow-y: auto;
}

/* Fixed brand link in the top-left corner of the auth gate. Lives
   inside .auth-gate (which is ``position: fixed; inset: 0``) so we
   anchor it absolutely to the top-left of the viewport. Sits above
   the auth-card with z-index so it stays visible regardless of how
   the centered card positions. The pill background + hover
   treatment match the topbar brand-link so the affordance reads as
   "back to landing" without needing a separate toolbar. */
.auth-gate .auth-gate-brand {
  position: absolute;
  top: 20px;
  left: 24px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--surface-3);
  color: var(--text);
  text-decoration: none;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.2px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.12s ease;
}
.auth-gate .auth-gate-brand:hover {
  background: var(--surface-3);
  border-color: var(--surface-4);
  text-decoration: none;
  transform: translateY(-1px);
}
.auth-gate .auth-gate-brand:active {
  background: var(--surface-4);
  transform: translateY(0);
}
.auth-gate .auth-gate-brand .logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0c16;
  font-size: 15px;
  box-shadow: 0 4px 14px rgba(124, 92, 255, 0.4);
}
.auth-gate .auth-gate-brand .name { color: var(--text); }
@media (max-width: 480px) {
  .auth-gate .auth-gate-brand { top: 14px; left: 14px; padding: 6px 10px; font-size: 13px; }
  .auth-gate .auth-gate-brand .logo { width: 22px; height: 22px; font-size: 13px; }
}
.auth-gate .auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg, 12px);
  padding: 32px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
}
.auth-gate .auth-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: -4px -6px 8px;
  padding: 4px 6px;
  border-radius: 8px;
  color: inherit;
  text-decoration: none;
  transition: background 120ms ease;
}
.auth-gate .auth-brand:hover {
  background: var(--surface-2);
  text-decoration: none;
}
.auth-gate .auth-brand:active {
  background: var(--surface-4);
}
.auth-gate .auth-brand .logo {
  font-size: 24px;
}
.auth-gate .auth-brand h1 {
  font-size: 20px;
  margin: 0;
  letter-spacing: 0.2px;
}
.auth-gate .auth-brand .tagline {
  color: var(--muted);
  font-size: 13px;
  margin-bottom: 22px;
}
.auth-gate .auth-tabs {
  display: flex;
  gap: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 4px;
  margin-bottom: 22px;
}
.auth-gate .auth-tab {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--muted);
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.auth-gate .auth-tab.active {
  background: var(--accent, #6c63ff);
  color: white;
}
.auth-gate .auth-tab:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.auth-gate .auth-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}
.auth-gate .auth-field label {
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-weight: 600;
}
.auth-gate .auth-field input {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  transition: border-color 0.15s ease;
}
.auth-gate .auth-field input:focus {
  outline: none;
  border-color: var(--accent, #6c63ff);
}
.auth-gate .auth-submit {
  width: 100%;
  background: var(--accent, #6c63ff);
  color: white;
  border: none;
  border-radius: var(--radius);
  padding: 11px 16px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease;
  margin-top: 6px;
}
.auth-gate .auth-submit:hover:not(:disabled) {
  filter: brightness(1.1);
}
.auth-gate .auth-submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.auth-gate .auth-error {
  background: rgba(220, 50, 50, 0.12);
  border: 1px solid rgba(220, 50, 50, 0.35);
  border-left: 3px solid #d63232;
  border-radius: var(--radius);
  color: #f08080;
  padding: 10px 12px;
  font-size: 13px;
  margin-bottom: 14px;
  line-height: 1.4;
}
.auth-gate .auth-info {
  background: rgba(0, 153, 255, 0.08);
  border: 1px solid rgba(0, 153, 255, 0.25);
  border-radius: var(--radius);
  color: var(--muted);
  padding: 10px 12px;
  font-size: 12px;
  margin-top: 16px;
  line-height: 1.45;
}
.auth-gate .auth-warning {
  background: rgba(230, 160, 30, 0.12);
  border: 1px solid rgba(230, 160, 30, 0.35);
  border-left: 3px solid #e6a01e;
  border-radius: var(--radius);
  color: #e6b95e;
  padding: 10px 12px;
  font-size: 12px;
  margin-bottom: 14px;
  line-height: 1.45;
}
.auth-gate .auth-hint {
  text-align: center;
  font-size: 12px;
  color: var(--muted);
  margin-top: 14px;
}
.auth-gate .auth-hint a {
  color: var(--accent-text, #6c63ff);
  text-decoration: none;
}
.auth-gate .auth-hint a:hover {
  text-decoration: underline;
}
.auth-gate .auth-hint a.disabled {
  opacity: 0.5;
  pointer-events: none;
}
.auth-gate .auth-hint.auth-hint-error {
  color: var(--err);
  text-align: left;
}
.auth-gate .auth-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--muted);
  margin: -4px 0 10px;
  cursor: pointer;
  user-select: none;
}
.auth-gate .auth-check input {
  width: auto;
  margin: 0;
  accent-color: var(--accent);
}

/* When the gate is visible the rest of the SPA shell + main
   content is hidden. We use a body class (toggled from JS) instead
   of an inline style so the rest of the styles don't have to know
   about the gate at all. */
body.auth-locked #app {
  display: none !important;
}

/* "Sign out" button + username pill in the topbar. Inline with the
   status pills so the user always knows who they're logged in as. */
.topbar .auth-user {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(108, 99, 255, 0.12);
  color: var(--accent-text, #6c63ff);
  font-size: 12px;
  font-weight: 600;
  border: 1px solid rgba(108, 99, 255, 0.3);
  /* Clickable affordance: the auth-user pill routes to the Account
     page (defined in goAccount()). We deliberately keep the
     padding/typography the same and only swap the visual treatment
     so the pill still reads as a status badge at a glance. */
  cursor: pointer;
  user-select: none;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.04s;
}
.topbar .auth-user:hover {
  background: rgba(108, 99, 255, 0.22);
  border-color: rgba(108, 99, 255, 0.55);
}
.topbar .auth-user:active {
  background: rgba(108, 99, 255, 0.30);
  transform: translateY(1px);
}
.topbar .auth-user:focus-visible {
  outline: 2px solid var(--accent, #6c63ff);
  outline-offset: 2px;
}
.topbar .auth-user .role {
  color: var(--muted);
  font-weight: 500;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
/* Chevron after the username signals the pill is clickable and
   routes to Account. Kept subtle (text-dim colour, tiny font) so it
   doesn't compete with the username / role badge — it just hints
   "this is actionable". */
.topbar .auth-user .auth-user-chevron {
  margin-left: 4px;
  font-size: 9px;
  color: var(--text-dim);
  opacity: 0.7;
  transition: opacity 0.15s ease;
}
.topbar .auth-user:hover .auth-user-chevron { opacity: 1; }
.topbar .auth-signout {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  padding: 4px 10px;
  border-radius: var(--radius);
  font-size: 12px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.topbar .auth-signout:hover {
  border-color: var(--accent, #6c63ff);
  color: var(--accent-text, #6c63ff);
}

/* ===================================================================
   DASHBOARD PAGE
   -------------------------------------------------------------------
   The Dashboard page is the default post-login landing. It groups
   system status into first-class cards (Ollama / DB / Default model)
   + summary cards for sessions and library entries. The grid uses
   auto-fit + minmax so cards wrap responsively on narrow viewports; on
   a wide screen they form a 3-column row, on a phone they stack.
   Status cards (``status-card``) get a coloured left border based on
   health (``is-ok`` / ``is-warn`` / ``is-err``). Summary cards
   (``summary-card``) are clickable buttons that jump to the
   matching topbar tab. */
.dashboard-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
  margin: 8px 0 4px 0;
}

/* On phones the auto-fit grid collapses OK to a single column, but the
   240px minimum still leaves the cards feeling cramped below 380px
   viewports. We lower the minimum and tighten the gutter. */
@media (max-width: 480px) {
  .dashboard-grid { grid-template-columns: 1fr; gap: 12px; }
  .dashboard-card { padding: 14px 16px; }
}

.dashboard-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px 20px;
  border-radius: 10px;
  background: var(--bg-2, #161b22);
  border: 1px solid var(--border, #2c3340);
  text-align: left;
  color: inherit;
  font: inherit;
  cursor: default;
  transition: border-color 0.15s ease, transform 0.1s ease, background 0.15s ease;
}

.dashboard-card.clickable {
  cursor: pointer;
}
.dashboard-card.clickable:hover {
  border-color: var(--accent, #7c5cff);
  background: rgba(124, 92, 255, 0.08);
  transform: translateY(-1px);
}
.dashboard-card.clickable:active {
  transform: translateY(0);
}
.dashboard-card.clickable:disabled {
  cursor: not-allowed;
  opacity: 0.55;
  transform: none;
}

.dashboard-card.status-card {
  border-left: 4px solid var(--text-dim, #8b949e);
}
.dashboard-card.status-card.is-ok {
  border-left-color: var(--ok, #3fb950);
}
.dashboard-card.status-card.is-warn {
  border-left-color: var(--warn, #d29922);
}
.dashboard-card.status-card.is-err {
  border-left-color: var(--err, #f85149);
}

.dashboard-card-icon {
  font-size: 24px;
  line-height: 1;
  flex-shrink: 0;
  padding-top: 2px;
}
.dashboard-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dashboard-card-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim, #8b949e);
  font-weight: 600;
}
.dashboard-card-value {
  font-size: 18px;
  font-weight: 600;
  color: var(--text, #e6edf3);
  word-break: break-word;
}
.dashboard-card-value.big {
  font-size: 32px;
  line-height: 1.1;
}
.dashboard-card-meta {
  font-size: 12px;
  color: var(--text-dim, #8b949e);
}

/* ===================================================================
   DASHBOARD — WELCOME BANNER
   -------------------------------------------------------------------
   Full-width hero card at the top of the Dashboard. Greets the
   signed-in user by name and shows a rotating pro-tip. The CTA
   ``+ New Agent`` button lives on the right edge so the user can
   jump straight into the wizard from the welcome surface. The
   layout collapses to a single column on phones (under 720px) so
   the text + CTA stack instead of fighting for horizontal space. */
.dashboard-section-heading {
  margin: 22px 0 10px 0;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.welcome-banner {
  background: linear-gradient(135deg,
    rgba(99, 102, 241, 0.15) 0%,
    rgba(56, 189, 248, 0.15) 100%);
  border: 1px solid rgba(99, 102, 241, 0.25);
  border-radius: 16px;
  padding: 22px 26px;
  margin-bottom: 6px;
}
.welcome-banner-main {
  display: flex;
  gap: 18px;
  align-items: center;
}
.welcome-banner-emoji {
  font-size: 38px;
  line-height: 1;
  flex-shrink: 0;
}
.welcome-banner-text {
  flex: 1;
  min-width: 0;
}
.welcome-banner-title {
  margin: 0 0 4px 0;
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
}
.welcome-banner-name {
  /* Solid colour: dropped the rainbow-gradient text fill in favour
     of a single high-contrast accent that reads consistently across
     themes. ``var(--accent)`` resolves to the same accent the rest of
     the UI uses (the indigo/blue pill colour), so the welcome
     banner's username now matches the design system instead of
     fighting it. */
  color: var(--accent-text);
  font-weight: 700;
}
.welcome-banner-sub {
  margin: 0 0 8px 0;
  color: var(--text-dim, #8b949e);
  font-size: 14px;
}
.welcome-banner-tip {
  margin: 0;
  font-size: 13px;
  display: flex;
  gap: 8px;
  align-items: flex-start;
  color: var(--text-dim, #a3b1c2);
}
.welcome-tip-label {
  flex-shrink: 0;
}
.welcome-tip-text {
  flex: 1;
  min-width: 0;
  font-style: italic;
  transition: opacity 450ms ease;
}
.welcome-tip-text.is-fading {
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .welcome-tip-text { transition: none; }
}
.welcome-banner-cta {
  flex-shrink: 0;
}
@media (max-width: 720px) {
  .welcome-banner-main { flex-direction: column; align-items: flex-start; }
  .welcome-banner-cta { align-self: stretch; }
  .welcome-banner-cta .primary {
    width: 100%;
    /* Hero CTA on the welcome banner: 48px tall (above the 44px
       standard) so the primary action of the dashboard is the most
       comfortable tap target on the page. */
    min-height: 48px;
    font-size: 15px;
    font-weight: 600;
  }
}

/* Dashboard mobile polish — applied to the welcome banner, status
   cards, and getting-started checklist. Above ~640px we lean on the
   desktop rules; below, every interactive surface grows to a
   comfortable thumb-friendly size, status-card icons get bigger so
   they read clearly in a stack, and checklist rows gain a minimum
   height so the tap target never collapses. */
@media (max-width: 640px) {
  .dashboard-card {
    padding: 18px 18px;
    min-height: 96px;
    gap: 16px;
  }
  .dashboard-card-icon { font-size: 28px; padding-top: 0; }
  .dashboard-card-value { font-size: 20px; }
  .dashboard-card-value.big { font-size: 36px; }
  .dashboard-card-label { font-size: 11.5px; }

  .welcome-banner { padding: 18px 18px; }
  .welcome-banner-cta .primary { padding: 12px 16px; }

  .checklist { padding: 4px 12px; }
  .checklist-item {
    padding: 14px 8px;
    min-height: 56px;
    gap: 12px;
  }
  .checklist-icon { width: 32px; height: 32px; font-size: 15px; }
  .checklist-label { font-size: 14.5px; }
  .checklist-sub { font-size: 13px; line-height: 1.4; }
  .checklist-done-badge { font-size: 11px; padding: 3px 8px; }

  /* Dashboard section headings: increase font + weight on mobile so
     the eye picks them up as distinct from card content. */
  .dashboard-section-heading { font-size: 13px; margin: 18px 0 8px; }
}

/* ===================================================================
   DASHBOARD — GETTING-STARTED CHECKLIST
   -------------------------------------------------------------------
   Four-step onboarding list with auto-checking items. ``.done``
   swaps the numbered bullet for a ✅ check and shows a Done badge
   on the right. Used by the Dashboard's "Getting started" section
   so brand-new users see concrete progress on first sign-in. */
.checklist {
  background: var(--card-bg, var(--surface-1));
  border: 1px solid var(--border, var(--surface-3));
  border-radius: 14px;
  padding: 6px 14px;
}
.checklist-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 4px;
  border-bottom: 1px solid var(--border, var(--surface-2));
}
.checklist-item:last-child { border-bottom: none; }
.checklist-item.done { opacity: 0.7; }
.checklist-icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(99, 102, 241, 0.15);
  color: var(--accent-text);
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.checklist-item.done .checklist-icon {
  background: rgba(34, 197, 94, 0.18);
}
.checklist-body {
  flex: 1;
  min-width: 0;
}
.checklist-label {
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
}
.checklist-sub {
  margin-top: 2px;
}
.checklist-done-badge {
  background: rgba(34, 197, 94, 0.18);
  color: #4ade80;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
  flex-shrink: 0;
}
.checklist-item .ghost.small {
  flex-shrink: 0;
}

/* ===================================================================
   DASHBOARD — GETTING-STARTED PROGRESS CARD (D-87)
   -------------------------------------------------------------------
   Replaces the legacy 4-step checklist with a 3-step progress card.
   The legacy ``.checklist`` / ``.checklist-item`` rules above are
   kept as aliases so any external links / CSS overrides that still
   target them continue to render. The new ``.progress-*`` rules
   add a header progress fill bar, a Dismiss button, and a
   re-show link that appears once the card has been dismissed. */
.progress-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.progress-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--border, var(--surface-2));
}
.progress-row:last-child { border-bottom: none; }
.progress-row.done { opacity: 0.7; }
.progress-icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(99, 102, 241, 0.15);
  color: var(--accent-text);
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.progress-row.done .progress-icon {
  background: rgba(34, 197, 94, 0.18);
}
.progress-body {
  flex: 1;
  min-width: 0;
}
.progress-label {
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
}
.progress-sub {
  margin-top: 2px;
}
.progress-done-pill {
  background: rgba(34, 197, 94, 0.18);
  color: #4ade80;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
  flex-shrink: 0;
}
.progress-cta,
.progress-row .ghost.small {
  flex-shrink: 0;
}
.progress-card {
  background: var(--card-bg, var(--surface-1));
  border: 1px solid var(--border, var(--surface-3));
  border-radius: 14px;
  padding: 6px 14px;
}
.progress-card-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 0 6px;
}
.progress-card-header .dashboard-section-heading {
  flex: 1 1 auto;
  margin: 0;
}
.progress-fill-track {
  flex: 1 1 100%;
  height: 4px;
  background: var(--border, var(--surface-3));
  border-radius: 2px;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  width: 100%;
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
  background: var(--accent, #7c5cff);
  transition: transform 0.25s ease;
  border-radius: 2px;
}
.progress-dismiss {
  flex-shrink: 0;
  align-self: center;
}
.coverage-meter {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 420px;
}
.re-show-link {
  display: inline-block;
  margin: 4px 2px 12px;
  cursor: pointer;
  text-decoration: underline;
}
.re-show-link:hover { opacity: 0.85; }

/* ===================================================================
   DASHBOARD — ACTIVITY FEED
   -------------------------------------------------------------------
   Two-column "Latest sessions" / "Latest library saves" layout.
   Each column has a header row with icon + title + "View all" link
   and a list of activity rows. ``.activity-item`` is clickable;
   hover deepens the background. ``.activity-dot`` is a coloured
   circle that shows the session's current status; library rows use
   a small 📚 emoji instead. Empty states use a friendly explainer
   that doubles as a CTA. */
.activity-feed-row { margin-top: 4px; }
.activity-feed {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}
.activity-column {
  background: var(--card-bg, var(--surface-1));
  border: 1px solid var(--border, var(--surface-3));
  border-radius: 14px;
  padding: 14px 16px;
}
.activity-column-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.activity-column-icon { font-size: 16px; }
.activity-column-title {
  flex: 1;
  font-weight: 600;
  font-size: 14px;
}
.activity-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.activity-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 6px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 120ms ease;
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
}
.activity-item:hover { background: var(--surface-2); }
.activity-item:focus-visible {
  outline: 2px solid var(--focus-ring, var(--accent));
  outline-offset: 2px;
}
.activity-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #6366f1;
  flex-shrink: 0;
}
.activity-dot.s-done { background: #22c55e; }
.activity-dot.s-in_progress { background: #f59e0b; }
.activity-dot.s-failed { background: #ef4444; }
.activity-dot.s-abandoned { background: #6b7280; }
.activity-dot.s-library {
  background: transparent;
  font-size: 14px;
  width: auto;
  height: auto;
}
.activity-body { flex: 1; min-width: 0; }
.activity-title {
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.activity-meta {
  display: flex;
  gap: 6px;
  align-items: center;
}
.activity-meta-sep { opacity: 0.5; }
.activity-empty {
  padding: 14px 4px;
  font-size: 13px;
  color: var(--text-dim, #8b949e);
}
.activity-empty a {
  color: var(--accent-text);
  text-decoration: none;
  cursor: pointer;
}
.activity-empty a:hover { text-decoration: underline; }

/* Activity feed mobile polish: bigger rows so taps land cleanly,
   slightly larger gap between items, more padding on the column
   headers, and bigger chevron / arrow indicators. Below 480px the
   columns collapse to a single column (already handled by the
   auto-fit minmax on the parent grid). */
@media (max-width: 640px) {
  .activity-feed { gap: 12px; }
  .activity-column { padding: 12px 14px; }
  .activity-column-header { gap: 10px; margin-bottom: 6px; }
  .activity-column-icon { font-size: 18px; }
  .activity-column-title { font-size: 14px; }
  .activity-item {
    min-height: 48px;
    padding: 10px 8px;
    gap: 12px;
    border-radius: 10px;
    /* Subtle row separator for clarity on mobile. */
    border-bottom: 1px solid var(--border, var(--surface-2));
  }
  .activity-list li:last-child .activity-item { border-bottom: none; }
  .activity-dot { width: 12px; height: 12px; }
  .activity-title { font-size: 14.5px; }
  .activity-meta { gap: 8px; font-size: 12px; }
}

/* ===================================================================
   DASHBOARD — QUICK-START TEMPLATES
   -------------------------------------------------------------------
   Three (or fewer, if the topic list is sparse) featured topic cards
   that jump straight into the v2 wizard pre-loaded with that topic.
   The cards mirror the wizard's topic-grid style so the transition
   feels native. Hover lifts the card and shifts the arrow. */
.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
}

/* Single column on phones for the template gallery. */
@media (max-width: 480px) {
  .template-grid { grid-template-columns: 1fr; }
}

/* Quick-start row (D-97). Fixed columns instead of ``auto-fit`` so the
   seven curated cards + the synthetic "More templates" tile always fill
   two full desktop rows (4 + 4). The two-class selector outranks every
   generic ``.template-grid`` rule above — including the single-column
   phone fallback — so this row steps 4 -> 2 and never collapses to one
   column (four rows of two on phones). */
.template-grid.template-grid-quickstart {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 899px) {
  .template-grid.template-grid-quickstart {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.template-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  background: var(--card-bg, var(--surface-1));
  border: 1px solid var(--border, var(--surface-3));
  border-radius: 12px;
  padding: 14px 16px;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--text);
  transition: transform 120ms ease, border-color 120ms ease, background 120ms ease;
}
.template-card:hover {
  transform: translateY(-2px);
  border-color: rgba(99, 102, 241, 0.4);
  background: rgba(99, 102, 241, 0.06);
}
.template-card-emoji {
  font-size: 24px;
  line-height: 1;
}
.template-card-title {
  font-weight: 600;
  font-size: 15px;
  margin-top: 2px;
}
.template-card-desc {
  font-size: 13px;
  color: var(--text-dim, #8b949e);
  line-height: 1.4;
}
.template-card-arrow {
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 16px;
  color: var(--text-dim, #8b949e);
  transition: transform 120ms ease, color 120ms ease;
}
.template-card:hover .template-card-arrow {
  transform: translateX(4px);
  color: var(--accent-text);
}
/* D-97: the synthetic "Other…" card in the quick-start row reads as
   a navigation tile (not a topic card), so we give it a dashed
   border, ghost background, and a centred vertical layout. We
   suppress the normal hover lift so it doesn't pretend to be a
   real pickable card. ``template-card-arrow`` keeps its colours
   but the down-chevron makes the intent obvious even at rest. */
.template-card-other {
  background: var(--surface-1);
  border: 1px dashed rgba(125, 125, 125, 0.5);
  justify-content: center;
}
.template-card-other:hover {
  box-shadow: none;
  transform: none;
}
/* D-99: hide any element with Alpine's ``x-cloak`` attribute until
   the SPA initialises, so the All-templates section doesn't flash
   visible on initial page load before Alpine flips the boolean.
   Pairs with ``x-cloak`` on the wrapper div in index.html. */
[x-cloak] { display: none !important; }
/* D-99: when the All-templates section is open, rotate the
   down-chevron on the "More templates" tile so the disclosure
   direction reads at a glance. ``aria-expanded="true"`` is set by
   the markup binding so this selector works without any extra
   state. */
.template-card-other .template-card-arrow {
  transition: transform 150ms ease;
}
.template-card-other[aria-expanded="true"] .template-card-arrow {
  transform: rotate(180deg);
}

/* ===================================================================
   ACCOUNT PAGE
   -------------------------------------------------------------------
   Cards are laid out by `.account-grid` (below): Profile / Security /
   Two-factor share the compact top row, and Saved Cards, Purchase,
   Plan & credits and Recent activity each span the full width. The
   container caps the page width so the grid stays readable. */
.account-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.account-grid {
  display: grid;
  /* Explicit 3-column grid with named areas:
       row 1: profile | security | twofactor
       row 2: savedcards                    (spans the full width)
       row 3: purchase                      (spans the full width)
       row 4: credits                       (spans the full width)
       row 5: activity                      (spans the full width)
     The compact Profile / Security / Two-factor cards share row 1,
     then Payment methods and Purchase credits each get a full-width
     row. Payment methods comes first (save a card, then buy) and needs
     the full width because its per-card rows (brand + last4 + expiry +
     Remove button) would force ugly wrapping in a column. Every card
     MUST carry a matching `grid-area`
     (see the assignments below): an unassigned card auto-places after
     the named rows, which is how the Two-factor card used to fall to
     the very bottom.
     The grid is capped at 960px and centered so the layout stays
     balanced and readable on ultra-wide monitors — spreading out
     the compact top cards across 1400px+ would look broken. */
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "profile    security   twofactor"
    "savedcards savedcards savedcards"
    "purchase   purchase   purchase"
    "credits    credits    credits"
    "activity   activity   activity";
  gap: 16px;
  margin: 8px 0;
  max-width: 960px;
  margin-inline: auto;
}

/* ---- Mobile-friendly Account page (≤640px) ------------------------
   Tighter padding, smaller icon/typography, safe-area padding so the
   bottom card doesn't slide under the iPhone home indicator. The
   breakpoint matches the existing .topbar mobile collapse at 640px
   so the Account page feels visually consistent with the rest of
   the SPA's mobile surface. */
@media (max-width: 640px) {
  /* Restructure the grid for single-column flow on phones. Every
     card that spanned multiple columns collapses back to a single
     column here, ordered Profile / Security / Two-factor / Saved Cards
     / Purchase / Plan & credits / Recent activity. The max-width
     is removed so the grid fills the available mobile width. */
  .account-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "profile"
      "security"
      "twofactor"
      "savedcards"
      "purchase"
      "credits"
      "activity";
    gap: 12px;
    max-width: none;
    margin-inline: 0;
  }
  .account-card {
    padding: 16px;
  }
  .account-card-icon {
    font-size: 24px;
  }
  .account-card-value {
    font-size: 16px;
  }
  .account-page {
    padding-bottom: max(16px, env(safe-area-inset-bottom));
  }
  /* Plan picker on phones: horizontal swipe carousel so the three
     plan cards don't stack into a long vertical scroll. The
     `scroll-snap-type` makes each card land flush at the start of
     the visible area, and `flex: 0 0 85vw` leaves a peek of the
     next card so users know there's more. */
  .plan-picker {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 12px;
    padding-bottom: 4px;
    /* Hide scrollbar for a cleaner look while keeping swipeability. */
    scrollbar-width: thin;
  }
  .plan-picker .plan-card {
    flex: 0 0 85vw;
    scroll-snap-align: start;
  }
  .saved-card-row {
    flex-wrap: wrap;
    gap: 6px 10px;
  }
  .saved-card-brand,
  .saved-card-last4 {
    flex: 0 0 auto;
  }
  .saved-card-exp {
    flex: 1 1 100%;
  }
}

.account-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px 22px;
  border-radius: 10px;
  background: var(--bg-2, #161b22);
  border: 1px solid var(--border, #2c3340);
}

/* Grid-area assignments for the Account cards.
   Linked to the named areas declared in `.account-grid` above.
   Every card in the grid MUST be listed here: an unassigned card
   auto-places into the first free cell, landing it after all the
   named rows (the Two-factor card used to render last this way). */
.profile-card     { grid-area: profile; }
.security-card    { grid-area: security; }
.two-factor-card  { grid-area: twofactor; }
.purchase-card    { grid-area: purchase; }
.saved-cards-card { grid-area: savedcards; }
.credits-card     { grid-area: credits; }
.activity-card    { grid-area: activity; }

.account-card-header {
  display: flex;
  align-items: center;
  gap: 14px;
}
.account-card-icon {
  font-size: 28px;
  line-height: 1;
}
.account-card-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim, #8b949e);
  font-weight: 600;
}
.account-card-value {
  font-size: 18px;
  font-weight: 600;
  color: var(--text, #e6edf3);
  word-break: break-word;
}

.account-card-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  border-bottom: 1px solid var(--surface-2);
}
.account-row:last-child {
  border-bottom: none;
}

/* ---- Tier & limits card --------------------------------------------
   Surfaces the per-tier subscription caps and live usage on the
   Account page. The card layout is two ``.tier-row`` blocks stacked
   vertically: each row has a label, a ``used / max`` value, an
   optional progress bar (hidden when the cap is unlimited), and a
   small "remaining" hint line. The ``.tier-badge`` is a coloured
   pill on the card header that maps each tier to a hue so the user
   can tell at a glance whether they're on free / starter / pro.
   Whatever the tier, the bar is clamped to 100% width so an
   over-quota state (used > cap) never breaks the layout. */
.tier-card {
  /* The shared .account-card supplies background + border; we only
     need to nudge the header so the badge sits flush right. */
}
.account-card-header {
  position: relative;
}
.tier-badge {
  margin-left: auto;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border: 1px solid transparent;
  white-space: nowrap;
}
.tier-badge.tier-free {
  color: #c9d1d9;
  background: rgba(139, 148, 158, 0.15);
  border-color: rgba(139, 148, 158, 0.35);
}
.tier-badge.tier-starter {
  color: #79c0ff;
  background: rgba(56, 139, 253, 0.15);
  border-color: rgba(56, 139, 253, 0.35);
}
/* "Paid" maps to a teal/cyan hue so it reads as a distinct middle
   step between Free (grey) and Pro (purple) on the Plan & Credits
   card and the change-tier modal. */
.tier-badge.tier-paid {
  color: #56d4dd;
  background: rgba(56, 211, 221, 0.15);
  border-color: rgba(56, 211, 221, 0.35);
}
.tier-badge.tier-pro {
  color: #d2a8ff;
  background: rgba(139, 92, 246, 0.18);
  border-color: rgba(139, 92, 246, 0.4);
}
.tier-badge.tier-default,
.tier-badge.tier-unlimited {
  color: #56d364;
  background: rgba(46, 160, 67, 0.15);
  border-color: rgba(46, 160, 67, 0.35);
}

.tier-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 0;
  border-bottom: 1px solid var(--surface-2);
}
.tier-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}
.tier-row-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
}
.tier-row-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text, #e6edf3);
}
.tier-row-value {
  font-size: 13px;
  color: var(--text-dim, #8b949e);
}
.tier-row-meta {
  font-size: 11px;
  color: var(--text-dim, #8b949e);
}

.tier-progress {
  position: relative;
  height: 6px;
  background: var(--surface-2);
  border-radius: 3px;
  overflow: hidden;
}
.tier-progress-fill {
  height: 100%;
  width: 100%;
  transform: scaleX(var(--progress, 0));
  transform-origin: left center;
  border-radius: 3px;
  background: linear-gradient(90deg, #388bfd, #1f6feb);
  transition: transform 0.3s ease;
}
.tier-progress-fill.is-warn {
  background: linear-gradient(90deg, #d29922, #9e6a03);
}
.tier-progress-fill.is-err {
  background: linear-gradient(90deg, #f85149, #b62324);
}
.tier-progress-fill.is-full {
  background: linear-gradient(90deg, #d2a8ff, #8957e5);
}

/* ---- Period footer (account page tier card) -----------------------
   Below the usage bars we show the cadence ("Monthly" / "Quarterly" /
   "Yearly"), the next-reset date, and a small override badge when
   the user has a per-user quota set that overrides the tier defaults. */
.tier-period-footer {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--surface-2);
  font-size: 12px;
  color: var(--text-dim, #8b949e);
}
.tier-period-footer-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.chip.period-chip {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 9px;
}
.chip.period-monthly {
  color: #79c0ff;
  border-color: rgba(56, 139, 253, 0.35);
  background: rgba(56, 139, 253, 0.10);
}
.chip.period-quarterly {
  color: #d2a8ff;
  border-color: rgba(139, 92, 246, 0.35);
  background: rgba(139, 92, 246, 0.10);
}
.chip.period-yearly {
  color: #56d364;
  border-color: rgba(46, 160, 67, 0.35);
  background: rgba(46, 160, 67, 0.10);
}

.override-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  background: rgba(210, 153, 34, 0.12);
  color: var(--warn, #d29922);
  border: 1px solid rgba(210, 153, 34, 0.35);
  letter-spacing: 0.04em;
}

/* ---- Topbar admin link --------------------------------------------
   Mirrors .nav-link but pulls right and uses a slightly different
   hue so admins can tell it's a separate management surface, not
   just another tab. */
.nav-link.nav-link-admin {
  margin-left: auto;
  color: var(--accent-text, #7c5cff);
  border-color: rgba(124, 92, 255, 0.25);
}
.nav-link.nav-link-admin:hover {
  background: rgba(124, 92, 255, 0.10);
  border-color: rgba(124, 92, 255, 0.5);
}
.nav-link.nav-link-admin.active {
  background: rgba(124, 92, 255, 0.20);
  border-color: rgba(124, 92, 255, 0.65);
}

/* ===================================================================
   ADMIN · USAGE MANAGEMENT PAGE
   -------------------------------------------------------------------
   A grid of cards, one per user, showing the per-tier limits that
   apply right now plus a one-click "Edit override" form. The grid
   is auto-fit so it wraps responsively (4 cols on a wide screen, 2
   cols on a tablet, 1 col on a phone). Each card surfaces: tier,
   period cadence chip, session cap, library cap, and the next reset
   date — all read-only when not in edit mode. The override flag is
   shown as a small "Custom override" badge next to the period chip
   when the user has a row in user_quotas; clicking Edit opens the
   inline edit form where the admin can change any of the three
   fields (period_type, max_sessions, max_library) and Save / Clear
   override. Saves / clears hit the JSON endpoints defined in
   promptskiller/web/api.py and the card reverts to read-only on
   success. */
.admin-page {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.admin-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding: 14px 18px;
  background: var(--bg-2, #161b22);
  border: 1px solid var(--border, #2c3340);
  border-radius: 10px;
  font-size: 13px;
  color: var(--text-dim, #8b949e);
}
.admin-summary-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.admin-summary-row strong {
  color: var(--text, #e6edf3);
  font-weight: 600;
}

.admin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 14px;
}

.admin-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  border-radius: 10px;
  background: var(--bg-2, #161b22);
  border: 1px solid var(--border, #2c3340);
}

.admin-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.admin-card-id {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.admin-card-id .username {
  font-size: 15px;
  font-weight: 600;
  color: var(--text, #e6edf3);
  word-break: break-word;
}
.admin-card-id .userid {
  font-size: 11px;
  color: var(--text-dim, #8b949e);
  font-family: var(--mono);
}
.admin-card-tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.admin-summary-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 0;
  border-top: 1px solid var(--surface-2);
}
.admin-summary-block:first-of-type {
  border-top: none;
  padding-top: 0;
}
.admin-summary-block-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim, #8b949e);
  font-weight: 600;
}
.admin-summary-block-value {
  font-size: 14px;
  color: var(--text, #e6edf3);
}

.override-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(210, 153, 34, 0.10);
  border: 1px solid rgba(210, 153, 34, 0.35);
  color: var(--warn, #d29922);
  font-weight: 600;
  letter-spacing: 0.03em;
}

.admin-edit {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 10px;
  margin-top: 4px;
  border-top: 1px dashed rgba(124, 92, 255, 0.25);
}
.admin-edit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
}
.admin-edit-grid label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim, #8b949e);
  margin-bottom: 4px;
  font-weight: 600;
}
.admin-edit-grid input,
.admin-edit-grid select {
  width: 100%;
  font-size: 13px;
  padding: 6px 9px;
  background: var(--bg, #0e1116);
}

.admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: flex-end;
}

/* ---- Admin toolbar: search + list/cards view toggle ------------------ */
.admin-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}
.admin-search {
  flex: 1 1 240px;
  min-width: 180px;
  font-size: 13px;
  padding: 7px 11px;
  border-radius: 8px;
  background: var(--bg, #0e1116);
  border: 1px solid var(--border, #2c3340);
  color: var(--text, #e6edf3);
}
.admin-search:focus {
  outline: none;
  border-color: var(--accent, #7c5cff);
  box-shadow: 0 0 0 2px rgba(124, 92, 255, 0.20);
}
.admin-view-toggle {
  display: inline-flex;
  border: 1px solid var(--border, #2c3340);
  border-radius: 8px;
  overflow: hidden;
  flex-shrink: 0;
}
.admin-view-toggle button {
  font-size: 12px;
  padding: 7px 14px;
  border: none;
  border-radius: 0;
  background: var(--bg-2, #161b22);
  color: var(--text-dim, #8b949e);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.admin-view-toggle button + button {
  border-left: 1px solid var(--border, #2c3340);
}
.admin-view-toggle button:hover:not(.active) {
  color: var(--text, #e6edf3);
}
.admin-view-toggle button.active {
  background: var(--accent, #7c5cff);
  color: #fff;
  font-weight: 600;
}
.admin-email-health {
  margin-bottom: 14px;
}
.admin-email-health .error,
.admin-email-health .success {
  font-size: 12px;
  line-height: 1.5;
}

/* ---- Admin: active AI backend summary line --------------------------- */
.admin-model-info {
  margin-bottom: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.admin-model-info code { font-size: 13px; color: var(--accent-text, #7c5cff); }

/* ---- Admin list/table view ------------------------------------------- */
.admin-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border, #2c3340);
  border-radius: 10px;
}
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.admin-table thead th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-dim, #8b949e);
  font-weight: 600;
  padding: 10px 14px;
  background: var(--bg-2, #161b22);
  border-bottom: 1px solid var(--border, #2c3340);
  white-space: nowrap;
}
.admin-table tbody td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--surface-2);
  vertical-align: middle;
}
.admin-table tbody tr:last-child td {
  border-bottom: none;
}
.admin-table tbody tr:hover {
  background: rgba(124, 92, 255, 0.05);
}
.admin-table-user {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.admin-table-user .mono {
  font-weight: 600;
  color: var(--text, #e6edf3);
  word-break: break-word;
}
.admin-th-actions,
.admin-table-actions {
  text-align: right;
  white-space: nowrap;
}
.admin-table-actions button {
  font-size: 12px;
  padding: 5px 10px;
}
.admin-table-actions button + button {
  margin-left: 6px;
}
.admin-table-actions .danger-text {
  padding: 5px 10px;
}

/* Text-only "danger" button used inside the admin edit form for
   "Clear override". Has a thin red border so it reads as
   destructive at a glance without being a full red filled button. */
.danger-text {
  color: var(--err, #f85149);
  background: transparent;
  border: 1px solid rgba(248, 81, 73, 0.4);
  cursor: pointer;
  padding: 5px 10px;
  border-radius: 6px;
  font-size: 12px;
  font-family: inherit;
  transition: background 120ms ease, border-color 120ms ease;
}
.danger-text:hover:not(:disabled) {
  background: rgba(248, 81, 73, 0.12);
  border-color: var(--err, #f85149);
}
.danger-text:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Admin page error / success banners match the existing inline
   .success / .error styles but are scoped so they don't collide
   with form-level errors elsewhere. */
.admin-banner {
  margin: 0 0 12px;
}

/* ====================================================================
   New UI components — appended below existing styles
   ==================================================================== */

/* 1. Toast Notification System */
.toast-stack {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 380px;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  padding: 12px 16px;
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 13px;
  box-shadow: var(--shadow);
  display: flex;
  align-items: center;
  gap: 10px;
  animation: toast-in 0.25s ease;
}
.toast.success { border-color: rgba(63,185,80,0.4); color: var(--ok); }
.toast.error   { border-color: rgba(248,81,73,0.4); color: var(--err); }
.toast.info    { border-color: rgba(124,92,255,0.4); color: var(--accent-text); }
.toast.leave { animation: toast-out 0.3s ease forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes toast-out { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-12px); } }

/* 2. Skeleton Loaders */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--bg-3);
  border-radius: 4px;
  height: 16px;
}
.skeleton.thin { height: 12px; }
.skeleton.thick { height: 24px; }
.skeleton.round { border-radius: var(--radius); }
.skeleton::after,
.skeleton-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, var(--surface-2) 50%, transparent 100%);
  transform: translateX(-100%);
  animation: skeleton-shimmer 1.2s infinite;
}
@keyframes skeleton-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* Skeleton placeholder containers: neutralize the hover/disabled
   styles their real counterparts inherit (``.summary-card``,
   ``.dashboard-card``, ``.grid tbody tr``) so the shimmer animation
   is the only thing visible. ``aria-hidden="true"`` on the markup
   keeps these out of the accessibility tree. */
.skeleton-card {
  cursor: default;
  pointer-events: none;
  opacity: 0.9;
  box-shadow: none;
  transform: none;
}
.skeleton-card:hover {
  background: var(--bg-2);
  border-color: var(--border);
  transform: none;
}
.skeleton-row { cursor: default; }
.skeleton-row:hover { background: transparent; box-shadow: 0 0 0 1px var(--border); }
.skeleton-row td { padding-top: 10px; padding-bottom: 10px; }
.skeleton-table thead th { color: var(--text-dim); }

/* Skeleton placeholder for the Library detail view: neutralizes the
   real action-button look so the shimmer reads as a placeholder
   instead of a disabled button. Sized by inline width/height so the
   same class works for every action slot in the bar. */
.skeleton-btn {
  cursor: default;
  pointer-events: none;
  opacity: 0.7;
  position: relative;
  overflow: hidden;
  background: var(--bg-3);
  border: 1px solid var(--border);
  height: 36px;
}
.library-detail-skeleton {
  padding: 8px 0;
}

/* 3. Wizard Progress Stepper */
.wizard-stepper {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 12px 16px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
}
.step {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
}
.step .dot {
  width: 20px; height: 20px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700;
  background: var(--bg-3);
  border: 1px solid var(--border);
}
.step.done .dot { background: var(--ok); color: var(--bg); border-color: var(--ok); }
.step.active .dot { background: var(--accent); color: #fff; border-color: var(--accent); }
.step.done, .step.active { color: var(--text); }
.step .label { display: inline; }
.step .sep { color: var(--text-dim); opacity: 0.4; margin: 0 2px; }
@media (max-width: 640px) {
  .wizard-stepper .step .label { display: none; }
  .wizard-stepper .step.active .label { display: inline; }
  .step .sep { display: none; }
}

/* 5. Empty States */
.empty-state {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-dim);
  max-width: 480px;
  margin: 24px auto;
}
.empty-state .emoji {
  font-size: 48px;
  display: block;
  margin: 0 auto 14px;
  width: 72px;
  height: 72px;
  line-height: 72px;
  background: var(--bg-3);
  border: 1px solid var(--border);
  border-radius: 50%;
  text-align: center;
}
.empty-state h3 { margin: 0 0 6px 0; font-size: 16px; color: var(--text); }
.empty-state p { margin: 0 0 16px 0; font-size: 13px; }
.empty-state .primary {
  margin-top: 4px;
  min-height: 40px;
  padding: 10px 18px;
}

/* 6. Mobile Hamburger Drawer */
.mobile-drawer-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  z-index: 250;
  display: none;
  touch-action: none;
}
.mobile-drawer-overlay.open { display: block; }
.mobile-drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 280px;
  background: var(--bg-2);
  border-left: 1px solid var(--border);
  z-index: 251;
  padding: 16px;
  padding-top: max(16px, env(safe-area-inset-top));
  padding-bottom: max(16px, env(safe-area-inset-bottom));
  padding-inline-start: max(16px, env(safe-area-inset-left));
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);
  transform: translateX(100%);
  transition: transform 0.25s ease;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overscroll-behavior: contain;
}
.mobile-drawer.open { transform: translateX(0); }
.mobile-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.mobile-drawer-head .drawer-close {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  width: 36px;
  height: 36px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 1.1rem;
}
.mobile-drawer .drawer-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  font-weight: 600;
}
.mobile-drawer .drawer-link {
  display: flex; align-items: center;
  padding: 12px 14px;
  color: var(--text-dim);
  text-decoration: none;
  border-radius: 8px;
  /* Comfortable tap target. */
  min-height: 48px;
  font-size: 15px;
  touch-action: manipulation;
}
.mobile-drawer .drawer-link:hover,
.mobile-drawer .drawer-link:active { background: var(--surface-2); color: var(--text); }
.mobile-drawer .drawer-link.active { color: var(--accent-text); background: rgba(124, 92, 255, 0.10); }
.hamburger {
  display: none; background: none; border: none;
  color: var(--text); font-size: 22px; cursor: pointer;
  /* Comfortable tap target on a hand-held device. */
  min-width: 44px;
  min-height: 44px;
  border-radius: 8px;
  padding: 0 10px;
}
.hamburger:active { background: var(--surface-2); }
@media (max-width: 768px) {
  .hamburger { display: inline-flex; align-items: center; justify-content: center; }
}

/* On very small phones the right-side drawer doesn't fit comfortably.
   Promote it to a bottom-sheet so the close action is in thumb range. */
@media (max-width: 420px) {
  .mobile-drawer {
    top: auto;
    right: 0;
    left: 0;
    width: 100%;
    max-width: 100%;
    border-left: none;
    border-top: 1px solid var(--border);
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
    transform: translateY(100%);
  }
  .mobile-drawer.open { transform: translateY(0); }
}

/* Mobile drawer footer (auth + sign out) ----------------------------
   The topbar collapses to brand + hamburger at <=768px, so the
   signed-in user pill and sign-out button need a home inside the
   drawer. This block pushes them to the bottom of the sheet with a
   hairline divider so they read as a separate "account" section. The
   footer element itself is always rendered so the layout doesn't
   shift when x-show toggles — the children handle their own visibility. */
.mobile-drawer-footer {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--surface-2);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.mobile-drawer-user {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 14px 0;
}
.mobile-drawer-username {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  /* The username is now an <a> tag routed to #account — strip the
     default link underline and add a hover affordance so users see
     it's interactive. */
  text-decoration: none;
  padding: 4px 0;
  border-radius: 4px;
  transition: color 0.15s ease;
}
.mobile-drawer-username:hover { color: var(--accent-text); }
.mobile-drawer-username:focus-visible {
  outline: 2px solid var(--accent, #6c63ff);
  outline-offset: 2px;
}
/* Chevron after the username — mirrors the topbar auth-user
   chevron so the two account entry points read consistently. */
.mobile-drawer-username .mobile-drawer-username-chevron {
  font-size: 14px;
  font-weight: 400;
  color: var(--text-dim);
  opacity: 0.7;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.mobile-drawer-username:hover .mobile-drawer-username-chevron {
  opacity: 1;
  color: var(--accent-text);
}
.drawer-link-signout {
  /* Inherits .drawer-link styles but reads as a destructive action
     (matches the topbar .auth-signout treatment). */
  color: var(--muted);
  justify-content: flex-start;
  font-size: 14px;
}
.drawer-link-signout:hover { color: var(--accent-text); }
.drawer-link-signin {
  /* Sign-in affordance for unauthenticated users. Visually equivalent
     to a CTA pill so it stands out in the drawer. */
  background: rgba(108, 99, 255, 0.14);
  border: 1px solid rgba(108, 99, 255, 0.4);
  color: var(--text);
  justify-content: center;
  margin: 4px 0;
  font-weight: 600;
}
.drawer-link-signin:hover { background: rgba(108, 99, 255, 0.22); }

/* 7. Responsive Tables (Horizontal Scroll, Larger Rows)
   Per UX direction, mobile users prefer horizontal-scroll tables with
   comfortable row heights over stacked-card tables. The table keeps
   its full grid layout but cells get bigger padding and the wrapper
   handles horizontal scrolling (with momentum on iOS). */
@media (max-width: 640px) {
  .table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  .table-wrap table { min-width: 560px; }
  .table-wrap th {
    position: sticky;
    top: 0;
    background: var(--bg-3);
    z-index: 1;
    padding: 14px 16px;
    font-size: 11px;
  }
  .table-wrap td {
    padding: 16px;
    /* Each row has comfortable height for thumb scanning; kebab
       menu and copy buttons still sit comfortably in line. */
    min-height: 56px;
  }
  .table-wrap tbody tr { min-height: 56px; }
}

/* 8. Full-Width Wizard Cards on Mobile */
@media (max-width: 640px) {
  .v2-wizard { padding: 0; }
  .wizard-card { max-width: 100%; border-radius: 0; border-left: none; border-right: none; }
  .choice { min-height: 44px; }
}

/* 9. Fact Grouping on Review Screen */
.fact-group { margin-bottom: 16px; }
.fact-group-header {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; background: var(--bg-3);
  border-radius: var(--radius) var(--radius) 0 0;
  cursor: pointer; font-weight: 600; font-size: 13px;
}
.fact-group-header .toggle { transition: transform 0.2s ease; }
.fact-group-header[aria-expanded="true"] .toggle { transform: rotate(90deg); }
.fact-group-body { border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius) var(--radius); padding: 12px; }
.fact-group-body.collapsed { display: none; }

/* 10. Inline Kebab Menu */
.kebab-wrap { position: relative; display: inline-block; }
.kebab-btn {
  background: none; border: none; color: var(--text-dim); font-size: 18px; cursor: pointer;
  padding: 4px 8px; border-radius: 6px;
  /* Ensure 44px tap target on mobile (override only the minimums, the
     visible button stays compact because of the existing padding/font). */
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* Mobile-only kebab tap target: explicit min-height/width so the
   button itself meets WCAG AA even though the icon stays small. */
@media (max-width: 640px) {
  .kebab-btn {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* Row chevron: a tiny "›" rendered to the right of the kebab so the
     user can see at a glance that the row opens by tapping the title.
     Per design decision (Q3 in the UX plan) we DON'T make the whole
     row tappable because horizontal-scroll on tables would cause
     accidental navigation on lift. The chevron provides the same
     discoverability signal without the conflict. */
  .row-chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 4px;
    color: var(--text-dim);
    font-size: 16px;
    width: 24px;
    height: 24px;
    pointer-events: none;
  }
}
@media (min-width: 641px) {
  .row-chevron { display: none; }
}

/* Title-as-button: lets the user open a session by tapping the title
   text instead of opening the kebab menu first. Matches iOS Mail /
   GitHub mobile pattern. The button is unstyled at rest (looks like
   a normal cell value) so the desktop layout is unaffected. */
.row-title-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.row-title-btn:focus-visible {
  outline: 2px solid var(--accent, #7c5cff);
  outline-offset: 2px;
  border-radius: 2px;
}

@media (max-width: 640px) {
  /* On mobile the title button takes a bigger vertical hit area
     without changing the row's visual height. The minimum height is
     inherited from the cell padding already in place (56px from the
     table-wrap rule above); we just ensure the button itself fills
     it. */
  .row-title-btn {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* ID chip — a small monospace pill shown in the first column.
   Replaces the bare "#a3Kf9nQp" text so the public_id reads as an
   identifier badge rather than a column value. */
.id-chip {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 6px;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  background: var(--bg-3);
  border: 1px solid var(--border);
  line-height: 1;
}
.id-chip-cell {
  white-space: nowrap;
}

/* D-103: row-level "View in Library" link column. Mirrors the
   kebab-menu entry as a dedicated cell so the affordance is visible
   without opening the menu. Compact chip-like button to stay within
   the action-column rhythm. */
.grid td.library-cell { white-space: nowrap; }
.library-link {
  background: rgba(124, 92, 255, 0.12);
  border: 1px solid rgba(124, 92, 255, 0.35);
  color: var(--accent-text, #7c5cff);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.library-link:hover {
  background: rgba(124, 92, 255, 0.22);
  border-color: rgba(124, 92, 255, 0.55);
}
.library-link:focus-visible {
  outline: 2px solid var(--accent, #7c5cff);
  outline-offset: 2px;
}
.kebab-btn:hover { background: var(--surface-2); color: var(--text); }
.kebab-menu {
  position: absolute; right: 0; top: 100%;
  background: var(--bg-2); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  z-index: 50; min-width: 160px; padding: 6px 0; display: none;
}
.kebab-menu.open { display: block; }
.kebab-menu button {
  display: block; width: 100%; text-align: left;
  background: none; border: none; color: var(--text);
  padding: 8px 14px; font-size: 13px; cursor: pointer;
}
.kebab-menu button:hover { background: var(--surface-2); }
.kebab-menu button.danger { color: var(--err); }

/* 11. Search & Filter Bar */
.search-bar {
  display: flex; gap: 10px; align-items: center; margin-bottom: 16px; flex-wrap: wrap;
}
.search-bar input {
  flex: 1 1 240px; min-width: 200px;
  background: var(--bg-3); border: 1px solid var(--border); color: var(--text);
  padding: 8px 12px; border-radius: var(--radius); font-size: 14px;
  /* iOS zoom fix: ensure the input font-size is at least 16px on
     mobile so the browser doesn't auto-zoom on focus. */
}
.search-bar input:focus { outline: none; border-color: var(--accent); }
/* Count sits inside the input on the right edge on all breakpoints
   (used to live on its own line below). The wrapper is a relative
   positioned flex container so the count overlays the input. */
.search-input-wrap {
  position: relative;
  flex: 1 1 240px;
  display: flex;
  align-items: center;
}
.search-bar .count {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-dim);
  font-size: 11px;
  white-space: nowrap;
  pointer-events: none;
  padding: 2px 4px;
  line-height: 1;
}
/* When a count is visible, reserve right padding so typed text
   doesn't slide underneath it. */
.search-input-wrap:has(.count:not([style*="display: none"])) input {
  padding-right: 100px;
}

/* Agents sort control: a compact label + native select that sits to
   the right of the search input in the .search-bar flex row. */
.sort-control {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  white-space: nowrap;
}
.sort-control select {
  background: var(--bg-3);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: var(--radius);
  font-size: 13px;
  min-height: 36px;
}
.sort-control select:focus { outline: none; border-color: var(--accent); }

/* Library toolbar mobile polish: compact single-row toolbar with
   the count badge overlaid inside the input on the right edge.
   Keeps the bar at ~40px tall instead of the previous stacked
   ~70px layout. */
@media (max-width: 640px) {
  .search-bar {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    padding-bottom: 0;
    border-bottom: none;
  }
  .search-input-wrap {
    flex: 1 1 auto;
    width: 100%;
  }
  .search-bar input {
    width: 100%;
    min-width: 0;
    min-height: 40px;
    font-size: 16px; /* iOS zoom fix */
    padding: 8px 12px;
  }
  .search-bar .count {
    right: 10px;
    font-size: 10.5px;
  }

  /* Library page header: title row + action buttons stack to a
     single column, each button becomes full-width with a 44px tap
     target. Imported .md is the heavier action so it stays on top. */
  section[x-show="view === 'agents'"] > .row.spread {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    margin-bottom: 8px;
  }
  section[x-show="view === 'agents'"] > .row.spread h2 {
    margin: 0;
  }
  section[x-show="view === 'agents'"] > .row.spread .row {
    flex-direction: column;
    gap: 8px;
  }
  section[x-show="view === 'agents'"] > .row.spread .row .ghost {
    width: 100%;
    min-height: 44px;
    font-size: 14px;
    justify-content: center;
  }
}

@media (max-width: 380px) {
  .search-bar .count { display: none; }
}

/* 12. Intent Example Chips */
.intent-examples { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.intent-chip {
  background: var(--bg-3); border: 1px solid var(--border); color: var(--text-dim);
  padding: 6px 10px; border-radius: 999px; font-size: 12px; cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}
.intent-chip:hover { border-color: var(--accent); color: var(--text); }

/* 13. Library Detail Split Pane (legacy).
       The split-pane (Raw Markdown + Preview side-by-side) used to
       live here but was removed: the raw textarea duplicated the
       editor below and added noise, and the split layout fought
       the preview's natural width. The pane + .library-detail-textarea
       rules are kept here so any stale markup from a cached page
       still renders sensibly (right side fills, left side collapses),
       but the canonical read-only view is now a single full-width
       ``.markdown-preview-full``. */
.split-pane { display: flex; gap: 16px; flex-wrap: wrap; }
.split-pane .pane { flex: 1 1 320px; min-width: 280px; }
.markdown-preview {
  background: var(--bg-3); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; min-height: 200px; overflow: auto; line-height: 1.6;
}
.markdown-preview h1,.markdown-preview h2,.markdown-preview h3 { margin-top: 0; }
.markdown-preview pre {
  background: var(--bg); padding: 10px; border-radius: 6px; overflow-x: auto;
}

/* Full-width preview variant for the library-detail read-only view.
   Same visual treatment as the standard preview but with a slightly
   larger min-height so long prompts fill the page rather than leaving
   a void at the bottom. */
.markdown-preview-full {
  min-height: 360px;
  padding: 20px 22px;
  font-size: 14.5px;
}

/* 13b. Library Action Bar.
       A single, symmetric action bar that replaces the old two-row
       button cluster on the Library Detail page. Design goals:

       * All buttons share the same height / padding / font, so the
         bar reads as one consistent toolbar rather than two mismatched
         groups. The base ``button`` rule already sets font-size 13px
         and padding 8px 14px; we tighten min-height so the primary
         (filled) button and the ghost buttons line up exactly even
         when one has a longer label than the other.
       * The wrapper is a flex row with wrap, so on wide screens the
         buttons sit in a single tidy line and on narrow screens they
         stack without overflowing the container.
       * Under 600px we switch to a single-column full-width stack so
         every button becomes easy to tap with a thumb. The stacked
         layout also gives each button a consistent touch target
         (~44px tall — the WCAG-recommended minimum) instead of the
         tiny 13px label-only height.
       * ``.action-btn`` carries the visual variant: ``.primary`` for
         the main CTA (Edit / Cancel), default for the secondary
         actions. The two are visually distinct but identically sized
         so they line up in a row.

       The action bar is given its own margin to breathe below the
       title block; it's the first interactive surface the eye lands
       on after the prompt name, so a small visual gap above helps
       separate "metadata" from "what can I do here?". */
.library-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 4px 0 16px;
  padding: 10px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.library-actions .action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 36px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  /* Inherit the existing button base (bg-3, border, radius, hover).
     We don't override background here so .primary and the default
     variant stay visually distinct. */
}
.library-actions .action-btn.primary {
  font-weight: 600;
}

/* Mobile fallback: under 600px the bar becomes a full-width column
   of large, thumb-friendly buttons. We also drop the inner border +
   background so the stacked buttons don't visually compete with the
   card beneath them; on a narrow viewport vertical rhythm is more useful
   than a boxed toolbar. */
@media (max-width: 600px) {
  .library-actions {
    flex-direction: column;
    align-items: stretch;
    padding: 8px;
    background: transparent;
    border: none;
  }
  .library-actions .action-btn {
    width: 100%;
    min-height: 48px;
    font-size: 14.5px;
    justify-content: flex-start;
  }
  /* On a stacked mobile layout the primary button (Edit) looks better
     filling the row; secondary buttons keep their ghost background so
     they read as a list of secondary actions. */
  .library-actions .action-btn.primary {
    order: -1; /* always first in the stacked column */
  }
}

/* ====================================================================
   LIBRARY MOBILE STICKY BAR
   --------------------------------------------------------------------
   A persistent bottom action bar for the Library Detail page on
   mobile (≤640px). Holds the 4 most-frequent actions so the user can
   always reach them without scrolling up through long markdown
   previews. Hidden on desktop where the inline .library-actions row
   is more appropriate.

   Layout: 4 equal-width icon+label buttons in a single flex row.
   Primary action (Edit) is highlighted via .lib-mobile-primary so
   the user knows where to go. ``safe-area-inset-bottom`` keeps the
   bar above the iOS home indicator.

   We also pad the page bottom by the same amount so the last line
   of the markdown preview doesn't sit underneath the bar. The
   padding is applied to the section so it doesn't shift the topbar.
   ==================================================================== */
.library-mobile-bar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  background: var(--glass-panel-strong);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--border, var(--surface-3));
  padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
  align-items: stretch;
  gap: 6px;
  /* Slide-up animation when the view becomes visible. */
  transform: translateY(110%);
  transition: transform 0.2s ease;
}
.library-mobile-bar.is-visible {
  display: flex;
  transform: translateY(0);
}
.lib-mobile-btn {
  flex: 1;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 48px;
  padding: 6px 4px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--text, #e6edf3);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.lib-mobile-btn:active { background: var(--surface-2); }
.lib-mobile-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.lib-mobile-btn .lib-mobile-icon {
  font-size: 18px;
  line-height: 1;
}
.lib-mobile-btn .lib-mobile-label {
  font-size: 11px;
  letter-spacing: 0.02em;
}
.lib-mobile-btn.lib-mobile-primary {
  background: rgba(124, 92, 255, 0.14);
  border-color: rgba(124, 92, 255, 0.45);
  color: #cbb6ff;
}
.lib-mobile-btn.lib-mobile-primary:active {
  background: rgba(124, 92, 255, 0.25);
}

@media (min-width: 641px) {
  /* Hide on tablet/desktop where the inline actions bar takes over.
     Belt-and-braces in case JS adds ``is-visible`` on a wide screen
     before the CSS catches up. */
  .library-mobile-bar { display: none !important; }
}

/* ====================================================================
   LIBRARY MOBILE MORE SHEET
   --------------------------------------------------------------------
   Bottom-sheet overlay that hosts the overflow actions (Open in
   Wizard / Re-run / Review / Download) on the Agent Detail page.
   Triggered by the ⋮ "More" button in .library-mobile-bar. Visible
   only on mobile (≤640px) where the inline .library-actions bar is
   hidden, so the user still has full access to all 8 actions without
   any redundant UI.

   Layout: a fixed full-viewport dimmed backdrop (``align-items:
   flex-end``) with a rounded-top panel anchored to the bottom. The
   panel uses ``width: 100%`` so on phones it fills the screen edge-
   to-edge, and ``safe-area-inset-bottom`` so the Cancel button sits
   above the iOS home indicator. Tapping the backdrop closes the
   sheet (``@click.self`` on the wrapper). Tapping a button inside
   the panel fires its handler and sets ``libraryMobileMoreOpen =
   false`` to dismiss the sheet in the same gesture.

   z-index 80 places this above the sticky bar (70) so the sheet
   cleanly covers it without the bar peeking through.
   ==================================================================== */
.library-mobile-more-sheet {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(0, 0, 0, 0.45);
  align-items: flex-end;
  justify-content: center;
}
.library-mobile-more-sheet.is-visible {
  display: flex;
}
.library-mobile-more-panel {
  width: 100%;
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  border-radius: 14px 14px 0 0;
  padding: 8px 12px calc(12px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lib-mobile-more-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  padding: 10px 14px;
  background: transparent;
  border: none;
  border-radius: 10px;
  color: var(--text);
  font-size: 15px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.lib-mobile-more-btn:active {
  background: var(--surface-2);
}
.lib-mobile-more-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.lib-mobile-more-btn .lib-mobile-more-icon {
  font-size: 18px;
  line-height: 1;
}
/* The Cancel row is visually separated from the action list with a
   top border and centred label, mirroring the iOS / Material
   "destructive" cancel pattern. */
.lib-mobile-more-btn.ghost {
  justify-content: center;
  font-weight: 600;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
  margin-top: 6px;
  padding-top: 14px;
}

/* Hide the bottom-sheet on tablet/desktop where the inline action
   bar already exposes every action. Belt-and-braces in case JS
   toggles ``is-visible`` on a wide viewport before CSS catches up. */
@media (min-width: 641px) {
  .library-mobile-more-sheet { display: none !important; }
}

@media (max-width: 640px) {
  /* Reserve room at the bottom of the markdown preview so the last
     paragraph isn't obscured by the sticky bar.
     (Selector note: the ``agent-detail-section`` class is on the
     wrapping <section> so we can target it without depending on the
     exact ``x-show`` attribute value. The x-show attribute is
     ``view === 'agent-detail' && activeLibraryItem`` so a plain
     attribute selector ``[x-show="view === 'agent-detail'"]``
     would never match — that's why the previous version of this
     rule silently did nothing.) */
  .agent-detail-section {
    padding-bottom: calc(72px + env(safe-area-inset-bottom));
  }

  /* Header layout: stack the title block and the inline action bar
     vertically on mobile. The desktop layout uses ``.row.spread``
     (``justify-content: space-between``) which leaves the action bar
     squished into the remaining horizontal space next to the title —
     ugly and hard to read. Stacking gives the title its own line and
     sends the action bar down to its own row, mirroring the
     conventional mobile detail-page pattern. */
  .agent-detail-section > .row.spread {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
  }
  /* ``min-width: 0`` lets the title child of the flex container shrink
     below its content's natural width so long agent names wrap instead
     of forcing horizontal overflow. */
  .agent-detail-section > .row.spread > div:first-child {
    min-width: 0;
  }

  /* Hide the desktop inline action bar on mobile: the sticky bottom
     bar (.library-mobile-bar) already exposes the 4 most-used actions
     in thumb range and a "More" sheet covers the rest. Showing both
     creates duplication and wastes vertical space. The skeleton's
     placeholder ``.library-actions`` is hidden by this rule too —
     that's intentional, the skeleton should mirror the final layout. */
  .agent-detail-section .library-actions {
    display: none !important;
  }

  /* Mobile header: flush-left vertical stack.
     The sticky bottom bar already provides a "Back" button, so the
     desktop-only "← Agents" back link is hidden here to remove the
     duplicate affordance. Name + pencil, topic pill, and saved-date
     are all left-aligned (flush with the left edge of the container)
     so the header reads as a clean left-justified column. */
  .library-detail-header {
    margin-bottom: 16px;
  }
  .library-detail-header .back {
    display: none;
  }
  .library-detail-header .library-detail-title {
    margin-top: 4px;
  }
  .library-detail-header .library-detail-title > .row {
    justify-content: flex-start;
  }

  /* Markdown preview: tighten padding on mobile so long prompts use
     the full available width instead of a 22px gutter. Min-height
     shrinks too — long previews still fill the page because the
     sticky bar accounts for the extra space. */
  .markdown-preview-full {
    padding: 14px 14px;
    min-height: 280px;
    font-size: 14px;
  }
  .markdown-preview { padding: 12px 12px; }
}

/* ====================================================================
   LIBRARY DETAIL HEADER — STACKED METADATA (global)
   --------------------------------------------------------------------
   On the agent-detail page, the session-meta block (topic pill + saved
   date) is stacked vertically with topic on its own row and date on
   its own row, both flush-left so they line up with the left edge of
   the agent name above them. This applies on EVERY viewport — on
   desktop the title-block sits in the left half of the ``.row.spread``
   row with the action bar on the right, and the stacked topic/date
   fits naturally below the name. On mobile it produces the same
   flush-left vertical column.
   ==================================================================== */
.library-detail-header .session-meta {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  margin-top: 8px;
}
.library-detail-header .session-meta .topic-pill {
  margin: 0;
}

/* 14. Manual Mode (Friendly Offline) */
.manual-mode-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(139,148,158,0.12); border: 1px solid rgba(139,148,158,0.3);
  color: var(--text-dim); font-size: 12px;
}

/* ====================================================================
   MOBILE FAB — "NEW AGENT"
   --------------------------------------------------------------------
   Floating action button for the primary verb of the app. Hidden on
   desktop (>=640px) because the welcome banner already has a CTA
   inline; on mobile the banner scrolls away but the FAB stays
   anchored bottom-right so a returning user can start a new prompt
   in one tap without scrolling. The button itself is 56px (Material
   Design FAB standard) with safe-area-inset padding so it sits above
   the home indicator on iOS. JS toggles ``.is-visible`` based on
   route — only show on /dashboard.
   ==================================================================== */
.new-agent-fab {
  display: none;
  position: fixed;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(20px + env(safe-area-inset-bottom));
  z-index: 80;
  min-width: 56px;
  min-height: 56px;
  padding: 0 22px;
  border-radius: 999px;
  border: 1px solid var(--surface-4);
  background: linear-gradient(135deg, #7c5cff 0%, #5b8def 100%);
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  box-shadow:
    0 8px 24px rgba(124, 92, 255, 0.40),
    0 2px 6px rgba(0, 0, 0, 0.35);
  transition: transform 0.15s ease;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.new-agent-fab.is-visible { display: inline-flex; }
.new-agent-fab:hover { transform: translateY(-1px); box-shadow: 0 10px 28px rgba(124, 92, 255, 0.45), 0 2px 6px rgba(0, 0, 0, 0.35); }
.new-agent-fab:active { transform: translateY(0); }
.new-agent-fab .fab-icon {
  font-size: 22px;
  line-height: 1;
  margin-left: -2px;
}
/* Compact variant: circular on narrow phones so it stays out of the
   way of content, expands to pill on tablets. */
@media (max-width: 380px) {
  .new-agent-fab {
    min-width: 56px;
    width: 56px;
    padding: 0;
    border-radius: 50%;
  }
  .new-agent-fab .fab-label { display: none; }
}

/* ====================================================================
   VISUAL POLISH PASS (Aug 2026)
   --------------------------------------------------------------------
   Lifts Prompt Skiller's visual language closer to modern AI SaaS:
   - ambient background depth (radial gradient + faint grid overlay)
   - glassmorphism utility (.glass-panel + backdrop-filter on cards/modal)
   - gradient text utility (.gradient-text)
   - hover micro-interactions (lift + glow on cards, shadow on buttons)
   - active wizard stepper pulse animation
   - subtle entrance fade on cards (staggered)

   All classes are additive: nothing existing is renamed. The body
   keeps its current --bg flat fallback so the ambient layers are an
   enhancement, not a requirement.
   ==================================================================== */

/* Ambient background depth ----------------------------------------
   Two stacked radial gradients + a faint grid SVG-style overlay
   created with linear-gradients. Applied to body via ::before /
   ::after so the existing topbar / footer paint order is
   unaffected. The grid is so subtle it reads as "paper texture"
   rather than "graph paper". */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(ellipse 80% 60% at 20% 10%, rgba(124, 92, 255, 0.10), transparent 60%),
    radial-gradient(ellipse 60% 50% at 80% 90%, rgba(56, 189, 248, 0.08), transparent 60%);
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.35;
  background-image:
    linear-gradient(rgba(124, 92, 255, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(124, 92, 255, 0.06) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse at center, black 30%, transparent 80%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 80%);
}

/* Glassmorphism utility --------------------------------------------
   Apply with ``class="glass-panel"`` to any card or modal surface to
   get a frosted-glass look. We rely on backdrop-filter; older
   browsers fall back to a slightly more opaque background so the
   content remains readable. The border is a faint gradient to
   match the promptskiller.ai aesthetic. */
.glass-panel {
  background: var(--glass-panel);
  border: 1px solid var(--surface-2);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}
/* @supports fallback for browsers without backdrop-filter:
   bump opacity so the panel doesn't go transparent. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .glass-panel { background: var(--glass-panel-strong); }
}

/* Apply glass to the existing modal surfaces so they pop over the
   ambient background. The base ``.modal`` / ``.modal-card`` styles
   stay; we just add a backdrop blur on top. */
.modal,
.modal-card {
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  background: var(--glass-panel);
}

/* Dashboard status cards + summary cards get a subtle glass
   treatment so they look like floating tiles. We override the
   solid --bg-2 background only when the parent is the dashboard. */
.dashboard-grid .dashboard-card,
.dashboard-grid .skeleton-card {
  background: var(--glass-panel);
  border: 1px solid var(--surface-2);
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
}

/* Gradient text utility --------------------------------------------
   Uses the same purple→cyan palette as the existing --accent so the
   brand stays consistent. The gradient only renders correctly when
   applied to a heading with a solid color baseline (the color
   property is the fallback when bg-clip is unsupported). */
.gradient-text {
  background: linear-gradient(135deg, #a78bff 0%, #7c5cff 35%, #5d3fd3 65%, #38bdf8 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  font-weight: 800;
  letter-spacing: -0.01em;
}
/* Slightly different palette for the v2 wizard so the wizard feels
   like its own visual moment rather than a clone of the dashboard. */
.v2-wizard .gradient-text {
  background: linear-gradient(135deg, #c4b5fd 0%, #a78bff 40%, #38bdf8 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

/* Hover micro-interactions -----------------------------------------
   Card hover lift + glow: bump the translateY by 2px and add a
   soft accent-tinted shadow + border highlight. Already clickable
   cards (summary cards) inherit this. */
.dashboard-card.clickable:hover {
  transform: translateY(-2px);
  border-color: rgba(124, 92, 255, 0.55);
  box-shadow: 0 8px 24px rgba(124, 92, 255, 0.15),
              0 0 0 1px rgba(124, 92, 255, 0.25);
}
.topic-card:hover {
  border-color: rgba(124, 92, 255, 0.55);
  box-shadow: 0 6px 20px rgba(124, 92, 255, 0.12);
}

/* Button hover: expand the shadow and bump scale slightly. */
button:hover:not(:disabled) {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}
button.primary:hover:not(:disabled) {
  box-shadow: 0 6px 20px rgba(124, 92, 255, 0.35);
}

/* Topbar nav link: animated underline + scale on hover. */
.topbar-nav .nav-link {
  transition: color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}
.topbar-nav .nav-link:hover {
  transform: translateY(-1px);
}

/* Library / Sessions table rows: the entire card lights up violet on
   hover, replacing the old per-cell tint + inset-left-border glow.
   The 0.08 alpha tint is noticeable but calm — readable across all
   text colors inside the row. The 1px accent box-shadow replaces the
   default border-color, so the card outline also lights up. */
.grid tbody tr:hover {
  background: rgba(124, 92, 255, 0.08);
  box-shadow: 0 0 0 1px var(--accent);
}

/* Wizard stepper pulse ---------------------------------------------
   The active step's dot pulses softly to draw the eye. The ring is a
   pseudo-element animating opacity + transform only, so the pulse stays
   on the compositor instead of repainting a box-shadow every frame. */
.step.active .dot {
  position: relative;
}
.step.active .dot::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 2px solid rgba(124, 92, 255, 0.6);
  pointer-events: none;
  animation: step-pulse 2s ease-in-out infinite;
}
@keyframes step-pulse {
  0%, 100% { opacity: 0.7; transform: scale(1); }
  50%      { opacity: 0; transform: scale(2); }
}

/* Staggered card entrance ----------------------------------------
   When the dashboard renders, the cards animate in one after
   another for a more dynamic feel. We use CSS custom property
   delays so each card's animation kicks off at a slightly
   different time. */
@keyframes card-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.dashboard-grid > * {
  animation: card-rise 0.4s ease-out backwards;
}
.dashboard-grid > *:nth-child(1) { animation-delay: 0.05s; }
.dashboard-grid > *:nth-child(2) { animation-delay: 0.10s; }
.dashboard-grid > *:nth-child(3) { animation-delay: 0.15s; }
.dashboard-grid > *:nth-child(4) { animation-delay: 0.20s; }
.dashboard-grid > *:nth-child(5) { animation-delay: 0.25s; }

/* Floating accent orb (dashboard hero) -----------------------------
   A single soft purple orb that sits behind the dashboard page so
   the page has a focal point rather than starting cold. Tied to
   .dashboard-page::before so the orb follows the page even when
   scrolled within the section.

   IMPORTANT: do NOT add ``overflow: hidden`` here. The Sessions /
   Library tables live inside .dashboard-page and their per-row
   kebab menus use ``position: absolute; top: 100%`` to drop down.
   An ``overflow: hidden`` ancestor would silently clip the menu
   whenever the table doesn't have enough vertical room (e.g. only
   one session row), making the menu invisible. */
.dashboard-page {
  position: relative;
}
.dashboard-page::before {
  content: "";
  position: fixed;
  top: 80px;
  right: -120px;
  width: 420px;
  height: 420px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(124, 92, 255, 0.18), transparent 70%);
  pointer-events: none;
  z-index: 0;
  filter: blur(20px);
}
.dashboard-page > * { position: relative; z-index: 1; }

/* =========================================================================
   Landing page (templates/landing.html)
   -------------------------------------------------------------------------
   Public marketing page served at ``/`` for unauthenticated visitors.
   Zero JS dependencies — pure CSS + the tiny inline smooth-scroll script
   inside the template. Reuses the brand tokens defined at the top of this
   file (``--bg``, ``--text``, ``--accent``, ``--accent-2``, ``--panel``)
   so a future rebrand stays consistent across the marketing page and the
   in-product chrome.

   The marketing page is rendered by ``landing.html`` which is mostly
   static markup. We rely on the ambient gradients declared earlier in
   the file (``body::before`` + ``body::after``) for the hero backdrop so
   we don't have to redeclare them here.
   ========================================================================= */

.landing-body {
  /* Prevent the global ambient backgrounds from being clipped by any
     overflow rules inside this page's own sections. */
  min-height: 100vh;
  /* Slightly tighter line-height on the marketing page so the long
     headline doesn't feel airy. */
  line-height: 1.55;
}

.landing-body main {
  /* Keep the marketing content above the ambient orbs but allow the
     page to extend below the fold without a horizontal scrollbar. */
  position: relative;
  z-index: 1;
}

/* ---- Nav bar --------------------------------------------------------- */

.landing-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Translucent so the ambient orbs bleed through the nav bar on scroll,
     matching the SPA's topbar treatment. */
  background: linear-gradient(
    180deg,
    var(--glass-1),
    var(--glass-2)
  );
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--surface-2);
}
.landing-nav-inner {
  /* Full-width nav toolbar: content (brand left, links right) is
     arranged by flex, but the bar itself stretches edge-to-edge.
     Padding replaces the previous max-width/margin so the brand and
     links sit a comfortable 24px from the viewport edges on any
     screen width. Safe-area insets keep the home-indicator / notch
     clear when the page is installed as a PWA. */
  padding: 16px 24px;
  padding-top: max(16px, env(safe-area-inset-top));
  padding-inline-start: max(24px, env(safe-area-inset-left));
  padding-inline-end: max(24px, env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.landing-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.2px;
}
.landing-brand-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0c16;
  font-size: 18px;
  box-shadow: 0 4px 18px rgba(124, 92, 255, 0.4);
}
.landing-brand-name { color: var(--text); }
.landing-nav-links {
  display: flex;
  align-items: center;
  gap: 22px;
}
.landing-nav-links a {
  color: var(--muted);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  transition: color 0.18s ease;
}
.landing-nav-links a:hover { color: var(--text); }
.landing-nav-links .landing-cta-mini {
  /* Purple/violet theme button — mirrors the .primary treatment used
     elsewhere in the app but trimmed for a compact pill shape that
     fits the nav bar without dominating the brand wordmark. The
     accent gradient + soft glow make it pop against the translucent
     nav background so returning users always see the way back in. */
  padding: 8px 18px;
  border-radius: 999px;
  background: var(--accent);
  border: 1px solid var(--accent-2);
  color: #fff;
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(124, 92, 255, 0.35);
  transition: background 0.18s ease, border-color 0.18s ease,
              transform 0.12s ease;
}
.landing-nav-links .landing-cta-mini:hover {
  background: var(--accent-2);
  border-color: var(--accent-2);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 10px 24px rgba(124, 92, 255, 0.5);
  text-decoration: none;
}
.landing-cta-mini:active {
  transform: translateY(0);
}

/* ---- Hero ------------------------------------------------------------ */

.landing-hero {
  max-width: 1120px;
  margin: 0 auto;
  padding: 40px 24px 32px;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 64px;
  align-items: center;
}
.landing-hero-inner { display: flex; flex-direction: column; gap: 10px; }
/* The flex gap is the single controlled spacing source for the
   hero column. Default browser margins on <h1>/<p> add extra
   invisible whitespace that survives even when flex gap is 0 —
   we zero them here so the layout responds to ``gap`` predictably. */
.landing-hero-inner > * { margin: 0; }
.landing-eyebrow {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(124, 92, 255, 0.12);
  border: 1px solid rgba(124, 92, 255, 0.3);
  color: var(--accent-text);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.landing-headline {
  font-size: clamp(38px, 5.4vw, 64px);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.02em;
}
.landing-sub {
  font-size: 18px;
  color: var(--muted);
  max-width: 56ch;
  line-height: 1.3;
}
.landing-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.landing-cta-primary,
.landing-cta-secondary {
  font-size: 15px;
  font-weight: 600;
  padding: 12px 22px;
  border-radius: 10px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.landing-cta-secondary { color: var(--text); }
.landing-cta-primary {
  box-shadow: 0 12px 36px rgba(124, 92, 255, 0.35);
}
.landing-cta-primary:hover {
  /* Inherit from .primary rules above; nudge lift for marketing page. */
  transform: translateY(-1px);
  box-shadow: 0 18px 48px rgba(124, 92, 255, 0.45);
}
.landing-footnote {
  font-size: 13px;
  color: var(--muted);
}
.landing-footnote a { color: var(--accent-text); text-decoration: none; }
.landing-footnote a:hover { text-decoration: underline; }

/* Hero decorative preview card ---------------------------------------- */
.landing-preview {
  position: relative;
  padding: 18px;
  border-radius: 16px;
  /* Opaque stops on purpose. The card is a dark "code window" in both
     themes and every colour inside it is theme-independent, so its own
     surface must be too: an alpha gradient composites against the page
     (~#131624 over the dark theme, ~#454859 over the light one) and
     washes the contents out in the light theme. */
  background: linear-gradient(180deg, #141828, #0e101c);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow:
    0 30px 60px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  overflow: hidden;
}
.landing-preview::after {
  /* Soft glow strip across the top edge to suggest "live" code. Runs
     edge-to-edge so it sits on the title bar's top border. */
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--accent),
    var(--accent-2),
    transparent
  );
  opacity: 0.6;
}
/* Window chrome (LAND-126). The bar is pulled flush to the card's
   rounded top corners — the negative margins cancel the card's 18px
   padding and ``overflow: hidden`` clips it — so it reads as a real
   title bar instead of three dots floating in space. The separator
   line plus the filename label are what make the chrome legible; the
   dots alone read as decoration rather than as a window. */
.landing-preview-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: -18px -18px 14px;
  padding: 12px 18px 10px;
  background: rgba(255, 255, 255, 0.04);
  border-bottom: 1px solid rgba(255, 255, 255, 0.10);
}
/* Dots are targeted by class (not ``.landing-preview-bar span``) so the
   filename label — also a ``span`` — keeps its own type styles. */
.landing-preview-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
}
.landing-preview-dot:nth-of-type(1) { background: #ff5f56; }
.landing-preview-dot:nth-of-type(2) { background: #ffbd2e; }
.landing-preview-dot:nth-of-type(3) { background: #27c93f; }
.landing-preview-name {
  margin-left: auto;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: rgba(230, 232, 240, 0.55);
}
/* Light theme: the card stays a dark "code window" on a light page by
   design, but its white hairline border and heavy shadow disappear
   against the light background. Swap both so the card keeps a defined
   edge and the chrome bar's separator stays readable. */
html[data-theme="light"] .landing-preview {
  border-color: rgba(0, 0, 0, 0.10);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.18);
}
html[data-theme="light"] .landing-preview-bar {
  border-bottom-color: rgba(255, 255, 255, 0.14);
}
.landing-preview-code {
  /* The code pane follows the theme — a deliberately lighter surface than
     the card, so the sample reads as a code window in both themes. Every
     colour here is a token, and the ``background`` declaration is
     mandatory: Pico's layered
     ``code,kbd,pre,samp { background: var(--pico-code-background-color) }``
     paints every <pre> with a theme-following value, and unlayered
     style.css wins only for properties it declares. Without it the pane
     inherits Pico's near-white background in the light theme and the text
     disappears. Keep both ``color`` and ``background`` on this rule. */
  margin: 0;
  background: var(--panel);
  border-radius: 10px;
  padding: 12px 14px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.65;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}
/* Keywords/strings use the theme's own accent and warn text colours: the
   pane underneath follows the theme, so the tokens resolve to a dark
   purple on the light pane and a light lavender on the dark one. */
.landing-preview-code .lp-keyword {
  color: var(--accent-text);
  font-weight: 700;
}
.landing-preview-code .lp-string {
  color: var(--warn);
}

/* ---- Trust bar ------------------------------------------------------- */

.landing-trust {
  border-top: 1px solid var(--surface-2);
  border-bottom: 1px solid var(--surface-2);
  background: var(--glass-flat);
}
.landing-trust-inner {
  max-width: 1120px;
  margin: 0 auto;
  padding: 22px 24px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}
.landing-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--surface-3);
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}
.landing-pill-icon { font-size: 14px; }

/* ---- FAQ (LAND-112) -------------------------------------------------- */

.landing-faq {
  display: grid;
  gap: 12px;
  max-width: 760px;
  margin: 0 auto;
}
.landing-faq-item {
  padding: 16px 20px;
}
.landing-faq-item summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}
.landing-faq-item summary::-webkit-details-marker {
  display: none;
}
.landing-faq-item summary::after {
  content: "+";
  float: right;
  color: var(--muted);
}
.landing-faq-item[open] summary::after {
  content: "−";
}
.landing-faq-item p {
  margin: 10px 0 0;
  color: var(--muted);
}

/* ---- Section scaffolding -------------------------------------------- */

.landing-section {
  max-width: 1120px;
  margin: 0 auto;
  padding: 88px 24px;
}
.landing-section-header {
  max-width: 720px;
  margin: 0 auto 48px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
}
.landing-section-header h2 {
  font-size: clamp(28px, 3.6vw, 40px);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.landing-section-sub {
  color: var(--muted);
  font-size: 16px;
  line-height: 1.6;
}

/* ---- Mid-page CTA banner ---------------------------------------------
   A single glass-panel card centered between the Features grid and
   the How-it-works section. Smaller vertical padding than the
   surrounding sections so it reads as a deliberate break in the page
   rhythm — not another content block the visitor scrolls past. The
   accent-tinted border + glow tie it back to the hero CTA so the
   "Create your first prompt" action feels like the natural next
   step from the feature cards above. */
.landing-cta-banner-section {
  padding: 40px 24px;
}
.landing-cta-banner {
  max-width: 820px;
  margin: 0 auto;
  padding: 36px 40px;
  border-radius: 18px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  border: 1px solid rgba(124, 92, 255, 0.25);
  box-shadow: 0 18px 48px rgba(124, 92, 255, 0.18);
}
.landing-cta-banner h2 {
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
}
.landing-cta-banner p {
  color: var(--muted);
  font-size: 15.5px;
  line-height: 1.55;
  max-width: 56ch;
  margin: 0 0 6px;
}

/* ---- Feature cards --------------------------------------------------- */

.landing-feature-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
}
.landing-feature {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 26px;
  border-radius: 16px;
  border: 1px solid var(--surface-2);
  /* Inherit the glass treatment from .glass-panel above; add a subtle
     hover lift so the cards feel interactive. */
  transition: transform 0.22s ease, border-color 0.22s ease;
}
.landing-feature:hover {
  transform: translateY(-4px);
  border-color: rgba(124, 92, 255, 0.4);
  box-shadow: 0 18px 48px rgba(124, 92, 255, 0.22);
}
.landing-feature-icon {
  font-size: 28px;
  width: 48px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: linear-gradient(135deg, rgba(124, 92, 255, 0.25), rgba(34, 211, 238, 0.18));
  border: 1px solid rgba(124, 92, 255, 0.3);
}
.landing-feature h3 {
  font-size: 18px;
  font-weight: 700;
  margin: 0;
}
.landing-feature p {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
}

/* ---- Model compatibility cards ------------------------------------- */

.landing-model-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 22px;
}
.landing-model {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 32px 24px;
  border-radius: 16px;
  border: 1px solid var(--surface-2);
  transition: transform 0.22s ease, border-color 0.22s ease;
}
.landing-model:hover {
  transform: translateY(-4px);
  border-color: rgba(124, 92, 255, 0.4);
  box-shadow: 0 18px 48px rgba(124, 92, 255, 0.22);
}
.landing-model-icon {
  width: 64px;
  height: 64px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px;
  border: 1px solid var(--surface-4);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}
.landing-model-icon img {
  display: block;
  width: 36px;
  height: 36px;
  filter: brightness(0) invert(1);
}
.landing-model-icon--claude {
  background: linear-gradient(135deg, #d97757, #b25530);
}
.landing-model-icon--chatgpt {
  background: linear-gradient(135deg, #10a37f, #0b7d5f);
}
.landing-model-icon--grok {
  background: linear-gradient(135deg, #2a2a2a, #050505);
  border-color: var(--surface-4);
}
.landing-model-icon--gemini {
  background: linear-gradient(135deg, #4285f4, #6f4dc7);
}
.landing-model h3 {
  font-size: 16px;
  font-weight: 700;
  margin: 0;
  color: var(--text);
}

/* ---- How-it-works flow ---------------------------------------------- */

.landing-flow {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
  counter-reset: landing-flow-step;
}
.landing-flow-step {
  position: relative;
  padding: 24px 22px;
  border-radius: 14px;
  background: var(--glass-panel);
  border: 1px solid var(--surface-2);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
.landing-flow-step:hover {
  transform: translateY(-2px);
  border-color: rgba(124, 92, 255, 0.35);
}
.landing-flow-num {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #0a0c16;
  font-weight: 800;
  font-size: 16px;
  margin-bottom: 12px;
  box-shadow: 0 8px 22px rgba(124, 92, 255, 0.35);
}
.landing-flow-step h3 {
  font-size: 16px;
  font-weight: 700;
  margin: 0 0 6px;
}
.landing-flow-step p {
  font-size: 13.5px;
  color: var(--muted);
  margin: 0;
  line-height: 1.55;
}

/* ---- Final CTA ------------------------------------------------------- */

.landing-final {
  max-width: 720px;
  margin: 0 auto;
  padding: 80px 24px 96px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
}
.landing-final h2 {
  font-size: clamp(28px, 3.6vw, 40px);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.landing-final p {
  color: var(--muted);
  font-size: 16px;
}

/* ---- Footer --------------------------------------------------------- */

.landing-footer {
  border-top: 1px solid var(--surface-2);
  background: var(--glass-2);
}
.landing-footer-inner {
  max-width: 1120px;
  margin: 0 auto;
  padding: 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  color: var(--muted);
  font-size: 13px;
}
.landing-footer-brand { font-weight: 700; color: var(--text); }

/* ---- Responsive breakpoints ----------------------------------------- */

@media (max-width: 880px) {
  .landing-hero {
    grid-template-columns: 1fr;
    padding: 56px 24px 40px;
    gap: 36px;
  }
  .landing-feature-grid { grid-template-columns: 1fr; }
  .landing-flow { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .landing-model-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .landing-nav { padding: 12px 16px; }
  .landing-nav-inner { gap: 8px; }
  .landing-nav-links { gap: 12px; }
  .landing-nav-links a:not(.landing-cta-mini) { display: none; }
  .landing-hero { padding: 36px 18px 28px; gap: 28px; }
  .landing-hero-inner { text-align: left; }
  .landing-headline {
    /* Fluid headline scale so 6xl→mobile without manual breakpoints. */
    font-size: clamp(1.75rem, 6vw + 0.5rem, 3rem);
    line-height: 1.15;
  }
  .landing-trust-pills { gap: 8px; }
  .landing-trust-pills .landing-pill { font-size: 12px; padding: 4px 10px; }
  .landing-section { padding: 32px 16px; }
  .landing-section-header h2 { font-size: 1.5rem; }
  .landing-feature { padding: 18px; }
  .landing-flow { grid-template-columns: 1fr; gap: 16px; }
  .landing-model-grid { grid-template-columns: 1fr; gap: 16px; }
  .landing-model { padding: 24px 18px; }
  .landing-cta-row { flex-direction: column; align-items: stretch; }
  .landing-cta-primary,
  .landing-cta-secondary { justify-content: center; }
  .landing-cta-banner { padding: 24px 18px; }
  .landing-cta-banner .landing-cta-primary { width: 100%; }
  .landing-final { padding: 36px 18px 56px; }
  .landing-footer { padding: 20px 16px calc(20px + env(safe-area-inset-bottom)); }
}
@media (max-width: 380px) {
  .landing-headline { font-size: 1.55rem; }
  .landing-cta-banner h2 { font-size: 1.25rem; }
}

/* ====================================================================
   PLAN & CREDITS — plan picker + change-tier modal
   --------------------------------------------------------------------
   Sits inside the Plan & Credits card and the dedicated change-tier
   modal. Mirrors the visual language already used by .account-card
   + .tier-row + .tier-badge so the picker feels native to the
   Account page rather than a bolted-on widget.

   The .plan-picker grid uses ``repeat(auto-fit, minmax(...))`` so
   the cards wrap responsively: 3 columns on wide screens, 1 column
   on phones. ``.plan-card`` itself is a non-interactive card with
   the "Current" badge, feature bullet list, and a CTA. When
   ``.is-current`` is applied (computed by Alpine from the response
   of /api/me/change-tier-options) the card flips to the accent
   outline + filled CTA so the user can clearly see which plan
   they're on. ``.is-downgrade`` highlights the muted-red border for
   downgrade CTAs (the action label is still "Switch" — the color
   shift is the cue). */

.plan-picker {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
  margin: 12px 0 18px;
}

.plan-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 14px 12px;
  background: var(--bg-3, #1f2530);
  border: 1px solid var(--border, #2c3340);
  border-radius: 10px;
  position: relative;
  transition: border-color 0.15s ease, transform 0.12s ease;
}
.plan-card.is-current {
  border-color: var(--accent, #7c5cff);
  box-shadow: 0 0 0 1px rgba(124, 92, 255, 0.25),
              0 6px 18px rgba(124, 92, 255, 0.18);
}
.plan-card.is-downgrade {
  border-color: rgba(248, 81, 73, 0.35);
}

.plan-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.plan-card-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text, #e6edf3);
}
.plan-card-price {
  font-size: 12px;
  color: var(--text-dim, #8b949e);
  font-family: var(--mono, monospace);
}
.plan-card-price strong {
  color: var(--text, #e6edf3);
  font-size: 16px;
  font-weight: 700;
}
.plan-card-cadence {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim, #8b949e);
}
.plan-card-features {
  list-style: none;
  margin: 0;
  padding: 4px 0 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12.5px;
  color: var(--text-dim, #8b949e);
  line-height: 1.45;
}
.plan-card-features li::before {
  content: "✓ ";
  color: var(--ok, #3fb950);
  margin-right: 4px;
  font-weight: 700;
}
.plan-card-cta {
  margin-top: 6px;
  width: 100%;
  text-align: center;
  padding: 7px 10px;
  font-size: 12.5px;
  font-weight: 600;
}
.plan-card-cta:disabled {
  cursor: default;
}

/* ---- Change-tier modal ------------------------------------------
   Stays in the same .modal-card modal system as the Buy Credits
   modal so backdrop-click + Esc-close behaviour is consistent. The
   summary panel summarises the delta ("+10 sessions / month", "–
   1 saved prompt") so the user sees what they're about to change
   without having to mentally diff two numbers. The deltas are
   positive (green) for upgrades, negative (red) for downgrades,
   zero (dim) for no-op transitions. */

.change-tier-summary {
  background: var(--bg-3, #1f2530);
  border: 1px solid var(--border, #2c3340);
  border-radius: 8px;
  padding: 12px 14px;
  margin: 12px 0 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.change-tier-summary-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 13px;
}
.change-tier-summary-label {
  color: var(--text-dim, #8b949e);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.change-tier-summary-value {
  font-family: var(--mono, monospace);
  font-weight: 700;
  font-size: 14px;
  color: var(--text, #e6edf3);
}
.change-tier-delta {
  font-family: var(--mono, monospace);
  font-weight: 700;
  font-size: 12.5px;
  padding: 2px 8px;
  border-radius: 999px;
}
.change-tier-delta.is-up {
  color: var(--ok, #3fb950);
  background: rgba(63, 185, 80, 0.10);
  border: 1px solid rgba(63, 185, 80, 0.30);
}
.change-tier-delta.is-down {
  color: var(--err, #f85149);
  background: rgba(248, 81, 73, 0.10);
  border: 1px solid rgba(248, 81, 73, 0.30);
}
.change-tier-delta.is-flat {
  color: var(--text-dim, #8b949e);
  background: rgba(139, 148, 158, 0.10);
  border: 1px solid rgba(139, 148, 158, 0.30);
}

.change-tier-callout {
  background: rgba(124, 92, 255, 0.06);
  border: 1px solid rgba(124, 92, 255, 0.25);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 12.5px;
  color: var(--text-dim, #a3b1c2);
  line-height: 1.45;
  margin-bottom: 14px;
}
.change-tier-callout.is-down {
  background: rgba(210, 153, 34, 0.06);
  border-color: rgba(210, 153, 34, 0.30);
  color: var(--warn, #d29922);
}

/* ============================================================
 * Credits transaction history (Plan & credits card)
 * ============================================================
 * The recent-activity section now renders as a compact data
 * table with paginated rows (7 per page). We keep the same
 * border / surface palette as the rest of the account cards
 * so the table reads as a denser extension of the card body
 * rather than as a separate UI. Column widths are tuned for
 * the four-cell layout (Date / Type / Notes / Δ) on the
 * narrowest supported card width (~360px) without horizontal
 * overflow, and the pager sits below the table on its own
 * row, centred, with the Prev/Next buttons reusing the
 * existing ``.ghost.small`` button styling.
 */
.credits-history {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.credits-history-title {
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 11px;
  font-weight: 600;
}
.credits-history-table-wrap {
  /* Horizontal scroll guards against very long notes cells
     on narrow viewports rather than letting them push the
     surrounding card layout around. */
  overflow-x: auto;
  border: 1px solid var(--border, var(--surface-2));
  border-radius: 8px;
  background: var(--bg-2, var(--surface-1));
}
.credits-history-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  table-layout: fixed;
}
.credits-history-table thead th {
  text-align: left;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10.5px;
  color: var(--text-dim, #8b949e);
  padding: 8px 10px;
  border-bottom: 1px solid var(--border, var(--surface-3));
  background: var(--bg-3, var(--surface-1));
}
.credits-history-table tbody td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--border, var(--surface-2));
  color: var(--text, #e6edf3);
  vertical-align: middle;
  word-break: break-word;
}
.credits-history-table tbody tr:last-child td {
  border-bottom: none;
}
.credits-history-table tbody tr:hover {
  background: rgba(124, 92, 255, 0.05);
}
/* Column widths: Date + Δ are fixed-width, Type + Notes share
   the remaining space. Keeping Δ tight prevents the rightmost
   column from looking visually unbalanced against the Notes
   column on wider screens. */
.credits-history-table th:nth-child(1),
.credits-history-table td:nth-child(1) {
  width: 28%;
}
.credits-history-table th:nth-child(2),
.credits-history-table td:nth-child(2) {
  width: 22%;
}
.credits-history-table th:nth-child(3),
.credits-history-table td:nth-child(3) {
  width: auto;
}
.credits-history-table th.credits-delta-col,
.credits-history-table td.credits-delta-cell {
  width: 22%;
  text-align: right;
  white-space: nowrap;
}
.credits-date-cell {
  font-family: var(--mono, monospace);
  font-size: 11.5px;
  color: var(--text-dim, #a3b1c2);
}
.credits-notes-cell {
  /* Cap at 3 lines so a single chatty description doesn't
     balloon the row height. ``-webkit-box-clamp`` keeps
     older WebKit-based browsers happy too. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;
  overflow: hidden;
}
.credits-delta-cell.is-positive {
  color: var(--ok, #3fb950);
  font-weight: 700;
}
.credits-delta-cell.is-negative {
  color: var(--err, #f85149);
  font-weight: 700;
}
.credits-delta-cell:not(.is-positive):not(.is-negative) {
  color: var(--text-dim, #8b949e);
}
.credits-history-table .credits-empty-row {
  text-align: center;
  padding: 14px 10px;
  font-style: italic;
}
/* Per-transaction-type chip colours. We colour-code the
   transaction type pill so users can scan a row and tell at
   a glance whether the entry was a usage, purchase, bonus,
   adjustment, etc. Each colour follows the same hue family
   used by the rest of the app (success green / warn amber /
   info blue / dim grey). */
.chip.credit-type-purchase    { color: var(--ok, #3fb950); border-color: rgba(63, 185, 80, 0.40); background: rgba(63, 185, 80, 0.08); }
.chip.credit-type-bonus       { color: #79c0ff;            border-color: rgba(121, 192, 255, 0.40); background: rgba(121, 192, 255, 0.08); }
.chip.credit-type-usage       { color: var(--err, #f85149); border-color: rgba(248, 81, 73, 0.40); background: rgba(248, 81, 73, 0.08); }
.chip.credit-type-session     { color: var(--err, #f85149); border-color: rgba(248, 81, 73, 0.40); background: rgba(248, 81, 73, 0.08); }
.chip.credit-type-library     { color: var(--warn, #d29922); border-color: rgba(210, 153, 34, 0.40); background: rgba(210, 153, 34, 0.08); }
.chip.credit-type-adjustment  { color: #d2a8ff;            border-color: rgba(210, 168, 255, 0.40); background: rgba(210, 168, 255, 0.08); }
.chip.credit-type-refund      { color: var(--ok, #3fb950); border-color: rgba(63, 185, 80, 0.40); background: rgba(63, 185, 80, 0.08); }
.chip.credit-type-signup      { color: #79c0ff;            border-color: rgba(121, 192, 255, 0.40); background: rgba(121, 192, 255, 0.08); }
.chip.credit-type-monthly     { color: #79c0ff;            border-color: rgba(121, 192, 255, 0.40); background: rgba(121, 192, 255, 0.08); }
.chip.credit-type-tier_change { color: #d2a8ff;            border-color: rgba(210, 168, 255, 0.40); background: rgba(210, 168, 255, 0.08); }
/* Fallback for any transaction_type the server returns that we
   haven't enumerated above. Keeps the chip readable instead of
   inheriting an unset colour. */
.chip[class*="credit-type-"] {
  font-size: 11px;
  letter-spacing: 0.02em;
  text-transform: lowercase;
}

/* Pager row: sits below the table inside the same card,
   separated by a faint divider so the eye knows it belongs to
   the table above. Centred flex row keeps the buttons
   equidistant from the page indicator. */
.credits-history-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding-top: 8px;
  margin-top: 4px;
  border-top: 1px solid var(--border, var(--surface-2));
  flex-wrap: wrap;
}
.pager-btn {
  min-width: 80px;
  /* ``.ghost.small`` already supplies most of the styling; we
     only nudge the width so Prev and Next are visually equal
     regardless of the unicode arrow glyph they contain. */
  font-variant-numeric: tabular-nums;
}
.pager-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.pager-status {
  font-size: 12px;
  color: var(--text-dim, #a3b1c2);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  justify-content: center;
}
.pager-status .mono {
  color: var(--text, #e6edf3);
}
.pager-total {
  margin-left: 2px;
}

/* ====================================================================
   WIZARD MOBILE POLISH (per UX plan)
   --------------------------------------------------------------------
   On phones (<=640px) the v2 wizard's interview screen gets:

   * Question-card padding tightened so long questions don't crowd
     against the viewport edge.
   * Choice cards raised to a 48px-min tap target so each option is
     comfortable to tap.
   * The progress bar gets slightly more height (5px → 6px) so it's
     easy to read at a glance.
   * Topic cards collapse to single-column with bigger tap targets
     so picking a category feels as solid as picking a choice.
   * The compact wizard stepper already collapses labels — we add
     a centered "Step N of 6" indicator above it on mobile so users
     see where they are without scrolling back to read the label.

   We also add bottom padding to the .v2-wizard section so the sticky
   action bar (defined in the template, see .v2-mobile-bar) doesn't
   cover the last choice / next button.
   ==================================================================== */
@media (max-width: 640px) {
  /* Question card: tighter padding, roomier line-height. */
  .question-card {
    padding: 16px 16px;
    margin-top: 12px;
  }
  .question-card h2 { font-size: 17px; line-height: 1.35; }
  .question-card .hint { font-size: 13px; margin-bottom: 12px; }

  /* The inline action rows are hidden on phones: the sticky
     ``.v2-mobile-bar`` below is the single action surface on the
     interview, review and draft screens, so the primary is never
     duplicated. */
  .wizard-actions,
  .review-actions,
  .draft-actions { display: none; }

  /* Choice cards: 48px min-height is the WCAG-suggested target.
     The label sits centered vertically and stays 14px so the option
     text is readable without making the card too tall. */
  .choice {
    min-height: 48px;
    padding: 12px 14px;
    font-size: 14px;
    gap: 12px;
  }
  .choice-label { font-size: 14px; }
  .choice-desc { font-size: 12px; }

  /* "Other" input row gets a comfortable mobile height too. */
  .other-box input { min-height: 44px; font-size: 16px; }

  /* Topic grid: 1 column with bigger tap targets. */
  .topic-grid {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .topic-card {
    min-height: 80px;
    padding: 16px;
    text-align: left;
    flex-direction: row;
    align-items: center;
    gap: 14px;
  }
  .topic-card h2 { font-size: 15px; }
  .topic-card p { font-size: 12.5px; }
  .topic-emoji { font-size: 28px; }

  /* Wizard stepper: shrink the dot size so the whole row fits on a
     narrow phone without horizontal scrolling. */
  .wizard-stepper { padding: 10px 14px; gap: 2px; }
  .step .dot { width: 22px; height: 22px; font-size: 11px; }

  /* Progress bar: slightly thicker. */
  .progress-bar { height: 6px; }

  /* Wizard header (title + subtitle): tighter rhythm. */
  .v2-header h1 { font-size: clamp(1.4rem, 5vw + 0.4rem, 2rem); }
  .v2-header .subtitle { font-size: 13.5px; }

  /* The wizard section itself gets bottom padding so the sticky
     action bar (below) doesn't sit on top of the last choice. */
  .v2-wizard { padding-bottom: calc(72px + env(safe-area-inset-bottom)); }
}

/* ====================================================================
   WIZARD MOBILE STICKY ACTION BAR
   --------------------------------------------------------------------
   Mobile-only bottom action bar for the v2 wizard interview screen.
   On phones (<=640px) it is the *only* action surface: the inline
   "Next / Skip / Finish" row above the question card is hidden, so
   there is a single primary action in thumb range. JS toggles
   ``is-visible`` based on v2Screen === 'interview'.

   Primary "Next" is full-width; secondary "Skip" + "Finish" sit
   alongside and stay visible even on very narrow phones (<380px) so
   skip / finish-early remain reachable.
   ==================================================================== */
.v2-mobile-bar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  background: var(--glass-panel-strong);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--border, var(--surface-3));
  padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
  gap: 8px;
  align-items: stretch;
  transform: translateY(110%);
  transition: transform 0.2s ease;
}
.v2-mobile-bar.is-visible {
  display: flex;
  transform: translateY(0);
}
.v2-mobile-bar .v2-mobile-primary {
  flex: 2;
  min-height: 48px;
  font-size: 15px;
  font-weight: 600;
  background: linear-gradient(135deg, #7c5cff 0%, #5b8def 100%);
  color: #ffffff;
  border: none;
  border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  cursor: pointer;
}
.v2-mobile-bar .v2-mobile-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.v2-mobile-bar .v2-mobile-secondary {
  flex: 1;
  min-height: 48px;
  font-size: 13px;
  font-weight: 500;
  background: transparent;
  color: var(--text, #e6edf3);
  border: 1px solid var(--border, var(--surface-4));
  border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  cursor: pointer;
}
.v2-mobile-bar .v2-mobile-secondary:disabled { opacity: 0.4; }
.v2-mobile-bar .v2-mobile-secondary:active {
  background: var(--surface-2);
}
/* Compact icon Back button, visible only once the user has stepped back.
   Kept narrow so Skip / Finish / Next still fit on a 360px phone. */
.v2-mobile-bar .v2-mobile-back {
  flex: 0 0 auto;
  width: 44px;
  min-height: 48px;
  font-size: 20px;
  line-height: 1;
  background: transparent;
  color: var(--text, #e6edf3);
  border: 1px solid var(--border, var(--surface-4));
  border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  cursor: pointer;
}
.v2-mobile-bar .v2-mobile-back:disabled { opacity: 0.4; }
/* Compact icon-only Cancel. Destructive, so it sits at the far left,
   away from the primary Next, and mirrors the ‹ Back button's size. */
.v2-mobile-bar .v2-mobile-cancel {
  flex: 0 0 auto;
  width: 44px;
  min-height: 48px;
  font-size: 16px;
  line-height: 1;
  background: transparent;
  color: var(--err);
  border: 1px solid var(--border, var(--surface-4));
  border-radius: 10px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  cursor: pointer;
}
.v2-mobile-bar .v2-mobile-cancel:disabled { opacity: 0.4; }

/* Thinking-overlay reduced-motion path. Kept with the bar section (rather
   than beside the overlay rules) so the first reduced-motion block in the
   sheet stays the welcome-tip fade rule the tips guard pins. */
@media (prefers-reduced-motion: reduce) {
  .v2-clock-hand { animation: none; }
  .v2-thinking-skeleton span { animation: none; opacity: 0.6; }
}

/* Skip and Finish stay visible on very narrow phones (<380px). Now
   that the sticky bar is the only action surface on mobile, hiding the
   secondaries there would remove skip / finish-early entirely. */
@media (min-width: 641px) {
  .v2-mobile-bar { display: none !important; }
}

/* =====================================================================
   D-112 FOLLOW-UP: SAVED CARDS + ADD-CARD MODAL + PAYMENT-RESULT MODAL
   =====================================================================
   The Saved Cards sub-card sits inside the Plan & Credits card; the
   Add-card modal mirrors the purchase / subscribe modals so users
   get a consistent tokenisation experience across all three flows.
   The payment-result modal pops up after every payment action
   (subscribe, purchase credits, add card, cancel, change tier) to
   give immediate, prominent success / failure feedback. */

/* Saved Cards is a top-level .account-card (grid-area: savedcards).
   Nested-subcard chrome (extra margin/border/bg) is stripped so it
   matches Profile / Security / Purchase. List + chip rules below
   still apply. */
.saved-cards-card {
  /* grid-area is assigned with the other Account cards above. */
}
.saved-cards-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.saved-cards-list {
  list-style: none;
  margin: 0 0 6px 0;
  padding: 0;
}
.saved-card-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border, var(--surface-2));
  font-size: 13px;
}
.saved-card-row:last-child { border-bottom: 0; }
.saved-card-row.is-default {
  background: rgba(96, 165, 250, 0.04);
  border-radius: 6px;
  padding-left: 6px;
  padding-right: 6px;
  margin: 0 -6px;
}
.saved-card-brand {
  flex: 0 0 80px;
  font-weight: 600;
  text-transform: capitalize;
}
.saved-card-last4 { flex: 0 0 80px; }
.saved-card-exp { flex: 0 0 70px; }
.saved-card-default-chip {
  background: var(--accent, #60a5fa);
  color: #0f172a;
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 11px;
  font-weight: 700;
}
.saved-card-processor {
  display: inline-block;
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: rgba(148, 163, 184, 0.18);
  color: #cbd5e1;
  border: 1px solid rgba(148, 163, 184, 0.35);
}
.saved-card-processor.is-mock {
  background: rgba(251, 191, 36, 0.12);
  color: #fbbf24;
  border-color: rgba(251, 191, 36, 0.4);
}
.saved-card-processor.is-square {
  background: rgba(96, 165, 250, 0.14);
  color: #93c5fd;
  border-color: rgba(96, 165, 250, 0.4);
}
.saved-card-processor.is-stripe {
  background: rgba(167, 139, 250, 0.14);
  color: #c4b5fd;
  border-color: rgba(167, 139, 250, 0.4);
}
.saved-card-processor.is-paypal {
  background: rgba(45, 142, 234, 0.16);
  color: #93c5fd;
  border-color: rgba(45, 142, 234, 0.45);
}
.saved-card-row.is-test-card {
  opacity: 0.78;
}
.saved-cards-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.saved-cards-actions button.danger {
  color: #fca5a5;
  border-color: rgba(239, 68, 68, 0.4);
  background: rgba(239, 68, 68, 0.08);
}
.saved-cards-actions button.danger:hover {
  background: rgba(239, 68, 68, 0.16);
}
.saved-card-row button { margin-left: auto; }
.saved-card-row button + button { margin-left: 6px; }

/* ---- Payment-result modal (success / error / info feedback) -------
   Pops up after every payment action. The status class on the
   .payment-result-icon element drives the colour so the same modal
   works for success (green tick), error (red triangle), and info
   (blue i). Centred, prominent, dismissable. */
.payment-result-card {
  text-align: center;
  padding: 18px 18px 6px 18px;
}
.payment-result-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 6px 0 6px 0;
}
.payment-result-icon {
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  line-height: 1;
  border-radius: 50%;
  border: 2px solid transparent;
  margin-bottom: 4px;
}
.payment-result-icon.status-success {
  background: rgba(34, 197, 94, 0.12);
  border-color: rgba(34, 197, 94, 0.55);
  color: #22c55e;
}
.payment-result-icon.status-error {
  background: rgba(239, 68, 68, 0.12);
  border-color: rgba(239, 68, 68, 0.55);
  color: #ef4444;
}
.payment-result-icon.status-info {
  background: rgba(96, 165, 250, 0.12);
  border-color: rgba(96, 165, 250, 0.55);
  color: #60a5fa;
}
.payment-result-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
}
.payment-result-detail {
  margin: 0;
  font-size: 14px;
  color: var(--muted, rgba(255, 255, 255, 0.75));
  line-height: 1.45;
  max-width: 36ch;
}
.payment-result-card.status-error .payment-result-title { color: #ef4444; }
.payment-result-card.status-success .payment-result-title { color: #22c55e; }

/* ---- Reset-result modal (auth-gate step 3) ------------------------
   Terminal confirmation for the forgot-password flow. Mirrors the
   payment-result modal's shape but is scoped to .auth-gate and coloured
   from the design tokens (--ok / --err) plus color-mix, so no new
   hardcoded colour literals are introduced. */
.auth-result-card {
  text-align: center;
  padding: 18px 18px 6px 18px;
}
.auth-result-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 6px 0 6px 0;
}
.auth-result-icon {
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  line-height: 1;
  border-radius: 50%;
  border: 2px solid transparent;
  margin-bottom: 4px;
}
.auth-result-icon.status-success {
  background: color-mix(in srgb, var(--ok) 12%, transparent);
  border-color: color-mix(in srgb, var(--ok) 55%, transparent);
  color: var(--ok);
}
.auth-result-icon.status-error {
  background: color-mix(in srgb, var(--err) 12%, transparent);
  border-color: color-mix(in srgb, var(--err) 55%, transparent);
  color: var(--err);
}
.auth-result-title {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
}
.auth-result-detail {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
  line-height: 1.45;
  max-width: 36ch;
}
.auth-result-card.status-error .auth-result-title { color: var(--err); }
.auth-result-card.status-success .auth-result-title { color: var(--ok); }

/* FE-104: honor the OS "reduce motion" setting. Decorative and infinite
   animations (skeleton shimmer, step pulse, toasts, card rise) collapse
   to a single near-instant frame and transitions lose their duration, so
   motion-sensitive users get a static interface. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
