/* cc-workflow PWA — Phase 2.5 visual system.
   All values come from CSS variables in :root. Spacing follows an 8px grid;
   nothing here should use a magic number that isn't in --space-* or --text-*. */

/* ============================================================================
 *  Design tokens
 * ============================================================================ */
:root {
  /* bg layers */
  --bg-canvas:     #0a0b0e;
  --bg-surface:    #14161b;
  --bg-elevated:   #1d1f26;
  --bg-hover:      #232631;

  /* borders */
  --border-subtle: #20222a;
  --border-strong: #2d3038;

  /* text */
  --text-primary:   #ededf0;
  --text-secondary: #9aa0aa;
  --text-tertiary:  #6b7280;
  --text-disabled:  #4a5060;

  /* semantic accents */
  --accent-blue:        #2f7eff;
  --accent-blue-hover:  #4a90ff;
  --accent-blue-faint:  rgba(47, 126, 255, 0.15);
  --accent-green:       #22c55e;    /* success — task done */
  --accent-cyan:        #06b6d4;    /* info — Claude reply, engine=claude */
  --accent-amber:       #f59e0b;    /* warning / queued */
  --accent-red:         #ef4444;    /* danger / failed */
  --accent-purple:      #a855f7;    /* roundtable role: 借鉴派 (kimi) */

  /* spacing — 8px rhythm */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  /* radius */
  --radius-sm:   6px;
  --radius-md:   10px;
  --radius-full: 999px;

  /* shadow */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-2: 0 4px 12px rgba(0, 0, 0, 0.4);
  --shadow-3: 0 8px 24px rgba(0, 0, 0, 0.5);

  /* font stacks — sans for UI, mono only for code/cron/run-id */
  --font-ui:   system-ui, -apple-system, "Segoe UI", "SF Pro Text",
               "Helvetica Neue", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, "Roboto Mono", monospace;

  /* type scale */
  --text-micro: 11px;
  --text-sm:    12px;
  --text-base:  13px;
  --text-md:    14px;
  --text-lg:    15px;
  --text-h3:    18px;
  --text-h2:    22px;
  --text-h1:    28px;

  /* line-height */
  --lh-tight:  1.3;
  --lh-normal: 1.5;
  --lh-loose:  1.7;

  /* transition */
  --t-fast: 100ms ease-out;
  --t-base: 150ms ease-out;
}

/* ============================================================================
 *  Base
 * ============================================================================ */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-ui);
  font-size: var(--text-md);
  line-height: var(--lh-normal);
  background: var(--bg-canvas);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Body is a column flex container so <main> can flex-grow to fill the
   remaining viewport space below the sticky topbar. This is what lets
   .ws-layout (a child of main) shrink-to-fit single-viewport, no
   page scroll, regardless of how many rows there are. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
code, pre { font-family: var(--font-mono); }

h1, h2, h3 {
  margin: 0;
  color: var(--text-primary);
  font-weight: 600;
  line-height: var(--lh-tight);
}
h1 { font-size: var(--text-h2); margin: var(--space-4) 0 var(--space-3); }
h2 { font-size: var(--text-h3); }
h3 { font-size: var(--text-lg); margin: var(--space-3) 0 var(--space-2); }

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

a { color: var(--accent-blue); text-decoration: none; transition: color var(--t-base); }
a:hover { color: var(--accent-blue-hover); }

::selection { background: var(--accent-blue-faint); }

/* ============================================================================
 *  Topbar
 * ============================================================================ */
.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
}
.brand {
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  font-size: var(--text-lg);
}
.tabs { display: flex; gap: var(--space-1); flex: 1; }
.tab {
  position: relative;
  padding: var(--space-2) var(--space-3);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--text-base);
  border-radius: var(--radius-sm);
  transition: color var(--t-base), background var(--t-base);
}
.tab:hover { background: var(--bg-elevated); color: var(--text-primary); }
.tab.active { color: var(--accent-blue); background: transparent; }
.tab.active::after {
  content: ''; position: absolute;
  bottom: -10px; left: var(--space-3); right: var(--space-3);
  height: 2px;
  background: var(--accent-blue);
  border-radius: var(--radius-full);
}
.status { font-size: var(--text-micro); color: var(--text-tertiary); }

/* ============================================================================
 *  Main / views
 * ============================================================================ */
main {
  padding: var(--space-4);
  max-width: 1600px;
  margin: 0 auto;
  width: 100%;
  flex: 1 1 auto;            /* fill viewport below topbar */
  min-height: 0;             /* allow children to shrink */
  display: flex;
  flex-direction: column;
}

/* ============================================================================
 *  Workspaces grid — used ONLY by the mobile carousel detail view now.
 *  PC overview uses .ws-layout > .ws-row (row-based, defined further down).
 *  The desktop / tablet @media rules below are dead in practice (PC takes
 *  the .ws-layout path, mobile takes carousel) but kept as harmless
 *  fallbacks for the brief pre-JS render moment.
 * ============================================================================ */
.ws-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1100px) { .ws-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px)  { .ws-grid { grid-template-columns: 1fr; } }

.ws-col {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  min-height: 240px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  box-shadow: var(--shadow-1);
}

/* Column header: 2-line layout.
     row 1 — [drag handle] [name h2 (flex grows)] [wide toggle]
     row 2 — "as <provider> · <engine>" */
.ws-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-1);
}
.ws-head-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.ws-head-row h2 { flex: 1; min-width: 0; }
.ws-col h2 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
}
.ws-provider {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  flex-wrap: wrap;        /* graceful fallback on very narrow screens */
  row-gap: var(--space-1);
}
/* "as" label was removed in favor of a colored engine chip — the
   workspace's identity is now visually anchored by the engine pill,
   not a small English word. Class kept around in case of future use. */
.ws-provider-label { display: none; }

/* Engine chip — small colored pill that anchors the workspace's
   identity. claude/codex are immutable post-create, so the chip
   doubles as a visual id (you learn to scan for "the cyan one"
   vs "the purple one"). Same chip shape as .tag, slightly smaller. */
.ws-engine {
  display: inline-flex;
  align-items: center;
  padding: 1px var(--space-2);
  font-size: var(--text-micro);
  font-weight: 500;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  line-height: 1.5;
  text-transform: lowercase;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}
.ws-engine[data-engine="claude"] {
  background: rgba(6, 182, 212, 0.10);
  color: var(--accent-cyan);
  border-color: rgba(6, 182, 212, 0.30);
}
/* Engine=codex chip styling removed 2026-05-14 along with codex support.
   Existing workspaces.json entries with engine=codex will fall through to
   the default chip (gray) — visual cue to delete-and-recreate. */

/* Per-workspace trust toggle — lock / unlock icon button inline with
   provider + engine. Click flips trust (with a confirm() when going
   from locked → unlocked since it's the security-relevant direction).
   Untrusted = neutral text-tertiary; trusted = amber (signals "this
   workspace skips tool approvals, treat with care"). */
.ws-trust-toggle {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  padding: 2px var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
  margin-left: var(--space-1);
  font: inherit;
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  flex-shrink: 0;
}
.ws-trust-toggle svg { width: 12px; height: 12px; display: block; }
.ws-trust-toggle:hover { color: var(--text-primary); background: var(--bg-elevated); border-color: var(--border-subtle); }
.ws-trust-toggle.is-trusted {
  color: var(--accent-amber);
  border-color: rgba(245, 158, 11, 0.30);
  background: rgba(245, 158, 11, 0.10);
}
.ws-trust-toggle:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-blue-faint); }
/* .ws-trust-toggle.is-locked removed 2026-05-14 along with codex support
   (codex was the only thing that locked trust). */

