/* ============================================================
   OmicsAgent — UI Stylesheet
   Genomac Holdings | Omicsboard Platform
   Dark genomics-themed desktop interface
   ============================================================ */

/* ── Reset & base ─────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Theme palettes — three states, one attribute (data-theme on <html>):
   "dark" (default), "light", "system" (follows the OS live via
   prefers-color-scheme, no JS re-render needed). See early-theme.js for
   the no-flash bootstrap and renderer.js initAppearance() for the switch. */
:root,
:root[data-theme="dark"] {
  --bg:            #0D1117;
  --panel-bg:      #161B22;
  --panel-bg-2:    #1C2128;
  --accent:        #B5530F;
  --accent-light:  #E8722C;
  --accent-hover:  #FF8C42;
  --text:          #E6EDF3;
  --text-muted:    #8B949E;
  --text-dim:      #484F58;
  --border:        #30363D;
  --border-light:  #3D444D;
  --success:       #E8722C;
  --error:         #da3633;
  --warning:       #d29922;
  --info:          #388bfd;
  --user-msg-bg:   #1C2128;
  --agent-msg-bg:  #161B22;
  --input-bg:      #1C2128;
  --scrollbar:     #30363D;
  --radius:        8px;
  --radius-sm:     4px;
  --radius-lg:     12px;
  --shadow:        0 4px 24px rgba(0,0,0,0.4);
  --font:          'Inter', system-ui, -apple-system, sans-serif;
  --mono:          'Consolas', 'Courier New', 'Lucida Console', monospace;
  /* categorical / status colors (tool chips, progress cards) — kept as
     their own tokens so light mode can deepen them for contrast without
     touching the general palette above */
  --label-r:         #58a6ff;
  --label-python:    #e3b341;
  --label-terminal:  #FF9D5C;
  --label-browser:   #f0883e;
  --status-info:     #58a6ff;
  --status-done:     #3fb950;
  --status-fail:     #e5534b;
}

:root[data-theme="light"] {
  --bg:            #FAFAF7;
  --panel-bg:      #FFFFFF;
  --panel-bg-2:    #F0EDE6;
  --accent:        #B5530F;
  --accent-light:  #E8722C;
  --accent-hover:  #C85F12;
  --text:          #23201A;
  --text-muted:    #6E6558;
  --text-dim:      #A39A89;
  --border:        #E6E1D5;
  --border-light:  #D9D3C4;
  --success:       #E8722C;
  --error:         #B3261E;
  --warning:       #92600A;
  --info:          #1B5FA8;
  --user-msg-bg:   var(--panel-bg-2);
  --agent-msg-bg:  #FFFFFF;
  --input-bg:      #FFFFFF;
  --scrollbar:     #D9D3C4;
  --shadow:        0 4px 20px rgba(35,32,26,0.10);
  --label-r:         #1B5FA8;
  --label-python:    #8A6708;
  --label-terminal:  #C1590E;
  --label-browser:   #B85E17;
  --status-info:     #1B5FA8;
  --status-done:     #1A7F37;
  --status-fail:     #B3261E;
}

@media (prefers-color-scheme: light) {
  :root[data-theme="system"] {
    --bg:            #FAFAF7;
    --panel-bg:      #FFFFFF;
    --panel-bg-2:    #F0EDE6;
    --accent:        #B5530F;
    --accent-light:  #E8722C;
    --accent-hover:  #C85F12;
    --text:          #23201A;
    --text-muted:    #6E6558;
    --text-dim:      #A39A89;
    --border:        #E6E1D5;
    --border-light:  #D9D3C4;
    --success:       #E8722C;
    --error:         #B3261E;
    --warning:       #92600A;
    --info:          #1B5FA8;
    --user-msg-bg:   var(--panel-bg-2);
    --agent-msg-bg:  #FFFFFF;
    --input-bg:      #FFFFFF;
    --scrollbar:     #D9D3C4;
    --shadow:        0 4px 20px rgba(35,32,26,0.10);
    --label-r:         #1B5FA8;
    --label-python:    #8A6708;
    --label-terminal:  #C1590E;
    --label-browser:   #B85E17;
    --status-info:     #1B5FA8;
    --status-done:     #1A7F37;
    --status-fail:     #B3261E;
  }
}

html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ── App layout (three-panel flex) ──────────────────────────── */
/* ── Custom title bar (replaces native Windows title strip) ─────── */
#titlebar {
  height: 38px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding-left: 10px;
  /* leave room on the right for the native min/max/close overlay */
  padding-right: 140px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  -webkit-app-region: drag;   /* whole bar is draggable */
  user-select: none;
}
#titlebar .tb-logo {
  width: 20px;
  height: 20px;
  object-fit: contain;
  flex-shrink: 0;
}
.tb-menu {
  display: flex;
  align-items: center;
  gap: 1px;
  -webkit-app-region: no-drag;   /* buttons must be clickable */
}
.tb-menu-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 12.5px;
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.tb-menu-btn:hover,
.tb-menu-btn.open { background: var(--panel-bg-2); color: var(--text); }
.tb-drag { flex: 1; height: 100%; -webkit-app-region: drag; }

/* Dropdown panel opened from a title-bar menu */
.tb-dropdown {
  position: fixed;
  z-index: 4000;
  min-width: 190px;
  padding: 5px;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0,0,0,0.45);
  -webkit-app-region: no-drag;
}
.tb-dropdown button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: 100%;
  background: none;
  border: none;
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  text-align: left;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tb-dropdown button:hover { background: var(--accent); color: #fff; }
.tb-dropdown button .tb-acc { color: var(--text-muted); font-size: 11px; }
.tb-dropdown button:hover .tb-acc { color: rgba(255,255,255,0.8); }
.tb-dropdown .tb-sep { height: 1px; margin: 5px 6px; background: var(--border); }

#app {
  display: flex;
  flex-direction: row;
  height: calc(100vh - 38px);
  overflow: hidden;
}

/* ── First-run environment setup modal ─────────────────────────── */
.env-setup-content {
  max-width: 560px;
  width: 92%;
  padding: 28px 30px;
}
.env-setup-title { font-size: 19px; font-weight: 700; margin: 0 0 10px; color: var(--text); }
.env-setup-sub   { font-size: 13px; color: var(--text-muted); line-height: 1.6; margin: 0 0 16px; }
.env-setup-missing {
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 14px;
}
.env-setup-missing-label { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--text-muted); margin-bottom: 6px; }
.env-setup-missing ul { margin: 0; padding-left: 18px; }
.env-setup-missing li { font-size: 13px; color: var(--text); margin: 3px 0; }
.env-setup-note { font-size: 12px; color: var(--text-muted); margin: 4px 0 18px; line-height: 1.5; }
.env-setup-card {
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 10px;
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.5;
}
.env-setup-actions { display: flex; gap: 10px; justify-content: flex-end; }
.env-setup-stage { font-size: 13px; font-weight: 600; color: var(--accent-light); margin-bottom: 10px; }
.env-setup-bar {
  height: 6px;
  background: var(--panel-bg-2);
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 12px;
}
.env-setup-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 3px;
  transition: width 0.4s ease;
}
.env-setup-bar-fill.indeterminate {
  width: 35% !important;
  animation: envBarSlide 1.3s ease-in-out infinite;
}
@keyframes envBarSlide {
  0%   { margin-left: -35%; }
  100% { margin-left: 100%; }
}
/* Always-moving shimmer so the bar reads as "working" even when its width isn't
   advancing (R goes quiet during big downloads). */