/* Sync skills button — same chrome as ws-trust-toggle for visual cohesion.
   Spins when syncing (CSS-only). */
.ws-sync-skills {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  padding: 2px var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
  margin-left: var(--space-1);
  font: inherit;
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  flex-shrink: 0;
}
.ws-sync-skills svg { width: 12px; height: 12px; display: block; }
.ws-sync-skills:hover { color: var(--text-primary); background: var(--bg-elevated); border-color: var(--border-subtle); }
.ws-sync-skills:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-blue-faint); }
.ws-sync-skills.is-syncing svg { animation: cc-spin 0.8s linear infinite; }
@keyframes cc-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* Reset session button — destructive, so hover lights up red to make
   the consequence visible before click. Same chrome as the sibling
   icons for visual consistency. */
.ws-reset-session {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  padding: 2px var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
  margin-left: var(--space-1);
  font: inherit;
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  flex-shrink: 0;
}
.ws-reset-session svg { width: 12px; height: 12px; display: block; }
.ws-reset-session:hover {
  color: var(--accent-red);
  background: rgba(239, 68, 68, 0.10);
  border-color: rgba(239, 68, 68, 0.30);
}
.ws-reset-session:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.30); }

/* Delete workspace button — same chrome as siblings, slightly more
   aggressive red on hover than .ws-reset-session because this op is
   strictly worse (whole dir + configs, vs just session history). */
.ws-delete-workspace {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  padding: 2px var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
  margin-left: var(--space-1);
  font: inherit;
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  flex-shrink: 0;
}
.ws-delete-workspace svg { width: 12px; height: 12px; display: block; }
.ws-delete-workspace:hover {
  color: var(--accent-red);
  background: rgba(239, 68, 68, 0.18);
  border-color: var(--accent-red);
}
.ws-delete-workspace:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.40); }

/* Slash command autocomplete popup — absolutely positioned over textareas.
   Single element re-used across all workspaces (see _ensureSlashPopup in app.js).
   Width tracks the textarea via inline style.minWidth. */
.slash-popup {
  position: absolute;
  z-index: 100;
  max-height: 260px;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  padding: var(--space-1);
  font-size: var(--text-sm);
}
.slash-popup[hidden] { display: none; }
.slash-item {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
  user-select: none;
}
.slash-item.selected { background: var(--bg-elevated); }
.slash-item:hover     { background: var(--bg-elevated); }
.slash-item-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.slash-item-name {
  color: var(--accent-blue);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}
.slash-item-source {
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  padding: 0 var(--space-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}
.slash-item-desc {
  color: var(--text-secondary);
  font-size: var(--text-micro);
  margin-top: 2px;
  line-height: 1.4;
}
.slash-popup-empty {
  padding: var(--space-3);
  color: var(--text-secondary);
}

/* Read-only trust badge in mobile overview cards. Same amber color cue as
   the PC toggle's trusted state. */
.ws-card-trust { display: inline-flex; align-items: center; }
.ws-card-trust svg { width: 12px; height: 12px; color: var(--accent-amber); vertical-align: middle; }

/* PC drag-to-reorder handle — larger hit area + clearer hover state
   so it's easy to target with a mouse. Hidden on mobile. */
.ws-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  color: var(--text-tertiary);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  user-select: none;
  transition: color var(--t-base), background var(--t-base);
  flex-shrink: 0;
  min-width: 32px;
  min-height: 32px;
}
.ws-drag-handle svg { width: 16px; height: 16px; display: block; }
.ws-drag-handle:hover {
  color: var(--accent-blue);
  background: var(--accent-blue-faint);
}
.ws-drag-handle:active { cursor: grabbing; }

/* Row-based PC overview layout. Each row is its own flex container so
   cards share the row's width equally; row count is implicit (= # of
   inner arrays in _wsLayout). The layout itself flex-grows to fill
   whatever vertical space <main> has left after h1 + add-form. */
.ws-layout {
  display: flex;
  flex-direction: column;
  gap: 0;
  flex: 1 1 auto;
  min-height: 0;
}
.ws-row {
  display: flex;
  gap: var(--space-3);
  flex: 1 1 0;               /* every row gets an equal slice of vertical space */
  min-height: 200px;         /* below this each card becomes unusable */
  /* No max-height: with fewer rows, each row expands to fill the viewport
     so there's no "reserved empty space" feel at the bottom. With many
     rows, each row hits min-height and the page scrolls. */
}
.ws-row .ws-col {
  flex: 1 1 0;
  min-width: 0;
  min-height: 0;             /* override the 240px default so flex can shrink */
}
.ws-row .ws-col .ws-timeline {
  flex: 1 1 auto;
  max-height: none;          /* let timeline use whatever's left after head + form */
  min-height: 0;
}

/* Slim drop zone between rows. Expands + highlights when a card is
   dragged over it. Dropping here splits/creates a row. */
.ws-row-gap {
  height: 8px;
  flex: 0 0 auto;
  margin: 0;
  border-radius: var(--radius-md);
  transition: height var(--t-base), background var(--t-base), border-color var(--t-base);
  border: 2px dashed transparent;
}
.ws-row-gap.drop-target {
  height: 56px;
  background: var(--accent-blue-faint);
  border-color: var(--accent-blue);
}

/* Hide button — small "eye-off" icon at the right of .ws-head-row, only
   on PC overview (mobile overview uses card list, no head row). */
.ws-hide-btn {
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-tertiary);
  padding: var(--space-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
  flex-shrink: 0;
  font: inherit;
  margin-left: auto;
}
.ws-hide-btn svg { width: 14px; height: 14px; display: block; }
.ws-hide-btn:hover { color: var(--text-primary); background: var(--bg-elevated); border-color: var(--border-subtle); }
.ws-hide-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-blue-faint); }

/* Hidden strip — slim row of pills, each restores a workspace back to
   the layout. Only rendered when _wsHidden is non-empty. */
.ws-hidden-strip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  flex: 0 0 auto;
}
.ws-restore-btn {
  background: transparent;
  color: var(--text-secondary);
  border: 1px dashed var(--border-strong);
  padding: 2px var(--space-2);
  font-size: var(--text-sm);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font: inherit;
  transition: color var(--t-base), background var(--t-base), border-color var(--t-base);
}
.ws-restore-btn:hover {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border-color: var(--accent-blue);
  border-style: solid;
}

/* Visual state during drag */
.ws-col.dragging {
  opacity: 0.35;
  transform: scale(0.98);
  box-shadow: none;
}
.ws-col {
  position: relative;            /* anchor for ::before / ::after insertion lines */
  transition: box-shadow var(--t-base);
}

/* Drop-target hover: render a vertical insertion line on the left OR right
   edge of the target card, depending on which side of the midline the
   cursor is on. Trello-style — tells the user EXACTLY where the source
   card will land before they commit. */
.ws-col.drop-target-left::before,
.ws-col.drop-target-right::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--accent-blue);
  border-radius: 2px;
  box-shadow: 0 0 12px var(--accent-blue);
  z-index: 5;
}
.ws-col.drop-target-left::before {
  left: calc(-1 * var(--space-2));
}
.ws-col.drop-target-right::after {
  right: calc(-1 * var(--space-2));
}

/* Global drag-state class on <body>. While ANY card is being dragged,
   make every potential drop zone discoverable — slim row-gaps puff up
   to 36px with dashed outlines so the user sees "you can drop here too,
   not just on a card". */
body.is-dragging {
  cursor: grabbing;
}
body.is-dragging .ws-row-gap {
  height: 36px;
  border: 2px dashed rgba(47, 126, 255, 0.45);
  background: rgba(47, 126, 255, 0.05);
}
body.is-dragging .ws-row-gap.drop-target {
  height: 56px;
  border-color: var(--accent-blue);
  background: var(--accent-blue-faint);
}
body.is-dragging .ws-col:not(.dragging) {
  box-shadow: var(--shadow-2);     /* slight lift so non-source cards look "ready to receive" */
}

.provider-inline {
  width: auto;
  padding: 2px var(--space-2);
  font-size: var(--text-sm);
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--t-base), color var(--t-base), border-color var(--t-base);
}
.provider-inline:hover { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-subtle); }
.provider-inline:focus-visible { border-color: var(--accent-blue); outline: none; box-shadow: 0 0 0 3px var(--accent-blue-faint); }

/* ============================================================================
 *  Timeline (chat-bubble rows)
 * ============================================================================ */
.ws-timeline {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-height: 360px;
  overflow-y: auto;
  padding-right: var(--space-1);
}
.ws-timeline::-webkit-scrollbar { width: 5px; }
.ws-timeline::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-full); }
.ws-timeline::-webkit-scrollbar-track { background: transparent; }

.ws-timeline .muted { font-size: var(--text-sm); padding: var(--space-2) 0; }

/* Generic row — used by Tasks tab loop list */
.row {
  padding: var(--space-2) var(--space-3);
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  transition: background var(--t-base), border-color var(--t-base);
}
.row code { color: var(--accent-blue); font-family: var(--font-mono); font-size: var(--text-sm); }

/* Clickable timeline rows */
.run-link, .run-link:hover, .run-link:focus, .run-link:visited {
  text-decoration: none;
  color: inherit;
}
.run-link:hover { background: var(--bg-elevated); border-color: var(--border-strong); cursor: pointer; }

.ws-timeline .row {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
}
.ws-timeline .row-head {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

/* Cancel button on the run-detail page. Visible only when the run is
 * actually 'running'. Sized as a normal action button — the previous
 * tiny ghost-button-in-row design was misclick bait on mobile, so we
 * moved this to the dedicated detail page where it gets its own row
 * (.run-actions) and a hit target you actually have to aim at. Red
 * theme communicates "destructive"; not pre-disabled so the click +
 * confirm path is the only barrier between intent and action. */
.run-actions {
  margin: var(--space-2) 0 var(--space-4);
}
.run-cancel-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: rgba(239, 68, 68, 0.10);
  color: var(--accent-red);
  border: 1px solid rgba(239, 68, 68, 0.40);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: background var(--t-base), color var(--t-base), border-color var(--t-base);
}
.run-cancel-btn:hover {
  background: rgba(239, 68, 68, 0.20);
  color: var(--text-primary);
  border-color: var(--accent-red);
}
.run-cancel-btn:disabled { opacity: 0.5; cursor: wait; }

/* Live output tail — only visible on detail pages of 'running' runs.
 * Pre-formatted (no markdown render) so the user sees raw stream-jsonl
 * lines the way claude emits them. Capped height so a long stream
 * doesn't push the Prompt/Output sections off-screen. */
.run-live-hint {
  font-size: var(--text-sm);
  font-weight: normal;
  margin-left: var(--space-2);
}
.run-live-tail {
  /* No max-height / inner scroll — moved to the bottom of run-detail
   * (2026-05-15), let the page scroll instead. Two-finger inner scroll
   * on mobile + box-within-box scroll on PC both got annoying once Live
   * output started containing 100KB Write tool_use payloads. */
  font-size: var(--text-xs);
  line-height: var(--lh-normal);
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--text-secondary);
}
.row-prompt, .row-output {
  font-size: var(--text-sm);
  padding-left: var(--space-1);
  word-break: break-word;
  overflow-wrap: break-word;
  white-space: pre-wrap;
  line-height: var(--lh-normal);
}
.row-prompt { color: var(--text-secondary); }      /* you said — dim gray */
.row-output { color: var(--accent-cyan); }         /* claude said — cyan (info), NOT green (status) */
.row-time   { cursor: help; }                      /* hover for absolute timestamp */
.row-time:hover { text-decoration: underline dotted; }

/* ============================================================================
 *  Pending tool-approval block — appears after a "running" run row when
 *  Claude's PreToolUse hook is blocked waiting on the user. Amber accents
 *  to match the trust-state color family (this IS the "needs attention"
 *  variant of trust).
 * ============================================================================ */
.approval-pending {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  background: rgba(245, 158, 11, 0.10);
  border: 1px solid rgba(245, 158, 11, 0.40);
  border-radius: var(--radius-md);
  margin: var(--space-1) 0;
}
.approval-pending-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--accent-amber);
  font-size: var(--text-sm);
  font-weight: 500;
}
.approval-pending-head svg { width: 14px; height: 14px; flex-shrink: 0; }
.approval-tool {
  font-size: var(--text-sm);
  color: var(--text-primary);
  word-break: break-word;
}
.approval-tool code {
  font-family: var(--font-mono);
  background: var(--bg-canvas);
  padding: 2px var(--space-1);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.approval-actions {
  display: flex;
  gap: var(--space-2);
}
/* Higher-specificity selectors (.approval-pending .approval-xxx) so
   the colors win regardless of source order / mobile browser default
   button styling. Some Android browsers were rendering these as plain
   neutral buttons when the rule was just .approval-approve alone. */
.approval-pending .approval-approve,
.approval-pending .approval-deny {
  flex: 1;
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  font-weight: 600;
  color: #fff;
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  pointer-events: auto;        /* defensive — no parent should block taps */
  touch-action: manipulation;  /* skip the 300ms double-tap-zoom delay on mobile */
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0.15);
  position: relative;
  z-index: 1;                  /* sit above any drag-state pseudo-elements */
}
.approval-pending .approval-approve {
  background: var(--accent-green);
  border-color: var(--accent-green);
}
.approval-pending .approval-approve:hover,
.approval-pending .approval-approve:active {
  background: #1ea34a;
  border-color: #1ea34a;
}
.approval-pending .approval-deny {
  background: var(--accent-red);
  border-color: var(--accent-red);
}
.approval-pending .approval-deny:hover,
.approval-pending .approval-deny:active {
  background: #d63838;
  border-color: #d63838;
}

/* ============================================================================
 *  Run-detail "Tool approvals" audit panel — collapsible read-only list
 *  of every PreToolUse hook firing for the run, including the trust=on
 *  auto_approved entries that the user never had to click. The point of
 *  this panel is *visibility*: under trust=on the .approval-pending
 *  block (above) never appears, but the user still wants to know what
 *  claude actually ran.
 * ============================================================================ */