.env-setup-bar-fill.working {
  background-color: var(--accent);
  background-image: linear-gradient(90deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,0.45) 50%, rgba(255,255,255,0) 100%);
  background-size: 45% 100%;
  background-repeat: no-repeat;
  animation: envBarShimmer 1.15s linear infinite;
}
@keyframes envBarShimmer {
  0%   { background-position: -45% 0; }
  100% { background-position: 145% 0; }
}
.env-setup-activity {
  font-size: 12px;
  color: var(--text-muted);
  margin: -4px 0 12px;
  font-variant-numeric: tabular-nums;
  min-height: 16px;
}
.env-setup-log {
  background: #0a0d12;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  height: 200px;
  overflow-y: auto;
  font-family: var(--font-mono, monospace);
  font-size: 11.5px;
  line-height: 1.55;
  color: #9fb0c3;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0 0 14px;
}
.env-setup-log .err { color: var(--error, #ff6b6b); }

/* Primary call-to-action variant of a header button (used in setup modal) */
.header-btn.primary {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.header-btn.primary:hover { background: var(--accent-hover, var(--accent)); color: #fff; }

/* ── Resize handles between panels ──────────────────────────── */
.resize-handle {
  width: 4px;
  flex-shrink: 0;
  background: transparent;
  cursor: col-resize;
  position: relative;
  z-index: 20;
  transition: background 0.2s;
}
.resize-handle:hover,
.resize-handle.dragging {
  background: rgba(232,114,44,0.5);
}
/* Widen the hit area without affecting layout */
.resize-handle::after {
  content: '';
  position: absolute;
  inset: 0 -3px;
}

/* ── Scrollbars ───────────────────────────────────────────── */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--border-light); }

/* ── Left panel ───────────────────────────────────────────── */
/* Deliberately NO border-right here — sidebar and chat-header share the same
   --panel-bg, so the top bar reads as one connected strip with nothing but
   the (still-real, still-draggable) #resize-left handle marking the seam;
   the only PERMANENT divider line anywhere across the whole top bar is the
   one between chat and notebook (.lab-resize), a genuinely different kind of
   boundary — two independently resizable, differently-colored columns. Below
   the header row the panel's own --panel-bg vs the centre panel's --bg still
   gives a real, if subtle, boundary without needing a hard rule. */
#left-panel {
  width: 220px;
  min-width: 160px;
  max-width: 360px;
  flex-shrink: 0;
  background: var(--panel-bg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  user-select: none;
}

/* Scrollable inner area of left panel (between logo and footer) */
.left-panel-scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;
}

/* Logo */
.logo-section {
  height: 53px;
  padding: 0 10px 0 14px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.logo-container {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

/* Sidebar collapse / expand toggle (Claude-style) */
.sidebar-toggle {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.sidebar-toggle:hover { background: var(--panel-bg-2); color: var(--text); }
/* The expand button lives in the chat header and only shows when collapsed */
.sidebar-expand { display: none; margin-right: 4px; }
#app.sidebar-collapsed #left-panel { display: none; }
#app.sidebar-collapsed #resize-left { display: none; }
#app.sidebar-collapsed .sidebar-expand { display: flex; }
.logo-icon {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.logo-icon img { width: 100%; height: 100%; object-fit: contain; display: block; }
.logo-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.logo-name { font-size: 12.5px; font-weight: 700; color: var(--text); letter-spacing: -0.2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.logo-sub  { font-size: 10px; color: var(--text-muted); letter-spacing: 0.2px; }

/* Panel sections */
.panel-section {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.panel-section + .panel-section { border-top: 1px solid var(--border); }
.panel-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 14px 5px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.7px;
  color: var(--text-dim);
}

/* "New chat" — plain text + icon, NO button chrome (no border/background at
   rest), pinned above the scrollable Projects list. */
.sidebar-new-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  margin: 10px 0 2px;
  box-sizing: border-box;
  padding: 7px 14px;
  border-radius: 0;
  background: none;
  border: none;
  color: var(--text);
  font-family: var(--font);
  font-size: 13px;
  font-weight: 400;
  cursor: pointer;
  flex-shrink: 0;
  transition: color 0.15s;
}
.sidebar-new-btn svg { flex-shrink: 0; color: var(--text-muted); }
.sidebar-new-btn:hover { color: var(--accent-light, #E8722C); }
.sidebar-new-btn:hover svg { color: var(--accent-light, #E8722C); }

/* Search icon on the Projects header row + its reveal-on-click input. */
.session-search-wrap { padding: 0 12px 6px; }
.session-search-input {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 6px 9px;
  font-size: 12px;
  font-family: var(--font);
  color: var(--text);
  outline: none;
  transition: border-color 0.15s;
}
.session-search-input:focus { border-color: var(--accent-light, #E8722C); }
.session-search-input::placeholder { color: var(--text-dim); }

/* Session list (Projects) */
.session-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 8px;
  /* No fixed max-height — the flex parent (.left-panel-scroll) already
     constrains this to the real available space between the logo and the
     account footer. A hardcoded pixel cap here doesn't scale with zoom,
     so it left a growing empty gap above the footer when zoomed out. */
}
.session-empty {
  padding: 12px 6px;
  color: var(--text-dim);
  font-size: 11px;
  text-align: center;
}
.session-item {
  position: relative;   /* anchors the overlaid .session-item-menu below */
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 12px;
  color: var(--text-muted);
  transition: background 0.15s;
}
.session-item:hover { background: var(--panel-bg-2); color: var(--text); }
/* Selected project = the same tint as the user's own chat bubble. */
.session-item.active { background: var(--user-msg-bg); color: var(--text); }
.session-item-title {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  /* No text-overflow:ellipsis on purpose — the title runs the row's FULL
     width; the ⋮ menu is absolutely positioned (see .session-item-menu)
     so it floats ON TOP of the text instead of reserving its own column,
     matching the Claude-Code sidebar pattern instead of early-truncating
     with "…". */
}
.session-item-menu {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: none;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.15s, color 0.15s, background 0.15s;
}
/* Visible on hover of ANY row, and always-on for the currently-selected one
   (its actions stay reachable without needing to hover it first). */
/* .menu-hover is set/cleared explicitly via JS mouseenter/mouseleave on the
   ROW (see buildSessionItem) — kept alongside plain :hover as a belt-and-
   suspenders pair, since :hover alone was found to only reliably reveal the
   menu near the row's right edge once the menu button became an absolute-
   positioned overlay (see .session-item-title). */
.session-item:hover .session-item-menu,
.session-item.menu-hover .session-item-menu,
.session-item.active .session-item-menu { opacity: 1; }
.session-item-menu:hover { color: var(--text); background: var(--panel-bg-2); }

/* Inline rename field (replaces the title while editing). */
.session-rename-input {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--accent-light);
  border-radius: 3px;
  padding: 2px 5px;
  outline: none;
}

/* 3-dots dropdown menu (appended to <body>, positioned in JS). */
.recent-menu {
  position: fixed;
  z-index: 1000;
  min-width: 150px;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  padding: 4px;
  display: flex;
  flex-direction: column;
}
.recent-menu-item {
  text-align: left;
  border: none;
  background: none;
  color: var(--text);
  cursor: pointer;
  font-size: 12px;
  font-family: inherit;
  padding: 7px 10px;
  border-radius: 4px;
  transition: background 0.12s;
}
.recent-menu-item:hover { background: var(--panel-bg-2); }
.recent-menu-item.danger { color: var(--error); }
.recent-menu-item.danger:hover { background: rgba(218,54,51,0.12); }
.recent-menu-item.subtle { color: var(--text-muted); }
.recent-menu-sep { height: 1px; background: var(--border); margin: 4px 6px; }
/* Curriculum checklist dropdown — reuses .recent-menu as its container, just
   wider (module titles run longer than a Recents menu item) and with status-
   colored rows instead of plain text buttons. */
.curriculum-menu { min-width: 260px; max-width: 340px; }
.curriculum-row { display: flex; align-items: flex-start; gap: 8px; white-space: normal; line-height: 1.35; }
.curriculum-row-icon { flex-shrink: 0; width: 16px; text-align: center; font-weight: 700; }
.curriculum-row--done .curriculum-row-icon { color: var(--accent-light); }
.curriculum-row--issue .curriculum-row-icon { color: #d9a000; }
.curriculum-row--current { background: rgba(232,114,44,0.08); }
.curriculum-row--current .curriculum-row-icon { color: var(--accent); }
.curriculum-row--current .curriculum-row-title { font-weight: 700; }
.curriculum-row--upcoming { color: var(--text-muted); cursor: default; }
.curriculum-row--upcoming:hover { background: none; }
.curriculum-row--upcoming .curriculum-row-icon { color: var(--text-muted); }
.recent-group-input {
  width: 100%;
  box-sizing: border-box;
  font-size: 12px;
  font-family: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--accent-light);
  border-radius: 4px;
  padding: 6px 8px;
  outline: none;
}

/* Recents section headers (Pinned / group names / Recents / Archived). */
.session-section-header {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  padding: 9px 8px 4px;
  user-select: none;
}
.session-section-header.clickable { cursor: pointer; }
.session-section-header.clickable:hover { color: var(--text-muted); }
.session-item.archived .session-item-title { opacity: 0.6; }

/* Clicked-but-not-yet-loaded feedback on a Recents row. */
.session-item.loading {
  background: rgba(181,83,15,0.15);
  color: var(--text);
  cursor: progress;
}
.session-item.loading .session-item-menu { display: none; }
.session-item.loading::after {
  content: '';
  flex-shrink: 0;
  width: 12px;
  height: 12px;
  margin-left: auto;
  border: 2px solid var(--text-dim);
  border-top-color: var(--accent-light);
  border-radius: 50%;
  animation: omics-spin 0.6s linear infinite;
}
@keyframes omics-spin { to { transform: rotate(360deg); } }

/* Environment status dots */
.env-list { padding: 6px 10px 10px; }
.env-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 4px;
  border-radius: var(--radius-sm);
  transition: background 0.15s;
  cursor: default;
}
.env-item:hover { background: var(--panel-bg-2); }
.env-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--text-dim);
  transition: background 0.3s;
  box-shadow: 0 0 0 0 transparent;
}
.env-dot.online {
  background: var(--success);
  box-shadow: 0 0 6px rgba(232,114,44,0.5);
}
.env-dot.offline { background: var(--error); }
.env-dot.checking {
  background: var(--warning);
  animation: pulse-dot 1s infinite;
}
@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}
.env-label { font-size: 12px; color: var(--text-muted); }

/* Engine status — single line above the footer */
.engine-status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}
.engine-status .engine-status-label { flex: 1; }
.engine-status .icon-btn { opacity: 0.6; }
.engine-status .icon-btn:hover { opacity: 1; }

/* Left panel footer — Claude-style account row + dropdown (+ an optional
   update card stacked above it, see .sidebar-update-card). */
.left-panel-footer {
  margin-top: auto;
  padding: 8px;
  border-top: 1px solid var(--border);
  position: relative;
  box-sizing: border-box;
  min-height: 66px;        /* match the chat input bar so the bottom edges align */
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
}

/* ── Sidebar update card — sits above the account row, only shown once a
   real update is available/downloading/ready (packaged builds only). */
.sidebar-update-card {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  box-sizing: border-box;
  padding: 8px 9px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--panel-bg-2);
  cursor: default;
  transition: background 0.15s, border-color 0.15s;
}
.sidebar-update-card.ready { cursor: pointer; }
.sidebar-update-card.ready:hover { background: var(--bg); border-color: var(--border-light); }
.suc-icon {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(232,114,44,0.14);
  color: var(--accent-light, #E8722C);
  font-size: 13px;
}
.suc-body { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.suc-title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.suc-sub {
  font-size: 10.5px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.suc-arrow {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
}
.sidebar-update-card.ready .suc-arrow { color: var(--text-muted); }
.sidebar-update-card.ready:hover .suc-arrow { color: var(--text); }

/* Account row (the always-visible button) */
.account-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 8px 10px;
  cursor: pointer;
  font-family: var(--font);
  transition: background 0.15s, border-color 0.15s;
}
.account-row:hover { background: var(--panel-bg-2); }
.account-row.open { background: var(--panel-bg-2); border-color: var(--border); }
.account-avatar {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent, #E8722C);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
}
.account-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  text-align: left;
}
.account-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-plan {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* One-Time Project Access mini-meter — sits under the account name, ALWAYS
   visible (no click needed), shown only for a topup-only user. Mirrors the
   "% used, fills toward danger" direction of the Settings subscription
   usage-meter (.high at >=80%) so the two read consistently across the app. */
.account-topup-meter { display: flex; flex-direction: column; gap: 2px; margin-top: 3px; }
.atm-label {
  font-size: 10px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.atm-track {
  width: 100%;
  height: 3px;
  border-radius: 2px;
  background: var(--panel-bg-2);
  overflow: hidden;
}
.atm-fill {
  display: block;
  height: 100%;
  background: var(--accent, #E8722C);
  border-radius: 2px;
  transition: width 0.3s ease;
}
.atm-fill.high { background: var(--error, #f85149); }
.account-chevron {
  flex-shrink: 0;
  color: var(--text-dim);
  transition: transform 0.15s;
}
.account-row.open .account-chevron { transform: rotate(180deg); }

/* Dropdown menu (opens upward) */
.account-menu {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 100%;
  margin-bottom: 6px;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0,0,0,0.45);
  padding: 6px;
  z-index: 50;
}
.account-menu-email {
  padding: 7px 10px 8px;
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-menu-sep {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}

/* ── Settings: Claude-style two-pane ────────────────────────────────────────── */
.modal-content.settings-wide {
  width: 92vw; max-width: 860px; height: 86vh; max-height: 640px;
  padding: 0; overflow: hidden; position: relative;
  display: flex; flex-direction: column;
}
.settings-x { position: absolute; top: 14px; right: 16px; z-index: 3; }
.settings-shell { display: flex; flex: 1; min-height: 0; }
.settings-nav {
  flex: 0 0 200px;
  background: var(--panel-bg-2);
  border-right: 1px solid var(--border);
  padding: 18px 10px;
  overflow-y: auto;
}
.settings-nav-title {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--text-dim); padding: 0 10px 10px;
}
.settings-nav-item {
  display: block; width: 100%; text-align: left;
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-family: inherit; font-size: 13px;
  padding: 9px 12px; border-radius: 7px; margin-bottom: 2px;
  transition: background 0.12s, color 0.12s;
}
.settings-nav-item:hover { background: var(--panel-bg); color: var(--text); }
.settings-nav-item.active { background: var(--accent); color: #fff; font-weight: 600; }
.settings-main { flex: 1; min-width: 0; overflow-y: auto; padding: 28px 32px; }
.settings-panel { display: none; }
.settings-panel.active { display: block; animation: msg-in 0.18s ease-out; }
.settings-h { font-size: 18px; font-weight: 700; color: var(--text); margin: 0 0 18px; }
.settings-subh { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin: 18px 0 8px; }
.settings-profile-head { display: flex; gap: 16px; align-items: flex-start; }
.settings-avatar {
  flex: 0 0 56px; width: 56px; height: 56px; border-radius: 50%;
  background: var(--accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; font-weight: 700; user-select: none;
}
.settings-profile-fields { flex: 1; min-width: 0; }
.settings-profile-fields .settings-field:last-child { margin-bottom: 0; }
.settings-field input:focus { border-color: var(--accent); outline: none; }
.settings-seg { display: inline-flex; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 3px; gap: 3px; }
.seg-btn {
  border: none; background: none; cursor: pointer; font-family: inherit;
  font-size: 12px; color: var(--text-muted); padding: 6px 16px; border-radius: 6px; transition: all 0.12s;
}
.seg-btn:hover { color: var(--text); }
.seg-btn.active { background: var(--accent); color: #fff; font-weight: 600; }
.settings-group {
  background: var(--panel-bg-2); border: 1px solid var(--border);
  border-radius: 10px; padding: 16px 18px; margin-bottom: 16px;
}
.settings-group .toggle-label { margin: 8px 0; }
.settings-hint { font-size: 11px; color: var(--text-dim); margin-left: 6px; }
.settings-note { font-size: 12px; color: var(--text-muted); margin: 0 0 12px; }
.settings-field { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 12px; }
.settings-field input {
  display: block; width: 100%; margin-top: 5px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
  color: var(--text); font-family: var(--mono); font-size: 12px; padding: 7px 9px;
}
.settings-row { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; padding: 6px 0; }
.sr-key { color: var(--text-dim); }
.sr-val { color: var(--text); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.settings-plan-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.spr-plan { font-size: 15px; font-weight: 700; color: var(--text); }
.spr-sub { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.settings-plan-btn, .settings-secondary-btn {
  background: var(--accent); color: #fff; border: none; border-radius: 7px;
  padding: 8px 14px; font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.settings-plan-btn:hover, .settings-secondary-btn:hover { background: var(--accent-light); }
.settings-secondary-btn { margin-top: 10px; background: var(--panel-bg); color: var(--accent-light); border: 1px solid rgba(232,114,44,0.3); }
.settings-secondary-btn:hover { background: rgba(232,114,44,0.12); }
.settings-signout-btn {
  display: flex; align-items: center; gap: 8px;
  background: rgba(218,54,51,0.08); color: var(--status-fail);
  border: 1px solid rgba(218,54,51,0.3); border-radius: 7px;
  padding: 9px 16px; font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.settings-signout-btn:hover { background: rgba(218,54,51,0.16); }
@media (max-width: 620px) { .settings-nav { flex-basis: 140px; } .settings-main { padding: 20px; } }

/* ── Plans / subscription modal ─────────────────────────────────────────────── */
/* Base = a small centered dialog. Reused as-is (unmodified) by the payment-
   waiting and contact-us dialogs — only .plans-fullpage/.plans-modal-full
   below (added ON TOP, not instead) turn the PLANS view itself into a real
   full page, per the "give it more room, better UI" request. */
.plans-overlay {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.plans-modal {
  position: relative;
  width: 100%; max-width: 680px; max-height: 90vh; overflow-y: auto;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 28px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}

/* Plans is a FULL PAGE, not a floating dialog — opaque, fills the whole
   window (matches how the app's own single-window "page" feels, since there's
   no separate browser chrome to navigate away from). */
.plans-overlay.plans-fullpage {
  padding: 0;
  align-items: stretch; justify-content: stretch;
  background: var(--panel-bg);
}
.plans-modal.plans-modal-full {
  max-width: none; width: 100%; height: 100%; max-height: none;
  border: none; border-radius: 0; box-shadow: none;
  padding: 0; margin: 0; overflow-y: auto;
  /* The PAGE sits a shade back from pure white/black (--bg, not the inherited
     --panel-bg) so the cards below (--panel-bg — see .plan-card) genuinely
     pop as bright/elevated surfaces, matching the reference's real contrast
     between the page and the cards, not just matching card color in isolation. */
  background: var(--bg);
}
/* Loading shell — what's on screen for the instant between the click and the
   real content arriving (see openPlansModal in renderer.js). Reuses the same
   .plans-modal-full frame + background so there's no size/color jump when the
   real content swaps in, and the existing omics-spin keyframe (styles.css,
   see .session-item.loading) so no new animation needed. */
.plans-loading-shell { display: flex; align-items: center; justify-content: center; }
.plans-loading-spinner {
  width: 28px; height: 28px;
  border: 3px solid var(--border);
  border-top-color: var(--accent-light, #E8722C);
  border-radius: 50%;
  animation: omics-spin 0.7s linear infinite;
}
.plans-page-header {
  position: relative;
  /* Trimmed bottom padding — the relocated discount/promo row (formerly above
     the pricing cards) now lives here, as the last thing before the header's
     own border-bottom, so it reads as sitting right at the divider. */
  padding: 28px 48px 16px;
  border-bottom: 1px solid var(--border, #30363d);
  text-align: center;
}
.plans-page-header .plans-close {
  position: absolute; top: 24px; left: 24px; right: auto;
  width: auto; height: auto; padding: 7px 14px;
  border: 1px solid var(--border, #30363d); border-radius: 8px; background: transparent;
  font-size: 12.5px; font-weight: 600; color: var(--text-muted); line-height: 1.2;
}
.plans-page-header .plans-close:hover { background: var(--panel-bg-2); color: var(--text); }
/* Single centered column now (was a 2-col split) — the 3 cards (Pro / Power /
   One-Time Access) sit in ONE row below, matching the reference's layout. */
.plans-page-body {
  max-width: 1040px; width: 100%; margin: 0 auto;
  padding: 32px 48px 48px;
}
@media (max-width: 640px) {
  .plans-page-header { padding: 20px 20px 18px; }
  .plans-page-header .plans-close { top: 16px; left: 16px; }
  .plans-page-body { padding: 20px; }
}

.plans-close {
  position: absolute; top: 14px; right: 16px;
  width: 30px; height: 30px; border: none; background: none;
  color: var(--text-dim); font-size: 22px; cursor: pointer; border-radius: 6px;
}
.plans-close:hover { background: var(--panel-bg-2); color: var(--text); }
.plans-head h2 { font-size: 20px; font-weight: 700; color: var(--text); margin: 0 0 4px; }
/* The page's own big headline — a system SERIF stack (no new webfont/network
   fetch) for the warmer, editorial feel of the reference layout; the rest of
   the app stays on the sans-serif body font. */
.plans-serif-title {
  font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Times New Roman', serif;
  font-size: 34px; font-weight: 500; letter-spacing: -0.3px;
  margin: 4px 0 10px !important;
}
.plans-head > p { font-size: 12px; color: var(--text-muted); margin: 0 0 14px; }
/* Section seams — real breathing room between the header / toggles / grid,
   instead of everything sitting flush against the next block. Used between
   .plans-cards-row and the "Compare all features" disclosure below it. */
.plans-divider { height: 1px; background: var(--border, #30363d); margin: 32px 0; }
.plans-status { font-size: 13px; color: var(--text); margin-bottom: 10px; }
/* Wraps the trial/plan status line + top-up-balance line (renderer.js) in one
   card, same visual language as .plan-card (rounded, elevated, no border
   line) but sized for a compact status area rather than a full feature card. */
.plans-status-card {
  border-radius: 10px;
  padding: 12px 16px;
  margin: 0 auto 16px;
  width: fit-content;
  max-width: 100%;
  background: var(--panel-bg);
  box-shadow: var(--shadow);
}
.plans-status-card .plans-status:last-child { margin-bottom: 0; }
.ps-badge { display: inline-block; padding: 2px 9px; border-radius: 20px; font-size: 11px; font-weight: 700; margin-right: 6px; }
.ps-badge.trial { background: rgba(210,153,34,0.18); color: var(--warning); }
.ps-badge.pro   { background: var(--panel-bg-2); color: var(--text); }
.ps-badge.topup { background: rgba(232,114,44,0.18); color: var(--accent-light, #E8722C); }
.plans-testing {
  font-size: 11px; color: var(--text-muted);
  background: rgba(210,153,34,0.08); border: 1px solid rgba(210,153,34,0.25);
  border-radius: 8px; padding: 8px 11px; margin-bottom: 6px;
}
.usage-meter { margin: 8px 0 4px; }
.um-label { display: flex; justify-content: space-between; font-size: 11px; color: var(--text-muted); margin-bottom: 4px; }
.um-bar { height: 7px; background: var(--panel-bg-2); border-radius: 5px; overflow: hidden; }
.um-fill { height: 100%; background: var(--text-dim); border-radius: 5px; transition: width 0.3s; }
.um-fill.high { background: var(--warning); }
.um-reset { font-size: 10px; color: var(--text-dim); margin-top: 3px; }
/* Rewards & submissions live INSIDE the usage card, under the bonus line */
.usage-claims { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.usage-claims-title { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.usage-claims-list { margin: 0; padding-left: 16px; font-size: 12px; line-height: 1.55; color: var(--text-muted); }
/* Settings → Plan & usage → "Earn more usage": a headline, then one card per way to earn */
.earn-h { font-size: 15px; font-weight: 700; color: var(--text); margin: 22px 0 4px; }
.earn-sub { font-size: 12px; color: var(--text-muted); margin: 0 0 12px; line-height: 1.45; }
.earn-card { font-size: 12.5px; line-height: 1.5; color: var(--text); }
.earn-title { font-size: 14px; font-weight: 700; margin-bottom: 3px; }
.earn-desc { color: var(--text-muted); margin-bottom: 10px; }
.earn-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.earn-btn { background: var(--accent); color: #fff; border: none; border-radius: 7px; padding: 7px 14px; font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit; }
.earn-btn:hover { background: var(--accent-light); }
.earn-btn:disabled { opacity: .6; cursor: default; }
.earn-btn.secondary { background: transparent; color: var(--text); border: 1px solid var(--border); }
.earn-btn.secondary:hover { background: var(--panel-bg); }

/* Organization card (Org tier Phase 2): an org's own owners/admins manage their people */
.org-card .org-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.org-card .org-name { font-weight: 700; font-size: 14px; }
.org-card .org-meta { font-size: 12px; opacity: .8; margin-top: 2px; }
.org-card .org-warn { margin: 8px 0; padding: 7px 9px; border-radius: 7px; font-size: 12px; background: rgba(248,81,73,.12); border: 1px solid rgba(248,81,73,.35); }
.org-card .org-table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 12.5px; }
.org-card .org-table th { text-align: left; font-weight: 600; font-size: 11px; opacity: .7; padding: 3px 6px; }
.org-card .org-table td { padding: 5px 6px; border-top: 1px solid var(--border); vertical-align: middle; }
.org-card .org-email { word-break: break-all; }
.org-card .org-you { font-size: 10.5px; opacity: .7; margin-left: 4px; }
.org-card .org-use { display: flex; align-items: center; gap: 6px; min-width: 90px; }
.org-card .org-use .um-bar { flex: 1; }
.org-card .org-use span { font-size: 11px; min-width: 30px; text-align: right; }
.org-card select, .org-card input[type="text"] { padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-bg); color: var(--text); font: inherit; font-size: 12.5px; }
.org-card .org-add { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.org-card .org-add input[type="text"] { flex: 1; min-width: 170px; }
.org-card .org-msg { font-size: 12px; margin-top: 6px; min-height: 16px; }
.org-card .org-msg.err { color: var(--danger, #f85149); }
.org-card .org-foot { font-size: 11.5px; opacity: .75; margin-top: 8px; }

/* Buying screens for self-serve organizations (Org tier B2 / C3) — built on the .org-card look */
.orgbuy-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 10px; margin: 12px 0 8px; }
.orgbuy-field { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--text-muted); }
.orgbuy-field input, .orgbuy-field select { padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel-bg); color: var(--text); font: inherit; font-size: 13px; }
.orgbuy-total { margin: 8px 0 4px; font-size: 13px; font-weight: 600; min-height: 18px; }
.orgbuy-owner { margin: 8px 0; }
.orgbuy-panel { margin-top: 8px; padding: 10px; border: 1px solid var(--border); border-radius: 8px; display: flex; flex-direction: column; gap: 8px; }
.org-pay-note { font-size: 11.5px; opacity: .75; }
.earn-btn.small { padding: 3px 9px; font-size: 11.5px; }
.earn-linkbox { display: flex; align-items: center; gap: 8px; background: var(--panel-bg); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; margin-bottom: 10px; }
.earn-sharebox { background: var(--panel-bg); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 10px; }
.earn-share-title { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; }
.earn-share-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.earn-desc code { user-select: all; font-weight: 700; letter-spacing: 1px; }
.earn-link { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: monospace; font-size: 12px; user-select: all; }

/* .plans-grid's OWN box is display:contents — its 2 children (the Pro/Power
   .plan-card divs, rebuilt on every rerender by cardHtml()) become direct
   items of the PARENT .plans-cards-row grid below, sitting alongside the
   top-up card as ONE unified 3-card row. rerender()'s existing
   `grid.innerHTML = cardHtml(pro)+cardHtml(power)` logic is untouched. */
.plans-grid { display: contents; }
.plans-cards-row {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
  align-items: stretch; margin-top: 8px;
}
/* Pro/Power's .plan-card divs are direct grid items (via .plans-grid's
   display:contents above), so the grid's align-items:stretch reaches them
   directly. The One-Time Access card sits one level deeper, inside
   #topup-card-wrap (a real wrapper div, needed since it's not repeated by
   cardHtml() the way Pro/Power are) — that wrapper is the actual grid item,
   and by default it wouldn't pass the stretched height down to its own
   child. display:flex here makes it do so (flex's own default
   align-items:stretch), so .plan-card-topup ends up exactly as tall as
   Pro/Power — same border, same frame, not a shorter/cut-off-looking card. */
#topup-card-wrap { display: flex; }
#topup-card-wrap > .plan-card { flex: 1 1 auto; }   /* fill the wrapper's full width AND height, not just shrink-to-fit */
/* Grid items (and flex items) default to min-width:auto — meaning they will
   NOT shrink below the min-content size of whatever's inside them, even past
   their own 1fr share, unless told otherwise. The NGN preset row's unwrapped
   buttons (.topup-presets, overflow-x:auto + flex-wrap:nowrap) have a large
   intrinsic content width, and without this override that width was
   propagating all the way up through .plan-card-topup and #topup-card-wrap,
   forcing this whole grid TRACK wider than Pro/Power's — the card visibly
   expanded instead of the presets row just scrolling inside a normal-width
   card. min-width:0 at both levels breaks that propagation, so the card
   stays the SAME width as Pro/Power and .topup-presets' own overflow-x:auto
   is what actually handles the extra buttons, via its scrollbar. */
#topup-card-wrap { min-width: 0; }
#topup-card-wrap > .plan-card { min-width: 0; }
@media (max-width: 900px) { .plans-cards-row { grid-template-columns: 1fr; } }

/* Payment-waiting / result panel (external-browser checkout, auto-confirmed by polling) */
.pay-wait { text-align: center; }
.pay-wait h2 { margin: 4px 0 8px; }
.pay-wait p { color: var(--text-muted, #8b949e); font-size: 13.5px; line-height: 1.5; margin: 0 auto 10px; max-width: 340px; }
.pay-wait code { background: rgba(255,255,255,0.06); padding: 3px 7px; border-radius: 5px; font-size: 12px; }
.pay-spinner { width: 38px; height: 38px; margin: 10px auto 16px; border: 3px solid rgba(232,114,44,0.25); border-top-color: var(--accent, #E8722C); border-radius: 50%; animation: pay-spin 0.8s linear infinite; }
@keyframes pay-spin { to { transform: rotate(360deg); } }
.pay-status { margin: 12px 0 16px; color: var(--accent-light, #E8722C); font-size: 13px; font-weight: 600; }
.pay-check { width: 48px; height: 48px; margin: 6px auto 14px; border-radius: 50%; background: var(--accent, #E8722C); color: #fff; font-size: 27px; line-height: 48px; }
.pay-wait .plan-cta { margin-top: 6px; min-width: 160px; }
.pay-link { background: transparent; border: none; color: var(--text-muted, #8b949e); font-size: 12px; cursor: pointer; text-decoration: underline; margin-top: 10px; }

/* Currency + billing-period toggles (plans modal) — now relocated to sit
   right above the pricing cards (the discount-code row's old spot), wrapped
   so the pill row visually sits ON the divider that precedes .plans-cards-row:
   the wrapper draws a full-width line at its own vertical center, and the
   toggle row (opaque background) sits on top, masking the line in the gaps —
   the classic "step sits on the line" look. */
.plans-toggle-divider-wrap {
  position: relative;
  display: flex; justify-content: center;
  margin: 12px 0 44px;
}
.plans-toggle-divider-wrap::before {
  content: '';
  position: absolute; top: 50%; left: 0; right: 0; height: 1px;
  background: var(--border, #30363d);
  z-index: 0;
}
.plan-toggles {
  position: relative; z-index: 1;
  display: flex; justify-content: center; gap: 12px; flex-wrap: wrap;
  margin: 0; padding: 0 16px;
  background: var(--bg);   /* matches .plans-modal-full's page background — masks the line behind it */
}
/* A fully-rounded pill (999px), matching the reference's "Individual / Team"
   selector look — the "on" state fills solid so the current choice reads
   at a glance. */
.seg { display: inline-flex; background: rgba(255,255,255,0.05); border: 1px solid var(--border, #30363d); border-radius: 999px; overflow: hidden; }
.seg button { background: transparent; border: none; color: var(--text-muted, #8b949e); padding: 7px 16px; font-size: 13px; cursor: pointer; font-weight: 600; display: flex; align-items: center; gap: 6px; }
.seg button em { font-style: normal; font-size: 10px; background: var(--panel-bg-2); color: var(--text-muted); padding: 1px 6px; border-radius: 10px; }
.seg button.on { background: var(--text); color: var(--panel-bg); }
.seg button.on em { background: rgba(255,255,255,0.22); color: var(--panel-bg); }
.plan-sub { font-size: 11.5px; color: var(--text-muted, #8b949e); font-weight: 500; margin-top: 3px; }
.plans-help { text-align: center; margin-top: 14px; }

/* "Enter discount code" + the featured-promo card share one row, now
   relocated into the HEADER (was above the pricing cards) — the last thing
   before the header's own border-bottom, so it reads as sitting right at
   that divider. The period/currency toggles took over its old spot above
   the cards (see .plans-toggle-divider-wrap). */
.plans-header-promo-row { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; margin: 14px 0 0; }

/* Discount-code entry — a neutral GRAY card (the orange tint is reserved for
   an actual active admin promo below), with a real, visible border on the
   INPUT itself so it's obvious that's where a code goes. Apply matches the
   same solid dark button every other CTA on this page uses. */
.plans-discount-inline {
  display: flex; align-items: center; gap: 6px;
  background: var(--panel-bg-2); border: 1px solid var(--border, #30363d);
  border-radius: 8px; padding: 4px;
}
.plans-discount-inline input {
  background: var(--panel-bg); color: var(--text);
  border: 1px solid var(--border, #30363d); border-radius: 6px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.3px;
  padding: 6px 8px; min-width: 150px; text-transform: uppercase;
}
.plans-discount-inline input::placeholder { text-transform: none; font-weight: 500; letter-spacing: normal; color: var(--text-muted, #8b949e); }
.plans-discount-inline input:focus { outline: none; border-color: var(--text-dim); }
.plans-discount-inline .promo-use-btn { padding: 5px 10px; font-size: 10.5px; }
.disc-result { font-size: 11.5px; text-align: center; min-height: 14px; margin: 4px 0; }
.disc-result.ok  { color: var(--ok, #3fb950); }
.disc-result.err { color: var(--error, #f85149); }
.price-strike { color: var(--text-dim); text-decoration: line-through; font-size: 0.7em; font-weight: 500; margin-right: 4px; }
.plan-sub.disc-applied { color: var(--ok, #3fb950); font-weight: 600; }

/* Featured promo — a small card, shrunk to fit its own content (width:
   fit-content). Sits inside .plans-header-promo-row alongside the
   discount-code entry, so its OWN centering margin is off (margin:0) — the
   row itself centers both items together via justify-content:center.
   Gray, same as .plans-discount-inline right next to it, not orange. */
.plans-promo {
  font-size: 10.5px; color: var(--text); margin: 0;
  width: fit-content; max-width: 100%;
  background: var(--panel-bg-2); border: 1px solid var(--border, #30363d);
  border-radius: 8px; padding: 6px 7px 6px 10px;
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 10px;
}
.promo-text { flex: 0 1 auto; }
.promo-code-card {
  display: flex; align-items: center; gap: 5px;
  background: var(--panel-bg, #fff); border: 1px solid var(--border, #30363d);
  border-radius: 7px; padding: 2px 3px 2px 8px; flex: 0 0 auto;
}
.promo-code-text { font-size: 11.5px; font-weight: 800; letter-spacing: 0.3px; color: var(--text); font-family: ui-monospace, monospace; }
/* Same solid dark button as every other CTA on this page (.plan-cta) — no
   separate orange-fill treatment. */
.promo-use-btn {
  background: var(--text); color: var(--panel-bg); border: none; border-radius: 6px;
  font-size: 11px; font-weight: 700; padding: 6px 11px; cursor: pointer; white-space: nowrap;
}
.promo-use-btn:hover { background: var(--text-muted); }
.plans-promo .promo-use-btn { font-size: 9.5px; padding: 4px 8px; }

/* Contact-us modal (2026-07-30: card-based redesign — two visually equal
   support-type cards instead of one long WhatsApp bar + a bare form below
   it. Cards reuse .plan-card's own recipe (rounded, --panel-bg, --shadow)
   for consistency with the Plans modal rather than a new visual language. */
.contact-modal { max-width: 460px; }
.contact-modal h2 { text-align: center; margin: 4px 0 6px; }
.contact-modal > p { text-align: center; color: var(--text-muted, #8b949e); font-size: 13.5px; margin: 0 auto 14px; max-width: 360px; }
.contact-card {
  border-radius: 12px;
  background: var(--panel-bg);
  box-shadow: var(--shadow);
  padding: 14px 16px;
  margin: 0 0 12px;
}
.contact-card-wa { display: flex; align-items: center; gap: 12px; }
.contact-card-icon { font-size: 22px; line-height: 1; flex-shrink: 0; }
.contact-card-body { flex: 1 1 auto; min-width: 0; }
.contact-card-title { font-weight: 700; font-size: 14px; color: var(--text); }
.contact-card-msg .contact-card-title { margin-bottom: 2px; }
.contact-card-sub { font-size: 11.5px; color: var(--text-muted, #8b949e); margin-top: 2px; }
.contact-card-msg .contact-card-sub { margin-bottom: 10px; }
/* Compact — sized to its own label ("Chat now"), NOT the full card/modal
   width, so it reads as one action among equals instead of dominating the
   modal (the original full-width bar was the thing being fixed here). */
.wa-cta {
  flex-shrink: 0; padding: 8px 16px; border: none; border-radius: 8px;
  background: #25D366; color: #fff; font-size: 13px; font-weight: 700;
  font-family: inherit; cursor: pointer; white-space: nowrap;
  transition: background 0.15s, transform 0.1s;
}
.wa-cta:hover { background: #20b957; transform: translateY(-1px); }
.contact-textarea { width: 100%; min-height: 96px; resize: vertical; background: rgba(255,255,255,0.05); border: 1px solid var(--border, #30363d); border-radius: 8px; color: var(--text, #c9d1d9); padding: 10px 12px; font-size: 13px; font-family: inherit; box-sizing: border-box; }
.contact-status { min-height: 18px; font-size: 12.5px; color: var(--error, #f85149); margin: 8px 0; text-align: center; }
.contact-status.ok { color: var(--accent-light, #E8722C); }
.contact-modal .plan-cta { width: 100%; margin-top: 4px; }
.contact-alt { text-align: center; margin-top: 10px; font-size: 12px; color: var(--text-muted, #8b949e); }

/* ── In-app feedback prompt (feedback_prompt.js) — reuses .plans-overlay /
   .plans-modal / .plans-close / .plan-cta / .contact-textarea / .contact-status;
   only the star row, secondary button and thank-you state are new. ── */
.fb-modal .fb-msg { font-size: 13px; color: var(--text-muted, #8b949e); margin: 0 0 14px; line-height: 1.5; }
.fb-stars { display: flex; gap: 6px; justify-content: center; margin: 4px 0 14px; }
.fb-star {
  background: none; border: none; cursor: pointer; font-size: 30px; line-height: 1;
  color: var(--border, #30363d); padding: 2px 4px; transition: color 0.12s, transform 0.12s;
  font-family: inherit;
}
.fb-star:hover { transform: scale(1.12); }
.fb-star.on { color: var(--accent-light, #E8722C); }
.fb-check {
  display: flex; align-items: center; gap: 8px; margin: 10px 0 2px;
  font-size: 12px; color: var(--text-muted, #8b949e); cursor: pointer;
}
.fb-check input { cursor: pointer; }
.fb-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.fb-actions .plan-cta { width: 100%; }
.fb-later {
  background: none; border: none; cursor: pointer; font-family: inherit;
  font-size: 12px; color: var(--text-muted, #8b949e); padding: 4px; text-decoration: underline;
}
.fb-later:hover:not(:disabled) { color: var(--text, #c9d1d9); }
.fb-later:disabled { opacity: 0.5; cursor: default; }
.fb-thanks {
  text-align: center; font-size: 14px; color: var(--accent-light, #E8722C);
  padding: 24px 8px; line-height: 1.5;
}

.plan-card {
  position: relative;
  border: none;
  border-radius: 12px;
  padding: 18px;
  /* --panel-bg (not --panel-bg-2) — the brighter/whiter of the two panel
     tones, so cards read as pure-white elevated surfaces in light mode
     (matching the reference exactly) and as the lighter dark-mode panel tone
     otherwise; contrast against the page comes from .plans-modal-full using
     --bg instead, not from dimming the card itself. Elevation comes from the
     theme-aware --shadow token (no border line) — the ribbon alone tells
     Pro/Power apart, so all three cards share the same soft drop shadow.
     Featured gets a slightly deeper shadow for a touch of extra emphasis. */
  background: var(--panel-bg);
  box-shadow: var(--shadow);
  display: flex; flex-direction: column;
}
.plan-card.featured { box-shadow: 0 10px 32px rgba(0,0,0,0.16); }
.plan-ribbon.current { background: var(--text-dim); }
.plan-ribbon {
  position: absolute; top: -10px; left: 50%; transform: translateX(-50%);
  background: var(--text); color: var(--panel-bg);
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;
  padding: 3px 8px; border-radius: 10px; white-space: nowrap;
}
/* The small line-art mark atop each card. */
.plan-icon { color: var(--accent-light, #E8722C); margin-bottom: 10px; }
.plan-icon svg { display: block; }
.plan-name { font-size: 16px; font-weight: 700; color: var(--text); }
.plan-tagline { font-size: 11.5px; color: var(--text-muted); margin: 2px 0 12px; }
.plan-price { font-size: 26px; font-weight: 800; color: var(--text); margin: 0 0 14px; }
.plan-price span { font-size: 12px; font-weight: 500; color: var(--text-dim); }
.plan-card-topup .plan-price { font-size: 20px; }
.plan-caption { font-size: 10.5px; color: var(--text-dim); text-align: center; margin-top: 6px; }
.plan-divider { height: 1px; background: var(--border, #30363d); margin: 16px 0 12px; }
.plan-features-head { font-size: 11px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.plan-features { list-style: none; padding: 0; margin: 0; flex: 1; }
.plan-features li { font-size: 12px; color: var(--text-muted); padding: 3px 0 3px 18px; position: relative; }
.plan-features li::before { content: '✓'; position: absolute; left: 0; color: var(--text); font-weight: 700; }
.plan-cta {
  width: 100%; padding: 9px; border: none; border-radius: 8px;
  background: var(--text); color: var(--panel-bg); font-size: 13px; font-weight: 600;
  font-family: inherit; cursor: pointer; transition: background 0.15s;
}
.plan-cta:hover { background: var(--text-muted); }
.plan-cta:disabled { opacity: 0.6; cursor: default; }
/* Every card's primary button (Pro/Power/Buy) shares the SAME solid style —
   no outlined variant. "Most popular"/"Current" are already signalled by the
   ribbon + border, so the button itself doesn't need a second, different
   treatment to carry that signal too. */

/* One-Time Project Access (top-up) — now framed as a 3rd matching card
 * (.plan-card-topup extends .plan-card) sitting alongside Pro/Power in
 * .plans-cards-row, with its own interior (presets + free-form amount)
 * where a fixed price/CTA would normally sit. */
.plans-status.topup-balance { color: var(--accent-light, #E8722C); }
/* Always exactly ONE row — never wraps to a second line. USD's 7 presets
   ($2..$500) already fit comfortably at this width, so "safe center" keeps
   them centered as before; NGN's wider values (up to ₦650,000) don't fit, so
   the row scrolls horizontally instead — "safe" falls back to start-aligned
   in that case (a centered-but-overflowing row would leave the leftmost
   buttons unreachable) — with a slim accent-colored scrollbar along the
   bottom acting as the "more presets this way" indicator. */
.topup-presets {
  display: flex; justify-content: safe center; gap: 3px;
  margin: 0 0 12px; padding-bottom: 4px;
  flex-wrap: nowrap; overflow-x: auto;
  scrollbar-width: thin; scrollbar-color: var(--accent-light, #E8722C) transparent;
}
/* Styling the thumb/track alone leaves Chromium's own NATIVE end-arrow
   buttons in place — a bulkier "real scrollbar" look than intended (that's
   what showed up chunky in practice). Suppressing them + shrinking the bar
   itself down to a hairline is what actually makes it read as a subtle
   "there's more this way" hint rather than a full scrollbar control. */
.topup-presets::-webkit-scrollbar { height: 2px; }
.topup-presets::-webkit-scrollbar-track { background: transparent; }
.topup-presets::-webkit-scrollbar-thumb { background: var(--accent-light, #E8722C); border-radius: 2px; }
.topup-presets::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
.topup-preset {
  flex: 0 0 auto; white-space: nowrap;
  padding: 4px 7px; border: 1px solid var(--border); border-radius: 999px;
  background: transparent; color: var(--text); font-size: 10px; font-weight: 600;
  font-family: inherit; cursor: pointer; transition: background 0.15s, border-color 0.15s;
}
.topup-preset:hover { background: rgba(232,114,44,0.12); border-color: var(--accent-light, #E8722C); }
.topup-custom { margin: 0 0 4px; text-align: center; }
.topup-amount-row {
  display: flex; align-items: center; border: 1px solid var(--border); border-radius: 8px;
  background: rgba(255,255,255,0.05); overflow: hidden;
}
.topup-cur-sym { padding: 9px 0 9px 12px; font-size: 14px; font-weight: 700; color: var(--text-muted); }
.topup-amount-row input {
  flex: 1; min-width: 0; border: none; background: transparent; color: var(--text);
  font-size: 14px; font-weight: 600; font-family: inherit; padding: 9px 12px 9px 6px; outline: none;
}
.topup-amount-row input::-webkit-outer-spin-button, .topup-amount-row input::-webkit-inner-spin-button { opacity: 0.5; }
/* Placeholder ("At least $2") matches the preset-button word size (.topup-preset,
   10px) — the typed value itself stays larger/readable (14px, above). */
.topup-amount-row input::placeholder { font-size: 10px; font-weight: 600; color: var(--text-dim); }
/* Matches .plan-caption's size (10.5px) — the same small note-under-CTA text
   every other card on this page uses. */
.topup-preview { font-size: 10.5px; color: var(--text-muted); margin: 8px 0; min-height: 16px; }
.topup-preview.ok { color: var(--accent-light, #E8722C); }
.topup-preview.err { color: var(--error, #f85149); }
/* Sets honest expectations on how far a top-up goes — sits between the live
   credit preview and the Buy button, so it's read right where someone is
   deciding an amount, not buried in a tooltip/footnote. */
.topup-hint { font-size: 9.5px; line-height: 1.35; color: var(--text-dim); margin: 0 0 10px; }
.topup-custom .plan-cta { width: 100%; }

/* Feature comparison table, below the cards — a plain, readable row-by-row
   summary for anyone who wants to compare before picking a card. */
/* Collapsed by default (native <details>, no JS needed) — the detailed
   row-by-row breakdown is opt-in, not forced on everyone who just wants to
   glance at the 3 cards. The seam above it is now a real .plans-divider line
   (in the markup, right after .plans-cards-row), so no extra top margin is
   needed here on top of that. */
.plans-compare-wrap { margin-top: 0; }
.plans-compare-toggle {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  list-style: none; cursor: pointer; user-select: none;
  font-size: 12.5px; font-weight: 600; color: var(--text-muted);
  padding: 10px; border: 1px solid var(--border, #30363d); border-radius: 8px;
  background: var(--panel-bg-2);
}
.plans-compare-toggle::-webkit-details-marker { display: none; }   /* hide the native disclosure triangle — we draw our own */
.plans-compare-toggle:hover { color: var(--text); }
.plans-compare-toggle::after {
  content: '▾'; font-size: 10px; transition: transform 0.15s;
}
.plans-compare-wrap[open] .plans-compare-toggle::after { transform: rotate(180deg); }
.plans-compare { margin-top: 16px; overflow-x: auto; }
.plans-compare-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.plans-compare-table th, .plans-compare-table td {
  padding: 10px 14px; text-align: center; border-bottom: 1px solid var(--border, #30363d);
}
.plans-compare-table td:first-child, .plans-compare-table th:first-child {
  text-align: left; color: var(--text-muted); font-weight: 500; white-space: nowrap;
}
.plans-compare-table thead th {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px;
  color: var(--text-dim); border-bottom: 1px solid var(--border, #30363d);
}
.plans-compare-table .cmp-price-row td { font-weight: 700; color: var(--text); }
.cmp-yes { color: var(--accent-light, #E8722C); font-weight: 700; }
.cmp-no { color: var(--text-dim); }

/* Learner profile block in the account popout. */
.account-menu-profile { padding: 4px 10px 8px; }
.amp-title {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  margin-bottom: 5px;
}
.amp-row { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; line-height: 1.7; }
.amp-label { color: var(--text-dim); flex-shrink: 0; }
.amp-val {
  color: var(--text-muted);
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.amp-edit {
  margin-top: 7px;
  width: 100%;
  padding: 5px 8px;
  font-size: 11px;
  font-family: inherit;
  color: var(--accent-light);
  background: rgba(232,114,44,0.12);
  border: 1px solid rgba(232,114,44,0.3);
  border-radius: 5px;
  cursor: pointer;
}
.amp-edit:hover { background: rgba(232,114,44,0.2); }
.account-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 13px;
  font-family: var(--font);
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.account-menu-item:hover { background: var(--panel-bg-2); color: var(--text); }
.account-menu-item svg { flex-shrink: 0; opacity: 0.85; }
.account-menu-icon { width: 15px; text-align: center; font-size: 13px; }
.account-menu-item > span:nth-child(2) { flex: 1; text-align: left; }
.account-menu-meta { font-size: 12px; color: var(--text-dim); font-weight: 600; }
.account-menu-meta.low { color: var(--warning); }
.account-menu-meta.critical { color: var(--error); }
.account-menu-item.danger { color: var(--status-fail); }
.account-menu-item.danger:hover { background: rgba(218,54,51,0.12); color: var(--status-fail); }

/* ── Centre panel ─────────────────────────────────────────── */
#centre-panel {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg);
  position: relative;
}

/* Chat header. Action icons stay pinned to the RIGHT (space-between), right
   up against the notebook divider next door — that's the fix: previously
   .chat-header-info carried flex:1 even though #agent-status-text no longer
   gets written to (that responsibility moved to the in-conversation
   thinking-placeholder), so that empty-but-still-flex:1 slot ate real width
   and visually shoved the icons further right than intended, reading as a
   second, disconnected gap of its own. With flex:1 dropped below, an empty
   .chat-header-info collapses to 0 width and space-between just pins the
   icons flush against the divider — connected to the notebook side, no
   dead space anywhere in between. */
.chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 53px;
  padding: 0 18px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-bg);
  flex-shrink: 0;
}
.chat-header-info { display: flex; flex-direction: column; min-width: 0; }
.chat-title { font-size: 14px; font-weight: 600; color: var(--text); }
.chat-subtitle {
  font-size: 11px;
  color: var(--text-muted);
  /* Long status lines truncate instead of pushing the action buttons. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-header-actions { display: flex; gap: 6px; flex-shrink: 0; }
.header-btn {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  white-space: nowrap;          /* keep the label on ONE line ("Beginner Friendly") */
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  cursor: pointer;
  font-family: var(--font);
  transition: all 0.15s;
}
.header-btn svg { flex-shrink: 0; }
.header-btn:hover { background: var(--border); color: var(--text); }
#stop-btn { color: var(--error); border-color: rgba(218,54,51,0.3); }

/* Beginner-mode toggle: clearly "on" when active. */
.beginner-toggle.active {
  background: rgba(232,114,44,0.22);
  border-color: var(--accent-light);
  color: var(--text);
}
.beginner-toggle.active:hover { background: rgba(232,114,44,0.30); }
#stop-btn:hover { background: rgba(218,54,51,0.1); }

/* One-time voice discoverability nudge — points at the voice toggle button,
   which now lives in the input toolbar at the BOTTOM of the chat column (not
   the header), so the card sits ABOVE the button with the arrow pointing DOWN.
   Positioned absolutely within .chat-column (already position:relative); JS sets
   `right`/`bottom` at show-time from the button's real on-screen position. */
.voice-nudge {
  position: absolute;
  bottom: 60px;
  right: 18px;
  z-index: 40;
  align-items: center;
  gap: 8px;
  max-width: 280px;
  padding: 10px 12px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border-light);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.35);
  font-size: 12.5px;
  color: var(--text);
  animation: voiceNudgeIn 0.25s ease-out;
}
.voice-nudge-arrow {
  position: absolute;
  bottom: -7px;
  right: 18px;
  width: 12px;
  height: 12px;
  background: var(--panel-bg-2);
  border-right: 1px solid var(--border-light);
  border-bottom: 1px solid var(--border-light);
  transform: rotate(45deg);
}
.voice-nudge-text { flex: 1; line-height: 1.4; }
.voice-nudge-close {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
}
.voice-nudge-close:hover { color: var(--text); }
@keyframes voiceNudgeIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Voice waveform */
.voice-waveform {
  background: rgba(181,83,15,0.08);
  border-bottom: 1px solid rgba(181,83,15,0.2);
  padding: 10px 18px;
  flex-shrink: 0;
}
.waveform-container {
  display: flex;
  align-items: center;
  gap: 14px;
}
.waveform-bars {
  display: flex;
  align-items: center;
  gap: 3px;
  height: 24px;
}
.waveform-bars .bar {
  width: 3px;
  background: var(--accent-light);
  border-radius: 2px;
  animation: wave 1.2s ease-in-out infinite;
  min-height: 4px;
}
.waveform-bars .bar:nth-child(1)  { animation-delay: 0.0s; }
.waveform-bars .bar:nth-child(2)  { animation-delay: 0.1s; }
.waveform-bars .bar:nth-child(3)  { animation-delay: 0.2s; }
.waveform-bars .bar:nth-child(4)  { animation-delay: 0.3s; }
.waveform-bars .bar:nth-child(5)  { animation-delay: 0.4s; }
.waveform-bars .bar:nth-child(6)  { animation-delay: 0.5s; }
.waveform-bars .bar:nth-child(7)  { animation-delay: 0.4s; }
.waveform-bars .bar:nth-child(8)  { animation-delay: 0.3s; }
.waveform-bars .bar:nth-child(9)  { animation-delay: 0.2s; }
.waveform-bars .bar:nth-child(10) { animation-delay: 0.1s; }
.waveform-bars .bar:nth-child(11) { animation-delay: 0.0s; }
.waveform-bars .bar:nth-child(12) { animation-delay: 0.1s; }
@keyframes wave {
  0%, 100% { height: 4px; }
  50% { height: 22px; }
}
.waveform-label { font-size: 12px; color: var(--accent-light); flex: 1; }
.waveform-stop-btn {
  background: none;
  border: 1px solid rgba(181,83,15,0.4);
  color: var(--accent-light);
  padding: 3px 10px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  cursor: pointer;
  font-family: var(--font);
  transition: all 0.15s;
}
.waveform-stop-btn:hover { background: rgba(181,83,15,0.15); }
/* Paused: freeze the bars and dim the strip so it reads as "held". */
.voice-waveform.paused .waveform-bars .bar { animation-play-state: paused; opacity: 0.4; }
.voice-waveform.paused .waveform-label { color: var(--text-muted); }

/* Chat messages — plain small inset by default. This is the SPLIT-VIEW
   width (chat sharing the panel with the notebook), which is already
   narrower on its own — it does NOT get the big reading-gutter treatment
   below; that's reserved for the full-bleed, notebook-hidden case. */
.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* Full-bleed mode ONLY (notebook hidden, chat alone fills the whole panel) —
   a comfortable reading column, centered, NOT edge-to-edge (Codex/ChatGPT-
   style). The scroll container itself still spans the panel's full width
   (scrollbar flush against the real right edge) — only the PADDING opens
   up. 15% of the panel's own width per side, floored at 18px — matched
   against Codex's own reference screenshot (~240px of gutter out of a
   ~1588px-wide panel is right at 15%). A straight percentage (not a fixed
   max-width column) so the gutter keeps growing in proportion on a very
   wide window instead of topping out. No inner max-width DOM wrapper
   needed — many call sites append straight into this element, so this
   stays a pure padding change on the element itself. */
.lab-split.notebook-hidden .chat-messages {
  padding-left:  max(18px, 15%);
  padding-right: max(18px, 15%);
}
/* Same full-bleed-only gutter for the input card, so its left/right edges
   keep lining up with the message column above it (see .input-area itself
   for the plain split-view default this overrides). */
.lab-split.notebook-hidden .input-area {
  margin-left:  max(16px, 15%);
  margin-right: max(16px, 15%);
}

/* Individual messages */
.message {
  display: flex;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius);
  max-width: 100%;
  animation: msg-in 0.2s ease-out;
}
@keyframes msg-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.user-message {
  /* Compact, right-aligned bubble (WhatsApp/Codex-style) instead of a
     full-width tinted band. align-self overrides .chat-messages' default
     'stretch', which is what makes every message fill the column width —
     switching it shrinks the box to its own content and pushes it to the
     right edge, with max-width as the wrap point for a long message.
     The shaded/bordered "card" look itself lives on .message-text below,
     NOT here — so the meta row (time · copy · retry), which now sits below
     the text, renders on the plain page background OUTSIDE that card,
     instead of appearing to still be inside it. */
  align-self: flex-end;
  max-width: 78%;
  flex-direction: row-reverse;
}
.user-message .message-text {
  background: var(--user-msg-bg);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 8px 12px;
}
.agent-message {
  /* Plain text on the page background — not a card. The avatar is already
     display:none globally, so this is the only thing still making an agent
     reply read as "boxed"; border was already transparent. */
  background: transparent;
  border: 1px solid transparent;
}

/* The in-conversation "Omi is thinking…" placeholder (renderer.js
   showThinkingMessage/hideThinkingMessage) — it's an .agent-message itself,
   so it already inherits the plain unboxed look above for free; this only
   adds the muted, gently pulsing text treatment that signals "not final
   content yet", matching the real Codex desktop app's own "Thinking"
   placeholder (verified live against it before building this). */
.thinking-placeholder .thinking-label {
  color: var(--text-muted);
  animation: thinking-pulse 1.6s ease-in-out infinite;
}
@keyframes thinking-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1; }
}
/* A genuine network-reconnect attempt (not just Omi thinking) — same pulse,
   tinted amber so it reads as a normal platform status, not Omi being stuck. */
.thinking-placeholder.warn .thinking-label { color: var(--warning, #d29922); }

/* Cold-start empty state (the "Learn / Analyze / Build / Explore" screen) — a
   Codex-style centered icon + headline + 4 prompt cards, shown ONLY while
   #chat-messages is still genuinely empty. No JS visibility state needed:
   clearChat()/newSession() already keep this one element and remove
   everything else (see renderer.js), so this tracks "is this session
   genuinely empty" purely from DOM structure and updates itself the instant
   a real message is appended as a sibling.
   Genuinely empty = .welcome-message is the ONLY child, OR its only sibling
   is a transient .thinking-placeholder (env-check / status line appearing
   before the user's first message) — a passing status shouldn't make the
   whole greeting vanish, only a REAL message/card/reply should. */
.welcome-message { display: none; }
.welcome-message:first-child:last-child,
.welcome-message:first-child:nth-last-child(2):has(+ .thinking-placeholder:last-child) {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  width: 100%;
  max-width: 100%;
  padding: 48px 24px;
  /* Push toward the bottom (closer to the composer) rather than dead-center:
     all the slack space collects above via margin-top:auto, and a fixed
     margin-bottom keeps a deliberate, comfortable gap above the composer
     instead of sitting flush against it. */
  margin-top: auto;
  margin-bottom: 40px;
  background: none;
  border: none;
  animation: none;
}
.wes-title {
  margin: 0;
  font-size: 21px;
  font-weight: 600;
  color: var(--text);
  text-align: center;
  max-width: 480px;
}
.wes-cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(120px, 1fr));
  gap: 10px;
  width: 100%;
  max-width: 640px;
  margin-top: 6px;
}
.wes-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 14px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--panel-bg);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s, transform 0.1s;
  font-family: inherit;
}
.wes-card:hover { border-color: rgba(232,114,44,0.45); transform: translateY(-1px); }
.wes-card:active { transform: translateY(0); }
.wes-card-icon { font-size: 15px; line-height: 1; }
.wes-card-label { font-size: 12.5px; font-weight: 400; color: var(--text); line-height: 1.35; }
.welcome-message {
  border: 1px solid rgba(181,83,15,0.2);
  background: rgba(181,83,15,0.04);
}

/* Avatars — hidden for a cleaner, more document-like chat (owner request). Kept as
   display:none rather than removing the DOM elements (several call sites create them),
   so this is a single, low-risk, fully-reversible change. .message's flex gap only
   applies between VISIBLE items, so hiding this leaves no empty gap. */
.message-avatar {
  display: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  margin-top: 2px;
}
.agent-avatar {
  background: rgba(181,83,15,0.15);
  border: 1px solid rgba(181,83,15,0.3);
}
.user-avatar {
  background: rgba(56,139,253,0.15);
  border: 1px solid rgba(56,139,253,0.3);
  font-size: 12px;
  font-weight: 600;
  color: var(--info);
}

/* Message content — a flex column so .message-header (below) can be pushed
   BELOW .message-text via `order` alone, without touching DOM order at any
   of the several call sites that build this markup. */
.message-content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* No visible "You"/"Omi" label — kept as display:none (not removed from the
   DOM) rather than touching every call site, same reversible-hide pattern
   already used for .message-avatar just above. */
.message-author { display: none; }
/* The meta row (time · copy · retry) now lives BELOW the message text, and
   only appears on hover of the whole message — mirrors how these same icons
   already worked (msg-copy-btn was already hover-only), just widened to the
   whole row instead of one icon, and moved out of the old author/time line. */
.message-header {
  display: flex;
  align-items: center;
  gap: 4px;
  order: 99;
  margin-top: 4px;
  margin-bottom: 0;
  opacity: 0;
  transition: opacity .12s;
}
.message:hover .message-header { opacity: 1; }
/* Right-align the meta row under a right-aligned user bubble, matching the
   bubble's own side instead of always starting flush left inside it. */
.user-message .message-header { justify-content: flex-end; }
/* order:1 pushes it after the icon buttons (default order:0) — copy/retry
   first, time last — without touching DOM order in any of the several
   places that build this markup. */
.message-time { font-size: 10px; color: var(--text-dim); order: 1; }
.message-text   {
  font-size: 13px;
  color: var(--text);
  line-height: 1.65;
  min-width: 0;
  /* Wrap long unbroken strings (e.g. a pasted DNA/protein sequence) inside the
     card instead of letting them spill past the right edge. Code blocks (<pre>)
     keep their own horizontal scroll. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.message-text p { margin-bottom: 8px; }
/* Clickable "source" chip — links fetched data to its public DB page. */
.source-link-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: 100%;
  margin: 2px 0 8px 40px;
  padding: 6px 12px;
  background: rgba(232,114,44,0.10);
  border: 1px solid rgba(232,114,44,0.3);
  border-radius: 16px;
  font-size: 12px;
  color: var(--accent-light);
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s;
}
.source-link-chip:hover { background: rgba(232,114,44,0.2); text-decoration: underline; }
.source-link-chip .slc-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Install-in-progress banner — visible feedback while a package installs */
.install-indicator {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 6px 0 8px 40px;
  padding: 9px 14px;
  background: rgba(56,139,253,0.10);
  border: 1px solid rgba(56,139,253,0.35);
  border-radius: 10px;
  font-size: 12.5px;
  max-width: 460px;
}
.install-indicator .ii-spinner {
  width: 14px; height: 14px; flex: none;
  border: 2px solid rgba(56,139,253,0.3);
  border-top-color: var(--status-info);
  border-radius: 50%;
  animation: omics-spin 0.7s linear infinite;
}
.install-indicator .ii-body { display: flex; flex-direction: column; min-width: 0; }
.install-indicator .ii-title { color: var(--status-info); font-weight: 600; }
.install-indicator .ii-sub { color: var(--text-secondary, #8b949e); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.install-indicator.done { background: rgba(46,160,67,0.10); border-color: rgba(46,160,67,0.35); }
.install-indicator.done .ii-spinner { border: none; animation: none; }
.install-indicator.done .ii-title { color: var(--status-done); }
.install-indicator.fail { background: rgba(210,80,40,0.10); border-color: rgba(210,80,40,0.35); }
.install-indicator.fail .ii-spinner { border: none; animation: none; }

/* Generic action-card — same visual language as .install-indicator, reused for any
   ongoing background action that has live progress (downloads, long fetches, etc.),
   instead of flickering through the shared top-of-chat status line. */
.action-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 6px 0 8px 40px;
  padding: 9px 14px;
  background: rgba(56,139,253,0.10);
  border: 1px solid rgba(56,139,253,0.35);
  border-radius: 10px;
  font-size: 12.5px;
  max-width: 460px;
}
.action-card .ac-spinner {
  width: 14px; height: 14px; flex: none; margin-top: 2px;
  border: 2px solid rgba(56,139,253,0.3);
  border-top-color: var(--status-info);
  border-radius: 50%;
  animation: omics-spin 0.7s linear infinite;
}
.action-card .ac-body { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 3px; }
.action-card .ac-title { color: var(--status-info); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.action-card .ac-sub { color: var(--text-secondary, #8b949e); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.action-card .ac-track { height: 4px; border-radius: 2px; background: rgba(56,139,253,0.15); margin-top: 2px; overflow: hidden; }
.action-card .ac-fill { display: block; height: 100%; width: 0%; background: var(--status-info); transition: width 0.25s ease; border-radius: 2px; }
.action-card.done { background: rgba(46,160,67,0.10); border-color: rgba(46,160,67,0.35); }
.action-card.done .ac-spinner { border: none; animation: none; }
.action-card.done .ac-title { color: var(--status-done); }
.action-card.done .ac-fill { background: var(--status-done); }
.action-card.fail { background: rgba(210,80,40,0.10); border-color: rgba(210,80,40,0.35); }
.action-card.fail .ac-spinner { border: none; animation: none; }
.action-card.fail .ac-title { color: var(--status-fail); }

/* Grouped source-link card — consecutive "Source:" chips collapse into ONE card
   instead of stacking as separate disconnected floating pills. */
.source-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 6px 0 8px 40px;
  padding: 8px 12px;
  background: rgba(232,114,44,0.06);
  border: 1px solid rgba(232,114,44,0.25);
  border-radius: 10px;
  max-width: 460px;
}
.source-card .sc-head {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--accent-light);
  opacity: 0.85;
  margin-bottom: 2px;
}
.source-card .source-link-chip {
  margin: 0;
  background: none;
  border: none;
  padding: 3px 0;
  border-radius: 0;
  display: block;
  white-space: normal;
}
/* The base .slc-text rule (line ~1914) forces nowrap+ellipsis so a lone chip
   truncates on one line — correct there, but inside a card the outer chip is
   already display:block+white-space:normal (above) to WRAP long text across
   lines. Without this, .slc-text's own nowrap still wins, so the text tries
   to stay on one line and spills past the card's right edge instead of
   wrapping or ellipsis-clipping (a real reported bug, not cosmetic nitpick). */
.source-card .source-link-chip .slc-text {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
.source-card .source-link-chip:hover { background: none; text-decoration: underline; }
.source-card .source-link-chip + .source-link-chip { border-top: 1px solid rgba(232,114,44,0.15); padding-top: 6px; margin-top: 1px; }

/* Drafted-manuscript card — title/count + Save-as-PDF/HTML buttons. */
/* A drafted manuscript is a PRIMARY deliverable (unlike the small transient
   status cards above — install/action/source), so it gets the same width as a
   chat message bubble (max-width:100% of the column) instead of a small,
   indented accessory box. */
.manuscript-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 8px 0 10px;
  padding: 14px 16px;
  background: var(--panel-bg-2, #161b22);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-width: 100%;
}
.ms-card-head { display: flex; align-items: flex-start; gap: 10px; }
.ms-card-icon { font-size: 20px; line-height: 1; flex: none; }
.ms-card-body { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.ms-card-title { font-weight: 600; color: var(--text); font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-card-sub { color: var(--text-muted); font-size: 11.5px; }
.ms-card-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.ms-card-btn {
  flex: 1 1 auto;
  min-width: 110px;
  padding: 6px 10px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  border-radius: 7px;
  /* Accent-tinted at rest (matches the accent text color) instead of the
     neutral --border — a neutral gray outline barely separates from the
     card's own near-neutral background in light mode; an orange outline
     has real contrast against either theme's background. */
  border: 1px solid rgba(232,114,44,0.28);
  background: rgba(232,114,44,0.05);
  color: var(--accent-light);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.ms-card-btn:hover { background: rgba(232,114,44,0.14); border-color: rgba(232,114,44,0.5); }
.ms-card-status { font-size: 11px; color: var(--text-muted); min-height: 14px; }

/* Interactive-chart card (today: the Krona-style taxonomy sunburst) and
   Streamlit-dashboard card — a real, clickable "View in panel" artifact, same
   pattern as the manuscript card above, but a single-row layout since there's
   only one action (open it). New, self-contained classes — the manuscript card
   above is left completely untouched. */
.chart-card, .dashboard-card, .file-card {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0 10px;
  padding: 10px 14px;
  background: var(--panel-bg-2, #161b22);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-width: 100%;
}
.cc-icon, .dc-icon, .fc-icon { font-size: 18px; line-height: 1; flex: none; }
.cc-body, .dc-body, .fc-body { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 2px; }
.cc-title, .dc-title, .fc-title { font-weight: 600; color: var(--text); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-sub, .dc-sub, .fc-sub { color: var(--text-muted); font-size: 11.5px; }
.cc-btn, .dc-btn, .fc-btn {
  flex: none;
  padding: 6px 12px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  border-radius: 7px;
  border: 1px solid rgba(232,114,44,0.28);
  background: rgba(232,114,44,0.05);
  color: var(--accent-light);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.cc-btn:hover, .dc-btn:hover, .fc-btn:hover { background: rgba(232,114,44,0.14); border-color: rgba(232,114,44,0.5); }

/* "Workflow card" — a step-track visual for a plan/curriculum roadmap. TEST
   FEATURE, rendered ALONGSIDE the existing text list + 📚 badge/dropdown (see
   renderer.js renderPlanCard's own comment for the reversibility flag). New,
   uniquely-prefixed classes — does NOT reuse or touch .plan-card (the Pro/
   Power subscription pricing cards further down this file) or any existing
   card styling above. */
.wf-card {
  margin: 8px 0 10px;
  padding: 14px 16px 6px;
  background: var(--panel-bg-2, #161b22);
  border: 1px solid var(--border);
  border-radius: 10px;
  max-width: 460px;
}
.wf-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.wf-head-icon { font-size: 15px; line-height: 1; flex: none; }
.wf-head-title { font-weight: 600; color: var(--text); font-size: 13px; }
.wf-steps { display: flex; flex-direction: column; }
.wf-step { display: flex; gap: 10px; }
.wf-step-track { display: flex; flex-direction: column; align-items: center; flex: none; }
.wf-step-marker {
  width: 22px;
  height: 22px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 700;
  border: 1.5px solid var(--border);
  color: var(--text-muted);
  background: var(--panel-bg);
}
.wf-step-line { width: 1.5px; flex: 1 1 auto; min-height: 14px; background: var(--border); margin: 2px 0; }
.wf-step-body { padding-bottom: 16px; min-width: 0; }
.wf-step:last-child .wf-step-body { padding-bottom: 0; }
.wf-step-title { font-size: 12.5px; font-weight: 500; color: var(--text-muted); line-height: 1.4; padding-top: 1px; }
.wf-step-done .wf-step-marker { border-color: rgba(232,114,44,0.5); background: rgba(232,114,44,0.14); color: var(--accent-light); }
.wf-step-done .wf-step-line { background: rgba(232,114,44,0.35); }
.wf-step-done .wf-step-title { color: var(--text); }
.wf-step-current .wf-step-marker { border-color: var(--accent-light); background: var(--accent-light); color: #fff; }
.wf-step-current .wf-step-title { color: var(--text); font-weight: 600; }
.wf-step-skipped .wf-step-marker { border-style: dashed; color: var(--text-dim); }
.wf-step-skipped .wf-step-title { color: var(--text-dim); text-decoration: line-through; }
.wf-step-issue .wf-step-marker { border-color: #d9a000; background: transparent; color: #d9a000; }
.wf-step-issue .wf-step-title { color: var(--text); }
.wf-card-superseded { opacity: 0.55; }
/* steps kept from an earlier version of a revised plan: one collapsed group above the current plan's steps */
.wf-carried { margin: 0 0 12px; padding-bottom: 10px; border-bottom: 1px dashed var(--border); opacity: 0.75; }
.wf-carried > summary { cursor: pointer; font-size: 12px; color: var(--text-muted); list-style: none; padding: 2px 0 8px; }
.wf-carried > summary::-webkit-details-marker { display: none; }
.wf-carried > summary::before { content: '▸ '; }
.wf-carried[open] > summary::before { content: '▾ '; }

/* Attached-file chips shown inside a sent message. */
.message-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.message-file-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 4px 9px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 11px;
  color: var(--text-muted);
}
.message-file-chip .mf-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.message-text p:last-child { margin-bottom: 0; }
.message-text ul, .message-text ol {
  padding-left: 18px;
  margin-bottom: 8px;
}
.message-text li { margin-bottom: 3px; }
.message-text strong { color: var(--text); font-weight: 600; }
.message-text em { color: var(--text-muted); font-style: italic; }
.message-text a { color: var(--accent-light); text-decoration: none; }
.message-text a:hover { text-decoration: underline; }

/* Code blocks inside messages */
.message-text pre {
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  padding: 10px 12px;
  overflow-x: auto;
  max-width: 100%;          /* scroll inside the card, never widen it */
  margin: 0;
  font-family: 'Consolas', 'Courier New', 'Lucida Console', monospace;
  font-size: 12px;
  line-height: 1.55;
  letter-spacing: normal;
  word-spacing: normal;
}
.message-text code {
  font-family: 'Consolas', 'Courier New', 'Lucida Console', monospace;
  font-size: 12px;
  background: var(--panel-bg-2);
  padding: 1px 5px;
  border-radius: 3px;
  color: var(--accent-light);
  letter-spacing: normal;
  overflow-wrap: anywhere;     /* a long sequence in backticks wraps, not overflows */
  word-break: break-word;
}
.message-text pre code {
  background: none;
  padding: 0;
  color: var(--text);
}

/* Code block wrapper — header bar + copy button */
.code-block-wrapper {
  margin: 8px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.code-block-wrapper pre {
  border: none;
  border-radius: 0;
  margin: 0;
}
.code-block-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #1C2128;
  border-bottom: 1px solid var(--border);
  padding: 4px 10px;
  height: 28px;
}
.code-lang-badge {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.copy-code-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 10px;
  font-family: var(--font);
  padding: 2px 8px;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.15s;
  line-height: 1.4;
}
.copy-code-btn:hover { background: var(--border); color: var(--text); }
.copy-code-btn.copied { border-color: var(--success); color: var(--accent-hover); }

/* Inline plot images */
.inline-image-wrapper {
  margin: 10px 0;
}
.inline-plot-img {
  max-width: 100%;
  max-height: 480px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  display: block;
  cursor: pointer;
  transition: opacity 0.15s;
}
.inline-plot-img:hover { opacity: 0.9; }

/* ── Tool header (label + title + description) ─────────────────────────────── */
.tool-header {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 12px 0 4px;
}
.tool-header-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  padding-left: 2px;
}
.tool-header-desc {
  font-size: 12px;
  color: var(--text-muted);
  padding-left: 2px;
  line-height: 1.5;
}

.tool-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-muted);
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  padding: 3px 12px;
  border-radius: 12px;
  width: fit-content;
}
.tool-label.r-label        { color: var(--label-r); border-color: rgba(88,166,255,0.35); background: rgba(88,166,255,0.08); }
.tool-label.python-label   { color: var(--label-python); border-color: rgba(227,179,65,0.35); background: rgba(227,179,65,0.07); }
.tool-label.terminal-label { color: var(--label-terminal); border-color: rgba(255,157,92,0.35);  background: rgba(255,157,92,0.08);  }
.tool-label.browser-label  { color: var(--label-browser); border-color: rgba(240,136,62,0.35); background: rgba(240,136,62,0.08); }

/* ── Tool output block — one block per tool call ──────────────────────────── */
.tool-output-block {
  background: #0d1117;
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin: 4px 0 14px;
  font-family: 'Consolas', 'Courier New', 'Lucida Console', monospace;
  font-size: 13.5px;
  line-height: 1.75;
  letter-spacing: normal;
  word-spacing: normal;
  word-break: break-word;
  /* No max-height — card expands to show ALL content */
  color: #c9d1d9;
}
.tool-output-block.r-block      { border-left-color: var(--label-r); }
.tool-output-block.python-block { border-left-color: var(--label-python); }

/* Individual output lines */
.output-line {
  display: block;
  padding: 2px 0;
  color: #c9d1d9;
  font-size: 13.5px;
  line-height: 1.75;
}
.output-line.output-error {
  color: #ff7b72;
  background: rgba(255,123,114,0.08);
  border-radius: 3px;
  padding: 2px 6px;
  margin: 2px -6px;
}

/* The old fixed-position typing/status bar is gone — "Omi is thinking…" and
   every platform status (env checks, opening a lesson, etc.) now render as a
   real in-conversation entry; see .thinking-placeholder below. */

/* Non-voiced connection notice (retries exhausted / real error). */
.net-notice {
  align-self: center;
  margin: 6px auto;
  padding: 6px 14px;
  max-width: 80%;
  font-size: 12px;
  color: var(--text-muted);
  background: var(--panel-bg-2, rgba(255,255,255,0.03));
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-align: center;
}

/* Notices docked at the top of the composer card (upgrade nudges etc.) — a slim
   strip INSIDE .input-area's first row, spanning the card edge to edge. */
.composer-notice {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -12px -12px 8px;
  padding: 6px 10px 6px 14px;
  border-radius: 19px 19px 0 0;
  background: rgba(232,114,44,0.10);
  border-bottom: 1px solid rgba(232,114,44,0.28);
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--text);
}
.composer-notice-text { flex: 1; min-width: 0; }
.composer-notice-btn {
  background: var(--accent, #E8722C); color: #fff; border: none; border-radius: 6px;
  padding: 3px 10px; font-size: 11px; font-weight: 600; cursor: pointer; flex-shrink: 0;
}
.composer-notice-link {
  background: transparent; color: var(--accent-light, #E8722C); border: none;
  font-size: 11px; cursor: pointer; text-decoration: underline; flex-shrink: 0;
}
.composer-notice-x {
  background: transparent; color: var(--text-muted); border: none;
  font-size: 12px; cursor: pointer; padding: 0 2px; flex-shrink: 0;
}

/* Update card docked above the message box: downloading (progress bar) and ready (Relaunch now / Later). */
.composer-notice.update-notice { background: rgba(232,114,44,0.10); }
.update-notice .un-icon { flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(232,114,44,0.18); color: var(--accent-light, #E8722C); font-size: 12px; }
.update-notice .un-body { display: flex; flex-direction: column; gap: 4px; }
.update-notice .un-title { font-weight: 600; }
.update-notice .un-bar { display: block; height: 3px; border-radius: 2px; background: rgba(232,114,44,0.20); overflow: hidden; }
.update-notice .un-fill { display: block; height: 100%; width: 3%; background: var(--accent, #E8722C); transition: width .3s ease; }

/* "You earned…" notice — same docked strip, green so it reads as good news, not a warning. */
.composer-notice.earn-notice { background: rgba(46,160,67,0.12); border-bottom-color: rgba(46,160,67,0.35); }

/* Out-of-credit card shown inside the chat (Top up / Subscribe). */
.quota-card {
  align-self: stretch;
  flex-shrink: 0;   /* overflow:hidden makes min-height 0 → it collapsed to a 1px line in a full chat */
  margin: 10px 16px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-bg);
  overflow: hidden;
  font-size: 12.5px;
}
.quota-card-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px;
  background: var(--panel-bg-2, rgba(255,255,255,0.04));
  color: var(--text); font-weight: 500;
}
.quota-card-icon { color: var(--accent, #E8722C); }
.quota-card-body { padding: 10px 14px 12px; color: var(--text-muted); line-height: 1.5; }
.quota-card-actions { display: flex; justify-content: flex-end; gap: 8px; padding: 0 14px 12px; }
.quota-card-btn {
  border-radius: 8px; padding: 5px 14px; font-size: 12px; font-weight: 600; cursor: pointer;
  border: 1px solid var(--border); background: transparent; color: var(--text);
}
.quota-card-btn.primary { background: var(--accent, #E8722C); border-color: var(--accent, #E8722C); color: #fff; }
.quota-card-btn.tertiary { border-color: var(--accent, #E8722C); color: var(--accent, #E8722C); }
.quota-card-actions { flex-wrap: wrap; }
/* The persistent card lives in its own slot above the message box; line it up with the box (same side gutters). */
.quota-dock { margin: 0 16px 10px; flex-shrink: 0; }
.lab-split.notebook-hidden .quota-dock { margin-left: max(16px, 15%); margin-right: max(16px, 15%); }
.quota-dock .quota-card { margin: 0; }
@keyframes ooc-pulse { 0% { box-shadow: 0 0 0 0 rgba(232,114,44,.55); } 100% { box-shadow: 0 0 0 12px rgba(232,114,44,0); } }
.quota-card.pulse { animation: ooc-pulse .7s ease-out; }

/* Attached files strip */
/* Nested inside .input-area now (its first child) — the card's own 12px
   padding already insets left/right, so this only needs a bottom gap
   before the textarea row, present only while it actually holds chips. */
.attached-files {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 0 8px;
}
.attached-file-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(181,83,15,0.1);
  border: 1px solid rgba(181,83,15,0.25);
  border-radius: 12px;
  padding: 3px 10px;
  font-size: 11px;
  color: var(--accent-light);
}
.attached-file-chip button {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 12px;
  padding: 0;
  line-height: 1;
  margin-left: 2px;
}
.attached-file-chip button:hover { color: var(--error); }

/* Input area — a floating shadow card (attach/mic/send icons keep the SAME
   bottom-left / bottom-right placement as before; only the container changed
   from a flat edge-to-edge bar into a rounded, elevated card with the text
   on its own top row and a slim icon toolbar along the bottom). Sits on the
   plain page background (--bg), not --panel-bg, so the card itself reads as
   genuinely raised via --shadow rather than blending into a same-toned strip. */
.input-area {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Plain small side margin by default — matches .chat-messages' own
     split-view-vs-full-bleed split just below: this fixed 16px is the
     split-view value, overridden by the 15%-of-panel gutter ONLY when the
     notebook is hidden (see .lab-split.notebook-hidden .input-area). */
  margin: 0 16px 16px;
  padding: 12px 12px 8px;
  border-radius: 20px;
  background: var(--panel-bg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  flex-shrink: 0;
  box-sizing: border-box;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.input-area:focus-within {
  border-color: var(--accent);
}
.message-input {
  width: 100%;
  background: transparent;
  border: none;
  color: var(--text);
  font-family: var(--font);
  font-size: 13.5px;
  line-height: 1.5;
  padding: 4px 6px 10px;
  resize: none;
  outline: none;
  max-height: 160px;
  overflow-y: auto;
  box-sizing: border-box;
}
.message-input::placeholder { color: var(--text-dim); }

/* Bottom toolbar row inside the card — attach on the left, mic + send on the
   right, matching the reference layout exactly. */
.input-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 2px 0;
}
.input-toolbar-left, .input-toolbar-right {
  display: flex;
  align-items: center;
  gap: 6px;
}

.input-action-btn {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.15s;
}
.input-action-btn:hover { background: var(--panel-bg-2); color: var(--text); border-color: var(--border-light); }

.send-btn {
  width: 32px;
  height: 32px;
  border-radius: 999px;
  border: none;
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.15s;
}
.send-btn:hover  { background: var(--accent-hover); }
.send-btn:active { transform: scale(0.95); }
/* Send arrow when idle, stop square while Omi works (one button, toggled). */
.send-btn .stop-icon { display: none; }
.send-btn.is-stopping .send-icon { display: none; }
.send-btn.is-stopping .stop-icon { display: inline; }
.send-btn.is-stopping {
  background: var(--error, #da3633);
  border-color: var(--error, #da3633);
}
.send-btn.is-stopping:hover { filter: brightness(1.08); }

/* Always-visible voice on/off toggle (header): speaker when on, muted-speaker when off. */
.voice-toggle .vt-off { display: none; }
.voice-toggle.is-muted .vt-on { display: none; }
.voice-toggle.is-muted .vt-off { display: inline; }
.voice-toggle.is-muted { color: var(--text-muted); }

/* In-app "choose your working folder" card (shown on the first message of a new session). */
.ws-modal-overlay {
  position: absolute; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.45); backdrop-filter: blur(2px);
  padding: 20px; animation: wsModalIn 0.14s ease;
}
@keyframes wsModalIn { from { opacity: 0; } to { opacity: 1; } }
.ws-modal-card {
  width: 100%; max-width: 430px;
  background: var(--panel-bg, #161b22); border: 1px solid var(--border, #30363d);
  border-radius: 12px; padding: 20px 20px 16px; box-shadow: 0 14px 44px rgba(0,0,0,0.55);
}
.ws-modal-title { font-size: 15px; font-weight: 600; color: var(--text, #e6edf3); margin-bottom: 8px; }
.ws-modal-desc  { font-size: 12.5px; color: var(--text-muted, #8b949e); line-height: 1.5; margin-bottom: 15px; }
.ws-modal-label { display: block; font-size: 10.5px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 5px; }
.ws-modal-input {
  width: 100%; box-sizing: border-box; padding: 8px 10px; font-size: 13px; font-family: var(--font);
  background: var(--panel-bg-2, #0d1117); color: var(--text); border: 1px solid var(--border); border-radius: 7px; outline: none;
}
.ws-modal-input:focus { border-color: var(--accent, #E8722C); }
.ws-modal-hint  { font-size: 11.5px; color: var(--text-dim, #6e7681); margin-top: 6px; }
.ws-modal-hint b { color: var(--text-muted); font-weight: 600; }
.ws-modal-actions { display: flex; gap: 8px; justify-content: flex-end; align-items: center; margin-top: 16px; flex-wrap: wrap; }
.ws-modal-browse {
  background: transparent; color: var(--text-muted); border: 1px solid var(--border);
  border-radius: 7px; font-size: 12.5px; padding: 7px 12px; cursor: pointer; font-family: var(--font);
}
.ws-modal-browse:hover { color: var(--text); border-color: var(--border-light, #484f58); }
.ws-modal-create {
  background: var(--accent, #E8722C); color: #fff; border: none; border-radius: 7px;
  font-size: 12.5px; font-weight: 600; padding: 7px 16px; cursor: pointer; font-family: var(--font);
}
.ws-modal-create:hover { filter: brightness(1.08); }

.mic-btn.recording {
  background: rgba(218,54,51,0.15);
  border-color: var(--error);
  color: var(--error);
  animation: pulse-mic 1s infinite;
}
@keyframes pulse-mic {
  0%, 100% { box-shadow: 0 0 0 0 rgba(218,54,51,0.4); }
  50%       { box-shadow: 0 0 0 6px rgba(218,54,51,0); }
}

.icon-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 3px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s;
}
.icon-btn:hover { background: var(--panel-bg-2); color: var(--text); }

/* ── Right panel ──────────────────────────────────────────── */
#right-panel {
  width: 240px;
  min-width: 180px;
  max-width: 400px;
  flex-shrink: 0;
  background: var(--panel-bg);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  user-select: none;
}

/* Task status box */
.task-status-box {
  margin: 0 10px 8px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  min-height: 60px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.task-status-idle {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-dim);
  font-size: 12px;
}
.task-status-active {
  font-size: 12px;
  color: var(--text);
  line-height: 1.5;
}
.task-status-active .task-step {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-bottom: 4px;
}
.task-step-icon { font-size: 13px; flex-shrink: 0; margin-top: 1px; }
.task-step-text { color: var(--text-muted); }
.task-step-text.current { color: var(--text); font-weight: 500; }
.task-step-text.done { color: var(--success); }

.task-spinner {
  width: 12px;
  height: 12px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-light);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.active-env-display {
  margin: 0 10px 10px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
}
.active-env-label { color: var(--text-dim); }
.active-env-name  {
  background: rgba(181,83,15,0.15);
  color: var(--accent-light);
  border: 1px solid rgba(181,83,15,0.3);
  padding: 1px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 500;
}

/* File list */
.files-section { flex-shrink: 0; }
.file-list {
  padding: 4px 10px 8px;
  max-height: 180px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.file-empty {
  font-size: 11px;
  color: var(--text-dim);
  text-align: center;
  padding: 10px 0;
}
.file-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 8px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all 0.15s;
}
.file-item:hover { border-color: var(--accent); }
.file-icon { font-size: 14px; flex-shrink: 0; }
.file-info { flex: 1; min-width: 0; }
.file-name {
  font-size: 11px;
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.file-meta { font-size: 10px; color: var(--text-dim); }
.file-count-badge {
  background: var(--accent);
  color: #fff;
  border-radius: 10px;
  padding: 1px 7px;
  font-size: 10px;
  font-weight: 600;
}

/* Quick actions */
.quick-actions {
  padding: 4px 10px 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.quick-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  cursor: pointer;
  font-family: var(--font);
  text-align: left;
  transition: all 0.15s;
  width: 100%;
}
.quick-btn:hover {
  background: rgba(181,83,15,0.1);
  border-color: rgba(181,83,15,0.3);
  color: var(--text);
}
.quick-btn svg { flex-shrink: 0; opacity: 0.7; }
.upload-btn { color: var(--accent-light); border-color: rgba(181,83,15,0.3); }
.upload-btn:hover { background: rgba(181,83,15,0.15); }

/* ── Modal ───────────────────────────────────────────────── */
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.6);
  backdrop-filter: blur(4px);
}
.modal-content {
  position: relative;
  background: var(--panel-bg);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  width: 480px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: var(--shadow);
  animation: modal-in 0.2s ease-out;
}
@keyframes modal-in {
  from { opacity: 0; transform: scale(0.95); }
  to   { opacity: 1; transform: scale(1); }
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--border);
}
.modal-header h2 { font-size: 15px; font-weight: 600; }
.modal-close-btn {
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 16px;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  transition: all 0.15s;
}
.modal-close-btn:hover { background: var(--panel-bg-2); color: var(--text); }
.modal-body { padding: 16px 20px 20px; }

.settings-section { margin-bottom: 20px; }
.settings-section h3 {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  margin-bottom: 10px;
}
.settings-note { font-size: 11px; color: var(--text-dim); margin-bottom: 8px; }
.settings-section label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 10px;
}
.settings-section input[type="text"] {
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--mono);
  font-size: 11px;
  padding: 6px 9px;
  outline: none;
  transition: border-color 0.15s;
}
.settings-section input[type="text"]:focus { border-color: var(--accent); }

/* Toggle switches */
.toggle-label {
  display: flex !important;
  flex-direction: row !important;
  align-items: center;
  gap: 10px !important;
  cursor: pointer;
  margin-bottom: 8px !important;
}
.toggle-label input[type="checkbox"] { display: none; }
.toggle-slider {
  width: 32px;
  height: 17px;
  background: var(--text-dim);
  border-radius: 17px;
  flex-shrink: 0;
  position: relative;
  transition: background 0.2s;
}
.toggle-slider::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 13px; height: 13px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.2s;
}
.toggle-label input:checked + .toggle-slider { background: var(--accent-light); }
.toggle-label input:checked + .toggle-slider::after { transform: translateX(15px); }

/* ── Proactive setup offer ───────────────────────────────── */
.setup-offer {
  border: 1px solid rgba(181,83,15,0.35) !important;
  background: rgba(181,83,15,0.05) !important;
}
.setup-list {
  list-style: none;
  padding: 0;
  margin: 8px 0 12px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.setup-item-critical,
.setup-item-optional {
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: 500;
}
.setup-item-critical {
  background: rgba(210,153,34,0.1);
  border: 1px solid rgba(210,153,34,0.25);
  color: var(--warning);
}
.setup-item-optional {
  background: rgba(181,83,15,0.08);
  border: 1px solid rgba(181,83,15,0.2);
  color: var(--text-muted);
}
.setup-actions {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  flex-wrap: wrap;
}
.setup-btn-primary {
  background: var(--accent);
  color: #fff;
  border: none;
  padding: 10px 22px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font);
  cursor: pointer;
  transition: background 0.15s, transform 0.1s;
  letter-spacing: 0.1px;
}
.setup-btn-primary:hover  { background: var(--accent-hover); }
.setup-btn-primary:active { transform: scale(0.97); }
.setup-btn-secondary {
  background: var(--panel-bg-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  padding: 10px 18px;
  border-radius: var(--radius);
  font-size: 13px;
  font-family: var(--font);
  cursor: pointer;
  transition: all 0.15s;
}
.setup-btn-secondary:hover { background: var(--border); color: var(--text); }
.setup-later-note {
  font-size: 12px;
  color: var(--text-muted);
  font-style: italic;
  margin-top: 8px !important;
}

/* ── Credit balance widget ──────────────────────────────── */
.credit-widget {
  margin: 0 10px 10px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.credit-balance-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 2px;
}
.credit-icon {
  color: #FF8C42;
  font-size: 14px;
  line-height: 1;
}
.credit-amount {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  flex: 1;
}
.credit-amount.low {
  color: #d29922;
}
.credit-amount.critical {
  color: #ff7b72;
}
.credit-topup-btn {
  background: var(--accent-light);
  border: none;
  color: #fff;
  font-size: 16px;
  font-weight: 700;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s;
  line-height: 1;
  padding: 0;
}
.credit-topup-btn:hover {
  background: var(--accent-hover);
}
.credit-label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

/* ── Utility ─────────────────────────────────────────────── */
.hidden { display: none !important; }

/* Selection highlight */
::selection { background: rgba(181,83,15,0.3); color: var(--text); }

/* Right panel (System Capacity / Current Task / Loaded Files / Quick Actions)
   removed from the UI — embedded-lab redesign no longer needs it. Hidden rather
   than deleted so the renderer's references to its elements stay valid. The
   centre/notebook expands to fill the freed space. */
#right-panel,
#resize-right { display: none !important; }

/* ── Lab split layout: chat (left) + notebook (right) ──────────────────────── */
.lab-split {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
  width: 100%;
}
.chat-column {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 0 0 42%;
  max-width: 600px;
  min-height: 0;
  position: relative;   /* positioning context for .chat-drop-overlay */
  /* Explicit, matching .notebook-column's own explicit rule — without this
     the chat column only ever gets its background via ancestor inheritance,
     which can render a step lighter/darker than its sibling depending on
     paint order. Keeping both explicit guarantees they're identical. */
  background: var(--bg);
}

/* Shown while dragging a file over the chat column — "drop to attach" affordance. */
.chat-drop-overlay {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(13, 17, 23, 0.82);
  border: 2px dashed var(--accent, #E8722C);
  border-radius: 8px;
  margin: 6px;
  pointer-events: none;   /* the drop event is handled by .chat-column itself */
}
.chat-drop-hint {
  font-size: 15px;
  font-weight: 600;
  color: var(--accent-light, #FF9D5C);
  padding: 10px 18px;
  border-radius: 8px;
  background: rgba(232, 114, 44, 0.12);
}
/* Draggable divider between the chat column and the notebook column. */
.lab-resize {
  flex: 0 0 5px;
  align-self: stretch;     /* full column height inside the flex row */
  height: auto;
}
/* Faint always-on seam (brightens on hover/drag via the base .resize-handle rule). */
.lab-resize::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--border);
}
.notebook-column {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  background: var(--bg);
}
.notebook-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 53px;
  flex-shrink: 0;          /* never let notebook content squeeze the header up */
  box-sizing: border-box;
  padding: 0 18px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-bg);
}
.notebook-title { font-size: 13px; font-weight: 600; color: var(--text); }

/* Notebook tabs: Lesson ⇄ My Workspace */
.nb-title { font-family: var(--font); font-size: 13px; font-weight: 600; color: var(--text); padding: 4px 2px 6px; }

/* Settings: action button rows + text inputs (Galaxy account etc.) */
.settings-actions { display: flex; align-items: center; gap: 10px; }
.settings-input {
  width: 100%;
  height: 32px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font);
  font-size: 12.5px;
  padding: 0 10px;
  box-sizing: border-box;
}
.settings-input:focus { outline: none; border-color: var(--accent); }

/* ── In-app web panel (Phase 4) ───────────────────────────────── */
.web-column {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  border-left: 1px solid var(--border);
  background: var(--bg);
  overflow: hidden;
}
.web-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 8px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.web-nav-btn {
  flex-shrink: 0;
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: var(--radius-sm);
  color: var(--text-muted); font-size: 16px; cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.web-nav-btn:hover { background: var(--panel-bg-2); color: var(--text); }
.web-url {
  flex: 1; min-width: 0; height: 28px;
  background: var(--panel-bg-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text);
  font-family: var(--font); font-size: 12px; padding: 0 10px;
}
.web-url:focus { outline: none; border-color: var(--accent); }
.web-view-wrap { position: relative; flex: 1; min-height: 0; }
.web-view-wrap webview { width: 100%; height: 100%; border: none; background: #fff; }
.web-loading {
  position: absolute; top: 8px; right: 12px;
  font-size: 11px; color: var(--text-muted);
  background: var(--panel-bg); padding: 2px 8px; border-radius: 10px; border: 1px solid var(--border);
}
.web-loading.web-load-error {
  color: var(--error); border-color: var(--error); font-weight: 500;
}
/* When the web panel is open, it TAKES OVER the notebook's space (notebook +
   its resize handle hide); chat stays so the agent can narrate. The web gets
   the full remaining width so wide sites (Galaxy) render properly. */
.lab-split.web-open .notebook-column,
.lab-split.web-open #resize-lab { display: none; }
/* Web-panel maximize — hides chat too (notebook is already hidden by .web-open
   above), so the panel fills the whole width. Off by default, one click to
   expand — matches the notebook's own maximize behavior. */
.lab-split.web-max .chat-column { display: none; }
.nb-tabs { display: flex; gap: 4px; }
.nb-tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--text-muted); font-family: var(--font); font-size: 13px; font-weight: 600;
  padding: 4px 10px 6px; cursor: pointer; transition: color 0.15s, border-color 0.15s;
}
.nb-tab:hover { color: var(--text); }
.nb-tab.active { color: var(--text); border-bottom-color: var(--accent-light); }
.header-btn.icon-only { padding: 5px 7px; }
/* Plan / Results carry counts, so they get a word next to the icon (the others stay icon-only). */
.header-btn.has-label { padding: 5px 9px; }
.header-btn .hb-label { font-size: 11.5px; font-weight: 600; letter-spacing: .01em; }
/* A table wider than the panel: fade the right edge so it is clear it continues. */
.nb-html.nb-wide { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); }
.nb-html.nb-wide:hover { -webkit-mask-image: none; mask-image: none; }
.nb-expand-btn { display: inline-block; margin: 2px 0 6px; padding: 3px 10px; font-size: 11.5px; font-weight: 600; color: var(--accent); background: transparent; border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
.nb-expand-btn:hover { border-color: var(--accent); }
#artifacts-btn, #curriculum-btn { position: relative; }
.artifacts-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  font-size: 9.5px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
  border: 1.5px solid var(--panel-bg);
  pointer-events: none;
}
.nb-view { flex: 1 1 auto; min-height: 0; }

/* Maximize: hide the chat column so the notebook fills the centre panel. */
.lab-split.nb-max .chat-column,
.lab-split.nb-max .lab-resize { display: none; }

/* Notebook panel starts CLOSED by default — a chat-first cold start (like
   Codex's own default-2-column layout), instead of showing two mostly-empty
   panels on a brand-new session. A persistent toggle in the chat header opens
   it manually anytime; it also auto-opens itself the moment this session's
   first real code cell appears (see revealNotebookPanel() in renderer.js).
   Reopening an already-populated saved session (Recents) skips straight to
   the open state — no collapse-then-reveal flicker.

   With the notebook gone there's no sibling column left to share width with,
   so the chat column drops its normal 42%/600px cap and fills the ENTIRE
   panel — full-bleed, scrollbar flush against the window's right edge —
   instead of sitting centered in its own narrower strip. */
.lab-split.notebook-hidden .notebook-column,
.lab-split.notebook-hidden .lab-resize { display: none; }
.lab-split.notebook-hidden .chat-column {
  flex: 1 1 auto;
  max-width: none;
  width: 100%;
}
/* The chat-header's own toggle button is only for REOPENING a hidden
   notebook — once the panel is showing, its own header already carries the
   matching "hide" icon (#hide-nb-btn, right next to maximize), so leaving
   this one visible too would just be two controls doing overlapping jobs at
   once. It comes back the instant the notebook is hidden again, since that's
   the one moment there's no notebook-header left to click on instead. */
.lab-split:not(.notebook-hidden) #notebook-toggle-btn { display: none; }
/* The "Notebook" button only appears once work has started: on a fresh new-chat page (the welcome card is the only
   message) there is nothing to open, so it stays hidden; it shows after the first message is sent and on reopened lessons. */
.chat-column:has(#chat-messages > .welcome-message:only-child) #notebook-toggle-btn { display: none; }

/* A light one-shot reveal so the panel appearing reads as a deliberate moment
   rather than a flat jump-cut. Purely a content fade/slide — never animates
   layout width, so it stays cheap and can't fight the flex sizing above. */
@keyframes nbReveal {
  from { opacity: 0; transform: translateX(6px); }
  to   { opacity: 1; transform: translateX(0); }
}
.lab-split:not(.notebook-hidden) .notebook-column {
  animation: nbReveal 0.3s ease;
}

.kernel-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-muted);
  background: var(--panel-bg-2);
}
.kernel-badge[data-state="starting"] { color: var(--warning); border-color: rgba(210,153,34,0.4); }
.kernel-badge[data-state="ready"]    { color: var(--accent-light); border-color: rgba(232,114,44,0.4); background: rgba(232,114,44,0.08); }
.kernel-badge[data-state="busy"]     { color: var(--info); border-color: rgba(56,139,253,0.4); }

.notebook-cells {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 18px;
}
.notebook-empty {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: var(--text-muted);
  padding: 24px;
}
.notebook-empty-icon { font-size: 40px; opacity: 0.5; margin-bottom: 14px; }
.notebook-empty-title { font-size: 14px; font-weight: 600; color: var(--text); margin: 0 0 6px; }
.notebook-empty-sub { font-size: 12.5px; line-height: 1.6; max-width: 360px; margin: 0; }

/* ── Markdown tables + math (rendered in chat / markdown blocks) ───────────── */
/* Wrapper scrolls horizontally so a wide table keeps its natural column widths
   instead of being squeezed to fit the chat bubble (which was distorting/wrapping
   cell text into an unreadable narrow mess). */
.md-table-wrap {
  /* A short/narrow table (2-3 columns) just fits — no scrollbar shows. A genuinely wide
     table (many columns, or a research-workflow table with several prose columns) is
     allowed to size itself naturally and scrolls HORIZONTALLY inside this wrapper rather
     than being crushed into the chat column's width. `overflow:auto` (not overflow-x-only)
     also gives a tall table (many rows) a bounded, scrollable height, matching the same
     pattern already proven for notebook DataFrame tables (.nb-html below) — that's what
     lets the sticky header actually have a real scrolling context to stick within.
     Fixed 2026-08: a table used to be forced to width:100% with aggressive cell wrapping,
     which crammed a 4-column workflow table into unreadably narrow columns with no way to
     scroll — the tradeoff is now real breathing room, with the wrapper (not the whole
     chat/page) absorbing any genuine overflow in either direction. */
  overflow: auto;
  max-height: 440px;
  margin: 12px 0;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.md-table {
  border-collapse: collapse;
  width: max-content;   /* size to its own content ... */
  min-width: 100%;      /* ... but never narrower than the chat column, so a short table still fills it */
  font-size: 13px;
  background: var(--panel-bg);
}
.md-table th, .md-table td {
  border-right: 1px solid rgba(255,255,255,0.06);   /* light gridlines, matches the notebook table */
  border-bottom: 1px solid rgba(255,255,255,0.06);
  padding: 9px 13px;
  text-align: left;
  vertical-align: top;
  overflow-wrap: anywhere;   /* long words/sentences still WRAP inside a cell */
  word-break: break-word;
  line-height: 1.45;
  max-width: 320px;   /* caps how wide ONE column can grow before its own prose wraps, so width:max-content above doesn't stretch a single long-text column indefinitely */
}
.md-table th:last-child, .md-table td:last-child { border-right: none; }
.md-table tbody tr:last-child td { border-bottom: none; }
.md-table thead th {
  position: sticky; top: 0; z-index: 1;   /* stays visible while a tall table scrolls */
  background: var(--panel-bg-2);
  color: var(--text);
  font-weight: 600;
  border-bottom: 1px solid var(--border-light, #3D444D);
}
.md-table tbody tr:nth-child(even) td { background: rgba(255,255,255,0.02); }
.md-table tbody td { color: var(--text-muted); }

.md-math {
  font-family: var(--mono);
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
}
div.md-math {                 /* display math — its own centered block */
  padding: 10px 14px;
  margin: 10px 0;
  text-align: center;
  font-size: 14px;
  overflow-x: auto;
}
span.md-math { padding: 1px 5px; font-size: 13px; }

/* ── In-app notebook cells (embedded kernel) ───────────────────────────────── */
.nb-markdown {
  margin: 10px 0 6px;
  padding: 4px 2px;
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
}
.nb-markdown p { margin: 4px 0; }

/* Section heading — titles a new step/module of the analysis above its cell */
.nb-section-title {
  margin: 22px 0 8px;
  padding: 0 0 6px;
  border-bottom: 2px solid var(--accent-light);
  color: var(--text);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.2px;
}
.nb-section-title::before {
  content: "▸ ";
  color: var(--accent-light);
}
.nb-section-title:first-child { margin-top: 4px; }

.nb-cell {
  display: flex;
  gap: 6px;
  margin: 10px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel-bg);
  overflow: hidden;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.nb-cell.nb-running { border-color: var(--accent-light); box-shadow: 0 0 0 1px rgba(232,114,44,0.25); position: relative; }
.nb-cell.nb-done    { border-color: var(--border); }
.nb-cell.nb-errored { border-color: rgba(218,54,51,0.5); }

/* Running indicator — an indeterminate progress bar slides across the bottom of
   the cell while the kernel is executing it, so it's obvious work is happening. */
.nb-cell.nb-running::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2.5px;
  background: linear-gradient(90deg, transparent, var(--accent-light), transparent);
  background-size: 35% 100%;
  background-repeat: no-repeat;
  animation: nb-run-slide 1.1s linear infinite;
}
@keyframes nb-run-slide {
  0%   { background-position: -35% 0; }
  100% { background-position: 135% 0; }
}
/* Pulse the [*] gutter so it reads as "live". */
.nb-cell.nb-running .nb-gutter { animation: nb-run-pulse 1s ease-in-out infinite; }
@keyframes nb-run-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }

/* Live progress bar + elapsed-time / ETA on a running (esp. long full-data) cell */
.nb-run-status {
  padding: 6px 12px 8px;
  border-top: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}
.nb-run-status.long { background: rgba(232,114,44,0.06); }
.nb-run-label { font-size: 11.5px; color: var(--text-muted); margin-bottom: 5px; }
.nb-run-status.long .nb-run-label { color: var(--accent-light); }
.nb-run-track {
  height: 5px;
  background: var(--panel-bg-2);
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}
.nb-run-fill {
  height: 100%;
  width: 0;
  background: var(--accent-light);
  border-radius: 3px;
  transition: width 0.6s linear;
}
.nb-run-fill.over { background: var(--warning); }
/* Unknown-duration cells: a sliding indeterminate block instead of a fill level */
.nb-run-track.indeterminate .nb-run-fill {
  width: 35%;
  position: absolute;
  transition: none;
  animation: nb-indet 1.2s ease-in-out infinite;
}
@keyframes nb-indet { 0% { left: -35%; } 100% { left: 100%; } }

/* Free coding mode — a cell the user types into and runs themselves */
.nb-user-cell { border-color: rgba(56,139,253,0.4); }
.nb-user-bar { display: flex; align-items: center; gap: 8px; padding: 7px 10px 0; }
.nb-user-lang {
  background: var(--panel-bg-2); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 11px; padding: 2px 6px; font-family: var(--font); cursor: pointer;
}
.nb-user-run {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--accent-light); color: #fff; border: none; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 600; padding: 3px 12px; cursor: pointer; font-family: var(--font);
  transition: background 0.15s;
}
.nb-user-run:hover { background: var(--accent-hover); }
.nb-user-run:disabled { opacity: 0.5; cursor: default; }
.nb-user-hint { font-size: 10px; color: var(--text-dim); flex: 1; }
.nb-user-del {
  background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 13px;
  padding: 2px 6px; border-radius: 3px;
}
.nb-user-del:hover { color: var(--error); background: rgba(218,54,51,0.12); }
.nb-user-code {
  width: 100%; resize: none; border: none; outline: none;
  background: transparent; color: var(--text);
  font-family: var(--font-mono, 'JetBrains Mono', Consolas, monospace);
  font-size: 13px; line-height: 1.55; padding: 8px 12px 10px; min-height: 38px;
}
.nb-user-code::placeholder { color: var(--text-dim); }

/* ── Practice card ("Your turn ✍️") — the learner's own cell, visually distinct ── */
.nb-practice-cell {
  border-color: rgba(232,114,44,0.55);
  box-shadow: 0 0 0 1px rgba(232,114,44,0.18) inset;
  background: rgba(232,114,44,0.04);
}
.nb-practice-cell.nb-practice-submitted { opacity: 0.85; }
.nb-practice-gutter { color: var(--accent-hover) !important; font-size: 14px; }
.nb-practice-head { padding: 9px 12px 4px; }
.nb-practice-badge {
  display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .3px;
  color: #fff; background: var(--accent-light); padding: 2px 9px; border-radius: 999px;
}
.nb-practice-prompt { font-size: 13px; color: var(--text); line-height: 1.55; margin-top: 7px; }
.nb-practice-bar { display: flex; align-items: center; gap: 8px; padding: 4px 10px 8px; flex-wrap: wrap; }
.nb-practice-check {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--success, #2ea043); color: #fff; border: none; border-radius: var(--radius-sm);
  font-size: 12px; font-weight: 600; padding: 3px 12px; cursor: pointer; font-family: var(--font);
  transition: filter 0.15s;
}
.nb-practice-check:hover { filter: brightness(1.1); }
.nb-practice-check:disabled { opacity: 0.5; cursor: default; }
.nb-practice-stuck {
  background: var(--panel-bg-2); color: var(--text-muted); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 12px; padding: 3px 12px; cursor: pointer; font-family: var(--font);
}
.nb-practice-stuck:hover { color: var(--text); border-color: var(--border-light); }
.nb-practice-stuck:disabled { opacity: 0.5; cursor: default; }
.nb-practice-skip {
  background: transparent; color: var(--text-muted); border: 1px solid var(--border);
  border-radius: var(--radius-sm); font-size: 12px; padding: 3px 12px; cursor: pointer; font-family: var(--font);
  margin-left: auto;
}
.nb-practice-skip:hover { color: var(--text); border-color: var(--border-light); }
.nb-practice-skip:disabled { opacity: 0.5; cursor: default; }
.nb-practice-cell.nb-practice-skipped { opacity: 0.6; }

.nb-gutter {
  flex: 0 0 46px;
  padding: 12px 6px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-dim);
  text-align: right;
  white-space: nowrap;          /* never wrap "[n]" onto a second line */
  user-select: none;
  border-right: 1px solid var(--border);
  background: var(--panel-bg-2);
}
.nb-cell.nb-running .nb-gutter { color: var(--accent-light); }

.nb-body { flex: 1 1 auto; min-width: 0; position: relative; }

/* A real header STRIP for the language badge + collapse/copy controls — always
   present, sharing .nb-cell's own background (var(--panel-bg)) so it reads as
   the top edge of one continuous white/panel block when the code is showing.
   The point of giving this its own element rather than absolutely positioning
   the controls over .nb-body: when the code collapses (.nb-code display:none),
   this strip is UNAFFECTED and keeps rendering — a proper small bar holding
   the controls — instead of them floating loose over whatever is now on top
   (the results). */
.nb-code-header {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px 8px;
  background: var(--panel-bg);
  min-height: 24px;
}
.nb-code-header-spacer { flex: 1 1 auto; }

/* Language tag (Python / R) — a normal flex child of the header strip now,
   not absolutely positioned over the code. */
.nb-lang-tag {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.2px;
  padding: 1px 5px;
  border-radius: 6px;
  user-select: none;
}
.nb-lang-python { background: rgba(53,114,165,0.18); color: #6ea8d8; }
.nb-lang-r      { background: rgba(117,99,216,0.20); color: #a99cf0; }
/* Tint the gutter so R cells read as a distinct language at a glance. */
.nb-cell.nb-cell-r .nb-gutter { color: #a99cf0; }
.nb-cell.nb-cell-r { border-left: 2px solid rgba(117,99,216,0.45); }

.nb-code {
  margin: 0;
  padding: 2px 14px 12px 8px;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.45;
  color: var(--text);
  white-space: pre;            /* like a real editor: lines never wrap, long ones scroll sideways */
  overflow-x: auto;
  scrollbar-width: thin;
  counter-reset: nbln;
  tab-size: 4;
  /* white code surface (same as the cell) + "scroll shadows": a faint shadow appears on a side only while there is more code that way */
  background:
    linear-gradient(to right, var(--panel-bg) 30%, transparent) left center / 34px 100% no-repeat local,
    linear-gradient(to left,  var(--panel-bg) 30%, transparent) right center / 34px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%,    rgba(0,0,0,0.14), transparent) left center / 10px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,0.14), transparent) right center / 10px 100% no-repeat scroll,
    var(--panel-bg);
}
/* Line numbers + syntax colours (renderer/code_highlight.js). The numbers are CSS counters, so they are never part of the
   code text (copy, the typing animation and export read the plain text). */
.nb-code .nb-ln::before {
  counter-increment: nbln; content: counter(nbln);
  display: inline-block; min-width: 2em; margin-right: 0.9em; text-align: right;
  color: var(--text-muted); opacity: 0.55; user-select: none; -webkit-user-select: none;
}
.nb-code .tok-k { color: var(--tok-kw); }
.nb-code .tok-s { color: var(--tok-str); }
.nb-code .tok-n { color: var(--tok-num); }
.nb-code .tok-b { color: var(--tok-bi); }
.nb-code .tok-f { color: var(--tok-bi); font-weight: 600; }
.nb-code .tok-d { color: var(--tok-dec); }
.nb-code .tok-c { color: var(--text-muted); font-style: italic; opacity: 0.7; }
:root, :root[data-theme="dark"] { --tok-kw: #d2a8ff; --tok-str: #7ee787; --tok-num: #ffa657; --tok-bi: #79c0ff; --tok-dec: #e3b341; }
:root[data-theme="light"] { --tok-kw: #8250df; --tok-str: #1a7f37; --tok-num: #b35900; --tok-bi: #0969da; --tok-dec: #9a6700; }
@media (prefers-color-scheme: light) { :root[data-theme="system"] { --tok-kw: #8250df; --tok-str: #1a7f37; --tok-num: #b35900; --tok-bi: #0969da; --tok-dec: #9a6700; } }
.nb-code code { font-family: inherit; background: none; padding: 0; }
/* `#` comment LINES read as explanation, not executable code — dimmed + italic,
   matching the convention every real code editor uses, so a learner can tell at
   a glance which lines run and which lines are Omi's explanation. */
.nb-code-comment { color: var(--text-muted); font-style: italic; opacity: 0.6; }

/* The separate "why this code" panel (nb-explain) was removed — nb_teach_code's
   explanation now lives as `#` comments directly inside the code cell itself
   (see agent/orchestrator.js buildTeachComment), so there's no more duplicate
   panel to style here. */

.nb-output {
  border-top: 1px solid var(--border);
  padding: 10px 14px;
  background: var(--bg);
}
/* --bg vs --panel-bg (the code area, inherited from .nb-cell) are far apart in
   dark mode (#0D1117 vs #161B22 — real separation) but nearly identical in
   light mode (#FAFAF7 vs #FFFFFF), so the output panel stops reading as
   distinct from the code above it. Light-mode-only override — dark mode is
   untouched, it already works. --panel-bg-2 is a real, visibly different tone
   (same one already used for the notebook gutter and manuscript card). */
:root[data-theme="light"] .nb-output { background: var(--panel-bg-2); }
@media (prefers-color-scheme: light) { :root[data-theme="system"] .nb-output { background: var(--panel-bg-2); } }
.nb-output:empty { display: none; }
.nb-output.nb-output-blank { display: none; }
/* Result area: a small label, and printed text set exactly like the code above (same font/size, no mid-item wrapping, thin sideways scroll). */
.nb-output::before { content: "Output"; display: block; margin-bottom: 6px; font-size: 10.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); opacity: 0.75; user-select: none; -webkit-user-select: none; }
.nb-output .nb-stream { font-size: 13px; line-height: 1.45; white-space: pre; overflow-x: auto; scrollbar-width: thin; font-weight: 400; color: var(--text); color: color-mix(in srgb, var(--text) 78%, var(--panel-bg-2)); }

.nb-stream {
  margin: 0;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}
.nb-figure { position: relative; display: inline-block; margin: 6px 0; max-width: 100%; }
.nb-image {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  background: #fff;
  cursor: zoom-in;   /* click to open the interactive lightbox */
}
.nb-save-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 4px 9px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
  background: rgba(13,17,23,0.82);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.nb-figure:hover .nb-save-btn { opacity: 1; }
.nb-save-btn:hover { background: var(--accent); border-color: var(--accent-light); }

/* ── Figure lightbox (click a plot → zoom/pan + PNG/PDF download) ───────────── */
.fig-lightbox {
  position: fixed;
  inset: 0;
  z-index: 4000;
  background: rgba(0,0,0,0.85);
  display: flex;
  flex-direction: column;
  /* The toolbar overlaps the draggable titlebar strip (#titlebar is app-region:drag).
     Without this, Windows captures clicks here as window-drags and the buttons never
     fire — so force the whole lightbox to be a clickable (no-drag) surface. */
  -webkit-app-region: no-drag;
}
.fig-lb-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  /* Reserve the top-right so buttons never sit under the OS window controls. */
  padding-right: 160px;
  background: var(--panel-bg);
  border-bottom: 1px solid var(--border);
  -webkit-app-region: no-drag;   /* clickable even over the draggable titlebar strip */
}
.fig-lb-toolbar button { -webkit-app-region: no-drag; }
.fig-lb-div { width: 1px; height: 20px; background: var(--border-light); margin: 0 5px; flex-shrink: 0; }
.fig-lb-toolbar button {
  background: var(--panel-bg-2);
  color: var(--text);
  border: 1px solid var(--border-light);
  border-radius: 6px;
  padding: 5px 11px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.fig-lb-toolbar button:hover { background: var(--accent); color: #fff; border-color: var(--accent-light); }
.fig-lb-stage {
  flex: 1;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
}
.fig-lb-stage.grabbing { cursor: grabbing; }
.fig-lb-img {
  max-width: 92%;
  max-height: 92%;
  transform-origin: center center;
  user-select: none;
  -webkit-user-drag: none;
  background: #fff;
  box-shadow: 0 8px 40px rgba(0,0,0,0.5);
  will-change: transform;
}
/* Title label + Prev/Next in the artifact viewer's toolbar (extends .fig-lb-*
   additively — the plain single-plot lightbox never renders these). */
.fig-lb-title { font-size: 12.5px; color: var(--text-muted); padding: 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A table artifact inside the lightbox — reuses .nb-html's sticky-header /
   gridline / zebra-stripe styling, just given far more room to breathe than
   its cramped in-notebook 440px cap. */
/* BUG FIX: `max-height:none` (no replacement bound) + a flex item's default
   min-height:auto let a big table grow past .fig-lb-stage's own edges instead
   of being clipped/scrolled — it visually spilled out over the rest of the
   app. Give it a REAL bound (relative to the viewport, not the cramped
   in-notebook 440px) and let flex actually respect it. */
.fig-lb-table-stage { align-items: flex-start; justify-content: center; padding: 24px; overflow: auto; min-height: 0; }
.fig-lb-table-wrap.nb-html { max-height: calc(100vh - 140px); width: 92%; max-width: 1100px; font-size: 13.5px; box-shadow: 0 8px 40px rgba(0,0,0,0.5); }
/* Same sizing discipline as .fig-lb-table-wrap, for a plain-text file preview
   (a disk .txt/.fasta/.vcf/etc opened from the Artifacts gallery). */
.fig-lb-text-wrap.nb-stream {
  max-height: calc(100vh - 140px);
  width: 92%;
  max-width: 1100px;
  overflow: auto;
  background: var(--panel-bg-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 20px;
  box-shadow: 0 8px 40px rgba(0,0,0,0.5);
  font-size: 13px;
}
.fig-lb-truncated { color: var(--warning, #d29922); font-weight: 600; }

/* ── ARTIFACTS GALLERY ─────────────────────────────────────────────────── */
.artifacts-gallery {
  position: fixed;
  inset: 0;
  z-index: 4000;
  background: rgba(0,0,0,0.85);
  display: flex;
  flex-direction: column;
}
.artifacts-gallery-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  padding-right: 160px;   /* clear the OS window controls, same as .fig-lb-toolbar */
  background: var(--panel-bg);
  border-bottom: 1px solid var(--border);
  -webkit-app-region: no-drag;
}
.artifacts-gallery-title { font-size: 13px; font-weight: 700; color: var(--text); padding: 0 4px; }
.artifacts-gallery-toolbar button {
  background: var(--panel-bg-2);
  color: var(--text);
  border: 1px solid var(--border-light);
  border-radius: 6px;
  padding: 5px 11px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
  -webkit-app-region: no-drag;
}
.artifacts-gallery-toolbar button:hover { background: var(--accent); color: #fff; border-color: var(--accent-light); }
.artifacts-filter-row {
  display: flex;
  flex-wrap: nowrap;          /* ONE row only — extra file types scroll sideways instead of stacking */
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  gap: 6px;
  padding: 10px 28px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-bg);
  -webkit-app-region: no-drag;
}
.artifacts-filter-chip {
  flex: 0 0 auto;            /* never squeeze or wrap a chip */
  white-space: nowrap;
  background: var(--panel-bg-2);
  color: var(--text-muted);
  border: 1px solid var(--border-light);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
  -webkit-app-region: no-drag;
}
.artifacts-filter-chip:hover { color: var(--text); border-color: var(--accent-light); }
.artifacts-filter-chip.active { background: var(--accent); color: #fff; border-color: var(--accent-light); }
.artifacts-filter-empty { text-align: center; font-size: 13px; color: var(--text-muted); margin: 40px 0 0; }
.artifacts-gallery-body { flex: 1; overflow-y: auto; padding: 22px 28px 40px; }
/* Top-level source separator — "In this lesson" vs "In your project folder".
   Bolder/larger than .artifacts-section-label (which groups by code section
   WITHIN the "in this lesson" source), so the two nest visually. */
.artifacts-source-label {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  margin: 30px 0 4px;
}
.artifacts-source-label:first-child { margin-top: 0; }
.artifacts-section-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 18px 0 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.artifacts-source-label + .artifacts-section-label { margin-top: 14px; }
.artifacts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
}
.artifact-tile {
  background: var(--panel-bg-2);
  border: 1px solid var(--border-light);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.12s, transform 0.12s;
}
.artifact-tile:hover, .artifact-tile:focus-visible { border-color: var(--accent-light); transform: translateY(-1px); outline: none; }
.artifact-tile-thumb {
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  overflow: hidden;
}
.artifact-tile-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
.artifact-tile-thumb.artifact-tile-table { background: var(--panel-bg); }
.artifact-tile-table-icon { font-size: 34px; color: var(--text-muted); }
.artifact-tile-label {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-top: 1px solid var(--border);
}
.artifact-tile-size { display: block; font-size: 10.5px; color: var(--text-muted); margin-top: 1px; }
.artifacts-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; text-align: center; padding: 40px; }
.artifacts-empty-icon { font-size: 40px; margin-bottom: 10px; opacity: 0.6; }
.artifacts-empty-title { font-size: 15px; font-weight: 600; color: var(--text); margin: 0 0 4px; }
.artifacts-empty-sub { font-size: 13px; color: var(--text-muted); margin: 0; max-width: 360px; }

.notebook-header-actions { display: flex; align-items: center; gap: 10px; }
#export-notebook-btn.saved { color: var(--accent-light); border-color: rgba(232,114,44,0.4); }
.nb-html {
  overflow: auto;                       /* horizontal + vertical scrollbars for big tables */
  max-height: 440px;
  font-size: 12.5px;
  color: var(--text);
  background: var(--panel-bg);          /* solid — must not rely on inheriting an ancestor's
                                            background; the artifact lightbox has none */
  border: 1px solid var(--border);
  border-radius: 8px;
  margin: 4px 0 2px;
}
.nb-html table {
  border-collapse: collapse;
  width: max-content;                   /* size to content so wide tables scroll… */
  min-width: 100%;                      /* …but at least fill the cell */
  font-variant-numeric: tabular-nums;   /* digits align in columns */
}
.nb-html thead th {                     /* sticky header row while scrolling */
  position: sticky; top: 0; z-index: 2;
  background: var(--panel-bg-2, #1C2128);
  font-weight: 600;
  border-bottom: 1px solid var(--border-light, #3D444D);
}
.nb-html thead th:first-child {         /* the corner cell above the sticky index column —
                                            needs to freeze BOTH ways at once (top AND left),
                                            not just inherit the header row's top-only stick,
                                            or it slides away under a later column on scroll */
  left: 0; z-index: 3;
}
.nb-html th, .nb-html td {
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  padding: 5px 12px;
  white-space: nowrap;
  text-align: left;
}
.nb-html tbody tr th:first-child {      /* the DataFrame index column: sticky + tinted */
  position: sticky; left: 0; z-index: 1;
  background: var(--panel-bg, #161B22);
  color: var(--text-muted);
  font-weight: 500;
  border-right: 1px solid var(--border);
}
.nb-html tbody tr:nth-child(even) td { background: rgba(255, 255, 255, 0.022); }
:root[data-theme="light"] .nb-html tbody tr:nth-child(even) td { background: rgba(35, 32, 26, 0.03); }
@media (prefers-color-scheme: light) { :root[data-theme="system"] .nb-html tbody tr:nth-child(even) td { background: rgba(35, 32, 26, 0.03); } }
.nb-html tbody tr:hover td { background: rgba(88, 166, 255, 0.07); }

.nb-error {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
  color: #ff7b72;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ── Scroll-to-latest floating buttons (chat + notebook) ───────────────────── */
.chat-column, .notebook-column { position: relative; }
.scroll-bottom-btn {
  position: absolute;
  left: 50%;
  bottom: 88px;                       /* above the chat input bar */
  transform: translateX(-50%) translateY(6px);
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--panel-bg-2);
  color: var(--text, #c9d1d9);
  border: 1px solid var(--border, #30363D);
  box-shadow: 0 4px 14px rgba(0,0,0,0.45);
  cursor: pointer;
  opacity: 0; pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
  z-index: 30;
}
.scroll-bottom-btn.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
.scroll-bottom-btn:hover { border-color: var(--accent, #E8722C); color: var(--accent-light, #FF9D5C); }
.scroll-bottom-btn.nb-scroll { bottom: 20px; }     /* notebook column has no input bar */

/* ── Copy + retry buttons: chat messages · copy buttons: code cells ───────── */
/* .msg-copy-btn / .msg-retry-btn are now plain inline-flex row items inside
   .message-header (see "Message content" above) — no absolute positioning,
   since the header is a normal row below the text now, not sharing a line
   with an author name it needs to float clear of. Visibility is governed
   entirely by the PARENT .message-header's opacity (hover-revealed), so
   these two buttons don't need their own opacity transition at all — only
   the color/background hover-feedback + the "just copied" flash state. */
.msg-copy-btn, .msg-retry-btn {
  background: none; border: none; cursor: pointer; padding: 3px;
  border-radius: 5px; color: var(--text-muted, #8b949e);
  transition: color .12s, background .12s;
  display: inline-flex; align-items: center; justify-content: center;
}
.msg-copy-btn:hover, .msg-retry-btn:hover {
  color: var(--text, #c9d1d9); background: rgba(255,255,255,0.07);
}
.msg-copy-btn.copied { color: var(--success, #3FB950); }
/* .cell-copy-btn (notebook code cells) is a DIFFERENT context — a normal flex
   child of .nb-code-header now (not absolutely positioned), with its own
   independent hover-reveal (.nb-body:hover — the header strip is still a
   descendant of .nb-body, so this still works), left otherwise untouched by
   the chat-message change above. */
.cell-copy-btn {
  background: none; border: none; cursor: pointer; padding: 3px;
  border-radius: 5px; color: var(--text-muted, #8b949e);
  opacity: 0; transition: opacity .12s, color .12s, background .12s;
  display: inline-flex; align-items: center; justify-content: center;
}
.nb-body:hover .cell-copy-btn { opacity: .55; }
.cell-copy-btn:hover { opacity: 1; color: var(--text, #c9d1d9); background: rgba(255,255,255,0.07); }
.cell-copy-btn.copied { color: var(--success, #3FB950); opacity: 1; }

/* Edit / Run buttons in a finished cell's header strip (renderer/cell_edit.js). */
.cell-edit-btn, .cell-run-btn, .cell-ask-btn {
  background: none; border: 1px solid transparent; cursor: pointer; padding: 1px 8px;
  border-radius: 5px; font-size: 11.5px; color: var(--text-muted, #8b949e);
  opacity: 0; transition: opacity .12s, color .12s, background .12s;
}
.nb-body:hover .cell-edit-btn, .nb-body:hover .cell-run-btn, .nb-body:hover .cell-ask-btn, .nb-cell.nb-editing .cell-edit-btn, .nb-cell.nb-editing .cell-run-btn, .nb-cell.nb-asking .cell-ask-btn { opacity: .85; }
.cell-edit-btn:hover, .cell-run-btn:hover, .cell-ask-btn:hover { opacity: 1; color: var(--accent-light); background: rgba(232,114,44,0.10); }
.cell-edit-btn:disabled, .cell-run-btn:disabled, .cell-ask-btn:disabled { opacity: .35; cursor: default; }
.nb-cell.nb-running .cell-edit-btn, .nb-cell.nb-running .cell-run-btn, .nb-cell.nb-running .cell-ask-btn { visibility: hidden; }
.nb-ask-box { display: flex; gap: 6px; padding: 6px 10px; background: var(--panel-bg); border-top: 1px dashed var(--accent-light); }
.nb-ask-input { flex: 1; min-width: 0; font-size: 12.5px; padding: 4px 8px; border: 1px solid var(--border, #d0d7de); border-radius: 6px; background: transparent; color: inherit; }
.nb-ask-send { font-size: 12px; padding: 3px 12px; border: 0; border-radius: 6px; background: var(--accent, #e8722c); color: #fff; cursor: pointer; }
/* In-place code editing: an invisible textarea sits exactly over the coloured code (position/size set by renderer/cell_edit.js);
   the coloured layer underneath is re-drawn on every keystroke, so the learner types "inside" the coloured code. */
.nb-code.nb-overlaying { position: relative; }
.nb-overlay-ta {
  position: absolute; margin: 0; padding: 0; border: 0; outline: none; resize: none; overflow: hidden;
  background: transparent; color: transparent; caret-color: var(--text);
  font: inherit; line-height: inherit; letter-spacing: inherit; tab-size: 4; white-space: pre;
}
.nb-overlay-ta::selection { background: rgba(232,114,44,0.28); }
.nb-cell.nb-editing .nb-code { box-shadow: inset 0 0 0 1px rgba(232,114,44,0.35); }
.nb-cell-editor { background: var(--panel-bg); border-top: 1px dashed var(--accent-light); font-size: 13px; line-height: 1.45; padding: 6px 14px 12px 12px; tab-size: 4; white-space: pre; overflow-x: auto; }

/* Collapse/expand a cell's code — a small real BUTTON (label + caret), not a
   bare icon, so "Show code"/"Hide code" is legible on its own even once the
   code itself is gone. Also fires automatically the moment an analyze/build
   cell finishes running (see nbFinishCell) — a completed cell settles down to
   just its result by default; still fully manual to reopen/re-hide, and
   never auto-fires for a LEARN cell. Always visible (not hover-only like
   copy) so the toggle stays discoverable even on a collapsed cell. A normal
   flex child of .nb-code-header now, not absolutely positioned. */
.cell-collapse-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: 1px solid var(--border); cursor: pointer;
  padding: 3px 8px; border-radius: 6px;
  font-family: var(--font); font-size: 11px; font-weight: 500;
  color: var(--text-muted, #8b949e);
  opacity: .8; transition: opacity .12s, color .12s, background .12s, border-color .12s;
  white-space: nowrap;
}
.cell-collapse-btn:hover {
  opacity: 1; color: var(--text, #c9d1d9);
  background: rgba(255,255,255,0.07); border-color: var(--border-light, var(--border));
}
.cell-collapse-label { line-height: 1; }
.cell-collapse-caret { flex-shrink: 0; }
.cell-collapse-caret { transition: transform .15s ease; }
.nb-cell.nb-code-collapsed .cell-collapse-caret { transform: rotate(-90deg); }
.nb-cell.nb-code-collapsed .nb-code { display: none; }


/* ══ Merged top row (desktop) ═══════════════════════════════════════════════════
   No chat / notebook header bars: their icons live in the title-bar row itself —
   same row as File/Edit/View/Help and the min/max/close controls, on the page
   background, no border. The buttons keep their place in the markup (so every
   handler/ID is untouched); CSS lifts them into the row. The web build restores the
   old bars in web-overrides.css (it has no title bar to merge into). */
#titlebar { border-bottom: none; }
.chat-header, .notebook-header {
  height: 0; min-height: 0; padding: 0; border: 0; background: transparent; overflow: visible; position: static;
}
.chat-header-info, .nb-title { display: none; }
.notebook-header-actions, .chat-header-actions {
  position: fixed; top: 0; right: 146px; height: 38px; z-index: 60;
  display: flex; align-items: center; gap: 4px; -webkit-app-region: no-drag;
}
.chat-header .sidebar-expand {
  position: fixed; top: 5px; left: 214px; z-index: 60; margin: 0; -webkit-app-region: no-drag;
}
.notebook-header-actions .header-btn, .chat-header-actions .header-btn, .chat-header .sidebar-expand {
  background: transparent; border-color: transparent; -webkit-app-region: no-drag;
}
.notebook-header-actions .header-btn:hover, .chat-header-actions .header-btn:hover, .chat-header .sidebar-expand:hover {
  background: var(--panel-bg-2); border-color: var(--border);
}
/* Maximize + export live in the three-dot menu now; the old buttons stay in the page (hidden) so nothing that references them breaks. */
.notebook-header-actions #export-menu-btn, .notebook-header-actions #maximize-nb-btn { display: none; }
/* With the in-app web panel open the notebook body is hidden, but its icons stay in the top row:
   the column collapses to zero size (children other than the header hidden) instead of display:none,
   so the fixed icon row keeps showing next to the window controls. */
.lab-split.web-open:not(.notebook-hidden) .notebook-column {
  display: block; flex: 0 0 0; width: 0; min-width: 0; max-width: 0; height: 0; border: 0; padding: 0; overflow: visible; animation: none;
}
.lab-split.web-open:not(.notebook-hidden) .notebook-column > :not(.notebook-header) { display: none; }
/* The icon row is position:fixed inside the notebook column; a TRANSFORM animation on that column would make it the
   fixed row's containing block (icons would slide down 38px for the first 0.3s). Fade only — no transform. */
@keyframes nbRevealFade { from { opacity: 0; } to { opacity: 1; } }
.lab-split:not(.notebook-hidden) .notebook-column { animation-name: nbRevealFade; }

/* ══ Full-height sidebar (desktop) ══════════════════════════════════════════════
   The sidebar runs from the very top of the window; the File/Edit/View/Help strip floats on top of it
   (transparent), so menu, logo row and navigation are ONE surface — no colour step, no line under the logo.
   Everything else keeps its 38px offset. (Web: see web-overrides.css.) */
#titlebar { position: fixed; top: 0; left: 0; right: 0; z-index: 50; background: transparent; }
#app { height: 100vh; }
#app > :not(#left-panel) { margin-top: 38px; }
#left-panel { padding-top: 38px; box-sizing: border-box; }
.logo-section { border-bottom: none; }

/* No run-number column ([57]) on code cells: the code gets the full width. The element stays in the page (hidden) so the code that
   updates it keeps working. A running cell is still obvious: accent border + glow + the sliding progress bar, and the language badge pulses.
   (Practice cells keep their pencil marker.) */
.nb-cell .nb-gutter:not(.nb-practice-gutter) { display: none; }
.nb-cell.nb-running .nb-lang-tag { animation: nb-run-pulse 1s ease-in-out infinite; }

/* ── Chat folded by plan step (step_groups.js) ── */
.step-head { display:flex; align-items:center; gap:8px; margin:10px 14px 4px; padding:6px 10px; border:1px solid var(--border, rgba(128,128,128,.25)); border-radius:8px; background:var(--panel-bg-2, rgba(128,128,128,.08)); color:var(--text); font-size:12px; font-weight:600; cursor:pointer; user-select:none; }
.step-head:hover { border-color: var(--accent, #E8722C); }
.step-head:focus-visible { outline:2px solid var(--accent, #E8722C); outline-offset:1px; }
.step-head-chev { width:12px; color:var(--accent, #E8722C); }
.step-folded { display:none !important; }
.step-note-hidden { display:none !important; }

/* working notes: ONE light dashed card inside the step. The toggle is its header; the notes are plain typewriter-font lines inside it (no boxes, no ticks). They stay direct children of the chat, so the single card is drawn by lining up side borders; the base .message has a 16px radius + padding (reset here) and .chat-messages has a 4px gap between children (cancelled with a -4px bottom margin so no seams show). */
.step-notes-toggle, .step-note { --sn-line: color-mix(in srgb, var(--accent, #E8722C) 42%, transparent); --sn-bg: color-mix(in srgb, var(--accent, #E8722C) 6%, var(--panel-bg, #fff)); }
.step-notes-toggle { margin:8px 14px 0 20px; padding:7px 12px; font-size:12px; color:var(--text); opacity:.95; cursor:pointer; user-select:none; border:1px dashed var(--sn-line); border-radius:10px; background:var(--sn-bg); }
.step-notes-toggle:hover { border-color:var(--accent, #E8722C); }
.step-notes-toggle.open { border-bottom-left-radius:0; border-bottom-right-radius:0; border-bottom-color:transparent; margin-bottom:-4px; }
.step-notes-toggle:focus-visible { outline:2px solid var(--accent, #E8722C); outline-offset:1px; }
.step-note { margin:0 14px -4px 20px; padding:3px 14px; background:var(--sn-bg); border:0 dashed var(--sn-line); border-left-width:1px; border-right-width:1px; border-radius:0; color:color-mix(in srgb, var(--text) 80%, transparent); }
.step-note, .step-note .message-text { font-family:var(--mono); font-size:12px; line-height:1.55; font-style:normal; }
.step-note em, .step-note i { font-style:italic; }
.step-note.step-note-last { margin-bottom:6px; padding-bottom:10px; border-bottom-width:1px; border-bottom-left-radius:10px; border-bottom-right-radius:10px; }

/* ── Notebook tables: no hard lines. The outer frame and the vertical column lines are gone; pandas' own table border="1" attribute (the hard dark top/left lines) is overridden; only a very faint line between rows and one soft line under the header remain. ── */
.nb-html { border: 0; }
.nb-html table, .nb-html th, .nb-html td { border: 0; }
.nb-html tbody th, .nb-html tbody td { border-bottom: 1px solid color-mix(in srgb, var(--border) 45%, transparent); }
.nb-html thead th { border-bottom: 1px solid color-mix(in srgb, var(--border) 80%, transparent); }
.nb-html tbody tr th:first-child { border-right: 0; }
/* Notebook tables: column titles centred and lined up (pandas right-aligns them with its own .dataframe thead th rule; this has higher specificity so it wins whatever order the pandas <style> lands in). */
.nb-html table thead th { text-align: center; vertical-align: middle; }