.run-approvals {
  margin-top: var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 0;
}
.run-approvals > summary {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-weight: 500;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  list-style: none;
}
.run-approvals > summary::-webkit-details-marker { display: none; }
.run-approvals > summary::before {
  content: '▸ ';
  display: inline-block;
  margin-right: var(--space-1);
  transition: transform 0.15s;
}
.run-approvals[open] > summary::before { transform: rotate(90deg); }
.run-approvals-list {
  padding: var(--space-2) var(--space-3) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-height: 320px;
  overflow-y: auto;
}
.a-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-sm);
  line-height: var(--lh-snug);
  flex-wrap: wrap;
}
.a-row .a-icon { flex-shrink: 0; font-weight: 700; width: 1.2em; text-align: center; }
.a-row .a-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  flex-shrink: 0;
  opacity: 0.85;
}
.a-row .a-tool {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  background: var(--bg-canvas);
  padding: 2px var(--space-1);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  word-break: break-all;
  flex: 1 1 auto;
  min-width: 0;
}
.a-row .a-time { font-size: var(--text-xs); flex-shrink: 0; }
/* Color the icon + label per status — keeps the row scannable. */
.a-row.a-ok    .a-icon, .a-row.a-ok    .a-label { color: var(--accent-green); }
.a-row.a-auto  .a-icon, .a-row.a-auto  .a-label { color: var(--accent-green); opacity: 0.75; }
.a-row.a-deny  .a-icon, .a-row.a-deny  .a-label { color: var(--accent-red); }
.a-row.a-warn  .a-icon, .a-row.a-warn  .a-label { color: var(--accent-amber); }

/* Transcript panel — lazy-loaded full conversation history for one run.
 * Shares <details>/<summary> visual language with .run-approvals so they
 * read as a "drill-deeper" pair. Body is <pre> (whitespace preserved)
 * because _formatStreamLine emits already-formatted text. */
.run-transcript {
  margin-top: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.run-transcript > summary {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-weight: 500;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  list-style: none;
}
.run-transcript > summary::-webkit-details-marker { display: none; }
.run-transcript > summary::before {
  content: '▸ ';
  display: inline-block;
  margin-right: var(--space-1);
  transition: transform 0.15s;
}
.run-transcript[open] > summary::before { transform: rotate(90deg); }
.run-transcript-body {
  margin: 0;
  padding: var(--space-3);
  font-size: var(--text-xs);
  line-height: var(--lh-normal);
  background: var(--bg-canvas);
  border-top: 1px solid var(--border-subtle);
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--text-secondary);
}

/* ============================================================================
 *  Trigger form (chat input at column bottom)
 * ============================================================================ */
.ws-col .trigger-form {
  margin-top: auto;
  display: flex;
  gap: var(--space-2);
  align-items: flex-end;
}
.ws-col .trigger-form textarea { flex: 1; min-height: 60px; }
.ws-col .trigger-form button   { white-space: nowrap; flex: 0 0 auto; }

/* ============================================================================
 *  Tags (status badges) — semantic color, distinguishable shapes
 * ============================================================================ */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  font-size: var(--text-micro);
  font-weight: 500;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  line-height: 1.4;
}
.tag-running { background: rgba(245, 158, 11, 0.10); color: var(--accent-amber); border-color: rgba(245, 158, 11, 0.30); }
.tag-queued  { background: rgba(47, 126, 255, 0.10); color: var(--accent-blue);  border-color: rgba(47, 126, 255, 0.30); }
.tag-done    { background: rgba(34, 197, 94, 0.10);  color: var(--accent-green); border-color: rgba(34, 197, 94, 0.30); }
.tag-failed  { background: rgba(239, 68, 68, 0.10);  color: var(--accent-red);   border-color: rgba(239, 68, 68, 0.30); }

/* ============================================================================
 *  Forms
 * ============================================================================ */
input, select, textarea {
  font: inherit;
  background: var(--bg-canvas);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  transition: border-color var(--t-base), box-shadow var(--t-base);
  width: 100%;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 3px var(--accent-blue-faint);
}
textarea { min-height: 60px; resize: vertical; line-height: var(--lh-normal); }

button {
  font: inherit;
  cursor: pointer;
  background: var(--accent-blue);
  color: #fff;
  border: 1px solid var(--accent-blue);
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: var(--text-base);
  padding: var(--space-2) var(--space-4);
  transition: background var(--t-base), border-color var(--t-base), transform var(--t-fast);
}
button:hover { background: var(--accent-blue-hover); border-color: var(--accent-blue-hover); }
button:active:not(:disabled) { transform: scale(0.97); }
button:disabled {
  background: var(--bg-elevated);
  color: var(--text-disabled);
  border-color: var(--border-subtle);
  cursor: not-allowed;
  transform: none;
}
button:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-blue-faint); }

button.secondary {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--border-strong);
}
button.secondary:hover { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-strong); }

button.danger {
  background: transparent;
  color: var(--accent-red);
  border-color: rgba(239, 68, 68, 0.30);
}
button.danger:hover { background: rgba(239, 68, 68, 0.10); border-color: var(--accent-red); }

label {
  display: block;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-top: var(--space-2);
  font-weight: 500;
}

.hidden { display: none; }

/* ============================================================================
 *  Tag icons — 12px line icons inside .tag chips
 * ============================================================================ */
.tag svg { width: 12px; height: 12px; flex-shrink: 0; }

/* ============================================================================
 *  Toast — replaces the old fixed-top error banner.
 *  Desktop: stack at bottom-right.  Mobile: above the bottom-nav.
 * ============================================================================ */
#toast-container {
  position: fixed;
  bottom: var(--space-4);
  right: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  z-index: 100;
  max-width: 400px;
  pointer-events: none;             /* container ignores clicks; toasts opt in */
}
@media (max-width: 768px) {
  #toast-container {
    /* sit above bottom-nav (64px nominal) + safe-area inset */
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    left: var(--space-3);
    right: var(--space-3);
    max-width: none;
  }
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-3);
  color: var(--text-primary);
  font-size: var(--text-sm);
  pointer-events: auto;
  opacity: 0;
  transform: translateX(20px);
  transition: opacity var(--t-base), transform var(--t-base);
}
.toast.toast-show { opacity: 1; transform: translateX(0); }
.toast.toast-hide { opacity: 0; transform: translateX(20px); }

@media (max-width: 768px) {
  .toast            { transform: translateY(20px); }
  .toast.toast-show { transform: translateY(0); }
  .toast.toast-hide { transform: translateY(20px); }
}

.toast-icon { display: flex; flex-shrink: 0; }
.toast-icon svg { width: 16px; height: 16px; }
.toast-message  { flex: 1; min-width: 0; word-break: break-word; line-height: var(--lh-normal); }
.toast-close {
  background: transparent;
  color: var(--text-tertiary);
  border: none;
  padding: 0;
  width: 22px;
  height: 22px;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  transition: color var(--t-base), background var(--t-base);
}
.toast-close:hover { color: var(--text-primary); background: var(--bg-hover); }

.toast-error   { border-color: rgba(239, 68, 68, 0.40); }
.toast-error   .toast-icon { color: var(--accent-red); }
.toast-success { border-color: rgba(34, 197, 94, 0.40); }
.toast-success .toast-icon { color: var(--accent-green); }
.toast-info    { border-color: rgba(6, 182, 212, 0.40); }
.toast-info    .toast-icon { color: var(--accent-cyan); }
.toast-warning { border-color: rgba(245, 158, 11, 0.40); }
.toast-warning .toast-icon { color: var(--accent-amber); }

/* ============================================================================
 *  Add-form (collapsible <details>)
 * ============================================================================ */
.add-form { margin: var(--space-4) 0; }
.add-form > summary {
  cursor: pointer;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  color: var(--text-secondary);
  list-style: none;
  transition: background var(--t-base), color var(--t-base), border-color var(--t-base);
  user-select: none;
}
.add-form > summary::-webkit-details-marker { display: none; }
.add-form > summary::before { content: "+ "; color: var(--accent-blue); font-weight: 700; }
.add-form > summary:hover { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--accent-blue); }
.add-form[open] > summary { background: var(--bg-elevated); color: var(--text-primary); border-style: solid; border-color: var(--border-strong); }
.add-form form {
  padding: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  margin-top: var(--space-1);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.form-row { display: flex; gap: var(--space-3); }
.form-row > label { flex: 1; }
.parse-row { display: flex; gap: var(--space-2); align-items: flex-end; }
.parse-row > input { flex: 1; }
.parse-row > button { white-space: nowrap; }

.add-loop-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
}
.inline-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: pointer;
}
.inline-check input[type="checkbox"] { width: auto; margin: 0; cursor: pointer; }

/* ============================================================================
 *  Run detail (#runs/<id>)
 * ============================================================================ */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
  transition: color var(--t-base);
}
.back-link:hover { color: var(--text-primary); }
/* Mobile already has the bottom-nav as the primary navigation surface;
 * an in-page "← Workspaces" link in the page header is visually redundant
 * and steals a row of screen real estate. Hide it on narrow viewports.
 * Trade-off: run-detail's back link (which points at the specific workspace
 * detail page, not the overview) also disappears — that one deep-nav path
 * is reachable via the browser back button. If that becomes painful, add
 * a per-page floating back button instead of unhiding this row. */
@media (max-width: 768px) {
  .back-link { display: none; }
}

.run-meta {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: var(--space-2) 0 var(--space-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
.run-meta code { color: var(--accent-blue); font-family: var(--font-mono); }

/* Markdown rendering for run-detail Output section. Style headings,
 * lists, inline code, and fenced code blocks so Claude's typical output
 * (** bold **, ## h2, - bullets, ``` code ```) reads naturally. Mostly
 * inherits from body color/font; just sets sensible spacing.
 */
.md-output {
  font-size: var(--text-md);
  line-height: var(--lh-relaxed);
  color: var(--text-primary);
  word-break: break-word;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-2) 0;
}
.md-output > *:first-child { margin-top: 0; }
.md-output > *:last-child { margin-bottom: 0; }
.md-output h1, .md-output h2, .md-output h3 {
  margin: var(--space-4) 0 var(--space-2);
  font-weight: 600;
  color: var(--text-primary);
}
.md-output h1 { font-size: var(--text-xl); }
.md-output h2 { font-size: var(--text-lg); }
.md-output h3 { font-size: var(--text-md); }
.md-output p { margin: var(--space-2) 0; }
.md-output ul { margin: var(--space-2) 0; padding-left: var(--space-5); }
.md-output li { margin: 2px 0; }
.md-output strong { color: var(--text-primary); font-weight: 600; }
.md-output em { color: var(--text-primary); font-style: italic; }
.md-output code {
  background: var(--bg-elevated);
  padding: 1px 4px;
  border-radius: 3px;
  font-size: 0.92em;
  font-family: var(--font-mono);
  color: var(--accent-blue);
}
.md-output pre.md-code {
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  overflow-x: auto;
  margin: var(--space-2) 0;
}
.md-output pre.md-code code {
  background: transparent;
  padding: 0;
  font-size: var(--text-sm);
  color: var(--text-primary);
}

pre {
  background: var(--bg-surface);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: var(--text-base);
  line-height: var(--lh-loose);
  max-width: 80ch;
  color: var(--text-primary);
}

/* Tasks-tab loop rows — multi-line "card" style so each cron is
   self-explanatory at a glance (name + status + actions + schedule +
   workspace + engine + prompt preview + stats). */
.task-list { display: flex; flex-direction: column; gap: var(--space-2); }

.loop-row {
  flex-direction: column !important;     /* override .row's row-flex */
  align-items: stretch !important;
  gap: var(--space-2) !important;
  padding: var(--space-3);
  font-size: var(--text-sm);
}
.loop-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.loop-name {
  color: var(--text-primary) !important;  /* override .row code's accent-blue */
  font-size: var(--text-md);
}
.loop-actions {
  display: flex;
  gap: var(--space-2);
  margin-left: auto;
  flex-shrink: 0;
}
.loop-actions button { min-height: auto; padding: var(--space-1) var(--space-3); font-size: var(--text-sm); }

.loop-spec {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.loop-spec code { color: var(--accent-blue); font-size: var(--text-sm); font-family: var(--font-mono); }
.loop-when {
  color: var(--accent-blue);
  font-weight: 500;
  /* No mono — human-readable "每天 09:00" / "每 10 分钟" reads better in sans.
     Cron-shaped fallback ("L 5 * * 2#3") will still show in this slot, in
     sans too — acceptable since it's the exception, not the norm. */
}
.loop-when[title] { cursor: help; }
.loop-when[title]:hover { text-decoration: underline dotted; }

.loop-prompt {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: break-word;
  line-height: var(--lh-normal);
  padding: var(--space-1) var(--space-2);
  border-left: 2px solid var(--border-strong);
  background: var(--bg-canvas);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.loop-stats {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

/* Last-run output preview — 200 chars truncated server-side by
 * agent-run.sh#on_exit. Styled like .row-output (cyan, "claude said")
 * so it reads as "what the cron last produced". Note: this is the
 * ONLY visibility into cron output the PWA currently has — full output
 * is on the server (~/.cc-state/logs/<date>.jsonl). If 200 chars is
 * too thin we'd need to route cron through backend so runs.db captures
 * the full row (currently cron calls agent-run directly, bypassing
 * runner.py + the DB). */
.loop-last-output {
  font-size: var(--text-sm);
  color: var(--accent-cyan);
  padding: var(--space-1) var(--space-2);
  border-left: 2px solid var(--accent-cyan);
  background: var(--bg-canvas);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: var(--lh-normal);
  opacity: 0.85;
}

/* ============================================================================
 *  Workspace name link — clicking the h2 in PC overview drills into the
 *  workspace's detail view (#workspaces/<name>). Subtle so it doesn't
 *  shout "this is a link" but reveals on hover.
 * ============================================================================ */
.ws-name-link, .ws-name-link:visited {
  color: inherit;
  text-decoration: none;
  transition: color var(--t-base);
}
.ws-name-link:hover { color: var(--accent-blue); }

/* ============================================================================
 *  Mobile overview list (#workspaces on phone) — compact cards, no inputs.
 *  Each card is an <a> drilling into #workspaces/<name>.
 * ============================================================================ */
.ws-list { display: flex; flex-direction: column; gap: var(--space-2); }

.ws-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  color: inherit;
  text-decoration: none;
  transition: background var(--t-base), border-color var(--t-base);
}
.ws-card:hover,
.ws-card:active {
  background: var(--bg-elevated);
  border-color: var(--border-strong);
}
/* PC: cards are rendered as <div> (not <a>) — see workspace card
 * generator in app.js. Suppress hover-as-clickable affordance so they
 * read as read-only summary tiles. Mobile keeps hover/click behavior. */
@media (min-width: 769px) {
  div.ws-card { cursor: default; }
  div.ws-card:hover,
  div.ws-card:active {
    background: var(--bg-surface);
    border-color: var(--border-subtle);
  }
}
.ws-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}
.ws-card-head h3 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
}
.ws-card-provider {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  flex-wrap: wrap;
  row-gap: var(--space-1);
  justify-content: flex-end;     /* hug the right side of .ws-card-head */
}
.ws-card-provider-name { color: var(--text-secondary); }
.ws-card-meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  flex-wrap: wrap;
}
.ws-card-prompt {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Pending-approval indicator on the mobile overview card. Amber tint
   matches the in-card .approval-pending block so the visual language
   is consistent: amber = "human action required". */
.ws-card-pending-row {
  display: flex;
  align-items: center;
}
.ws-card-pending {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 500;
  padding: 2px var(--space-2);
  background: rgba(245, 158, 11, 0.10);
  border: 1px solid rgba(245, 158, 11, 0.40);
  border-radius: var(--radius-sm);
  color: var(--accent-amber);
}
.ws-card-pending svg { width: 13px; height: 13px; }

/* ============================================================================
 *  PC detail view (#workspaces/<name> on desktop) — single .ws-col centered,
 *  wider, more history rows visible.
 * ============================================================================ */
.ws-col-detail {
  max-width: 900px;
  margin: 0 auto;
}
.ws-col-detail .ws-timeline {
  max-height: 70vh;                                /* much taller than overview's 360px */
}
.ws-col-detail .ws-col {
  min-height: 60vh;
}

/* ============================================================================
 *  Bottom nav + dot indicator — desktop hides both
 * ============================================================================ */
.bottom-nav { display: none; }
.ws-dots    { display: none; }

/* ============================================================================
 *  Mobile (≤ 768px) — carousel + bottom-nav + sticky chat input
 *
 *  Layout on phone:
 *      ┌───────────────┐
 *      │  topbar       │ ← brand + status only (top tabs hidden)
 *      │               │
 *      │  Workspaces   │
 *      │  ┌─────────┐  │
 *      │  │ ws-col  │← scroll-snap; flick L/R between workspaces
 *      │  │  …      │  │
 *      │  │ [textarea Run] │ ← sticky at column bottom (= viewport bottom)
 *      │  └─────────┘  │
 *      │  ◯ ◉ ◯ ◯      │ ← dots
 *      ├───────────────┤
 *      │ ⊞    ☑         │ ← bottom-nav
 *      └───────────────┘
 * ============================================================================ */
@media (max-width: 768px) {

  /* Topbar simplifies — title only, tabs go to bottom-nav */
  .topbar { gap: var(--space-3); padding: var(--space-3); }
  .topbar .tabs { display: none; }
  .brand { font-size: var(--text-md); }
  .status { font-size: 10px; }

  /* Page padding leaves room for bottom-nav */
  main {
    padding: var(--space-3);
    padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  }
  h1 { font-size: var(--text-h3); margin: var(--space-2) 0 var(--space-3); }

  /* Workspaces grid → horizontal carousel with native scroll-snap.
     CSS-only mechanism — no touch-event JS, browser handles momentum + snap. */
  .ws-grid {
    display: flex;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 calc(-1 * var(--space-3));    /* eat main's padding edge-to-edge */
    padding: 0 var(--space-3);
    gap: var(--space-3);
    /* Fixed height: viewport minus topbar (~50) + h1 row (~50) + dots (~30) +
       bottom-nav (~64) + padding (~24). dvh respects iOS dynamic toolbar. */
    height: calc(100dvh - 220px);
  }
  .ws-grid::-webkit-scrollbar { display: none; }

  .ws-col {
    flex: 0 0 calc(100vw - var(--space-6));
    min-width: calc(100vw - var(--space-6));
    scroll-snap-align: center;
    scroll-snap-stop: always;
    height: 100%;
    min-height: 0;                          /* let timeline flex shrink properly */
  }

  /* Inside a fixed-height column: timeline grows to fill, form pinned at bottom */
  .ws-timeline {
    flex: 1 1 0;
    max-height: none;                       /* override the 360px desktop cap */
    min-height: 0;
  }

  /* Dots — one per workspace, active widens into a pill */
  .ws-dots {
    display: flex;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) 0 var(--space-1);
  }
  .ws-dot {
    width: 7px;
    height: 7px;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    border: none;
    cursor: pointer;
    padding: 0;
    transition: width var(--t-base), background var(--t-base);
  }
  .ws-dot.active { background: var(--accent-blue); width: 22px; }
  .ws-dot:focus-visible { outline: 2px solid var(--accent-blue-faint); outline-offset: 2px; }

  /* Bottom nav — fixed, thumb-reach, safe-area-aware */
  .bottom-nav {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    background: var(--bg-surface);
    border-top: 1px solid var(--border-subtle);
    justify-content: space-around;
    z-index: 20;
    padding: var(--space-1) 0;
    padding-bottom: max(var(--space-1), env(safe-area-inset-bottom));
    box-shadow: var(--shadow-2);
  }
  .bottom-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: var(--space-2) var(--space-3);
    color: var(--text-tertiary);
    text-decoration: none;
    font-size: var(--text-micro);
    min-height: 48px;
    flex: 1;
    transition: color var(--t-base);
  }
  .bottom-tab.active { color: var(--accent-blue); }
  .bottom-tab svg { width: 22px; height: 22px; display: block; }

  /* Larger tap targets — minimum 44px height for inputs / buttons */
  button, .provider-inline, .ws-dot, .bottom-tab, .add-form > summary {
    min-height: 44px;
  }
  .ws-dot { min-height: 7px; }              /* dots stay small visually but tap area is wider via padding */

  /* Slightly larger base text on mobile so reading isn't strenuous */
  body { font-size: var(--text-md); }
  .row-prompt, .row-output { font-size: var(--text-base); }

  /* Detail page <pre>: stay readable on narrow viewport */
  pre { font-size: var(--text-sm); padding: var(--space-3); }

  /* Drag-to-reorder + hide button are PC-only —
     mobile uses overview-card-list, no grid to rearrange. */
  .ws-drag-handle,
  .ws-hide-btn { display: none; }

  /* ---------------------------------------------------------------
   *  Add-form on mobile, layered behavior:
   *    - CLOSED:  the <summary> shrinks to a circular FAB at bottom-
   *               right (above bottom-nav). The container .add-form
   *               itself is zero-height (out of flow) so the workspace
   *               list isn't pushed down by an inline dashed "+"
   *               summary anymore.
   *    - OPEN:    .add-form becomes a fixed bottom-sheet (existing
   *               pattern). Summary becomes the sheet's sticky header.
   * --------------------------------------------------------------- */

  /* CLOSED: take up no inline space. The summary is fixed-positioned
     (next rule), so leaving the container in flow with 0 height keeps
     content stable. */
  .add-form { margin: 0; }
  .add-form:not([open]) {
    height: 0;
    overflow: visible;       /* the fixed summary still renders */
  }

  /* CLOSED summary = small round "+" button at top-right of <main>,
     visually aligned with the page h1 ("Workspaces" / "Tasks"). Avoids
     overlapping the carousel's trigger form or the bottom-nav.
     Requires main { position: relative } (set below). */
  main { position: relative; }
  .add-form:not([open]) > summary {
    position: absolute;
    top: var(--space-2);
    right: var(--space-3);
    width: 36px;
    height: 36px;
    min-height: 36px;
    padding: 0;
    border-radius: 999px;
    background: var(--accent-blue);
    border: none;
    box-shadow: var(--shadow-1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0;            /* hide the "New workspace" text */
    z-index: 5;
    transition: background var(--t-base), box-shadow var(--t-base);
  }
  .add-form:not([open]) > summary::before {
    content: '+';
    font-size: 22px;
    font-weight: 600;
    color: #fff;
    line-height: 1;
  }
  .add-form:not([open]) > summary:hover,
  .add-form:not([open]) > summary:active {
    background: var(--accent-blue-hover);
    box-shadow: var(--shadow-2);
  }

  /* OPEN: bottom sheet. Same pattern as before. */
  .add-form[open] {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(64px + env(safe-area-inset-bottom, 0px));
    z-index: 50;
    margin: 0;
    max-height: 70vh;
    overflow-y: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border-strong);
    border-bottom: none;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    box-shadow: var(--shadow-3);
  }
  .add-form[open] > summary {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: var(--space-3) var(--space-4);
    background: var(--bg-elevated);
    border: none;
    border-radius: 0;
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-primary);
    font-weight: 600;
    font-size: var(--text-md);
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .add-form[open] > summary::before { content: ''; }
  .add-form[open] > summary::after  { content: '✕'; color: var(--text-tertiary); font-size: var(--text-md); }

  .add-form[open] form {
    border: none;
    background: transparent;
    margin: 0;
    padding: var(--space-4);
    border-radius: 0;
  }
}

/* ============================================================================
 *  Roundtable (third tab — multi-agent debate)
 * ============================================================================ */

/* List view */
.rt-list { display: flex; flex-direction: column; gap: var(--space-2); }
.rt-row {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  transition: border-color var(--t-base), background var(--t-base);
}
.rt-row:hover { border-color: var(--border-strong); }
.rt-row-link {
  flex: 1;
  padding: var(--space-3);
  color: inherit;
  text-decoration: none;
  min-width: 0;
}
.rt-row-q {
  font-size: var(--text-md);
  color: var(--text-primary);
  margin-bottom: var(--space-1);
  /* Two-line clamp so long questions don't make the row tall */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.rt-row-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  flex-wrap: wrap;
}
.rt-delete {
  align-self: center;
  margin: 0 var(--space-2);
  padding: 2px 10px;
}

/* Detail view */
.rt-question {
  margin-bottom: var(--space-1);
  font-size: var(--text-h2);
  line-height: 1.3;
}
.rt-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-4);
  font-size: var(--text-sm);
  flex-wrap: wrap;
}
.rt-error {
  background: rgba(239, 68, 68, 0.10);
  border: 1px solid rgba(239, 68, 68, 0.30);
  color: var(--accent-red);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-4);
  font-size: var(--text-sm);
}

.rt-detail { display: flex; flex-direction: column; gap: var(--space-5); }

/* R3 synthesis — highest priority, sits at top */
.rt-r3 {
  background: var(--bg-surface);
  border: 1px solid var(--accent-blue);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}
.rt-r3.rt-r3-pending { border-color: var(--border-subtle); }
.rt-r3 h2 {
  margin-top: 0;
  margin-bottom: var(--space-3);
  color: var(--accent-blue);
  font-size: var(--text-h3);
}
.rt-r3-section { margin-bottom: var(--space-3); }
.rt-r3-section:last-of-type { margin-bottom: 0; }
.rt-r3-section h3 {
  font-size: var(--text-md);
  color: var(--text-primary);
  margin-bottom: var(--space-2);
}
.rt-r3-section ul {
  list-style: disc;
  margin-left: var(--space-4);
  padding-left: 0;
}
.rt-r3-section li {
  margin-bottom: var(--space-2);
  color: var(--text-secondary);
  line-height: 1.6;
}
.rt-yesno label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}
.rt-yesno input[type="checkbox"] {
  margin-top: 4px;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  accent-color: var(--accent-blue);
}
.rt-r3-raw {
  margin-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
  padding-top: var(--space-3);
}
.rt-r3-raw summary {
  cursor: pointer;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}
.rt-r3-raw pre {
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  margin-top: var(--space-2);
  font-size: var(--text-micro);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* "辩论轮数" inline row in the new-roundtable form — a label + a thin
   native <select>. Native is fine here because: (a) there are only 2
   options, (b) the select isn't nested inside a dark menu like the
   workspace ⋯ where the system popup looked foreign. Inherits dark
   styling from the global `input, select, textarea` rule. */
.rt-rounds-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.rt-rounds-label {
  flex: 0 0 auto;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0;    /* override the form-level <label> indent */
}
.rt-rounds-select {
  flex: 1;
  font-size: var(--text-sm);
  padding: var(--space-1) var(--space-2);
}
/* The .rt-rounds-row now embeds a .form-picker; make it flex naturally
 * so the picker fills remaining space next to the label. */
.rt-rounds-row > .form-picker { flex: 1; }

/* ============================================================================
 *  Form picker — unified <details>+radio drop-in replacement for native
 *  <select>. Used by:
 *    - new-workspace form's provider field
 *    - new-cron-loop form's workspace field
 *    - new-roundtable form's critique_rounds field
 *  Shares .ws-menu-radio / .ws-radio-dot styling with the workspace ⋯ menu's
 *  provider picker and roundtable's per-role model picker, so the dark-theme
 *  surface looks the same everywhere.
 * ============================================================================ */
.form-picker {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-canvas);
}
.form-picker > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  user-select: none;
  list-style: none;
  color: var(--text-primary);
  font-size: var(--text-md);
}
.form-picker > summary::-webkit-details-marker { display: none; }
.form-picker > summary::after {
  content: '▾';
  color: var(--text-tertiary);
  font-size: 10px;
  flex: 0 0 auto;
  transition: transform var(--t-base);
}
.form-picker[open] > summary::after { transform: rotate(180deg); }
.form-picker > summary:hover { background: var(--bg-elevated); }
.form-picker[open] > summary {
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border-subtle);
}
.form-picker-current {
  flex: 1;
  /* Long workspace / provider names shouldn't push the chevron off-screen. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.form-picker-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-1);
  /* Cap height so a wallload of providers / workspaces doesn't push the
   * form's Submit button off the viewport. Scroll within the list. */
  max-height: 280px;
  overflow-y: auto;
}

/* Per-role model override block — collapsible <details> inside the new-
 * roundtable form. Default collapsed; opens to 5 role pickers (4 personas
 * + 1 synthesizer). Each picker is itself a <details> that, when expanded,
 * shows a .ws-menu-radio list — reusing the workspace ⋯-menu radio styling
 * for dark-theme consistency (one organism, one CSS surface).
 */
.rt-model-config { margin: 0; }
.rt-model-config > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  padding: var(--space-2) 0;
  user-select: none;
  list-style: none;
}
.rt-model-config > summary::-webkit-details-marker { display: none; }
.rt-model-config > summary:hover { color: var(--text-primary); }
.rt-model-config[open] > summary { color: var(--text-primary); }
.rt-model-config-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--bg-canvas);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  margin-top: var(--space-2);
}

/* One role picker = collapsed summary (role name + current pick) +
   expanded radio list. Self-contained <details>, so opening one doesn't
   collapse another — but the JS re-renders on selection, which naturally
   collapses everything (the new DOM defaults to closed). Fine UX:
   "click → pick → done" without a stale half-open row sitting around. */
/* Aligned visually with .form-picker (border-strong / bg-canvas /
 * text-md / shared chevron + hover + [open] rules). Only the SUMMARY
 * LAYOUT differs — role pickers show "{role name} ... {current model}"
 * (two spans), whereas .form-picker shows just "{current label}".
 * Same chrome, different content = "one dropdown shape, used in
 * multiple form positions". */
.rt-role-picker {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-canvas);
  overflow: hidden;
}
.rt-role-picker > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  list-style: none;
  user-select: none;
  color: var(--text-primary);
  font-size: var(--text-md);
}
.rt-role-picker > summary::-webkit-details-marker { display: none; }
.rt-role-picker > summary::after {
  content: '▾';
  color: var(--text-tertiary);
  font-size: 10px;
  flex: 0 0 auto;
  transition: transform var(--t-base);
}
.rt-role-picker[open] > summary::after { transform: rotate(180deg); }
.rt-role-picker > summary:hover { background: var(--bg-elevated); }
.rt-role-picker[open] > summary {
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border-subtle);
}

.rt-role-name {
  flex: 0 0 auto;
  min-width: 70px;
  color: var(--text-primary);
}
.rt-role-kind {
  font-size: var(--text-micro);
  margin-left: var(--space-1);
}
.rt-role-current {
  flex: 1;
  text-align: right;
  font-size: var(--text-micro);
  color: var(--text-tertiary);
}
.rt-role-current.is-override { color: var(--accent-blue); }

.rt-role-radio-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-1);
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-canvas);
}

.rt-model-reset-all {
  align-self: flex-start;
  background: transparent;
  border: 1px solid var(--border-subtle);
  color: var(--text-tertiary);
  font-size: var(--text-micro);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  margin-top: var(--space-2);
}
.rt-model-reset-all:hover {
  border-color: var(--accent-blue);
  color: var(--accent-blue);
}

/* Narrow screens — let the role-summary's "current pick" wrap underneath
   the role name if needed, instead of squishing it into 30px. */
@media (max-width: 480px) {
  .rt-role-picker > summary { flex-wrap: wrap; }
  .rt-role-current { flex: 1 0 100%; text-align: left; }
}

/* R1/R2 grid — 4 roles side-by-side on wide screens, stack on narrow */
.rt-round h3 {
  font-size: var(--text-md);
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
.rt-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}
@media (max-width: 1100px) {
  .rt-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  .rt-grid { grid-template-columns: 1fr; }
}

.rt-cell {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  border-top-width: 3px;
}
.rt-cell h4 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
}
.rt-cell-empty { opacity: 0.6; }
.rt-content {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  word-break: break-word;
}
.rt-content code {
  background: var(--bg-canvas);
  padding: 1px 4px;
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
}

/* Per-role accent on the top border + h4 (4 different colors so the
   grid is glanceable even when content is dense). */
.rt-cell-minimalist { border-top-color: var(--accent-green); }
.rt-cell-minimalist h4 { color: var(--accent-green); }
.rt-cell-scenario   { border-top-color: var(--accent-cyan); }
.rt-cell-scenario h4 { color: var(--accent-cyan); }
.rt-cell-precedent  { border-top-color: var(--accent-purple); }
.rt-cell-precedent h4 { color: var(--accent-purple); }
.rt-cell-pessimist  { border-top-color: var(--accent-amber); }
.rt-cell-pessimist h4 { color: var(--accent-amber); }

/* Mobile: R1/R2 collapsed by default — R3 is the product value, so the
   detail view leads with it and lets R1/R2 be on-demand evidence. */
.rt-round-collapsible {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.rt-round-collapsible > summary {
  padding: var(--space-3);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.rt-round-collapsible > summary::-webkit-details-marker { display: none; }
.rt-round-collapsible > summary h3 { margin: 0; flex: 1; }
.rt-round-collapsible > summary::after {
  content: '▾';
  color: var(--text-tertiary);
  transition: transform var(--t-base);
}
.rt-round-collapsible[open] > summary::after { transform: rotate(180deg); }
.rt-round-collapsible[open] > .rt-grid { padding: 0 var(--space-3) var(--space-3); }

/* Mobile: shrink the form padding & question heading so the actual
   list isn't pushed below the fold. */
@media (max-width: 600px) {
  .rt-question { font-size: var(--text-h3); }
  .rt-cell { padding: var(--space-2); }
  .rt-r3 { padding: var(--space-3); }
}

/* ============================================================================
 *  Mobile: collapsed column-header actions menu (⋯)
 *  ----------------------------------------------------------------------------
 *  On phone widths the column header had 6 inline elements (provider select +
 *  engine chip + 4 icon buttons). That's a wall. New layout:
 *    Row 1: drag-handle / name / hide-btn (unchanged)
 *    Row 2 (.ws-meta-mobile):
 *      [provider name label] [engine chip]              [⋯ trigger]
 *                                                       └ <details> menu body
 *  The menu body floats absolutely below the trigger and stacks 4 wide
 *  text-labeled buttons + a Provider <select> row.
 * ============================================================================ */

.ws-meta-mobile {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--text-sm);
  margin-top: 2px;
}
.ws-meta-provider {
  color: var(--text-secondary);
}
.ws-actions-menu {
  position: relative;
  margin-left: auto;     /* push the ⋯ trigger to the right edge */
}
.ws-actions-trigger {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding: 4px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
  list-style: none;     /* hide default disclosure */
  user-select: none;
}
.ws-actions-trigger::-webkit-details-marker { display: none; }
.ws-actions-trigger svg { width: 16px; height: 16px; display: block; }
.ws-actions-menu[open] > .ws-actions-trigger {
  background: var(--bg-hover);
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.ws-actions-menu-body {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 50;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-2);
  min-width: 240px;
  max-width: 320px;
  box-shadow: var(--shadow-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Provider <select> row inside the menu — now only used in the
   new-workspace form; the per-ws menu uses .ws-menu-radio rows below. */
.ws-menu-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
}
.ws-menu-row > .muted { flex: 0 0 auto; font-size: var(--text-sm); }
.ws-menu-row .provider-inline {
  flex: 1;
  min-width: 0;
}

/* Provider radio-list (replaces native <select> inside the ⋯ menu —
   native popups break the dark theme). Each provider is a clickable
   row with a colored dot indicating selection. */
.ws-menu-section {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--space-1);
}
.ws-menu-section-label {
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px var(--space-2);
}
.ws-menu-radio {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  transition: background var(--t-base), color var(--t-base);
}
.ws-menu-radio:hover { background: var(--bg-elevated); color: var(--text-primary); }
.ws-menu-radio.is-selected {
  background: var(--accent-blue-faint);
  color: var(--text-primary);
}
.ws-menu-radio:disabled { opacity: 0.5; cursor: wait; }
.ws-radio-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1.5px solid var(--text-tertiary);
  flex-shrink: 0;
  position: relative;
  transition: border-color var(--t-base);
}
.ws-radio-dot.is-selected {
  border-color: var(--accent-blue);
}
.ws-radio-dot.is-selected::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-blue);
  transform: translate(-50%, -50%);
}
.ws-radio-label { flex: 1; }

/* Wide, text-labeled buttons inside the menu — override the icon-only
   inline styles by being more specific (.is-menu-item modifier). */
.ws-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: var(--text-sm);
  border: 1px solid var(--border-subtle);
  background: var(--bg-canvas);
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  /* override the inline icon-only width/padding/border-style rules */
  margin-left: 0;
}
.ws-menu-item svg { width: 14px; height: 14px; flex-shrink: 0; }
.ws-menu-item > span { flex: 1; }
.ws-menu-item:hover { background: var(--bg-elevated); }
.ws-menu-item.is-trusted { color: var(--accent-amber); }
.ws-menu-item-danger:hover {
  background: rgba(239, 68, 68, 0.18);
  border-color: var(--accent-red);
  color: var(--accent-red);
}

/* The .ws-provider inline row was the PC-only fallback. As of 2026-05-14
   the universal layout is .ws-meta-mobile (the name is now misleading —
   it's used on both PC + mobile). Leave the .ws-provider class rules
   above intact; nothing references them in current renderers but harmless. */
