/* COLOR SCHEME → ui/shared/css/design-system.css (single source of truth).
   No raw hex/rgb here: use var(--brand) / var(--accent) / var(--accent-hover) for
   the brand accent, rgba(var(--brand-rgb), a) for any accent opacity, var(--success|
   warning|danger) (or rgba(var(--*-rgb), a)) for status, var(--purple) for the 2nd hue.
   Need a colour that doesn't exist yet? Add a token to the palette in design-system.css
   (the dark :root block AND its body.light-mode twin), then reference it here.
   Re-theming the whole app (e.g. peach → green) is then a one-file edit. */

* { margin: 0; padding: 0; box-sizing: border-box; }
body {
    background: var(--bg-0);
    color: var(--fg-1);
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    /* Use dynamic viewport height so mobile browser chrome (address bar,
       bottom toolbar) doesn't cause the top or bottom of the app to be
       clipped. --app-height is set from JS via window.visualViewport so
       the app also shrinks when the mobile on-screen keyboard appears
       (dvh alone does not shrink for keyboards on Android Chrome).
       Fallback chain: 100vh → 100dvh → JS-driven --app-height. */
    height: 100vh;
    height: 100dvh;
    height: var(--app-height, 100dvh);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

/* ── Stage: split layout ── */
  #stage {
    display: flex;
    flex: 1;
    overflow: hidden;
  }

/* ── Left: Main panel ── */
  #main-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    position: relative;
  }

#terminal-container {
    flex: 1;
    background: var(--bg-0);
  }
#terminal-container .xterm { height: 100%; padding: 4px; }
#terminal-container .xterm-viewport { scrollbar-width: thin; scrollbar-color: var(--bg-tint) transparent; }
#terminal-container .xterm-viewport::-webkit-scrollbar { width: 6px; }
#terminal-container .xterm-viewport::-webkit-scrollbar-thumb { background: var(--bg-tint); border-radius: 3px; }

/* ── Chat resize handle ── */
  #chat-resize-handle {
    width: 14px;
    flex-shrink: 0;
    cursor: col-resize;
    background: transparent;
    z-index: 10;
    position: relative;
    transition: background 0.15s;
  }
  /* Subtle center grip line so the wider hit-area still reads visually as a divider. */
  #chat-resize-handle::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 2px;
    height: 36px;
    border-radius: 2px;
    background: color-mix(in oklab, currentColor 18%, transparent);
    transition: background 0.15s, height 0.15s;
    pointer-events: none;
  }
#chat-resize-handle:hover,
#chat-resize-handle.resizing {
    background: rgba(var(--brand-rgb), 0.13);
  }
#chat-resize-handle:hover::before,
#chat-resize-handle.resizing::before {
    background: rgba(var(--brand-rgb), 0.67);
    height: 56px;
  }
  @media (max-width: 800px) {
#chat-resize-handle { display: none; }
  }

/* ── Right: Chat panel ── */
  #chat-panel {
    width: 380px;
    min-width: 280px;
    display: flex;
    flex-direction: column;
    border-left: var(--border-width) solid var(--border);
    background: var(--bg-1);
    flex-shrink: 0;
    position: relative;
  }

/* TWO GENUINE ROWS — #chat-header is a flex COLUMN of two full-width rows
   (.chat-header-row), NOT a row of two stacked columns. Each row pins its own
   controls right independently, so row 2 reserves only the delete button on the
   right and the session dropdown flex-fills up to just-left-of it. */
#chat-header {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 10px 14px;
    border-bottom: var(--border-width) solid var(--border);
    font-size: 15px;
    font-weight: 600;
    color: var(--fg-1);
    flex-shrink: 0;
    gap: 2px;
  }
/* One header row — a flex row of in-flow controls; the single right-pinned control
   (row 1 = new (+); row 2 has none — its chevron is the dropdown's own right edge) is
   pushed over with margin-left:auto. */
.chat-header-row { display: flex; flex-direction: row; align-items: center; gap: 6px; width: 100%; min-width: 0; }
/* Session line (row 2 left) — prev/next arrows + the session dropdown, side by side;
   flex-fills the row so the dropdown's chevron reaches just-left-of the delete button. */
.header-session-line { display: flex; flex-direction: row; align-items: center; gap: 4px; flex: 1 1 auto; min-width: 0; }
/* Agent-name row (row 1) — the agent's name + the refresh button, sized to CONTENT
   (flex 0 1 auto) so refresh sits snug to the RIGHT of the name instead of being pushed
   to the far edge; the name shrinks with an ellipsis (min-width:0) when space is tight.
   Sits between the far-left more (⋯) and the right-pinned new (+) button. */
.chat-header-name-row { display: flex; flex-direction: row; align-items: center; gap: 6px; flex: 0 1 auto; min-width: 0; }
.chat-header-agent-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--fg-2);
  letter-spacing: 0.01em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;            /* allow ellipsis inside the flex row */
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}
.chat-header-agent-name:hover { color: var(--brand); }
.chat-header-agent-name .header-agent-icon {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  line-height: 1;
}
.chat-header-agent-name .header-agent-icon svg,
.chat-header-agent-name .header-agent-icon i {
  display: block;
}
.chat-header-agent-name .header-agent-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Transcript-refresh button — sits beside the agent name; resets ONLY the chat
   transcript (session-init.js → session-load.js refreshTranscript), never the
   whole page. Colour/hover come from the generic #chat-header button rules. */
.chat-refresh-btn {
  background: none;
  border: none;
  color: var(--fg-3);
  cursor: pointer;
  padding: 1px 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: var(--r-sm);
}
.chat-refresh-btn:hover { color: var(--brand); }
.chat-refresh-btn.spinning i,
.chat-refresh-btn.spinning svg { animation: chat-refresh-spin 0.6s linear infinite; }
@keyframes chat-refresh-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
#chat-header .sub { color: var(--fg-3); font-weight: 400; font-size: 12px; }
#chat-header .actions { display: flex; gap: 8px; }
#chat-header button {
    background: none; border: none; color: var(--fg-3); cursor: pointer; font-size: 14px;
  }
#chat-header button:hover { color: var(--brand); }

/* New-session (+) button — sits in the row-1 LEFT cluster, immediately RIGHT of the
   refresh button (more (⋯) · name · refresh · new (+)), so it's no longer pinned to the
   far edge. Shares the compact transparent icon-button look of its neighbours (refresh /
   more); its 18px glyph matches them. Single-click → new session / agent picker,
   middle-click → new browser tab, both wired in ui/chat-side-panel/js/session-init.js. */
#chat-header .session-new-header-btn {
  background: none;
  border: none;
  color: var(--fg-3);
  cursor: pointer;
  padding: 1px 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: var(--r-sm);
  /* Background-only transition (no `color`): a color transition makes the glyph
     lag the live --fg-3 during a theme switch, since the value arrives via a
     custom property — its sibling refresh/delete buttons have no color
     transition and snap instantly, so this matches them. */
  transition: background 0.15s;
}
#chat-header .session-new-header-btn > * { pointer-events: none; }
#chat-header .session-new-header-btn:hover { color: var(--brand); }
#chat-header .session-new-header-btn:active { background: rgba(var(--brand-rgb),0.15); }

/* ── Header "more" (⋯) button + its dropdown menu ─────────────────────────────
   Kebab button beside refresh / new-session; shares their compact transparent
   icon look. Opens #chat-header-more-menu, anchored under it (chrome mirrors
   .session-dropdown-menu). ADMIN-ONLY: the wrapper stays hidden until the
   `is-admin` body class lands (ui/shared/js/main.js), since its only content
   today is the Agent/Session debug row. Wiring: ui/chat-side-panel/js/
   session-init.js (_wireHeaderMoreMenu). */
#chat-header .chat-header-more {
  position: relative;
  display: none;            /* revealed for admins only — see body.is-admin rule */
  align-items: center;
  flex-shrink: 0;
}
body.is-admin #chat-header .chat-header-more { display: inline-flex; }
#chat-header .chat-header-more-btn {
  background: none;
  border: none;
  color: var(--fg-3);
  cursor: pointer;
  padding: 1px 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: var(--r-sm);
  transition: background 0.15s;
}
#chat-header .chat-header-more-btn > * { pointer-events: none; }
#chat-header .chat-header-more-btn:hover,
#chat-header .chat-header-more.open .chat-header-more-btn { color: var(--brand); }
#chat-header .chat-header-more-btn:active { background: rgba(var(--brand-rgb),0.15); }
#chat-header .chat-header-more.open .chat-header-more-btn { background: rgba(var(--brand-rgb),0.12); }

/* The dropdown panel — the kebab sits at the FAR LEFT of the header, so anchor
   the menu to the kebab's LEFT edge and open it down-and-RIGHT, into the panel
   interior. (Opening leftward ran the 248px menu off the panel's left edge,
   where #chat-panel's `overflow:hidden` clipped it behind the main panel.) The
   menu width (248px) fits inside the 280px min chat-panel width — see
   chatResize.js MIN_WIDTH — so it never overflows the right edge either. */
.chat-header-more-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 248px;
  max-width: min(320px, 80vw);
  background: var(--bg-elev);
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.4);
  z-index: 1000;
  padding: 8px;
  box-sizing: border-box;
}
body.light-mode .chat-header-more-menu {
  background: var(--bg-0);
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.1);
}
.chat-header-more-menu[hidden] { display: none; }

/* Debug section inside the menu: a title + copy button on one row, then the
   Agent / Session id rows below (monospace, selectable). */
.chm-section { display: flex; flex-direction: column; gap: 7px; }
.chm-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.chm-section-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.chm-copy {
  background: none;
  border: none;
  color: var(--fg-3);
  cursor: pointer;
  padding: 2px 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-sm);
  transition: color 0.12s, background 0.12s;
}
.chm-copy > * { pointer-events: none; }
.chm-copy:hover { color: var(--brand); background: rgba(var(--brand-rgb),0.12); }
.chm-copy.copied { color: var(--success); }
.chm-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 11px;
}
.chm-key {
  flex: 0 0 auto;
  width: 52px;
  color: var(--fg-3);
}
.chm-val {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-mono, monospace);
  color: var(--fg-2);
  word-break: break-all;
  user-select: text;
}

/* ── Chat sub-header: related spawns & browser tabs ── */
#chat-sub-header {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 2px 0 4px;
  border-bottom: var(--border-width) solid var(--border);
  flex-shrink: 0;
  min-height: 0;
  font-size: 12px;
  position: relative;
}
/* The scroll wrap — holds the chip groups in a single nowrap row with overflow-x
   auto. The chevrons overlay the edges; the edge-fade mask blends the overflow
   into the background without a scrollbar cluttering the header. */
#chat-sub-header #chat-sub-scroll-wrap {
  display: flex;
  align-items: center;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 14px;
  gap: 1px;
  -webkit-overflow-scrolling: touch;
}
#chat-sub-header #chat-sub-scroll-wrap::-webkit-scrollbar { display: none; }
/* The three chip groups lay their own chips out as a 1px-gap tab strip. */
#chat-sub-header #chat-sub-scroll-wrap > span {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  flex-shrink: 0;
}
#chat-sub-header .sub-label {
  color: var(--fg-3);
  font-weight: 500;
  flex-shrink: 0;
  margin-right: 2px;
}
#chat-sub-header .sub-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 7px;
  height: 20px;
  border-radius: 10px;
  background: var(--bg-tint);
  color: var(--fg-2);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  white-space: nowrap;
  flex-shrink: 0;
}
#chat-sub-header .sub-chip:hover {
  background: var(--bg-tint-2);
  color: var(--fg-1);
}
#chat-sub-header .sub-chip .chip-status {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
#chat-sub-header .sub-chip .chip-status svg {
  width: 10px;
  height: 10px;
}
#chat-sub-header .sub-chip.chip-main .chip-status { color: var(--brand); }
#chat-sub-header .sub-chip.chip-running .chip-status { color: var(--brand); }
#chat-sub-header .sub-chip.chip-done .chip-status { color: var(--success); }
#chat-sub-header .sub-chip.chip-error .chip-status { color: var(--danger); }
#chat-sub-header .sub-chip.chip-browser { color: var(--brand); }
#chat-sub-header .sub-chip.chip-private { opacity: 0.6; }
/* Active tab — the session currently open in the panel. A filled accent tint +
   inset ring marks it as "you are here"; it stays put on hover (no pointer). */
#chat-sub-header .sub-chip.chip-active {
  background: rgba(var(--brand-rgb), 0.16);
  color: var(--fg-1);
  box-shadow: inset 0 0 0 1px rgba(var(--brand-rgb), 0.55);
  cursor: default;
}
#chat-sub-header .sub-chip.chip-active:hover {
  background: rgba(var(--brand-rgb), 0.16);
  color: var(--fg-1);
}
/* ── Sub-header carousel chevrons + edge fades ──
   Mirrors the Agents-page carousel pattern (see .agents-carousel-chev in
   ui/main-panel/agents/agents.css). Chevrons overlay the scrollable area and
   are shown/hidden by .can-scroll-* classes on the wrap. The scroller gets a
   right-edge gradient mask when there's content to scroll toward. */
.sub-carousel-chev {
  position: absolute;
  top: 2px;
  bottom: 4px;
  width: 28px;
  display: none;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--fg-3);
  font-size: 14px;
  line-height: 1;
  z-index: 2;
}
.sub-carousel-chev:hover { color: var(--fg-1); }
.sub-carousel-chev.left  { left: 0; }
.sub-carousel-chev.right { right: 0; }
#chat-sub-scroll-wrap.can-scroll-left  ~ .sub-carousel-chev.left  { display: flex; }
#chat-sub-scroll-wrap.can-scroll-right ~ .sub-carousel-chev.right { display: flex; }

#chat-sub-scroll-wrap.can-scroll-right {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent 100%);
          mask-image: linear-gradient(to right, #000 calc(100% - 40px), transparent 100%);
}
#chat-sub-scroll-wrap.can-scroll-left {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 40px);
          mask-image: linear-gradient(to right, transparent 0, #000 40px);
}
#chat-sub-scroll-wrap.can-scroll-left.can-scroll-right {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
}

#chat-sub-header .chip-sep {
  display: inline-block;
  width: 1px;
  height: 14px;
  background: var(--bg-tint);
  margin: 0 2px;
  flex-shrink: 0;
}
body.light-mode #chat-sub-header { border-bottom-color: var(--border); }
body.light-mode #chat-sub-header .sub-label { color: var(--fg-3); }
body.light-mode #chat-sub-header .chip-sep { background: var(--border); }

/* ── Chat input area ── */
/* PILL-PANEL-LAYOUT: canonical "floating pill over full-height content" layout
   that EVERY panel hosting a .chat-pill must mirror. Instances: chat panel
   (here); agents page (#agent-builder-bar over #agents-grid in agents.css);
   Gen UI page (#genui-footer over #genui-viewport in genui.css).
   Full rationale in docs/claude/ui-guidance.md ("Chat pills" → panel layout).
   Three pieces:
     1. Panel is a flex column with position: relative (here #chat-panel).
     2. ONE scroller fills it full-height (#chat-messages → #chat-messages-inner,
        flex:1 + overflow-y:auto) with bottom padding so the last lines clear /
        scroll behind the pill. Its scrollbar runs to the panel's bottom.
     3. The pill is an ABSOLUTE OVERLAY (below), not a flow child — so it never
        reserves layout space and never shrinks the scroller.
   HARD RULE: never put the pill, a footer, or any control in normal flow BELOW
   the scroller — a flow sibling there steals height and the content/scrollbar
   stop short of the bottom (the agents-page bug). Auxiliary controls belong
   INSIDE the scroller as trailing content.
   Bottom aligns with #main-panel (which has 8px margin), so the pill sits level
   with the pages card instead of pushed to the bottom of the chat panel.
   This area is a flex COLUMN holding the pill on top and the .chat-footer-row
   (token/mode row) below it. NOTE: this is the TALL composer pill — it is now
   intentionally taller than every other pill, and there is NO LONGER a rule
   keeping its bottom edge level with the other panels' pills (the old
   PILL-PANEL-ALIGN reserve on the agents/genui bars has been removed). So you
   can change this `bottom`, this `gap`, or the .chat-footer-row height freely
   without touching the other panels. (Pill skin/geometry is separately
   commonized; see the CHAT-PILL-SYNC block lower in this file.) */
  #chat-input-area {
    display: flex;
    flex-direction: column;
    padding: 6px 12px 0;
    border-top: none;
    background: transparent;
    gap: 6px;
    flex-shrink: 0;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 8px;
    z-index: 5;
    min-height: 0;
  }

/* ── Chat footer row (token bar + mode button) ───────────────────────────
   A single flex row below the pill that holds the token counter on the left
   and the execution-mode button on the right. */
/* CHAT-PILL-ALIGN: the rows that sit around the composer (suggestions, the
   Stop/Continue actions, and this footer of token-bar + abilities/model/mode
   buttons) are capped to the SAME width as the pill (--chat-pill-max-width) and
   centred, so every control lines up with the composer's left/right edges
   instead of stretching across a wide panel. They share the pill's width knob,
   so widening the pill widens this band with it. */
.chat-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* No horizontal padding: the left token pill and the right
       model/abilities/mode buttons sit FLUSH with the composer's left/right
       edges, matching the Stop/Continue action row above (which is also
       padding-free). Per CHAT-PILL-ALIGN these satellite rows line up with the
       pill's edges — an inset here would push these controls in past Continue. */
    padding: 0;
    min-height: 0;
    width: 100%;
    max-width: var(--chat-pill-max-width, 500px);
    margin-inline: auto;
  }
/* FOOTER-STATS-FIT: the left stats pill (token bar + ctx + cost) sheds content
   in priority order when the space the right-side buttons leave it runs short.
   This is driven by JS (_fitFooterStats in chat-activity.js), NOT @container
   queries: the old container rules keyed off the WHOLE row width, but the stats
   only ever get `row − right buttons (~100px)` of usable space, so the cost was
   clipping ~100px before its breakpoint ever fired. JS measures the real
   rendered widths (locale-formatted numbers, the live font) and toggles the
   classes below, so an element is always either fully shown or fully hidden —
   never half-cut under the buttons.
   Shed order = least → most useful, so the context window survives longest:
     Step 1 (.fs-hide-tok-labels): drop the "in" / "out" labels.
     Step 2 (.fs-hide-ctx-label):  drop the "ctx" label.
     Step 3 (.fs-hide-cost):       drop the session cost.
     Step 4 (.fs-hide-tokbar):     drop the in/out token bar.
     Step 5 (.fs-hide-ctx):        drop the ctx readout + the now-empty pill.
   To change the priority, reorder the _FOOTER_SHED array in chat-activity.js. */
.chat-footer-row.fs-hide-tok-labels #chat-token-bar .chat-token-label {
      display: none;
    }
.chat-footer-row.fs-hide-ctx-label #chat-model-ctx .chat-token-label {
      display: none;
    }
.chat-footer-row.fs-hide-cost #chat-cost {
      display: none !important;
    }
.chat-footer-row.fs-hide-tokbar #chat-token-bar {
      display: none;
    }
.chat-footer-row.fs-hide-ctx #chat-model-ctx {
      display: none !important;
    }
  /* Nothing left to show — hide the whole pill so no empty button remains.
     !important: the base .chat-footer-left rule is declared later in the file
     and would otherwise win on equal specificity. */
.chat-footer-row.fs-hide-ctx .chat-footer-left {
      display: none !important;
    }

/* ── Chat token bar ──────────────────────────────────────────────────────
   Sits below the chat pill, left-aligned. Shows tokens-in, a spinning shape
   during activity, and tokens-out. The spinner animates while the agent is
   working (chat-activity.visible is set). */
.chat-token-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
    font-size: 11px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--fg-3);
    pointer-events: none;
    user-select: none;
    opacity: 0.6;
    transition: opacity 0.2s ease;
    flex-shrink: 0;
  }
.chat-token-bar.active {
    opacity: 1;
  }
.chat-token-label {
    color: var(--fg-4);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 10px;
  }
.chat-token-value {
    color: var(--fg-3);
    min-width: 24px;
    text-align: center;
    font-variant-numeric: tabular-nums;
  }

/* Left group: token counters + active-model context + session cost, kept
   together so the mode button stays pushed to the right edge (footer row is
   space-between). Styled as ONE pill — since the whole group is clickable and
   opens the model picker.
   CHAT-PILL-SYNC: wears the chat pill's exact glass skin (see .chat-continue-btn)
   so it reads as the same opaque material as the pill rather than a faint tint. */
.chat-footer-left {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    /* Safety net for the FOOTER-STATS-FIT logic: if JS hasn't fit yet (or is
       mid-frame), clip any spill at this pill's own edge instead of letting the
       rightmost stat paint UNDER the abilities/mode buttons. JS sheds whole
       elements before it ever reaches this, so in steady state nothing clips. */
    overflow: hidden;
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    border-radius: 14px;
    padding: 4px 12px;
    transition: background 0.15s, border-color 0.15s;
  }
body.light-mode .chat-footer-left {
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 85%, transparent));
  }
body.light-mode .chat-activity-bar {
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 85%, transparent));
  }
.chat-footer-left:hover {
    border-color: rgba(var(--brand-rgb), 0.45);
  }
/* Right side of the chat footer row: skill button + mode button, kept on the
   far right via margin-left: auto so left and right groups are independent.
   PANEL-SYNC-FOOTER: mirror any layout change in .chat-footer-right across
   the agents page footer (#agent-builder-footer) if it gains similar controls. */
.chat-footer-right {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex-shrink: 0;
  }
/* Active model's context window / max output, styled like the token bar but
   theme-aware via design-system vars and always visible (no activity gating). */
.chat-model-ctx {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--fg-3, var(--fg-3));
    user-select: none;
    flex-shrink: 0;
    white-space: nowrap;
  }
.chat-ctx-value {
    color: var(--fg-3, var(--fg-3));
    font-variant-numeric: tabular-nums;
  }
/* Session cost so far — accumulated tokens × active model price. Styled like
   the ctx indicator (theme-aware via design-system vars), sits in the footer
   left group between the token bar and the ctx indicator. Hidden when the
   active model has no published price. */
.chat-cost {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-family: 'JetBrains Mono', monospace;
    color: var(--warning);
    user-select: none;
    flex-shrink: 0;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }
.chat-cost .chat-token-label {
    color: var(--fg-4, var(--fg-4));
  }
.chat-ctx-sep {
    color: var(--fg-4, var(--fg-4));
    margin: 0 1px;
  }
/* Spinning shape — a diamond that rotates. While the agent is active
   (.chat-activity.visible on the sibling #chat-activity) it spins;
   otherwise it sits still as a small dot. */
.chat-token-spinner {
    display: inline-block;
    width: 10px;
    height: 10px;
    border: 1.5px solid var(--fg-3);
    border-radius: 2px;
    transform: rotate(45deg);
    transition: border-color 0.3s ease, opacity 0.3s ease;
    opacity: 0.4;
  }
.chat-token-bar.spinning .chat-token-spinner {
    animation: tokenSpinnerSpin 1.2s linear infinite;
    border-color: var(--brand);
    opacity: 1;
  }
@keyframes tokenSpinnerSpin {
    0%   { transform: rotate(45deg); }
    100% { transform: rotate(405deg); }
  }
/* Output tokens flowing — clockwise orange spin */
.chat-token-spinner.spinning-out {
    animation: tokenSpinOut 0.9s linear infinite;
    border-color: var(--warning);
    opacity: 1;
  }
@keyframes tokenSpinOut {
    0%   { transform: rotate(45deg); }
    100% { transform: rotate(405deg); }
  }
/* Input tokens flowing — counter-clockwise orange spin */
.chat-token-spinner.spinning-in {
    animation: tokenSpinIn 0.9s linear infinite;
    border-color: var(--warning);
    opacity: 1;
  }
@keyframes tokenSpinIn {
    0%   { transform: rotate(45deg); }
    100% { transform: rotate(-315deg); }
  }

/* ── Footer model picker (click the token/ctx footer to switch model) ─────
   A floating panel anchored to the LEFT edge of the chat pill footer, sitting
   just above the context-details line. Lists the curated models (App Config saved
   models + the agent's own model), with a detail footer (description + cost)
   for the focused row. Themed entirely via design-system vars (dark + light).
   PANEL-SYNC-FOOTER: keep .chat-model-picker and .chat-skill-picker in sync —
   if you change max-height, border-radius, padding, shadow, z-index, or
   background on one, mirror it on the other. */
.chat-model-picker {
    display: none;
    position: fixed;
    z-index: 1000;
    box-sizing: border-box;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    padding: 6px;
    min-width: 280px;
    max-width: 380px;
    box-shadow: var(--shadow-md);
  }
.chat-model-picker .cmp-search {
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
    border: var(--border-width) solid var(--border);
    border-radius: 4px;
    background: var(--bg-1);
    color: var(--fg-1);
    font-size: 12px;
    outline: none;
  }
.chat-model-picker .cmp-search:focus { border-color: var(--accent-line); }
.chat-model-picker .cmp-list {
    max-height: 360px;
    overflow-y: auto;
    margin-top: 4px;
  }
.chat-model-picker .cmp-empty {
    padding: 8px;
    color: var(--fg-3);
    font-size: 11px;
    text-align: center;
  }
.chat-model-picker .cmp-item {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px;
    padding: 5px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    color: var(--fg-1);
  }
.chat-model-picker .cmp-item:hover,
.chat-model-picker .cmp-item.cmp-active { background: var(--accent-soft); }
.chat-model-picker .cmp-item.cmp-selected { background: var(--accent-mid); }
.chat-model-picker .cmp-item-id {
    flex: 1;
    min-width: 0;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
.chat-model-picker .cmp-item-meta {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    flex-shrink: 0;
    margin-left: auto;
  }
.chat-model-picker .cmp-item-ctx {
    color: var(--fg-3);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
  }
.chat-model-picker .cmp-item-tok {
    color: var(--accent);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
/* Per-model session cost (priced at this model's own rate). */
.chat-model-picker .cmp-item-cost {
    color: var(--fg-2);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
/* Per-row save status slot (spinner / green check / red cross). */
.chat-model-picker .cmp-item-status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    flex-shrink: 0;
  }
.chat-model-picker .cmp-item-status:empty { display: none; }
.chat-model-picker .cmp-spinner {
    width: 11px;
    height: 11px;
    border: 1.5px solid var(--accent-mid);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: cmpSpin 0.7s linear infinite;
  }
@keyframes cmpSpin { to { transform: rotate(360deg); } }
.chat-model-picker .cmp-check { color: var(--success); font-size: 12px; font-weight: 700; line-height: 1; }
.chat-model-picker .cmp-cross { color: var(--danger); font-size: 12px; font-weight: 700; line-height: 1; }
.chat-model-picker .cmp-item.cmp-saving { opacity: 0.7; }
/* Per-row reasoning-effort selector (Default · Min · Low · Med · High). Wraps to
   a full-width second line under the model id + meta. */
.chat-model-picker .cmp-effort {
    flex-basis: 100%;
    display: flex;
    gap: 3px;
    margin-top: 3px;
  }
.chat-model-picker .cmp-effort.cmp-eff-saving { opacity: 0.6; pointer-events: none; }
.chat-model-picker .cmp-eff-btn {
    flex: 1;
    padding: 2px 0;
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--fg-3);
    background: var(--bg-2);
    border: var(--border-width) solid var(--border-soft);
    border-radius: 3px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
  }
.chat-model-picker .cmp-eff-btn:hover { color: var(--fg-1); border-color: var(--accent-line); }
.chat-model-picker .cmp-eff-btn.cmp-eff-on {
    color: var(--accent);
    background: var(--accent-soft);
    border-color: var(--accent-line);
  }
/* Detail footer — description + cost for the focused/selected model. */
.chat-model-picker .cmp-detail {
    margin-top: 6px;
    padding-top: 6px;
    border-top: var(--border-width) solid var(--border-soft);
    font-size: 11px;
    color: var(--fg-2);
  }
.chat-model-picker .cmp-detail-name {
    font-weight: 600;
    color: var(--fg-1);
    font-size: 11px;
    margin-bottom: 3px;
    word-break: break-all;
  }
.chat-model-picker .cmp-detail-desc {
    color: var(--fg-2);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
.chat-model-picker .cmp-detail-cost {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 5px;
  }
.chat-model-picker .cmp-cost-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--bg-tint);
    color: var(--fg-2);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
.chat-model-picker .cmp-cost-chip.cmp-cost-free { background: var(--success-soft); color: var(--success); }
.chat-model-picker .cmp-detail-muted { color: var(--fg-3); font-style: italic; }

/* ── Context-compaction section (bottom of the model picker) ────────────────
   Two sliders ("compact at %" / "keep verbatim %") tune this chat's compaction,
   plus a "Compact now" button that folds the conversation immediately. Saved as a
   per-session override (POST /api/v1/chat/session-compaction). */
.chat-model-picker .cmp-compact {
    margin-top: 6px;
    padding-top: 8px;
    border-top: var(--border-width) solid var(--border-soft);
  }
.chat-model-picker .cmp-cc-head {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--fg-3);
    margin-bottom: 8px;
  }
.chat-model-picker .cmp-cc-row { margin-bottom: 10px; }
.chat-model-picker .cmp-cc-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 3px;
  }
.chat-model-picker .cmp-cc-label { font-size: 12px; color: var(--fg-1); font-weight: 500; }
.chat-model-picker .cmp-cc-val {
    font-size: 12px;
    font-weight: 600;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
    border-radius: 3px;
    padding: 0 2px;
  }
/* Brief flash after a save lands (green ok / red fail), then fades back. */
.chat-model-picker .cmp-cc-val.cmp-cc-saved { animation: cmpCcSaved 0.9s ease-out; }
.chat-model-picker .cmp-cc-val.cmp-cc-failed { animation: cmpCcFailed 0.9s ease-out; }
@keyframes cmpCcSaved {
    0% { background: var(--success-soft); color: var(--success); }
    100% { background: transparent; color: var(--accent); }
  }
@keyframes cmpCcFailed {
    0% { background: var(--danger-soft); color: var(--danger); }
    100% { background: transparent; color: var(--accent); }
  }
.chat-model-picker .cmp-cc-slider {
    width: 100%;
    margin: 2px 0;
    accent-color: var(--accent);
    cursor: pointer;
  }
.chat-model-picker .cmp-cc-hint {
    font-size: 10px;
    line-height: 1.35;
    color: var(--fg-3);
    margin-top: 2px;
  }
.chat-model-picker .cmp-cc-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    box-sizing: border-box;
    margin-top: 2px;
    padding: 7px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--accent);
    background: var(--accent-soft);
    border: var(--border-width) solid var(--accent-line);
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
  }
.chat-model-picker .cmp-cc-btn:hover { background: var(--accent-mid); color: var(--fg-1); }
.chat-model-picker .cmp-cc-btn-status { font-size: 10px; color: var(--fg-3); font-weight: 500; }
.chat-model-picker .cmp-cc-note {
    font-size: 10px;
    color: var(--fg-3);
    text-align: center;
    margin-top: 6px;
  }

/* ── Local Claude Code footer panel ────────────────────────────────────────
   Opens in place of the model picker for claude_code agents (see chat-activity.js
   _toggleClaudePanel). Borrows the .chat-model-picker container chrome; these
   rules style its sections: context, plan usage, and the Claude model rows. */
.chat-claude-panel .ccp-sec { padding: 6px 4px; }
.chat-claude-panel .ccp-sec + .ccp-sec {
    border-top: var(--border-width) solid var(--border-soft);
    margin-top: 4px;
  }
.chat-claude-panel .ccp-head {
    font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
    color: var(--fg-3); margin-bottom: 5px;
  }
.chat-claude-panel .ccp-ctx-row {
    display: flex; align-items: baseline; gap: 1px;
    font-size: 13px; font-weight: 600; color: var(--fg-1);
  }
.chat-claude-panel .ccp-ctx-max { color: var(--fg-3); font-weight: 500; font-size: 12px; }
.chat-claude-panel .ccp-bar {
    height: 5px; border-radius: 3px; background: var(--bg-1);
    overflow: hidden; margin-top: 5px;
  }
.chat-claude-panel .ccp-bar-fill {
    height: 100%; background: var(--accent); border-radius: 3px;
    transition: width .25s ease;
  }
.chat-claude-panel .ccp-bar-fill.warn { background: var(--danger); }
.chat-claude-panel .ccp-u-row { margin-bottom: 8px; }
.chat-claude-panel .ccp-u-row:last-child { margin-bottom: 0; }
.chat-claude-panel .ccp-u-top {
    display: flex; justify-content: space-between;
    font-size: 12px; color: var(--fg-1);
  }
.chat-claude-panel .ccp-u-pct { font-weight: 600; }
.chat-claude-panel .ccp-u-reset { font-size: 10px; color: var(--fg-3); margin-top: 3px; }
.chat-claude-panel .ccp-model-row { display: flex; align-items: center; justify-content: space-between; }
.chat-claude-panel .ccp-m-main { display: flex; flex-direction: column; gap: 1px; }
.chat-claude-panel .ccp-m-label { font-size: 13px; color: var(--fg-1); }
.chat-claude-panel .ccp-m-sub { font-size: 10px; color: var(--fg-3); }

/* ── Footer skill-selector panel (right-aligned, mirrors model picker) ─────
   A floating panel anchored to the RIGHT edge of the chat pill footer, sitting
   just above the context-details line. Shows every selectable skill (authored +
   ability-bundled), greyed out when not active, highlighted when engaged.
   Uses the same max-height as the model picker so the two panels are cohesive.
   PANEL-SYNC-FOOTER: keep .chat-skill-picker and .chat-model-picker in sync —
   if you change max-height, border-radius, padding, shadow, z-index, or
   background on one, mirror it on the other. */
.chat-skill-picker {
    display: none;
    position: fixed;
    z-index: 1000;
    box-sizing: border-box;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    padding: 6px;
    min-width: 220px;
    max-width: 300px;
    box-shadow: var(--shadow-md);
  }
.chat-skill-picker .csp-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 6px;
    font-size: 11px;
    font-weight: 600;
    color: var(--fg-2);
    border-bottom: var(--border-width) solid var(--border-soft);
  }
.chat-skill-picker .csp-header svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
  }
.chat-skill-picker .csp-list {
    max-height: 360px;
    overflow-y: auto;
    margin-top: 4px;
  }
.chat-skill-picker .csp-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    color: var(--fg-1);
    transition: background 0.1s;
  }
.chat-skill-picker .csp-item:hover { background: var(--accent-soft); }
.chat-skill-picker .csp-item.csp-active { background: var(--accent-mid); }
.chat-skill-picker .csp-item.csp-always-on { cursor: default; opacity: 0.9; }
.chat-skill-picker .csp-item.csp-always-on:hover { background: var(--accent-mid); }
.chat-skill-picker .csp-item.csp-idle {
    color: var(--fg-3);
    opacity: 0.7;
  }
.chat-skill-picker .csp-item.csp-idle:hover {
    color: var(--fg-1);
    opacity: 1;
  }
.chat-skill-picker .csp-item-indicator {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border-radius: 50%;
    font-size: 9px;
    font-weight: 700;
  }
.chat-skill-picker .csp-item.csp-active .csp-item-indicator {
    background: var(--success);
    color: #fff;
  }
.chat-skill-picker .csp-item.csp-idle .csp-item-indicator {
    background: var(--bg-2);
    color: var(--fg-4);
    border: var(--border-width) solid var(--border-soft);
  }
.chat-skill-picker .csp-item-name {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
.chat-skill-picker .csp-empty {
    padding: 12px 8px;
    color: var(--fg-3);
    font-size: 11px;
    text-align: center;
  }

/* ── Skill toggle button in footer (counter badge) ────────────────────────
   Sits in the footer row, right side next to the mode button. Shows the
   Blocks icon and a badge with the number of active skills. PANEL-SYNC-FOOTER:
   keep visual style aligned with .chat-mode-btn. */
.chat-skills-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    /* CHAT-PILL-SYNC: same glass skin as the pill (see .chat-continue-btn) so the
       abilities button matches the pill instead of being a see-through icon. */
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    border-radius: 8px;
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    color: var(--fg-3);
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
    flex-shrink: 0;
  }
.chat-skills-btn svg {
    width: 20px;
    height: 20px;
  }
/* Lucide SVG inside button must not swallow clicks (lucide SVG quirk). On touch
   devices a tap that lands on the inner <svg>/badge never fires the button's
   click handler, so the abilities panel wouldn't open on mobile — the icon fills
   nearly the whole button, so on a phone every tap lands on it. Mirrors the same
   fix on the other icon buttons (#chat-toggle-btn > *, .chat-header-more-btn > *). */
  .chat-skills-btn > * { pointer-events: none; }
body.light-mode .chat-skills-btn {
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 85%, transparent));
  }
.chat-skills-btn:hover {
    border-color: color-mix(in oklab, var(--accent) 45%, transparent);
    color: var(--accent);
  }
.chat-skills-btn .csp-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    border-radius: 9px;
    padding: 0 5px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    background: var(--accent);
    color: #fff;
    pointer-events: none;
    user-select: none;
    transition: opacity 0.2s;
    opacity: 0;
  }
.chat-skills-btn .csp-badge.visible { opacity: 1; }

/* The Terminal-keys toggle + in-pill key view styling is PLUGIN-OWNED — it ships
   with the terminal_chat engine at plugins/engines/terminal_chat/ui/terminal-keys.css
   and is injected only when a Terminal Chat session mounts, so the core carries none
   of it (nor the .chat-term-keys-btn / .chat-term-keypad / .ctk-chip rules). */

/* ── Execution mode button (single cycle: Read → Ask → Auto → Read) ─────
   Sits above the chat pill, right-aligned alongside continue/stop buttons.
   Styled identically to .chat-continue-btn for visual consistency. */
.chat-mode-btn {
    display: inline-flex;
    align-self: center;
    /* CHAT-PILL-SYNC: same glass skin as the pill (see .chat-continue-btn). */
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    color: var(--brand);
    font-size: 13px;
    font-weight: 600;
    padding: 5px 16px;
    border-radius: 14px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    line-height: 1.5;
    user-select: none;
    -webkit-user-select: none;
    font-family: inherit;
  }
.chat-mode-btn:hover {
    border-color: rgba(var(--brand-rgb), 0.45);
  }
.chat-mode-btn:active {
    border-color: rgba(var(--brand-rgb), 0.6);
  }
body.light-mode .chat-mode-btn {
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 85%, transparent));
    color: var(--brand);
  }
body.light-mode .chat-mode-btn:hover {
    border-color: rgba(var(--brand-rgb), 0.45);
  }

/* ── Chat target-device pill — sets which device runs the next turn ────────
   Sibling of .chat-mode-btn; opens the shared DevicePicker. Resting = a subtle
   monitor icon (runs locally on THIS device); active = brand-tinted with the
   chosen device's label shown. See ui/chat-side-panel/js/chat-ui.js. */
.chat-target-btn {
    display: inline-flex; align-items: center; gap: 6px; align-self: center;
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    color: var(--fg-3); font-size: 13px; font-weight: 600;
    padding: 5px 12px; border-radius: 14px; cursor: pointer;
    transition: background .15s, border-color .15s, color .15s;
    line-height: 1.5; user-select: none; -webkit-user-select: none; font-family: inherit;
  }
.chat-target-btn:hover { border-color: rgba(var(--brand-rgb), 0.45); color: var(--fg-1); }
.chat-target-btn svg { width: 16px; height: 16px; }
.chat-target-btn .chat-target-label {
    display: none; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
.chat-target-btn.targeting {
    color: var(--brand); border-color: rgba(var(--brand-rgb), 0.5);
    background: color-mix(in oklab, var(--brand) 12%, var(--chat-pill-bg, transparent));
  }
.chat-target-btn.targeting .chat-target-label { display: inline; }

/* ── Shared target-device picker (ui/shared/js/device-picker.js) ──────────
   Floating fleet menu reused by the chat pill, the automations "Run on"
   column, and the sessions badge. Themed from the design-system palette so it
   is correct in dark AND light mode. */
.device-picker-backdrop { position: fixed; inset: 0; z-index: 9998; background: transparent; }
.device-picker-pop {
    position: fixed; z-index: 9999; min-width: 220px; max-width: 300px;
    background: var(--bg-elev); color: var(--fg-1);
    border: var(--border-width) solid var(--border); border-radius: 14px;
    box-shadow: 0 14px 36px rgba(0,0,0,0.30); padding: 8px; font-size: 13px;
  }
.device-picker-head {
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--fg-3); padding: 2px 6px 7px;
  }
.device-picker-list { display: flex; flex-direction: column; gap: 2px; max-height: 264px; overflow-y: auto; }
.device-picker-row {
    display: flex; align-items: center; gap: 9px; width: 100%;
    background: transparent; border: none; cursor: pointer; padding: 7px 8px;
    border-radius: 9px; color: var(--fg-1); font-size: 13px; text-align: left; font-family: inherit;
  }
.device-picker-row:hover { background: color-mix(in oklab, var(--brand) 12%, transparent); }
.device-picker-row.selected { background: color-mix(in oklab, var(--brand) 16%, transparent); }
.device-picker-row .dp-label { flex: 1; display: flex; flex-direction: column; line-height: 1.25; }
.device-picker-row .dp-sub { font-size: 11px; color: var(--fg-3); font-weight: 400; }
.device-picker-row .dp-check { width: 15px; height: 15px; color: var(--brand); flex: 0 0 auto; }
.device-picker-pop .dp-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.device-picker-pop .dp-dot.online { background: var(--success); box-shadow: 0 0 0 3px rgba(var(--success-rgb), 0.25); }
.device-picker-pop .dp-dot.offline { background: var(--fg-3); opacity: .5; }
.device-picker-empty { padding: 9px; color: var(--fg-3); font-size: 12px; text-align: center; }
.device-picker-offline { margin-top: 8px; padding-top: 8px; border-top: var(--border-width) solid var(--border); }
.dp-offline-title { font-size: 11px; color: var(--fg-3); padding: 0 6px 6px; }
.dp-offline-seg { display: flex; gap: 5px; padding: 0 4px; }
.dp-off-opt {
    flex: 1; padding: 5px 0; border-radius: 8px; cursor: pointer;
    background: color-mix(in oklab, var(--bg-elev) 60%, transparent);
    border: var(--border-width) solid var(--border); color: var(--fg-3);
    font-size: 12px; font-weight: 600; font-family: inherit;
  }
.dp-off-opt.on {
    background: color-mix(in oklab, var(--brand) 18%, transparent);
    color: var(--brand); border-color: rgba(var(--brand-rgb), .45);
  }

/* Sessions device badge + automations "Run on" chip — shared compact look. */
.sess-device-badge, .auto-device-chip {
    display: inline-flex; align-items: center; gap: 4px; vertical-align: middle;
    font-size: 11px; color: var(--fg-3); padding: 1px 7px; border-radius: 999px;
    border: var(--border-width) solid var(--border);
    background: color-mix(in oklab, var(--bg-elev) 70%, transparent);
    max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
.auto-device-chip { cursor: pointer; }
.auto-device-chip:hover { color: var(--fg-1); border-color: rgba(var(--brand-rgb), .4); }
.auto-device-chip.targeting {
    color: var(--brand); border-color: rgba(var(--brand-rgb), .5);
    background: color-mix(in oklab, var(--brand) 12%, transparent);
  }
.auto-device-chip svg, .sess-device-badge svg { width: 11px; height: 11px; flex: 0 0 auto; }

/* Model switcher pill — sibling of the mode pill; shows the active model's short
   name and opens the model picker. Muted (secondary) vs. the brand mode pill, and
   truncates long model ids. */
.chat-model-btn {
    display: inline-flex;
    align-items: center;
    align-self: center;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: rgba(var(--brand-rgb), 0.05);
    border: var(--border-width) solid var(--border);
    color: var(--fg-3);
    font-size: 12px;
    font-weight: 600;
    font-family: var(--font-mono, 'JetBrains Mono', monospace);
    padding: 5px 12px;
    border-radius: 14px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    line-height: 1.5;
    user-select: none;
    -webkit-user-select: none;
  }
.chat-model-btn:hover {
    background: var(--accent-soft);
    border-color: rgba(var(--brand-rgb), 0.38);
    color: var(--accent);
  }

/* Chat messages container — drop target with overlay */
  #chat-messages {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    position: relative;
    overflow: hidden;
  }
#chat-messages-inner {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    padding: 12px 8px 40px 15px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    scrollbar-width: thin;
    scrollbar-color: var(--bg-tint) transparent;
  }
  /* Chat jump-nav FABs — the round chevron buttons that float over the chat
     scroller: the scroll-to-bottom button above the pill, and the two
     top-of-panel jump buttons (#chat-top-nav). All share ONE themed look so a
     palette swap (green / blue / orange …) re-tints every one of them. Glyph
     + halo all derive from the SAME accent family (--accent / --accent-line /
     --accent-mid) so they stay one hue under any theme or custom palette
     override — never a hard-coded grey. Hidden by default; JS toggles
     `.visible` per-button. */
  .chat-nav-fab {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: var(--border-width) solid var(--accent-line);
    background: var(--accent-mid);
    color: var(--accent);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.2s, background 0.2s, color 0.2s, border-color 0.2s;
    opacity: 0;
    pointer-events: none;
    box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.3);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }
  .chat-nav-fab.visible {
    opacity: 1;
    pointer-events: auto;
  }
  .chat-nav-fab:hover {
    background: var(--accent-line);
    border-color: var(--accent);
    color: var(--accent);
  }
  /* Scroll-to-bottom FAB — floats centered above the chat pill, just below
     the message fade mask. Shown when the user has scrolled up away from the
     latest message. Clicking it scrolls to the bottom and re-engages
     auto-scroll. (Visual look comes from the shared .chat-nav-fab above.) */
  #chat-scroll-bottom-btn {
                position: absolute;
                /* Floats above the action row inside chat-above-pill, right-aligned
                   so Continue/Stop don't hide it. 4px gap breathes between the
                   action row and the FAB. */
                bottom: calc(100% + 4px);
                right: 14px;
                z-index: 10;
              }
  /* Top-of-panel jump nav — a side-by-side pair floating over the top edge of
     the message scroller. Double chevron (left) → start of session; single
     chevron (right) → step up one user message. The container is click-through
     so the gaps never block scrolling; each visible button re-enables its own
     clicks. */
  #chat-top-nav {
                position: absolute;
                top: 8px;
                right: 14px;
                z-index: 10;
                display: flex;
                flex-direction: row-reverse;
                gap: 6px;
                pointer-events: none;
              }
/* Session-switch loading spinner — shown in the chat area while messages
   are being fetched from the server after selecting a different session. */
.chat-loading-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  gap: 12px;
  padding: 48px 16px;
  color: var(--fg-3);
}
.chat-loading-spinner {
  animation: chat-loading-spin 1s linear infinite;
}
.chat-loading-spinner svg {
  display: block;
}
.chat-loading-text {
  font-size: 12px;
  letter-spacing: 0.3px;
  opacity: 0.7;
}
@keyframes chat-loading-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
body.light-mode .chat-loading-wrap {
  color: var(--fg-3);
}

/* ── Pin-swipe toast ── */
.pin-swipe-toast {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.92);
  background: color-mix(in oklab, var(--bg-elev) 88%, transparent);
  backdrop-filter: blur(8px);
  border: var(--border-width) solid var(--border);
  border-radius: 12px;
  padding: 12px 24px;
  font-size: 13px;
  color: var(--fg-1);
  white-space: nowrap;
  pointer-events: none;
  z-index: 15;
  opacity: 0;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.pin-swipe-toast.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* ── Pin-swipe next panel ── */
.chat-messages-next {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  padding: 12px 8px 40px 15px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--bg-tint) transparent;
  z-index: 14;
  background: var(--bg-1);
  opacity: 0;
  pointer-events: none;
  border-left: var(--border-width) solid var(--border);
}

.chat-bubble {
    padding: 10px 14px;
    border-radius: 12px;
    font-size: 13px;
    line-height: 1.5;
    max-width: 90%;
    word-wrap: break-word;
    white-space: pre-wrap;
    flex-shrink: 0;
  }

/* Header row inside every chat bubble: timestamp on the left, role label on the right. */
.bubble-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 4px;
}

/* Relative timestamp in the bubble-actions row — "5m", "1h 13m", etc.
   Small text, sits at the left of the footer. The auto-margin that pushes the
   action buttons to the right lives on the model tag when present, else here
   (see the :has() rules below). */
.bubble-time {
  font-size: 11px;
  color: var(--fg-3);
  opacity: 0.7;
  user-select: none;
  pointer-events: none;
  line-height: 1;
  align-self: center;
}
/* The left-most footer element carries the auto-margin that right-aligns the
   buttons: the model tag if there is one, otherwise the timestamp. */
.bubble-actions .bubble-model { margin-right: auto; }
.bubble-actions:not(:has(.bubble-model)) .bubble-time { margin-right: auto; }

/* Per-turn model tag — "deepseek-v4-flash HIGH" (model + reasoning-effort mode).
   Left-truncated (direction: rtl + ellipsis) so the END of the name stays
   visible when space is tight; the inner <bdi> keeps the text reading L→R. */
.bubble-model {
  font-size: 11px;
  color: var(--fg-3);
  opacity: 0.7;
  align-self: center;
  line-height: 1;
  user-select: none;
  max-width: 55%;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  direction: rtl;
}
.bubble-model > bdi { unicode-bidi: isolate; }
.chat-bubble.user {
    background: rgba(var(--brand-rgb), 0.08);
    border: 1px solid rgba(var(--brand-rgb), 0.19);
    color: var(--fg-1);
    align-self: flex-end;
    border-bottom-right-radius: 4px;
  }
.chat-bubble.agent {
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    color: var(--fg-2);
    align-self: flex-start;
    border-bottom-left-radius: 4px;
  }

.chat-bubble.agent.streaming {
  border-color: rgba(var(--brand-rgb), 0.31);
  display: flex;
  flex-direction: column;
}

/* Grouped agent bubbles — consecutive prose from one reply "joins up" into a
   single stack: the gap tightens and the touching corners square off so the run
   reads as one block, with the rounded tail surviving only at the very bottom.
   Toggled by _applyGrouping in chat-bubble.js; see GROUP_GAP_MS there for the
   time-apart threshold past which bubbles stay separate. */
.chat-bubble.agent.grouped-cont {
  margin-top: -8px;                 /* pulls back the 12px flex gap to a tight ~4px */
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
}
.chat-bubble.agent.grouped-open {
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

/* Task frames — a border drawn around every run of bubbles that belong to the
   same "task" (one request + the agent's work + the user's approval/feedback).
   Drawn by ui/chat-side-panel/js/chat-task-frames.js as a non-reparenting
   overlay: absolutely-positioned boxes behind the bubbles. Bubbles are lifted
   above the frame layer so the frame reads as an enclosing band in the gaps.
   Themed from the brand palette so it tracks any dark/light scheme swap. */
#chat-messages-inner > .chat-bubble { position: relative; z-index: 1; }

/* ── Terminal Chat overlay — xterm.js replaces the bubble area ── */
  #chat-terminal-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow: hidden;
    background: #0d0d1a;
    z-index: 5;
    border-radius: 8px;
    margin: 4px;
  }
  #chat-terminal-overlay .xterm { height: 100%; padding: 8px 4px; }
  #chat-terminal-overlay .xterm-viewport { scrollbar-width: thin; }
.task-frame {
  position: absolute;
  left: 2px;
  right: 2px;
  z-index: 0;
  pointer-events: none;
  border: var(--border-width) solid rgba(var(--brand-rgb), 0.28);
  border-radius: 14px;
  background: rgba(var(--brand-rgb), 0.035);
  box-sizing: border-box;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.stop-btn {
  align-self: flex-end;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  background: none;
  border: none;
  padding: 0;
  margin-top: 4px;
}

.stop-btn:hover {
  opacity: 0.7;
}

/* Per-bubble action row: read-aloud (TTS) + copy. Rendered by
   chat.js _addBubbleActions for every bubble — including streaming/recovering
   agent bubbles (the delete button is held back until the turn finalizes). */
.bubble-actions {
  display: flex;
  gap: 4px;
  margin-top: 4px;
  padding-top: 4px;
  border-top: none;
  opacity: 0.55;
  transition: opacity 0.15s ease;
}
.chat-bubble:hover .bubble-actions,
.chat-bubble:focus-within .bubble-actions { opacity: 1; }
.chat-bubble.user .bubble-actions { justify-content: flex-end; }
.bubble-action-btn {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 3px 6px;
  cursor: pointer;
  color: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
.bubble-action-btn:hover {
  background: rgba(var(--ink-rgb), 0.06);
  border-color: rgba(var(--ink-rgb), 0.12);
}
.bubble-action-btn.copied { color: var(--success); }
.bubble-action-btn[data-speaking="true"] { color: var(--brand); }
/* Per-turn delete button — danger-tinted on hover, filled when armed (⚠️). */
.bubble-action-btn.bubble-collapse-btn:hover { color: var(--brand); }
.bubble-action-btn.bubble-delete-btn:hover { color: var(--danger); }
.bubble-action-btn.bubble-delete-btn.warning {
  color: var(--danger);
  background: var(--danger-soft);
  border-color: var(--danger-mid);
}

.chat-bubble.error {
    background: var(--error-bubble-bg);
    border: 1px solid var(--error-bubble-border);
    color: var(--danger);
    align-self: flex-start;
  }
/* "Session not found" — shown in place of the transcript when the open session
   was permanently deleted elsewhere (agentWs.js 'session_deleted'). Reuses the
   error-bubble palette (correct in both themes) but centres itself as a standalone
   notice rather than a left-aligned reply. */
.chat-bubble.agent.session-deleted-notice {
    background: var(--error-bubble-bg);
    border: 1px solid var(--error-bubble-border);
    color: var(--danger);
    align-self: center;
    text-align: center;
    max-width: 320px;
    margin: auto;
    padding: 16px 14px;
    font-weight: 500;
  }
/* Passive heads-up from the Git Control repo-change watcher (see
   plugins/abilities/Administrator/git_control + agentWs.js 'repo_change_notice').
   Keeps the agent-bubble background (correct in either theme) and adds a green
   left accent — Git Control's colour — to mark it as a watcher notice rather
   than a normal agent reply. */
.chat-bubble.agent.git-watch-notice {
    border-left: 3px solid var(--success);
  }
.chat-bubble .label {
    font-size: 12px;
    font-weight: 600;
    color: var(--fg-3);
    display: block;
    margin-bottom: 5px;
  }
/* Collapsed bubble — hides the message body but keeps the action footer visible. */
.chat-bubble.collapsed {
  padding-bottom: 0;
}
.chat-bubble.collapsed > :not(.bubble-actions):not(.label),
.chat-bubble.collapsed > .bubble-body,
.chat-bubble.collapsed > .md-body {
  display: none;
}
.chat-bubble.collapsed > .label {
  margin-bottom: 0;
}
.chat-bubble.collapsed .bubble-actions {
  opacity: 1;
  margin-top: 0;
  border-top: none;
  padding-top: 4px;
  padding-bottom: 4px;
}

.chat-bubble.interrupted {
    border: 1px solid var(--warning);
    color: var(--warning);
    align-self: flex-start;
    font-style: italic;
  }

/* ── Pending (outbox) bubble — message queued locally because the server
     was unreachable. Retry and dismiss buttons appear in the action bar. */
.chat-bubble.user.pending {
  border: 1px dashed var(--warning);
  background: rgba(var(--warning-rgb), 0.03);
}
.chat-bubble.user.pending .label {
  color: var(--warning);
}
.chat-bubble.user.pending .label::after {
  content: ' ⏳';
  font-size: 11px;
}
.pending-actions {
  display: flex;
  gap: 6px;
  margin-top: 4px;
  padding-top: 4px;
  opacity: 0.7;
}
.bubble-action-btn.pending-retry {
  color: var(--brand);
}
.bubble-action-btn.pending-retry:hover {
  color: var(--brand);
  background: rgba(var(--brand-rgb), 0.12);
}
.bubble-action-btn.pending-dismiss {
  color: var(--danger);
}
.bubble-action-btn.pending-dismiss:hover {
  color: var(--danger);
  background: rgba(var(--danger-rgb), 0.12);
}
/* Light mode adjustments — a stronger warm fill than the base 3% tint. */
body.light-mode .chat-bubble.user.pending {
  border-color: var(--warning);
  background: var(--warning-soft);
}
body.light-mode .chat-bubble.user.pending .label {
  color: var(--warning);
}

/* Virtual-scroll placeholder — invisible spacer that preserves scroll height */
.chat-bubble-placeholder {
  padding: 10px 14px;
  margin: 0;
  border: none;
  background: transparent;
  pointer-events: none;
  flex-shrink: 0;
}

/* ── Rendered Markdown inside agent bubbles ───────────────────────────────
   Agent replies render as Markdown into a `.md-body` (see
   ui/chat-side-panel/js/chat-bubble.js _renderMarkdownBody). The bubble keeps
   its own bg/text colour; these rules
   only lay out the block elements. Theme-safe by design: structural surfaces
   (code, tables, rules) use design-system tokens, while text inherits the
   bubble colour, so dark + light both work without per-theme overrides. */
.chat-bubble.md { white-space: normal; }
.md-body { white-space: normal; line-height: 1.55; }
.md-body > :first-child { margin-top: 0; }
.md-body > :last-child { margin-bottom: 0; }
.md-body p { margin: 0 0 8px; }
.md-body h1, .md-body h2, .md-body h3,
.md-body h4, .md-body h5, .md-body h6 {
  margin: 14px 0 6px;
  line-height: 1.3;
  font-weight: 600;
  color: var(--fg-1);
}
.md-body h1 { font-size: 1.4em; }
.md-body h2 { font-size: 1.25em; }
.md-body h3 { font-size: 1.12em; }
.md-body h4, .md-body h5, .md-body h6 { font-size: 1em; }
.md-body ul, .md-body ol { margin: 0 0 8px; padding-left: 20px; }
.md-body li { margin: 2px 0; }
.md-body li > ul, .md-body li > ol { margin: 2px 0; }
.md-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.md-body strong { font-weight: 700; color: var(--fg-1); }
.md-body em { font-style: italic; }
.md-body del { opacity: 0.7; }
.md-body hr { border: 0; border-top: var(--border-width) solid var(--border); margin: 12px 0; }
.md-body blockquote {
  margin: 0 0 8px;
  padding: 2px 0 2px 12px;
  border-left: 3px solid var(--border-strong);
  color: var(--fg-3);
}
/* Inline code */
.md-body code {
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 0.92em;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}
/* Fenced code block */
.md-body pre {
  margin: 0 0 8px;
  padding: 10px 12px;
  background: var(--bg-0);
  border: var(--border-width) solid var(--border);
  border-radius: 8px;
  overflow-x: auto;
}
.md-body pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: 0.88em;
  white-space: pre;
  color: inherit;
}
/* Tables — block + max-content so wide tables scroll horizontally instead
   of bursting the bubble (the GitHub-markdown pattern). */
.md-body table {
  display: block;
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 0 0 8px;
  font-size: 0.95em;
}
.md-body th, .md-body td {
  border: var(--border-width) solid var(--border);
  padding: 5px 9px;
  text-align: left;
}
.md-body th { background: var(--bg-tint); font-weight: 600; color: var(--fg-1); }
.md-body tbody tr:nth-child(even) td { background: var(--bg-tint); }
.md-body img { max-width: 100%; border-radius: 8px; }

/* ── Stop + Continue action buttons (above the chat pill) ──────────────────
   Both buttons are ALWAYS present, sitting in #chat-action-row at the pill's
   right edge. Their *resting* state is quiet (dimmed, no glow); the one that's
   relevant right now gets `.active` (full opacity + a breathing glow) from
   ui/chat-side-panel/js/chat-ui.js. Stop is active while the agent processes; Continue is active
   while the agent is idle and waiting. The row itself hides only when no agent
   is selected. The glow uses currentColor in its box-shadow, so the single
   keyframe adapts to each button's own colour and to light mode automatically. */
/* CHAT-ABOVE-PILL: the band directly above the pill that holds BOTH the live
   activity bar (the "Thinking…"/tool-call chip + chevron) and the Stop/Continue
   actions on ONE row. The action row rides in normal flow on the right (defining
   the band's height); #chat-activity is absolutely positioned to fill the band
   from the left, baselined to the same row, at a LOWER z-index — so when the
   activity note runs long it extends rightward and slides BEHIND the buttons
   (which carry the pill's opaque glass) instead of wrapping or shoving them
   off-row. The expandable tool-call panel grows upward as a floating overlay.
   Capped to the pill width and centred per CHAT-PILL-ALIGN. pointer-events:none
   lets the empty band pass clicks through to the messages behind; the buttons,
   bar and panel each re-enable their own. */
.chat-above-pill {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;      /* Stop/Continue hug the pill's right edge */
    width: 100%;
    max-width: var(--chat-pill-max-width, 500px);
    margin-inline: auto;
    z-index: 6;
    pointer-events: none;
  }
/* Whole band collapses when no agent is selected. The action row is the only
   in-flow child, so hiding it alone would otherwise leave a stray gap. */
.chat-above-pill:has(.chat-action-row.hidden) { display: none; }

.chat-action-row {
    display: flex;
    justify-content: flex-end;      /* Stop/Continue hug the pill's right edge */
    align-items: center;
    gap: 6px;
    /* Rides ABOVE the activity bar (z-index) so a long activity note slides
       behind these buttons; re-enables pointer events the wrapper switched off. */
    position: relative;
    z-index: 2;
    pointer-events: auto;
  }
/* Hidden when no agent is selected (toggled by chat.js). */
.chat-action-row.hidden { display: none; }

.chat-continue-btn,
.chat-stop-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    padding: 5px 16px;
    border-radius: 14px;
    cursor: pointer;
    line-height: 1.5;
    user-select: none;
    -webkit-user-select: none;
    font-family: inherit;
    /* CHAT-PILL-SYNC: wear the chat pill's exact glass skin — same background
       (the customisable --chat-pill-bg, falling back to the panel-tint the pill
       uses), same soft currentColor border and the same blur — so the controls
       around the pill read as the same opaque material instead of see-through
       tints. The relevant button is signalled by the active-glow below, not by
       being dimmed, so they rest at full opacity. */
    opacity: 1;
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    transition: background 0.15s, border-color 0.15s, opacity 0.2s, box-shadow 0.2s;
  }
body.light-mode .chat-continue-btn,
body.light-mode .chat-stop-btn {
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 85%, transparent));
  }
/* The relevant button: full strength + a breathing glow that matches the
   chat pill's rhythm. currentColor keeps it tied to each button's own colour. */
.chat-continue-btn.active,
.chat-stop-btn.active {
    opacity: 1;
    box-shadow: 0 0 0 1px color-mix(in oklab, currentColor 50%, transparent),
                0 0 10px -1px color-mix(in oklab, currentColor 60%, transparent);
  }

/* Continue — clicking it sends a "continue" message. Background/border/blur come
   from the shared glass rule above (matching the pill); only the brand text and
   the hover/active border-strengthening live here. */
.chat-continue-btn {
    color: var(--brand);
  }
.chat-continue-btn:hover {
    border-color: rgba(var(--brand-rgb), 0.45);
  }
.chat-continue-btn:active {
    border-color: rgba(var(--brand-rgb), 0.6);
  }
body.light-mode .chat-continue-btn {
    color: var(--brand);
  }
body.light-mode .chat-continue-btn:hover {
    border-color: rgba(var(--brand-rgb), 0.45);
  }

/* Stop — clicking it interrupts the run. Same shared pill glass; the red
   identity stays in the text colour and the hover/active border. */
.chat-stop-btn {
    color: var(--danger);
  }
.chat-stop-btn:hover {
    border-color: rgba(var(--danger-rgb), 0.38);
  }
.chat-stop-btn:active {
    border-color: rgba(var(--danger-rgb), 0.56);
  }

/* ── Suggested-reply chips (above the chat pill) ────────────────────────────
   Populated by ui/chat-side-panel/js/suggestions.js from the silent user-impersonator agent.
   Tapping a chip fills the pill. Theme-safe via design-system variables. */
.chat-suggest-row {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    align-self: stretch;
    margin-bottom: 4px;
    padding: 0 6px;
    width: 100%;
    max-width: var(--chat-pill-max-width, 500px);
    margin-inline: auto;
  }
.chat-suggest-row:empty {
    display: none;
  }
/* Rendered as low-emphasis grey "ghost" text — a quiet hint of what the user
   might say next, not a prominent button. Tappable; brightens on hover. */
.chat-suggest-chip {
    max-width: 100%;
    background: none;
    border: none;
    padding: 3px 0;
    color: var(--fg-4);
    font-size: 13px;
    line-height: 1.4;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color 0.15s;
    user-select: none;
    -webkit-user-select: none;
  }
.chat-suggest-chip:hover {
    color: var(--fg-2);
    text-decoration: underline;
    text-underline-offset: 2px;
  }
.chat-suggest-chip:active {
    color: var(--fg-1);
  }

/* ── Chat-pill ─────────────────────────────────────────────────────────────
   CHAT-PILL-SYNC: single source of truth for the GEOMETRY/behavior of every
   chat-style "pill" input in the app (layout, buttons, has-text swap, sizing).

   TWO SHAPES, intentionally different:
     • The BASE .chat-pill block below is the TALL multi-line composer — a
       roomy ~96px box whose mic/send + attach stack bottom-right and whose
       textarea grows up to 6 lines. This is used by ONE pill only: the
       right-hand CHAT PANEL (#chat-input-row). It is the app's primary
       conversation box, so it gets the big composer.
     • Every OTHER pill is a COMPACT SINGLE-LINE bar — see the
       `.chat-pill-1line` modifier further down. It lays the same three
       controls out on ONE row (attach · input · mic/send) at ~46px tall and
       only grows a little when the text wraps. These are quick "ask"/"build"
       bars, not full composers, so they stay slim.
   The two used to be the same height and were bottom-aligned across
   side-by-side panels; that alignment rule is GONE — the chat pill is now
   deliberately taller than the rest, and no pill computes a bottom offset to
   match another.

   NOTE: the base skin here is an opaque dark pill with a hard border. Four
   pills are re-skinned as transparent "floating glass" in ui/shared/css/index.css
   (loaded last): the web-chat pill (#chat-panel .chat-pill), the agents-page
   builder bar (#agent-builder-bar > .chat-pill), the Gen UI prompt bar
   (#genui-prompt-row), and the in-app browser pill (.browser-prompt-row).
   If you change the float look, edit index.css; geometry stays here.
   Apply these classes to opt in:
     .chat-pill        — outer container (the rounded box; TALL composer)
     .chat-pill-1line  — modifier on .chat-pill → COMPACT single-line bar
     .chat-pill-input  — textarea / text input inside the pill
     .chat-pill-attach — left-side paperclip / attach icon button
     .chat-pill-voice  — right-side mic icon button
     .chat-pill-send   — right-side send icon button
   Row state classes:
     .has-text   — set by JS when input has content (swaps mic→send)
     .no-voice   — force send-only (used by web-chat sign-in gate)
     .thinking   — set by chat-activity.js while the agent works (glow)
    Known pills using these classes:
      #chat-input-row              (web chat — chat.js)            TALL
      #ac-int-admin-chat-input-row (integration admin chat)        1-line
      #agent-builder-bar-row       (agents page — agents.js)       1-line
      #genui-prompt-row        (Gen UI tab — genui.js)     1-line
      .browser-prompt-row          (in-app browser — browser.js)   1-line
    When tweaking, change here and every pill in the app updates; add
    `.chat-pill-1line` to a pill's markup to make it the compact variant.
    SIBLING-BTN-COHESION: all buttons displayed around the pill (Continue,
    Stop, tool-call bar, suggested chips, etc.) must share the same box math
    — 12px font-size, 1.4 line-height, 4px 14px padding, 12px radius — so
    they sit at the same visual height and look like one cohesive set of
    controls. If you add or change a sibling button, grep SIBLING-BTN-COHESION
    and match every instance.
    SEPARATE concern — the FLOATING pills (web-chat, agents builder bar,
   Gen UI, browser) also share a PANEL LAYOUT this block does NOT cover: the
   grep tag PILL-PANEL-LAYOUT (full-height scroller + absolute pill overlay),
   anchored at #chat-input-area above and mirrored on #agent-builder-bar in
   agents.css etc. (The old cross-panel bottom-alignment rule — PILL-PANEL-ALIGN
   — has been removed now that the chat pill is intentionally taller than the
   rest; pills no longer compute a bottom offset to match each other.) */

.chat-pill {
    /* Grid layout: text fills the left column and spans every row; the
       right column stacks send (or mic) on TOP and attach immediately
       BELOW it, both anchored at the bottom of the pill via the 1fr
       spacer row above them. Position: relative is for the ::before
       scroll-position indicator that sits on the left edge. */
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: 1fr auto auto;
    column-gap: 6px;
    row-gap: 0;
    align-items: stretch;
    position: relative;
    width: 100%;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: var(--chat-pill-radius, 20px);
    /* Extra left padding so the caret/text sits visually inside the pill
       and leaves room for the scroll-position indicator on the left. */
    padding: var(--chat-pill-padding, 4px 4px 4px 14px);
    transition: border-color 0.15s;
  }
/* CHAT-PILL-VARS: every geometry + button dimension of the TALL composer
   (#chat-input-row) is admin-configurable from app-settings.json. Each
   `chat_pill_*` key is injected app-wide as the matching `--chat-pill-*` CSS
   variable by ui/shared/js/appearance.js; the fallback after each `var()` here
   mirrors the JSON default, so every value holds even before the override
   stylesheet lands. The knobs (key → variable → what it sets):
     chat_pill_max_width   → --chat-pill-max-width   composer width cap
     chat_pill_radius      → --chat-pill-radius      corner roundness
     chat_pill_padding     → --chat-pill-padding     outer padding (shorthand)
     chat_pill_min_height  → --chat-pill-min-height  resting text-area height
     chat_pill_max_height  → --chat-pill-max-height  grow cap before scrolling
     chat_pill_font_size   → --chat-pill-font-size   input + placeholder text
     chat_pill_attach_size → --chat-pill-attach-size attach (+) button box
     chat_pill_attach_icon → --chat-pill-attach-icon attach (+) glyph
     chat_pill_button_size → --chat-pill-button-size mic / send button box
     chat_pill_button_icon → --chat-pill-button-icon mic / send glyph
   These drive only the TALL composer — the compact 1-line bars
   (search/builder/genui/browser) keep their own fixed sizing via the more
   specific `.chat-pill-1line *` rules further down, which override these. */
.chat-pill:not(.chat-pill-1line) {
    max-width: var(--chat-pill-max-width, 500px);
    margin-inline: auto;
  }
/* Vertical position indicator — a 2x6px dot on the left edge that slides
   between the top and bottom padding to show the user's scroll position
   within an overflowed textarea. JS (chat.js _updateScrollIndicator)
   writes --scroll-pct (0..1) and --indicator-opacity (0 when content
   fits, 1 when it overflows) onto the pill element. */
.chat-pill::before {
    content: "";
    position: absolute;
    left: 4px;
    /* Track runs between top and bottom padding minus the dot's height. */
    top: calc(6px + (100% - 12px - 6px) * var(--scroll-pct, 0));
    width: 2px;
    height: 6px;
    border-radius: 1px;
    background: var(--brand);
    opacity: var(--indicator-opacity, 0);
    transition: opacity 0.15s;
    pointer-events: none;
  }
.chat-pill:focus-within { border-color: var(--brand); }
.chat-pill.drag-over {
    border-color: var(--brand);
    border-style: dashed;
    background: rgba(var(--brand-rgb), 0.06);
    transition: border-color 0.15s, background 0.15s;
  }

.chat-pill-input {
    /* Span all grid rows so the textarea fills the full pill height
       while the attach/send icons stack in the right column. */
    grid-column: 1;
    grid-row: 1 / -1;
    min-width: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    padding: 14px 8px 14px 0;
    color: var(--fg-1);
    font-size: var(--chat-pill-font-size, 14px);
    font-weight: 400;
    /* Explicit line-height so the JS auto-resize and the min/max heights
       below all line up. 1 line of content = 18px. */
    line-height: 22px;
    /* Default pill is a 2-row textarea: 22*2 content + 14+14 padding = 72px. */
    min-height: var(--chat-pill-min-height, 96px);
    /* 6 visible content lines: 22px * 6 = 132px, plus 14px+14px vertical
       padding = 160px. JS toggles overflow-y to 'auto' when content
       exceeds this. */
    max-height: var(--chat-pill-max-height, 160px);
    overflow-y: hidden;
    outline: none;
    font-family: inherit;
    resize: none;
    -webkit-appearance: none;
            appearance: none;
    box-shadow: none;
    /* Hide the native scrollbar entirely. When content overflows past the
       6-line cap the textarea is still scrollable via wheel/keyboard, but
       no visual bar takes space from the text column. */
    scrollbar-width: none;
  }
.chat-pill-input::-webkit-scrollbar { width: 0; height: 0; display: none; }
.chat-pill-input:focus { border: none; outline: none; box-shadow: none; }
.chat-pill-input::placeholder { color: var(--fg-4); font-size: var(--chat-pill-font-size, 14px); }

/* iOS Safari auto-zooms inputs < 16px — bump on mobile to prevent that */
@media (max-width: 768px) {
  .chat-pill-input { font-size: 16px; }
}

/* Send / mic swap driven by .has-text on the row */
.chat-pill .chat-pill-send  { display: none; }
.chat-pill.has-text .chat-pill-send  { display: inline-flex; }
.chat-pill.has-text .chat-pill-voice { display: none; }
.chat-pill.no-voice .chat-pill-voice { display: none !important; }
.chat-pill.no-voice .chat-pill-send  { display: inline-flex; }

/* Unified icon-button styling: attach, voice, AND send share the same
   transparent-circle look; only the icon glyph differentiates them. */
.chat-pill-attach {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    width: var(--chat-pill-attach-size, 38px);
    height: var(--chat-pill-attach-size, 38px);
    padding: 0;
    border-radius: 50%;
    transition: 0.15s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
.chat-pill-voice,
.chat-pill-send {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    width: var(--chat-pill-button-size, 62px);
    height: var(--chat-pill-button-size, 62px);
    padding: 0;
    border-radius: 50%;
    transition: 0.15s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
/* Send (or mic) is the TOP of the right-side button stack; attach (+) sits
   immediately BELOW. Both rows are auto-sized and the 1fr spacer row above
   them pushes the whole stack to the bottom of the pill — so when the pill
   grows the buttons stay anchored at the bottom-right rather than spreading
   to the top/bottom corners. */
.chat-pill-voice,
.chat-pill-send {
    grid-column: 2;
    grid-row: 2;
    align-self: end;
    justify-self: end;
  }
.chat-pill-attach {
    grid-column: 2;
    grid-row: 3;
    align-self: end;
    justify-self: center;
  }
.chat-pill-attach:hover,
.chat-pill-voice:hover,
.chat-pill-send:hover {
    color: var(--brand);
    background: rgba(var(--brand-rgb), 0.08);
  }
.chat-pill-attach svg,
.chat-pill-voice svg,
.chat-pill-send svg { pointer-events: none; }
/* Icons inside the pill buttons — sized to fill the button comfortably.
   Targets <svg> because Lucide replaces <i data-lucide> with an inline SVG
   and copies the inline style attribute to it. !important beats that. */
.chat-pill-attach svg {
    width: var(--chat-pill-attach-icon, 24px) !important;
    height: var(--chat-pill-attach-icon, 24px) !important;
  }
.chat-pill-voice svg,
.chat-pill-send svg {
    width: var(--chat-pill-button-icon, 44px) !important;
    height: var(--chat-pill-button-icon, 44px) !important;
  }
.chat-pill-send:disabled { opacity: 0.4; cursor: default; }
.chat-pill-voice.recording {
    color: var(--danger);
    background: rgba(var(--danger-rgb), 0.08);
    animation: pulse 1s infinite;
  }

/* ── Compact single-line pill (.chat-pill-1line) ─────────────────────────────
   The variant used by EVERY pill except the chat panel (see CHAT-PILL-SYNC
   above). It re-lays the same three controls onto ONE centred row — attach ·
   input · mic/send — and shrinks the buttons + textarea so the whole pill rests
   at ~46px and only grows a few lines when the text wraps. The base TALL grid
   above is left untouched; these rules just re-place its cells. */
.chat-pill-1line {
    grid-template-columns: auto 1fr auto;   /* attach · input · mic/send */
    grid-template-rows: auto;
    align-items: center;
    /* No big left inset — the attach button (or the symmetric padding when a
       pill has none, e.g. the .no-voice integration bar) handles the gap. */
    padding: 4px 8px;
  }
/* No scroll-position indicator on the short pill (nothing to scroll). */
.chat-pill-1line::before { display: none; }
.chat-pill-1line .chat-pill-input {
    grid-column: 2;
    grid-row: 1;
    padding: 9px 8px;
    line-height: 20px;
    /* 1 resting line: 20 + 9 + 9 = 38px. Grows on wrap, then scrolls. */
    min-height: 38px;
    max-height: 132px;
    overflow-y: auto;
  }
.chat-pill-1line .chat-pill-attach {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    justify-self: center;
    width: 34px;
    height: 34px;
  }
.chat-pill-1line .chat-pill-voice,
.chat-pill-1line .chat-pill-send {
    grid-column: 3;
    grid-row: 1;
    align-self: center;
    justify-self: center;
    width: 34px;
    height: 34px;
  }
.chat-pill-1line .chat-pill-attach svg {
    width: 20px !important;
    height: 20px !important;
  }
.chat-pill-1line .chat-pill-voice svg,
.chat-pill-1line .chat-pill-send svg {
    width: 22px !important;
    height: 22px !important;
  }

/* ── Chat activity ("thinking") indicator ───────────────────────────────────
   While the agent works on the current session's turn, the web-chat pill glows
   and a small chip above it (#chat-activity) ticks through each interaction
   (e.g. "Toolcall get_time"). Driven by ui/shared/js/chat-activity.js off the per-user
   WebSocket stream. Theme-aware via design tokens — no hard-coded colors. The
   glow uses box-shadow only: the glass override in index.html sets the pill's
   background/border with !important, leaving box-shadow free to animate. */
.chat-pill.thinking {
    animation: chatPillGlow 1.8s ease-in-out infinite;
  }
@keyframes chatPillGlow {
    0%, 100% { box-shadow: 0 0 0 1px var(--accent-soft), 0 0 12px -2px var(--accent-mid); }
    50%      { box-shadow: 0 0 0 1px var(--accent-mid),  0 0 22px 0   var(--accent-line); }
  }

/* Container: CHAT-ABOVE-PILL — absolutely fills the .chat-above-pill band so the
   live bar shares the SAME row as the Stop/Continue buttons (pinned to the bottom
   edge, baselining with them), while the expandable tool-call panel grows upward
   as a floating overlay. z-index BELOW the action row so a long note slides behind
   the buttons. pointer-events:none here so the empty width passes clicks through
   to the messages behind it; the bar + panel re-enable pointer events themselves. */
.chat-activity {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    pointer-events: none;
    opacity: 0;
    transform: translateY(3px);
    transition: opacity 0.2s ease, transform 0.2s ease;
  }
.chat-activity.visible { opacity: 1; transform: translateY(0); }

/* The live header chip — dot + ticking note + chevron. Clickable (when there
   are tool calls) to toggle the panel below. Shrink-wraps; ellipsises long notes. */
.chat-activity-bar {
    pointer-events: auto;
    max-width: 100%;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    /* Matches the continue/stop buttons' size so the row above the pill reads as
       one set of same-sized controls (padding + radius below; text size on
       .chat-activity-text). */
    padding: 5px 16px;
    border-radius: 14px;
    /* Same faded glass as the chat pill (see index.html "#chat-panel .chat-pill"):
       a 7% tint of the foreground over transparent, a 12% neutral hairline, and
       the same blur+saturate — so the bubble reads as the same surface as the
       pill instead of a separate accent-bordered chip. We use currentColor (the
       pill's mechanism) and force `color: inherit` below so the bubble takes the
       pill's foreground — the tint is then computed from the identical base in
       every theme. (The ticking note keeps its muted colour via
       .chat-activity-text below.) */
    background: var(--chat-pill-bg, color-mix(in oklab, var(--bg-elev) 82%, transparent));
    border: var(--border-width) solid color-mix(in oklab, currentColor 12%, transparent);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
            backdrop-filter: blur(12px) saturate(140%);
    /* <button> carries a UA `color` (buttontext) that does NOT inherit; force it
       to inherit so currentColor (the glass tint above) equals the pill's fg. */
    color: inherit;
    font: inherit;
    /* SIBLING-BTN-COHESION: explicit size + line-height so the box math
       matches the Continue/Stop buttons. font:inherit above would give us
       the browser default ~16px, making this ~2px taller. */
    font-size: 13px;
    line-height: 1.5;
    text-align: left;
    user-select: none;
    -webkit-user-select: none;
    cursor: default;
    transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
  }
.chat-activity-bar.has-tools { cursor: pointer; }
.chat-activity-bar.has-tools:hover {
    background: color-mix(in oklab, currentColor 11%, transparent);
    border-color: color-mix(in oklab, var(--accent) 45%, transparent);
  }
/* While the agent is thinking, the bubble glows in lockstep with the chat pill —
   same accent, same 1.8s rhythm — so the chip and the pill read as one breathing
   surface, not two separate controls. The pill animates box-shadow only (its
   bg/border are locked by the glass override); we do the same here, scaled down
   for the smaller chip. */
.chat-pill.thinking .chat-activity-bar {
    animation: chatBubbleGlow 1.8s ease-in-out infinite;
  }
@keyframes chatBubbleGlow {
    0%, 100% { box-shadow: 0 0 0 1px var(--accent-soft), 0 0 7px -2px var(--accent-mid); }
    50%      { box-shadow: 0 0 0 1px var(--accent-mid),  0 0 13px 0   var(--accent-line); }
  }
.chat-activity-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
    opacity: .5;
    flex-shrink: 0;
  }
/* Idle safety-net: when the agent isn't working there's no ticking note and
   nothing to expand, so the bar must collapse entirely rather than linger as a
   lone dot. Hides any bar that has an empty note AND no tool calls to inspect,
   regardless of a stray `.visible` left on the container. */
.chat-activity-bar:not(.has-tools):has(.chat-activity-text:empty) { display: none; }
.chat-activity-text {
    font-size: 12px;
    font-weight: 500;
    color: var(--fg-4);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: 0.01em;
  }
.chat-activity-chevron {
    flex: 0 0 auto;
    font-size: 12px;
    line-height: 1;
    color: var(--fg-3);
    transition: transform 0.18s ease;
    display: none;            /* shown only when there are tool calls to expand */
  }
.chat-activity-bar.has-tools .chat-activity-chevron { display: inline-block; }
.chat-activity.expanded .chat-activity-chevron { transform: rotate(90deg); }

/* Expandable panel: the list of THIS turn's tool calls, stacked above the bar. */
.chat-activity-panel {
    pointer-events: auto;
    width: 100%;
    box-sizing: border-box;
    max-height: 240px;
    overflow-y: auto;
    padding: 4px;
    border-radius: 11px;
    background: color-mix(in oklab, var(--bg-elev) 96%, transparent);
    border: var(--border-width) solid var(--border);
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    box-shadow: 0 8px 24px -12px rgba(var(--shadow-rgb), 0.35);
    display: flex;
    flex-direction: column;
    gap: 2px;
    scrollbar-width: thin;
  }
.chat-activity-panel[hidden] { display: none; }

/* ── Bubble-attached tool call panel ──────────────────────────────────────────
   Rendered inside an agent bubble after the turn ends. A small clickable chip
   ("N tool calls") that expands to show the same accordion tool list as the
   live activity panel. Uses the same .ca-tool-* classes for row rendering. */
.bubble-tool-calls {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}
.bubble-tool-calls-head {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 8px;
  background: color-mix(in oklab, currentColor 6%, transparent);
  border: 1px solid color-mix(in oklab, currentColor 10%, transparent);
  color: var(--fg-2);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
  -webkit-backdrop-filter: blur(8px) saturate(120%);
          backdrop-filter: blur(8px) saturate(120%);
}
.bubble-tool-calls-head:hover {
  background: color-mix(in oklab, currentColor 10%, transparent);
  border-color: color-mix(in oklab, var(--accent) 40%, transparent);
}
.bubble-tool-calls-icon {
  font-size: 11px;
  line-height: 1;
  opacity: 0.7;
}
.bubble-tool-calls-chevron {
  font-size: 12px;
  line-height: 1;
  color: var(--fg-4);
  transition: transform 0.15s ease;
}
.bubble-tool-calls.open .bubble-tool-calls-chevron {
  transform: rotate(90deg);
}
.bubble-tool-calls-panel {
  margin-top: 0;
  border-radius: 0;
  background: transparent;
  border: none;
  padding: 0;
  max-height: none;
  overflow: visible;
}
.bubble-tool-calls-panel[hidden] { display: none; }

/* When panel is open, hide the header chip */
.bubble-tool-calls.open .bubble-tool-calls-head {
  display: none;
}

/* One tool call (accordion row) */
.ca-tool-row { border-radius: 8px; }
.ca-tool-head {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 7px;
    background: transparent;
    border: none;
    border-radius: 8px;
    color: var(--fg-2);
    font: inherit;
    font-size: 11.5px;
    cursor: pointer;
    text-align: left;
    transition: background 0.12s ease;
  }
.ca-tool-head:hover { background: var(--bg-tint); }
.ca-tool-status {
    flex: 0 0 auto;
    width: 12px;
    height: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    line-height: 1;
  }
.ca-status-done  { color: var(--success); }
.ca-status-error { color: var(--danger); }
.ca-status-running {
    box-sizing: border-box;
    border: 1.5px solid var(--accent-line);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: caSpin 0.7s linear infinite;
  }
@keyframes caSpin { to { transform: rotate(360deg); } }
.ca-tool-turn {
    flex: 0 0 auto;
    font-size: 9.5px;
    line-height: 1.4;
    color: var(--fg-3);
    background: var(--bg-tint);
    border-radius: 5px;
    padding: 1px 5px;
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
.ca-tool-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--fg-1);
  }
.ca-tool-meta {
    flex: 0 0 auto;
    font-size: 10px;
    color: var(--fg-3);
  }
.ca-tool-caret {
    flex: 0 0 auto;
    font-size: 11px;
    color: var(--fg-4);
    transition: transform 0.15s ease;
  }
.ca-tool-row.open .ca-tool-caret { transform: rotate(90deg); }
.ca-tool-body { display: none; padding: 2px 7px 8px 26px; }
.ca-tool-row.open .ca-tool-body { display: block; }
.ca-tool-label {
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--fg-4);
    margin: 6px 0 2px;
  }
.ca-tool-pre {
    margin: 0;
    padding: 6px 8px;
    border-radius: 6px;
    background: var(--bg-2);
    border: var(--border-width) solid var(--border-soft);
    color: var(--fg-2);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10.5px;
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 160px;
    overflow: auto;
    scrollbar-width: thin;
  }
.ca-tool-img {
    max-width: 100%;
    border-radius: 6px;
    border: var(--border-width) solid var(--border);
    margin-top: 2px;
  }

/* Copy button on tool call rows and pre blocks */
.ca-tool-copy-btn {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 1px 5px;
  cursor: pointer;
  color: var(--fg-4);
  font-size: 10px;
  line-height: 1.4;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  font-family: inherit;
}
.ca-tool-head:hover .ca-tool-copy-btn,
.ca-tool-pre-wrap:hover .ca-tool-copy-btn {
  opacity: 1;
}
.ca-tool-copy-btn:hover {
  color: var(--accent);
  border-color: var(--border-soft);
}
.ca-tool-copy-btn.copied {
  color: var(--success);
  opacity: 1;
}
.ca-tool-pre-wrap {
  position: relative;
}
.ca-tool-pre-wrap .ca-tool-copy-btn {
  position: absolute;
  top: 3px;
  right: 3px;
  background: var(--bg-2);
  border: var(--border-width) solid var(--border-soft);
  border-radius: 4px;
  padding: 1px 6px;
  z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
    .chat-pill.thinking {
      animation: none;
      box-shadow: 0 0 0 1px var(--accent-mid), 0 0 16px -2px var(--accent-mid);
    }
    .chat-pill.thinking .chat-activity-bar {
      animation: none;
      box-shadow: 0 0 0 1px var(--accent-mid), 0 0 10px -2px var(--accent-mid);
    }
    .chat-activity-dot, .ca-status-running { animation: none; }
    .chat-continue-btn.active, .chat-stop-btn.active {
      animation: none;
      box-shadow: 0 0 0 1px color-mix(in oklab, currentColor 60%, transparent),
                  0 0 12px -2px color-mix(in oklab, currentColor 70%, transparent);
    }
  }

	/* ── Preview bar (absolute, sits above the pill, overlaps action row at higher z) ── */
  #chat-preview-bar {
    display: none;
    gap: 6px;
    flex-wrap: wrap;
    padding: 4px 2px;
    position: absolute;
    left: 8px;
    right: 8px;
    /* Pin the bar's bottom to the pill's top edge — extends upward into the
       action-row / activity zone. Higher z-index makes it visually overlay them. */
    bottom: 100%;
    z-index: 7;
    pointer-events: none;
  }
  #chat-preview-bar .chat-attachment-pill { pointer-events: auto; }
#chat-preview-bar:not(:empty) {
    display: flex;
  }
.chat-attachment-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 11px;
    color: var(--fg-2);
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.chat-attachment-pill.uploading {
    color: var(--fg-3);
    border-color: var(--border-strong);
    font-style: italic;
  }
.chat-attachment-pill.error {
    color: var(--danger);
    border-color: rgba(var(--danger-rgb), 0.25);
  }
.chat-attachment-thumb {
    width: 24px;
    height: 24px;
    object-fit: cover;
    border-radius: 4px;
    flex-shrink: 0;
  }
.chat-attachment-remove {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    font-size: 12px;
    padding: 0 1px;
    line-height: 1;
    margin-left: 2px;
  }
.chat-attachment-remove:hover {
    color: var(--danger);
  }

/* ── Attachments rendered in chat bubbles ── */
  .chat-attachment-img {
    display: block;
    max-width: 100%;
    max-height: 400px;
    border-radius: 8px;
    margin-top: 8px;
    border: var(--border-width) solid var(--border);
  }
.chat-audio-wrapper {
    margin-top: 8px;
    padding: 8px;
    background: var(--bg-0);
    border-radius: 8px;
    border: var(--border-width) solid var(--border);
  }
.chat-attachment-label {
    font-size: 11px;
    color: var(--fg-3);
    margin-bottom: 4px;
  }
.chat-audio-player {
    width: 100%;
    height: 36px;
    border-radius: 6px;
  }
.chat-attachment-video {
    display: block;
    max-width: 100%;
    max-height: 400px;
    border-radius: 8px;
    margin-top: 8px;
    border: var(--border-width) solid var(--border);
  }
.chat-attachment-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--brand);
    text-decoration: none;
    font-size: 12px;
    margin-top: 6px;
    padding: 4px 8px;
    background: rgba(var(--brand-rgb), 0.06);
    border-radius: 6px;
  }
.chat-attachment-link:hover {
    background: rgba(var(--brand-rgb), 0.13);
    text-decoration: underline;
  }

/* Clickable previews (chips + history images) — hint that they expand. */
  .chat-attachment-pill.clickable,
  .chat-attachment-img.clickable,
  .chat-attachment-link.clickable { cursor: zoom-in; }
  .chat-attachment-img.clickable:hover { filter: brightness(1.08); }

/* ── Full-screen attachment viewer (lightbox) ── */
  .attachment-viewer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10001;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(var(--bg-0-rgb), 0.82);
    backdrop-filter: blur(3px);
    padding: 32px;
    animation: attViewerFade 0.12s ease-out;
  }
@keyframes attViewerFade { from { opacity: 0; } to { opacity: 1; } }
  .attachment-viewer-frame {
    display: flex;
    flex-direction: column;
    max-width: min(1100px, 92vw);
    max-height: 90vh;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(var(--shadow-rgb), 0.55);
  }
.attachment-viewer-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: var(--border-width) solid var(--border);
    flex-shrink: 0;
  }
.attachment-viewer-title {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    color: var(--fg-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.attachment-viewer-close {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    display: inline-flex;
    padding: 2px;
    border-radius: 6px;
    flex-shrink: 0;
  }
.attachment-viewer-close:hover { color: var(--danger); background: rgba(var(--danger-rgb), 0.10); }
  .attachment-viewer-body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
  }
.attachment-viewer-img {
    max-width: 100%;
    max-height: calc(90vh - 60px);
    object-fit: contain;
    border-radius: 6px;
  }
.attachment-viewer-video {
    max-width: 100%;
    max-height: calc(90vh - 60px);
    border-radius: 6px;
  }
.attachment-viewer-audio { width: min(560px, 80vw); }
  .attachment-viewer-pdf {
    width: min(1000px, 88vw);
    height: calc(90vh - 60px);
    border: none;
    background: #fff; /* KEEP (intentional): paper-white behind the rendered PDF, theme-independent */
    border-radius: 6px;
  }
.attachment-viewer-text {
    align-self: stretch;
    width: 100%;
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--fg-1);
    white-space: pre-wrap;
    word-break: break-word;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  }
.attachment-viewer-md {
    white-space: normal;
    font-family: inherit;
  }
.attachment-viewer-download {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--brand);
    text-decoration: none;
    font-size: 14px;
    padding: 10px 16px;
    background: rgba(var(--brand-rgb), 0.08);
    border-radius: 8px;
  }
.attachment-viewer-download:hover { background: rgba(var(--brand-rgb), 0.14); }
  .attachment-viewer-loading {
    display: inline-flex;
    color: var(--fg-3);
    animation: spin 1s linear infinite;
  }
.attachment-viewer-missing { color: var(--danger); font-size: 14px; }

/* ── Main header ── */
  #main-header {
    display: flex;
    align-items: center;
    /* Gap between the tab carousel (right chevron pinned to its right edge) and
       #status-right (the Chat button). Kept tight so the chevron sits close to
       the chat icon — added padding already comes from the chevron (4px) and the
       chat button (8px). */
    gap: 2px;
    padding: 0 4px;
    background: transparent;
    border-bottom: none;
    font-size: 11px;
    color: var(--fg-3);
    flex-shrink: 0;
  }
.status-dot {
    width: 7px; height: 7px; border-radius: 50%; display: inline-block;
  }
.status-dot.green { background: var(--brand); box-shadow: 0 0 4px var(--brand); }
.status-dot.yellow { background: var(--warning); animation: pulse 1s infinite; }
.status-dot.red { background: var(--danger); }
  @keyframes pulse {0%,
100% {opacity:1}50% {opacity:0.3} }
.status-group { display: flex; align-items: center; gap: 6px; }
#status-right { margin-left: auto; display: flex; gap: 0; align-items: center; }
.status-select {
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    color: var(--fg-1);
  }

/* ── Model dropdown ── */
  .model-dropdown-item {
    padding: 8px 12px;
    cursor: pointer;
    font-size: 13px;
    color: var(--fg-1);
    border-bottom: var(--border-width) solid var(--border);
    transition: background 0.1s;
  }
.model-dropdown-item:hover {
    background: var(--bg-tint-2);
  }
.model-dropdown-item:last-child {
    border-bottom: none;
  }

/* ── Settings Dropdown Menu ── */
  .settings-dropdown {
    position: relative;
    display: inline-block;
  }
.settings-dropdown-content {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 4px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(var(--shadow-rgb),0.5);
    z-index: 1000;
    min-width: 140px;
    padding: 4px 0;
  }
.settings-menu-item {
    padding: 8px 16px;
    color: var(--fg-1);
    font-size: 13px;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    transition: background 0.15s;
  }
.settings-menu-item:hover {
    background: var(--bg-tint-2);
  }
.settings-menu-item .arrow {
    font-size: 10px;
    color: var(--fg-3);
  }
/* Submenus */
  .settings-submenu {
    display: none;
    position: absolute;
    top: -5px; /* align with parent item */
    left: 100%; /* open to the right */
    margin-left: 4px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(var(--shadow-rgb),0.5);
    z-index: 1001;
    min-width: 160px;
    padding: 4px 0;
    max-height: 400px;
    overflow-y: auto;
  }
/* Show submenu on hover of parent item */
  .settings-menu-item:hover > .settings-submenu,
.settings-submenu:hover {
    display: block;
  }
.submenu-item {
    padding: 8px 16px;
    color: var(--fg-1);
    font-size: 13px;
    cursor: pointer;
    transition: background 0.15s;
    background: none;
    border: none;
    width: 100%;
    text-align: left;
    display: block;
  }
.submenu-item:hover {
    background: var(--bg-tint-2);
  }
.submenu-item.active {
    background: rgba(var(--brand-rgb), 0.08);
    color: var(--brand);
    font-weight: 600;
  }
/* Specific styling for LLM submenu panel (since it contains inputs) */
  #llm-submenu {
    padding: 16px;
    width: 320px;
    cursor: default; /* override pointer for input area */
  }
#llm-submenu label {
    display: block; margin-bottom: 6px; color: var(--fg-2); font-size: 12px; font-weight: 600;
  }

/* ── Chat toggle button (status-right) ── */
  #chat-toggle-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    color: var(--fg-3);
    padding: 6px 8px;
    margin: 0;
    border-radius: 8px;
    transition: color 0.15s, background 0.15s;
    user-select: none;
  }
/* Lucide SVG inside button must not swallow clicks. */
  #chat-toggle-btn > * { pointer-events: none; }
#chat-toggle-btn.active {
    color: var(--accent);
  }
#chat-toggle-btn:hover {
    background: rgba(var(--brand-rgb),0.08);
    color: var(--accent);
  }
/* Long-press fired (≥500ms) → started a new default-agent session. Brief accent
   pulse as feedback; toggled by the long-press handler in index.html. */
#chat-toggle-btn.lp-active {
    background: rgba(var(--brand-rgb),0.22);
    color: var(--accent);
  }
.header-divider {
    color: var(--bg-tint);
    margin: 0 8px;
    user-select: none;
    font-size: 13px;
  }

/* The new-session (+) button moved out of the main header into the chat panel
   header (#chat-header .header-actions-top); its styling now lives beside the chat
   header's refresh/delete buttons in the "#chat-header" block near the top of
   this file. */

/* ── User Dropdown (status-right) ── */
  .user-dropdown {
    position: relative;
    display: inline-flex;
    align-items: center;
  }
.user-dropdown-trigger {
    cursor: pointer !important;
    user-select: none;
    transition: opacity 0.15s;
  }
.user-dropdown-trigger:hover {
    opacity: 0.8;
  }
.user-dropdown-arrow {
    font-size: 9px;
    color: var(--fg-3);
    margin-left: 2px;
    transition: transform 0.2s;
  }
.user-dropdown.open .user-dropdown-arrow {
    transform: rotate(180deg);
  }
.user-dropdown-menu {
		    position: absolute;
		    top: 100%;
		    left: 0;
		    margin-top: 6px;
	    background: var(--bg-elev);
	    border: var(--border-width) solid var(--border);
	    border-radius: 12px;
	    box-shadow: 0 8px 32px rgba(var(--shadow-rgb),0.5);
	    z-index: 1100;
	    min-width: 180px;
	    padding: 6px 0;
	    animation: user-dd-fadein 0.15s ease;
	  }
  @keyframes user-dd-fadein {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
  }
.user-dropdown-header {
    padding: 8px 14px 6px;
    border-bottom: var(--border-width) solid var(--border);
    margin-bottom: 4px;
    font-size: 11px;
    color: var(--fg-3);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 200px;
  }
.user-dropdown-divider {
    height: 1px;
    background: var(--bg-tint);
    margin: 4px 8px;
  }
.user-dropdown-action {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 9px 14px;
    background: none;
    border: none;
    color: var(--fg-1);
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.15s;
    text-align: left;
  }
.user-dropdown-action:hover {
    background: var(--bg-tint-2);
    color: var(--danger);
  }
.user-dropdown-action-icon {
    font-size: 14px;
  }

.user-dropdown-legal {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px 14px 6px;
    margin-top: 2px;
    font-size: 11px;
    color: var(--fg-3);
  }
.user-dropdown-legal a {
    color: var(--fg-3);
    text-decoration: none;
    transition: color 0.15s;
  }
.user-dropdown-legal a:hover {
    color: var(--fg-2);
    text-decoration: underline;
  }
.user-dropdown-legal span {
    color: var(--fg-4);
    user-select: none;
  }

/* ── Feedback sub-view inside the user dropdown ──
   Replaces the default dropdown contents when "Send feedback" is clicked.
   Shares the same panel dimensions (360px, 12px border-radius, same
   background/border/shadow) — keep it consistent. */
  .dropdown-feedback-view {
    display: flex;
    flex-direction: column;
    width: 100%;
  }
  .dropdown-feedback-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 14px 10px;
    border-bottom: var(--border-width) solid var(--border);
    font-size: 14px;
    font-weight: 600;
    color: var(--fg-1);
  }
  .dropdown-feedback-back,
  .dropdown-feedback-close {
    background: none;
    border: none;
    color: var(--fg-2);
    font-size: 16px;
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
    transition: background 0.15s;
  }
  .dropdown-feedback-back:hover,
  .dropdown-feedback-close:hover {
    background: rgba(var(--ink-rgb),0.06);
  }
  .dropdown-feedback-body {
    padding: 12px 14px 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
  }

  /* 3-way segmented toggle */
  .feedback-segmented-wrap {
    display: flex;
  }
  .feedback-segmented {
    display: flex;
    width: 100%;
    background: var(--bg-0);
    border-radius: 8px;
    padding: 2px;
    gap: 2px;
  }
  .fb-seg-opt {
    flex: 1;
    text-align: center;
    padding: 7px 6px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--fg-3);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .fb-seg-opt.active {
    background: var(--bg-tint);
    color: var(--fg-1);
  }
  .fb-seg-opt:hover:not(.active) {
    color: var(--fg-2);
  }

  /* Form fields (same style as login panel inputs) */
  .dropdown-feedback-field {
    display: flex;
    flex-direction: column;
    gap: 5px;
    position: relative;
  }
  .dropdown-feedback-label {
    font-size: 12px;
    color: var(--fg-2);
  }
  .dropdown-feedback-optional {
    color: var(--fg-3);
    font-weight: 400;
  }
  .dropdown-feedback-field textarea,
  .dropdown-feedback-field input[type="email"] {
    width: 100%;
    background: var(--bg-0);
    color: var(--fg-1);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    padding: 8px 10px;
    font: inherit;
    resize: vertical;
    outline: none;
    transition: border-color 0.2s;
    box-sizing: border-box;
  }
  .dropdown-feedback-field textarea:focus,
  .dropdown-feedback-field input[type="email"]:focus {
    border-color: var(--brand);
  }
  .dropdown-feedback-field textarea::placeholder,
  .dropdown-feedback-field input::placeholder {
    color: var(--fg-4);
  }
  .dropdown-feedback-counter {
    position: absolute;
    right: 6px;
    bottom: 6px;
    font-size: 10px;
    color: var(--fg-3);
    pointer-events: none;
  }
  .dropdown-feedback-turnstile {
    min-height: 0;
  }
  .dropdown-feedback-turnstile:not(:empty) {
    min-height: 65px;
  }
  /* Honeypot: a decoy field hidden from humans (off-screen, no focus, no a11y
     exposure) but present in the DOM so naive spam bots auto-fill it. The
     backend silently drops any submission where it's non-empty. Not a themed
     surface — pure functional hiding, so no palette tokens apply. */
  .dropdown-feedback-hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
  }
  .dropdown-feedback-status {
    font-size: 12px;
    min-height: 1em;
  }
  .dropdown-feedback-status.error { color: var(--danger); }
  .dropdown-feedback-status.success { color: var(--success); }
  .dropdown-feedback-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 2px;
  }
  .dropdown-feedback-cancel {
    background: transparent;
    color: var(--fg-2);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    padding: 6px 14px;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
    transition: background 0.15s;
  }
  .dropdown-feedback-cancel:hover {
    background: rgba(var(--ink-rgb),0.04);
  }
  .dropdown-feedback-send {
    background: var(--brand);
    color: #fff;
    border: 0;
    border-radius: 6px;
    padding: 6px 16px;
    cursor: pointer;
    font: inherit;
    font-size: 13px;
  }
  .dropdown-feedback-send:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

/* ── Theme toggle in user dropdown ── */
  .theme-toggle-section {
    padding: 4px 14px 8px;
  }
.theme-toggle-label {
    display: block;
    font-size: 11px;
    color: var(--fg-3);
    font-weight: 600;
    margin-bottom: 6px;
  }
.theme-toggle-group {
    display: flex;
    gap: 4px;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 999px;
    padding: 2px;
  }
.theme-option {
    flex: 1;
    background: none;
    border: none;
    color: var(--fg-3);
    font-size: 16px;
    padding: 6px 4px;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.15s;
    line-height: 1;
  }
.theme-option > * {
    pointer-events: none;
  }
.theme-option:hover {
    color: var(--fg-1);
    background: var(--bg-tint);
  }
.theme-option.active {
    background: var(--bg-tint-2);
    color: var(--brand);
    box-shadow: 0 0 0 1px rgba(var(--brand-rgb), 0.25);
  }
#llm-submenu input,
#llm-submenu select {
    width: 100%; padding: 8px; background: var(--bg-0); border: var(--border-width) solid var(--border); border-radius: 6px; color: var(--fg-1); font-size: 13px; margin-bottom: 4px;
  }
#llm-submenu .hint { margin-top: 2px; margin-bottom: 12px; font-size: 11px; color: var(--fg-3); }

/* ═══════════════════════════════════════════
     LIGHT MODE — white / light orange / peach
     ═══════════════════════════════════════════ */
  body.light-mode {
    background: var(--bg-0);
    color: var(--fg-1);
  }

body.light-mode #main-header {
    background: transparent;
    border-bottom: none;
  }
body.light-mode .status-select {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-2);
  }
body.light-mode .status-select:focus { border-color: var(--brand); }
body.light-mode #terminal-container,
body.light-mode #main-panel,
body.light-mode #loop-container,
body.light-mode #loop-list {
    background: var(--bg-0);
  }
body.light-mode #loop-toolbar,
body.light-mode #db-toolbar,
body.light-mode #loop-visual-toolbar {
    background: var(--bg-elev);
    border-bottom-color: var(--border);
  }
body.light-mode #db-table-list { border-right-color: var(--border); }
body.light-mode .db-table-item:hover { background: var(--bg-tint); }
body.light-mode .db-table-item.active {
    background: var(--bg-tint-2);
    color: var(--brand);
  }
body.light-mode .db-table td {
    color: var(--fg-1);
    border-bottom-color: var(--bg-tint);
  }
body.light-mode .db-table tr:hover td { background: var(--bg-elev-2); }
body.light-mode .db-row-even td { background: var(--bg-elev); }
body.light-mode .db-hint { color: var(--fg-3); }

body.light-mode #chat-panel {
    background: var(--bg-1);
    border-left-color: var(--border);
  }
body.light-mode #chat-header {
    border-bottom-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode #chat-header .sub { color: var(--fg-3); }
body.light-mode .chat-bubble.user {
    background: rgba(var(--brand-rgb), 0.12);
    border-color: rgba(var(--brand-rgb), 0.28);
    color: var(--fg-1);
  }
body.light-mode .chat-bubble.agent {
    color: var(--fg-1);
  }
body.light-mode .chat-bubble .label { color: var(--fg-3); }

body.light-mode #chat-input-area {
    border-top: none;
    background: transparent;
  }
/* CHAT-PILL-SYNC: light-mode counterparts to the .chat-pill block above. */
body.light-mode .chat-pill {
    background: var(--bg-0);
    border-color: rgba(var(--brand-rgb), 0.4);
  }
body.light-mode .chat-pill:focus-within { border-color: var(--brand); }
body.light-mode .chat-pill::before { background: var(--brand); }
body.light-mode .chat-pill-attach,
body.light-mode .chat-pill-voice,
body.light-mode .chat-pill-send {
    background: none;
    color: var(--fg-3);
  }
body.light-mode .chat-pill-attach:hover,
body.light-mode .chat-pill-voice:hover,
body.light-mode .chat-pill-send:hover {
    color: var(--brand);
    background: rgba(var(--brand-rgb), 0.08);
  }
body.light-mode .chat-pill-send:disabled { opacity: 0.4; }
body.light-mode .json-punc { color: var(--fg-4); }
body.light-mode .json-null { color: var(--fg-4); font-style: italic; }
body.light-mode .json-key { color: var(--brand); }
body.light-mode .json-str { color: var(--success); }
body.light-mode .json-num { color: var(--warning); }
body.light-mode .json-bool { color: var(--purple); }
body.light-mode .json-toggle:hover { color: var(--brand); }
body.light-mode .json-content-md {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode .json-content-md h1 { color: var(--brand); }
body.light-mode .json-content-md h2 { color: var(--warning); }
body.light-mode .json-content-md h3 { color: var(--fg-1); }
body.light-mode .json-content-md h4,
body.light-mode .json-content-md h5,
body.light-mode .json-content-md h6 { color: var(--fg-2); }
body.light-mode .json-content-md pre {
    background: var(--bg-elev);
    border-color: var(--border);
  }
body.light-mode .json-content-md code {
    background: var(--bg-elev);
    color: var(--warning);
  }
body.light-mode .json-content-md pre code {
    background: none;
    color: var(--fg-1);
  }
body.light-mode .json-content-md strong { color: var(--fg-1); }
body.light-mode .json-content-md em { color: var(--fg-2); }
body.light-mode .json-content-md a { color: var(--brand); }
body.light-mode .json-content-md a:hover { color: var(--brand); }

body.light-mode #cell-modal-panel {
    background: var(--bg-elev);
    border-color: var(--border);
    color: var(--fg-1);
  }

body.light-mode .user-dropdown-menu {
    background: var(--bg-elev);
    border-color: var(--border);
    box-shadow: 0 6px 20px rgba(var(--shadow-rgb),0.1);
  }
body.light-mode .user-dropdown-header { color: var(--fg-3); border-bottom-color: var(--border); }
body.light-mode .user-dropdown-action { color: var(--fg-1); }
body.light-mode .user-dropdown-action:hover {
    background: var(--bg-elev-2);
    color: var(--danger);
  }
body.light-mode .user-dropdown-divider { background: var(--border); }
body.light-mode .user-dropdown-legal { color: var(--fg-3); }
body.light-mode .user-dropdown-legal a { color: var(--fg-3); }
body.light-mode .user-dropdown-legal a:hover { color: var(--fg-2); }
body.light-mode .user-dropdown-legal span { color: var(--fg-4); }
body.light-mode .theme-toggle-label { color: var(--fg-3); }
body.light-mode .theme-toggle-group {
    background: var(--bg-elev-2);
    border-color: var(--border);
  }
body.light-mode .theme-option { color: var(--fg-4); }
body.light-mode .theme-option:hover {
    color: var(--brand);
    background: var(--bg-tint);
  }
body.light-mode .theme-option.active {
    background: var(--bg-tint-2);
    color: var(--brand);
    box-shadow: 0 0 0 1px rgba(var(--brand-rgb), 0.25);
  }

body.light-mode #cell-modal-title { color: var(--brand); }
body.light-mode .loop-detail-label,
body.light-mode .loop-hint { color: var(--fg-3); }
body.light-mode .loop-node-summary,
body.light-mode .loop-detail-pre { color: var(--fg-1); }

body.light-mode .loop-turn { border-color: var(--border); }
body.light-mode .loop-turn-header {
    background: var(--bg-elev);
    color: var(--fg-2);
  }
body.light-mode .loop-turn-header:hover { background: var(--bg-tint); }
body.light-mode .loop-node { border-left-color: var(--fg-4); }
body.light-mode .loop-detail-pre,
body.light-mode .loop-detail-panel {
    background: var(--bg-0);
    border-color: var(--border);
  }
body.light-mode .loop-filter-btn {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-3);
  }
body.light-mode .loop-filter-btn:hover {
    border-color: var(--brand);
    color: var(--fg-2);
  }
body.light-mode .loop-filter-btn.active {
    background: var(--bg-tint);
    border-color: var(--brand);
    color: var(--brand);
  }
body.light-mode #loop-autoscroll {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-3);
  }
body.light-mode #loop-autoscroll:hover {
    border-color: var(--brand);
    color: var(--fg-2);
  }
body.light-mode #loop-autoscroll.active {
    background: var(--bg-tint);
    border-color: var(--brand);
    color: var(--brand);
  }

body.light-mode .chat-attachment-pill {
    background: var(--bg-elev);
    border-color: var(--border);
    color: var(--fg-2);
  }
body.light-mode .chat-attachment-pill.uploading {
    color: var(--fg-3);
    border-color: var(--fg-4);
  }
body.light-mode .chat-attachment-img,
body.light-mode .chat-audio-wrapper,
body.light-mode .chat-attachment-video { border-color: var(--border); }
body.light-mode .chat-audio-wrapper { background: var(--bg-0); }
body.light-mode .chat-attachment-label { color: var(--fg-3); }
body.light-mode .chat-attachment-link {
    color: var(--brand);
    background: var(--bg-tint);
  }
body.light-mode .chat-attachment-link:hover { background: var(--bg-tint-2); }

/* KEEP (intentional): bespoke warm dim-scrim for the lightbox in light mode — the
   base uses rgba(var(--bg-0-rgb),...) which is near-white in light and would not dim. */
body.light-mode .attachment-viewer-backdrop { background: rgba(60, 40, 30, 0.55); }
body.light-mode .attachment-viewer-head { border-bottom-color: var(--border); }
body.light-mode .attachment-viewer-title { color: var(--fg-2); }
body.light-mode .attachment-viewer-close { color: var(--fg-3); }
body.light-mode .attachment-viewer-text { color: var(--fg-2); }
body.light-mode .attachment-viewer-download {
    color: var(--brand);
    background: var(--bg-tint);
  }
body.light-mode .attachment-viewer-download:hover { background: var(--bg-tint-2); }
body.light-mode .attachment-viewer-loading { color: var(--fg-3); }

body.light-mode #cell-modal-header { border-bottom-color: var(--border); }
body.light-mode #cell-modal-editor,
body.light-mode #cell-modal-viewer {
    background: var(--bg-0);
    color: var(--fg-1);
  }
body.light-mode #btn-restart { color: var(--fg-3); }
body.light-mode .status-dot.green {
    background: var(--brand);
    box-shadow: 0 0 4px var(--brand);
  }

body.light-mode .chat-pill.drag-over {
    border-color: var(--brand);
    background: rgba(var(--brand-rgb), 0.09);
  }

body.light-mode #control-host-toggle { border-right-color: var(--border); }
body.light-mode #control-host-toggle .ch-label { color: var(--fg-3); }
body.light-mode #control-host-toggle .segment-track {
    background: var(--bg-elev-2);
    border-color: var(--border);
  }
body.light-mode #control-host-toggle .segment-track button { color: var(--fg-3); }
body.light-mode #control-host-toggle .segment-track button:hover:not(.active) { color: var(--fg-2); }
body.light-mode #control-host-toggle .segment-track button.active {
    background: var(--bg-tint);
    color: var(--brand);
  }
body.light-mode .tab-btn-group { border-right-color: var(--border); }
body.light-mode .tab-btn-group .tab-btn { color: var(--fg-3); }
body.light-mode .tab-btn-group .tab-btn:hover {
    color: var(--fg-2);
    background: var(--bg-tint);
  }
body.light-mode .tab-btn-group .tab-btn.active {
    color: var(--brand);
    background: var(--bg-tint);
  }
body.light-mode #user-label,
body.light-mode #session-label,
body.light-mode #agent-label { color: var(--fg-3); }
body.light-mode .status-btn {
    background: var(--bg-elev);
    border-color: var(--border);
    color: var(--fg-2);
  }
body.light-mode .status-btn:hover {
    background: var(--bg-tint);
    border-color: var(--brand);
    color: var(--fg-1);
  }
body.light-mode #top-user-id { color: var(--brand) !important; }
body.light-mode .settings-menu-item#btn-signout { color: var(--danger) !important; }
body.light-mode #llm-submenu label { color: var(--fg-2); }
body.light-mode #llm-submenu input,
body.light-mode #llm-submenu select {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode #llm-submenu .hint { color: var(--fg-3); }

body.light-mode #loop-visual-container { background: var(--bg-0); }
body.light-mode .loop-visual-hint,
body.light-mode .lv-page-summary { color: var(--fg-3); }
body.light-mode .lv-page-summary {
    background: var(--bg-elev);
    border-color: var(--border);
  }
body.light-mode .lv-page-summary .lv-summary-label { color: var(--fg-2); }
body.light-mode .lv-node {
    background: var(--bg-1);
    border-color: var(--border);
    color: var(--fg-2);
  }
body.light-mode .lv-node.lv-type-input {
    border-color: var(--brand);
    background: var(--bg-elev-2);
    color: var(--brand);
  }
body.light-mode .lv-node.lv-type-output {
    border-color: var(--success);
    background: rgba(var(--success-rgb), 0.12);
    color: var(--success);
  }
body.light-mode .lv-node.lv-type-process {
    border-color: var(--border);
    background: var(--bg-0);
    color: var(--fg-1);
  }
body.light-mode .lv-node.lv-type-decision {
    border-color: var(--warning);
    background: var(--bg-elev-2);
    color: var(--warning);
  }
body.light-mode .lv-node.lv-type-llm {
    border-color: var(--purple);
    background: rgba(var(--purple-rgb), 0.10);
    color: var(--purple);
  }
body.light-mode .lv-node.lv-active {
    border-color: var(--brand) !important;
    box-shadow: 0 0 16px rgba(var(--brand-rgb), 0.4);
    background: var(--bg-tint) !important;
  }
body.light-mode .lv-node.lv-done {
    border-color: var(--success) !important;
    background: rgba(var(--success-rgb), 0.12) !important;
  }
body.light-mode .lv-arrow { stroke: var(--border); }
body.light-mode .lv-arrow.lv-arrow-active { stroke: var(--brand); }
body.light-mode .lv-arrow.lv-arrow-done { stroke: var(--success); }
body.light-mode .lv-arrow-label { fill: var(--fg-4); }
body.light-mode .lv-arrow-label.lv-arrow-label-active { fill: var(--brand); }
body.light-mode .loop-visual-page-btn {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-3);
  }
body.light-mode .loop-visual-page-btn:hover {
    border-color: var(--brand);
    color: var(--fg-2);
  }
body.light-mode .loop-visual-page-btn.active {
    background: var(--bg-tint);
    border-color: var(--brand);
    color: var(--brand);
  }
body.light-mode #loop-visual-graph-area {
    background: var(--bg-0);
  }
body.light-mode #loop-visual-legend { color: var(--fg-3); }

body.light-mode ::-webkit-scrollbar-track { background: transparent; }
body.light-mode ::-webkit-scrollbar-thumb {
    background: rgba(var(--brand-rgb), 0.35);
    border-radius: 3px;
  }
body.light-mode #db-table-data::-webkit-scrollbar-thumb,
body.light-mode #db-table-list::-webkit-scrollbar-thumb,
body.light-mode #agents-grid::-webkit-scrollbar-thumb,
body.light-mode #chat-messages-inner::-webkit-scrollbar-thumb,
body.light-mode .agent-detail-body::-webkit-scrollbar-thumb {
    background: rgba(var(--brand-rgb), 0.35) !important;
  }
body.light-mode #agents-grid,
body.light-mode #chat-messages-inner,
body.light-mode .agent-detail-body {
    scrollbar-color: rgba(var(--brand-rgb), 0.35) transparent !important;
  }
body.light-mode .xterm-viewport {
    scrollbar-color: rgba(var(--brand-rgb), 0.35) transparent !important;
  }

/* ── Chat header label rows: the dropdown flex-fills the row ── */
  #agent-label,
#session-label { display: flex; align-items: center; flex: 1 1 auto; min-width: 0; gap: 4px; }
#agent-label .agent-dropdown,
#session-label .session-dropdown { flex: 1 1 auto; min-width: 0; }
/* Row 2 dropdown is itself a flex ROW so the delete button + chevron can sit inside it,
   after the label trigger: trigger flex-fills, then delete, then chevron at the right
   edge — putting delete just LEFT of the chevron. Scoped to #session-dropdown so the
   agent dropdown / sessions-page dropdowns (shared .session-dropdown class) are untouched. */
#session-dropdown { display: flex; align-items: center; gap: 2px; }
#session-dropdown #session-delete-header { margin-left: 0; }   /* trigger's flex-grow does the pushing, not an auto margin */
.header-plus-btn {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    font-size: 18px;
    padding: 4px 6px;
    vertical-align: middle;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }
.header-plus-btn:hover { color: var(--brand); }
/* Loading state: the + is a spinner (not a plus) until the agent list is ready
   to be clicked. Reuses the session-status-spin keyframes. */
.header-plus-btn.loading {
    cursor: default;
    opacity: 0.6;
  }
.header-plus-btn.loading:hover { color: var(--fg-3); }
.header-plus-btn.loading svg {
    animation: session-status-spin 1s linear infinite;
  }

/* TUI bridge indicator — shows a small icon when the TUI bridge agent is selected */
.tui-bridge-indicator {
    font-size: 13px;
    margin-left: 4px;
    cursor: help;
    vertical-align: middle;
    display: inline-flex;
    align-items: center;
    transition: opacity 0.3s ease;
    flex-shrink: 0;
}

/* Header delete button — right end of row 2, pinned via margin-left:auto so it
   lands directly below the row-1 more (⋯) button, just-right of the dropdown
   chevron. Padding matches the refresh / new-session buttons (1px 3px). */
.header-delete-btn {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    font-size: 20px;
    padding: 1px 3px;
    vertical-align: middle;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-left: auto;
    border-radius: var(--r-sm);
  }
.header-delete-btn:hover { color: var(--danger); }
/* SHARED-DELETE-CONTROL — armed (hazard) hue; see ui/shared/js/delete-control.js. The
   spinner state animates via the global .session-status-running wrapper. */
.header-delete-btn.warning { color: var(--warning); }
.header-delete-btn.warning:hover { color: var(--warning); }

/* ── Session nav arrows (prev/next pinned session) ── */
.header-session-nav {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 1px;
  flex-shrink: 0;
}
.session-nav-btn {
  background: none;
  border: none;
  color: var(--fg-3);
  cursor: pointer;
  padding: 6px 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: 4px;
  transition: color 0.15s, background 0.15s;
}
.session-nav-btn:hover {
  color: var(--brand);
  background: rgba(var(--ink-rgb),0.06);
}
body.light-mode .session-nav-btn { color: var(--fg-3); }
/* Hide session nav arrows on mobile to save space for the session name */
@media (max-width: 800px) {
  .header-session-nav { display: none; }
}

/* Status icon inside the trigger buttons */
.session-dropdown-status,
.agent-dropdown-status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    margin-right: 4px;
  }
.session-dropdown-status.session-status-running,
.agent-dropdown-status.session-status-running {
    color: var(--brand);
    animation: session-status-spin 1s linear infinite;
  }
.session-dropdown-status.session-status-unread,
.agent-dropdown-status.session-status-unread {
    color: var(--success);
  }

/* ── Session / Agent dropdown (custom) ───────────────────────── */

/* Loading shimmer animation for unloaded labels */
@keyframes dropdown-label-shimmer {
  0% { opacity: 0.3; }
  50% { opacity: 0.8; }
  100% { opacity: 0.3; }
}

/* When the dropdown is NOT yet loaded, show a shimmer on the label
   and disable the trigger so the user can't open an empty menu. */
.session-dropdown:not([data-loaded="true"]) #session-dropdown-label,
.agent-dropdown:not([data-loaded="true"]) #agent-dropdown-label {
  animation: dropdown-label-shimmer 1.2s ease-in-out infinite;
  color: var(--fg-3);
}
body.light-mode .session-dropdown:not([data-loaded="true"]) #session-dropdown-label,
body.light-mode .agent-dropdown:not([data-loaded="true"]) #agent-dropdown-label {
  color: var(--fg-3);
}
.session-dropdown:not([data-loaded="true"]) .session-dropdown-trigger,
.agent-dropdown:not([data-loaded="true"]) .agent-dropdown-trigger,
.session-dropdown[data-offline="true"] .session-dropdown-trigger {
  pointer-events: none;
  opacity: 0.5;
  cursor: default;
}

/* Session-dropdown chevron / spinner swap. The trigger shows a spinner while
   it is waiting for data (session not yet loaded) or the server is unreachable
   (data-offline, set from the health poll), and the chevron arrow only once a
   session has loaded AND the server is reachable. */
.session-dropdown-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
/* In the chat header the chevron now lives OUTSIDE the trigger button (so the delete
   button can sit to its left), so it carries its own click affordance + colour, and is
   disabled in lock-step with the trigger while the session is loading / the server is
   offline (mirrors the .session-dropdown-trigger disable rule below). */
#chat-header .session-dropdown-chevron { cursor: pointer; color: var(--fg-3); border-radius: var(--r-sm); }
#chat-header .session-dropdown-chevron:hover { color: var(--brand); }
#session-dropdown:not([data-loaded="true"]) .session-dropdown-chevron,
#session-dropdown[data-offline="true"] .session-dropdown-chevron { pointer-events: none; cursor: default; }
.session-dropdown-chevron .session-chevron-arrow { display: none; }
.session-dropdown-chevron .session-chevron-spinner {
  animation: session-status-spin 1s linear infinite;
}
.session-dropdown[data-loaded="true"]:not([data-offline="true"]) .session-dropdown-chevron .session-chevron-spinner { display: none; }
.session-dropdown[data-loaded="true"]:not([data-offline="true"]) .session-dropdown-chevron .session-chevron-arrow { display: inline-flex; }
/* While the session is being auto-named (Session Namer ability,
   plugins/abilities/Core/session_titler/), re-show
   the flower spinner over the chevron — same indicator as initial load. Matches
   the data-loaded specificity (0,5,0) and comes later, so it wins. */
.session-dropdown[data-titling="true"]:not([data-offline="true"]) .session-dropdown-chevron .session-chevron-spinner { display: inline-flex; }
.session-dropdown[data-titling="true"]:not([data-offline="true"]) .session-dropdown-chevron .session-chevron-arrow { display: none; }

  /* ── Agent-name dropdown (chat header row 1) ──────────────────────────
     Clicking the agent name opens a dropdown to switch agents. Mirrors the
     session-dropdown affordance. */
  /* The agent dropdown menu — absolute below the name row, wide enough for
     agent names, styled like the session dropdown. */
  .chat-header-name-row {
    position: relative;
  }
  .agent-dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    width: auto;
    min-width: 240px;
    max-width: 420px;
    max-height: min(400px, 50vh);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--bg-tint) transparent;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.4);
    z-index: 1001;
    padding: 4px;
    box-sizing: border-box;
  }
  body.light-mode .agent-dropdown-menu {
    background: var(--bg-0);
    border-color: var(--border);
    box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.1);
  }
  .agent-dropdown-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--fg-2);
    cursor: pointer;
    border-radius: 6px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .agent-dropdown-item:hover {
    background: var(--bg-hov);
    color: var(--fg-1);
  }
  .agent-dropdown-item.selected {
    color: var(--brand);
  }
  /* Agent icon inside the dropdown row */
  .agent-dropdown-item .agent-row-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
  }
  /* Empty state when no agents are available. */
  .agent-dropdown-empty {
    padding: 10px 8px;
    font-size: 12px;
    color: var(--fg-3);
    text-align: center;
  }

        .session-dropdown,
  .agent-dropdown {
      position: relative;
      display: inline-block;
      vertical-align: middle;
    }
.session-dropdown-trigger,
.agent-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    cursor: pointer;
    font-family: inherit;
    font-size: 13px;
  }
/* Chat-header session trigger has a transparent (pill-less) background, so the
   .status-select 10px right padding is dead space that pushes the chevron away
   from the delete button. Trim it to ~2px so the chevron sits snug to the delete,
   mirroring the row-1 (+)→(⋯) spacing. Scoped to the chat header only. */
#chat-header .session-dropdown-trigger { padding-right: 2px; }
/* The chat-header trigger is now a flex CHILD of #session-dropdown (which holds the
   trigger, the delete button, and the chevron). Let it flex-fill instead of forcing a
   rigid width:100%, so the delete + chevron get their intrinsic width on the right. */
#chat-header .session-dropdown-trigger { width: auto; flex: 1 1 auto; min-width: 0; }
/* Icons inside the trigger and row buttons must not swallow clicks (lucide SVG quirk). */
  .session-dropdown-trigger > *,
.agent-dropdown-trigger > *,
.session-row-kebab > *,
.agent-row-kebab > *,
.session-row-delete > *,
.agent-row-delete > *,
.agent-row-config > * {
    pointer-events: none;
  }
.session-dropdown-trigger #session-dropdown-label,
.agent-dropdown-trigger #agent-dropdown-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
  }
.session-dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    /* Match the trigger row width — the wrapping .session-dropdown /
       .agent-dropdown stretches to fill its label row, so 100% lines the
       menu up with the trigger above it. */
    width: 100%;
    min-width: 0;
    max-width: none;
    max-height: min(480px, 60vh);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--bg-tint) transparent;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.4);
    z-index: 1000;
    padding: 4px;
    box-sizing: border-box;
  }
body.light-mode .session-dropdown-menu {
    background: var(--bg-0);
    border-color: var(--border);
    box-shadow: 0 8px 24px rgba(var(--shadow-rgb),0.1);
  }
/* Thin scrollbar for the session dropdown (Chrome/Safari) */
.session-dropdown-menu::-webkit-scrollbar {
    width: 4px;
}
.session-dropdown-menu::-webkit-scrollbar-track {
    background: transparent;
}
.session-dropdown-menu::-webkit-scrollbar-thumb {
    background: var(--bg-tint);
    border-radius: 2px;
}
body.light-mode .session-dropdown-menu::-webkit-scrollbar-thumb {
    background: var(--bg-tint);
}
/* New-session agent picker: a small floating menu anchored under the + button.
   Reuses .agent-dropdown-menu chrome but is body-mounted + fixed-positioned. */
.new-session-agent-picker {
    top: auto;
    left: auto;
    width: auto;
    min-width: 180px;
    max-width: 280px;
    z-index: 4000;
  }
.new-session-picker-head {
    padding: 4px 8px 6px;
    font-size: 11px;
    color: var(--fg-3);
  }

.session-row,
.agent-row-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    border-radius: 6px;
    cursor: pointer;
    color: var(--fg-1);
    font-size: 12px;
  }
body.light-mode .session-row,
body.light-mode .agent-row-item { color: var(--fg-2); }
.session-row:hover,
.agent-row-item:hover { background: rgba(var(--brand-rgb), 0.1); }
.session-row.selected,
.agent-row-item.selected { background: rgba(var(--brand-rgb), 0.16); }
.session-row.pinned .session-row-pin-icon,
.agent-row-item.pinned .agent-row-pin-icon {
    display: inline-flex;
    color: var(--warning);
  }
.session-row-pin-icon,
.agent-row-pin-icon { display: none; flex-shrink: 0; }
.session-row-status {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
  }
.session-status-running {
    color: var(--brand);
    animation: session-status-spin 1s linear infinite;
  }
@keyframes session-status-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
  }
.session-status-unread {
    color: var(--success);
  }
.session-row-title,
.agent-row-title {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 3px;
  }
/* Muted agent-name prefix on each unified session row: "Agent — Title". */
.session-row-agent { color: var(--fg-3); }
.session-row-sep { color: var(--fg-3); }
/* Agent emoji icon prefix in session rows */
.session-row-agent-icon { font-size: 14px; line-height: 1; flex-shrink: 0; display: inline-flex; align-items: center; }
.session-row-agent-icon svg,
.session-row-agent-icon i { display: block; }
/* Spawn badge: orchestration helper sessions (id prefix `spawn-`). Uses the
   accent colour so it reads as a tag in both dark and light themes. */
.session-row-spawn-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--accent);
    margin-right: 3px;
    vertical-align: middle;
    opacity: 0.85;
}
.session-row-time {
    flex-shrink: 0;
    font-size: 11px;
    color: var(--fg-3);
    padding: 0 4px;
    min-width: 36px;
    text-align: right;
    white-space: nowrap;
  }
/* ── Session-list group tree (orchestrator → spawns, Planner → closer) ──
   A family-root row gets an expand caret; expanding nests its member sessions
   as indented child rows directly beneath it. Mirrors how the sub-header tab
   bar groups the same family, so the two views stay consistent. */
.session-row-expand {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    color: var(--fg-3);
    border-radius: 4px;
    transition: color 0.12s ease, background 0.12s ease;
  }
.session-row-expand:hover { color: var(--fg-1); background: rgba(var(--brand-rgb), 0.16); }
.session-row.expanded .session-row-expand { color: var(--brand); }
/* Keep non-parent rows' titles aligned with parents' (same width as the caret). */
.session-row-expand-spacer {
    display: inline-block;
    width: 16px;
    flex-shrink: 0;
  }
/* Nested child rows: indented under their parent, with a left rail to read as a
   tree. They are still .session-row so hover/selected/click all work. */
.session-child-row {
    padding-left: 22px;
    font-size: 11.5px;
    color: var(--fg-2);
  }
.session-child-rail {
    position: absolute;
    left: 14px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--border);
  }
.session-row-child-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--accent);
    opacity: 0.85;
  }
.session-child-loading {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 8px 5px 22px;
    font-size: 11.5px;
    color: var(--fg-3);
  }
.session-child-loading [data-lucide] { animation: session-status-spin 1s linear infinite; }
/* ── Drag-to-reorder grip (agent + session dropdown rows) ── */
.row-drag-handle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--fg-4);
    cursor: grab;
    opacity: 0.5;
    border-radius: 4px;
    /* Claim touch gestures so a drag on the handle reorders instead of
       scrolling the menu. */
    touch-action: none;
  }
.session-row:hover .row-drag-handle,
.agent-row-item:hover .row-drag-handle { opacity: 1; color: var(--fg-3); }
.row-drag-handle:active { cursor: grabbing; }
.session-row.dragging,
.agent-row-item.dragging {
    opacity: 0.9;
    background: var(--accent-soft);
    box-shadow: 0 4px 14px rgba(var(--shadow-rgb), 0.25);
    cursor: grabbing;
  }
.agent-row-sep {
    height: 1px;
    background: var(--bg-tint);
    margin: 4px 0;
  }
body.light-mode .agent-row-sep { background: var(--border); }
.session-row-title-input {
    flex: 1;
    background: var(--bg-0);
    border: 1px solid var(--brand);
    border-radius: 4px;
    color: inherit;
    font: inherit;
    
    padding: 2px 6px;
    outline: none;
  }
.session-row-kebab,
.agent-row-kebab {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
  }
.session-row-kebab:hover,
.agent-row-kebab:hover { background: rgba(var(--ink-rgb),0.06); color: var(--fg-1); }
body.light-mode .session-row-kebab:hover,
body.light-mode .agent-row-kebab:hover { color: var(--fg-2); }
/* Per-row action buttons (delete on every row; config on agent rows). Share the
   muted kebab look; delete brightens to the danger hue on hover. */
.session-row-delete,
.session-row-visibility,
.session-manage-eye,
.session-manage-delete-all,
.agent-row-delete,
.agent-row-config {
    background: none;
    border: none;
    color: var(--fg-3);
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
  }
.agent-row-config:hover,
.session-row-visibility:hover,
.session-manage-eye:hover { background: rgba(var(--ink-rgb),0.06); color: var(--fg-1); }
body.light-mode .agent-row-config:hover,
body.light-mode .session-row-visibility:hover,
body.light-mode .session-manage-eye:hover { color: var(--fg-2); }
/* Active manage-eye (hidden sessions revealed): tint it so the mode reads clearly.
   Scoped under #chat-header to outrank the generic `#chat-header button` colour. */
#chat-header .session-manage-eye[data-state="on"] { color: var(--brand); }
.session-row-delete:hover,
.session-manage-delete-all:hover,
.agent-row-delete:hover { background: rgba(var(--danger-rgb), 0.12); color: var(--danger); }
.session-row-delete.warning,
.session-manage-delete-all.warning { color: var(--warning); }
.session-row-delete.warning:hover,
.session-manage-delete-all.warning:hover { background: rgba(var(--warning-rgb), 0.15); color: var(--warning); }
/* ── Management footer row ── */
.session-manage-row {
    display: flex;
    align-items: center;
    margin-top: 4px;
    padding: 6px 4px 2px;
    border-top: var(--border-width) solid var(--border);
  }
body.light-mode .session-manage-row { border-top-color: var(--border); }
.session-manage-actions {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
  }
.session-row-actions,
.agent-row-actions {
    position: fixed;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    box-shadow: 0 6px 16px rgba(var(--shadow-rgb),0.4);
    z-index: 2000;
    min-width: 140px;
    padding: 4px;
  }
body.light-mode .session-row-actions,
body.light-mode .agent-row-actions {
    background: var(--bg-0);
    border-color: var(--border);
  }
.session-row-action,
.agent-row-action {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
    padding: 6px 8px;
    border-radius: 4px;
    text-align: left;
  }
.session-row-action:hover,
.agent-row-action:hover { background: rgba(var(--brand-rgb), 0.1); }
.session-row-action.danger,
.agent-row-action.danger { color: var(--danger); }
.session-row-action.danger:hover,
.agent-row-action.danger:hover { background: rgba(var(--danger-rgb), 0.12); }
/* SHARED-DELETE-CONTROL — armed (hazard) hue when a danger menu row is using the
   two-click delete affordance (delete-control.js adds .warning). Specificity
   (0,3,0) beats the .danger rule above so it wins regardless of source order. */
.session-row-action.danger.warning,
.agent-row-action.danger.warning { color: var(--warning); }
.session-row-action.danger.warning:hover,
.agent-row-action.danger.warning:hover { background: rgba(var(--warning-rgb), 0.12); }
.session-dropdown-empty,
.agent-dropdown-empty {
    padding: 12px;
    text-align: center;
    color: var(--fg-3);
    font-size: 12px;
  }

/* ── Circular letter avatar ── */
  .user-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--bg-tint-2);
    color: var(--fg-1);
    font-weight: 600;
    line-height: 1;
    user-select: none;
    flex-shrink: 0;
  }
.user-avatar[data-size="sm"] { width: 30px;  height: 30px; font-size: 15px; }
.user-avatar[data-size="md"] { width: 32px;  height: 32px; font-size: 14px; }
.user-avatar[data-size="lg"] { width: 44px;  height: 44px; font-size: 18px; }
.user-avatar[data-size="xl"] { width: 64px;  height: 64px; font-size: 26px; }
body.light-mode .user-avatar {
    background: var(--bg-tint-2);
    color: var(--brand);
  }

/* ── User-dropdown redesign ── */
  .user-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    padding: 0;
    border-radius: 50%;
    transition: background 0.15s;
    background: none;
    border: none;
    -webkit-appearance: none;
    appearance: none;
    touch-action: manipulation;
  }
.user-dropdown-trigger:hover {
    background: rgba(var(--brand-rgb), 0.1);
  }
.user-dropdown-chevron {
    transition: transform 0.2s;
  }
.user-dropdown.open .user-dropdown-chevron {
    transform: rotate(180deg);
  }

/* Widen menu to fit large current-user row.
   position:fixed (not absolute) so the popover escapes the overflow clip of
   its scrolling ancestor #main-tabs; sessions.js anchors top/left to the
   trigger on open. ID+class specificity overrides the base .user-dropdown-menu
   rule's position:absolute.

   ── PANEL COHESION ──
   All dropdown panels (user menu, feedback view, login) MUST share the same
   visual dimensions: 360px wide, 12px border-radius, var(--bg-elev) background,
   1px solid var(--bg-tint) border, box-shadow 0 8px 32px rgba(var(--shadow-rgb),0.5).
   This keeps the UI consistent — never change one without the others. */
  #user-dropdown-menu.user-dropdown-menu {
    position: fixed;
    min-width: 280px;
    width: 360px;
    padding: 8px 0 6px;
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 12px;
    box-shadow: 0 8px 32px rgba(var(--shadow-rgb),0.5);
  }

/* Current user row */
  .user-dropdown-current-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px 6px;
  }
.user-dropdown-current-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
  }
.user-dropdown-current-name {
    font-size: 14px;
    font-weight: 600;
    color: var(--fg-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.user-dropdown-current-email {
    font-size: 12px;
    color: var(--fg-3);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

/* Manage Account button */
  .user-dropdown-manage-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: calc(100% - 24px);
    margin: 6px 12px 4px;
    padding: 8px 12px;
    background: var(--bg-tint);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    color: var(--fg-1);
    font-size: 13px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
.user-dropdown-manage-btn:hover {
    background: var(--bg-tint-2);
    border-color: var(--brand);
  }

/* Other accounts list */
  .user-dropdown-accounts-list {
    display: flex;
    flex-direction: column;
  }
.user-dropdown-accounts-list:empty {
    display: none;
  }
.user-dropdown-account-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 14px;
    background: none;
    border: none;
    color: inherit;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    transition: background 0.15s;
  }
.user-dropdown-account-row:hover {
    background: rgba(var(--brand-rgb), 0.08);
  }
.user-dropdown-account-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
  }
.user-dropdown-account-name {
    font-size: 13px;
    color: var(--fg-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.user-dropdown-account-email {
    font-size: 11px;
    color: var(--fg-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
.user-dropdown-add-account {
    color: var(--brand);
  }
.user-dropdown-add-account:hover {
    background: rgba(var(--brand-rgb), 0.1);
    color: var(--brand);
  }

/* Light-mode overrides for new dropdown elements */
  body.light-mode .user-dropdown-current-name { color: var(--fg-1); }
body.light-mode .user-dropdown-current-email,
body.light-mode .user-dropdown-account-email { color: var(--fg-3); }
body.light-mode .user-dropdown-account-name { color: var(--fg-1); }
body.light-mode .user-dropdown-manage-btn {
    background: var(--bg-elev);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode .user-dropdown-manage-btn:hover {
    background: var(--bg-tint-2);
    border-color: var(--brand);
  }
body.light-mode .user-dropdown-account-row:hover {
    background: var(--bg-elev-2);
  }
body.light-mode .user-dropdown-add-account { color: var(--brand); }
body.light-mode #user-dropdown-menu.user-dropdown-menu {
  background: var(--bg-elev);
  border-color: var(--border);
  box-shadow: 0 8px 32px rgba(var(--shadow-rgb),0.1);
}

/* ── Manage Account tab ── */
  #tab-account {
    overflow-y: auto;
    padding: 24px 32px 48px;
  }
.account-tab-wrap {
    max-width: 560px;
    margin: 0 auto;
  }
.account-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 24px;
    padding-bottom: 16px;
    border-bottom: var(--border-width) solid var(--border);
  }
.account-header-id {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
.account-header-name {
    font-size: 18px;
    font-weight: 600;
    color: var(--fg-1);
  }
.account-header-email {
    font-size: 13px;
    color: var(--fg-3);
    margin-top: 4px;
  }
.account-section {
    background: var(--bg-elev);
    border: var(--border-width) solid var(--border);
    border-radius: 8px;
    padding: 18px 20px;
    margin-bottom: 16px;
  }
.account-section-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--brand);
    margin: 0 0 12px;
  }
.account-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 12px;
  }
.account-field label {
    font-size: 12px;
    font-weight: 600;
    color: var(--fg-2);
  }
.account-field input {
    padding: 8px 10px;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    color: var(--fg-1);
    font-size: 13px;
    font-family: inherit;
    outline: none;
  }
.account-field input:focus {
    border-color: var(--brand);
  }
.account-field select {
    padding: 8px 10px;
    background: var(--bg-0);
    border: var(--border-width) solid var(--border);
    border-radius: 6px;
    color: var(--fg-1);
    font-size: 13px;
    font-family: inherit;
    outline: none;
    cursor: pointer;
  }
.account-field select:focus {
    border-color: var(--brand);
  }
.account-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 4px;
    flex-wrap: wrap;
  }
.account-btn {
    padding: 8px 16px;
    border-radius: 6px;
    border: var(--border-width) solid var(--border);
    background: var(--bg-tint);
    color: var(--fg-1);
    font-size: 13px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
.account-btn:hover {
    background: var(--bg-tint-2);
    border-color: var(--brand);
  }
.account-btn-primary {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--bg-0);
    font-weight: 700;
  }
.account-btn-primary:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
  }
.account-btn-danger {
    background: var(--danger);
    border-color: var(--danger);
    color: #fbf1c7;
  }
.account-btn-danger:hover {
    background: color-mix(in oklab, var(--danger) 82%, black);
    border-color: var(--danger);
  }
.account-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
.account-status {
    font-size: 12px;
    color: var(--fg-2);
  }
.account-danger-zone {
    border-color: rgba(var(--danger-rgb), 0.25);
  }
.account-danger-desc {
    font-size: 12px;
    color: var(--fg-2);
    margin: 0 0 12px;
  }
.account-delete-confirm {
    margin-top: 12px;
    padding-top: 12px;
    border-top: var(--border-width) solid var(--border);
  }
.account-delete-ack {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--fg-2);
    margin: 8px 0;
    cursor: pointer;
  }
.account-empty {
    color: var(--fg-3);
    font-size: 13px;
    padding: 24px;
    text-align: center;
  }
  .account-pref-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    cursor: pointer;
    padding: 4px 0;
  }
  .account-pref-row input[type="checkbox"] {
    margin-top: 3px;
    flex-shrink: 0;
    cursor: pointer;
  }
  .account-pref-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .account-pref-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--fg-1);
  }
  .account-pref-desc {
    font-size: 11px;
    color: var(--fg-3);
    line-height: 1.4;
  }
  body.light-mode .account-pref-title { color: var(--fg-1); }
  body.light-mode .account-pref-desc { color: var(--fg-3); }

/* Light-mode for Manage Account tab */
  body.light-mode .account-header { border-bottom-color: var(--border); }
body.light-mode .account-header-name { color: var(--fg-1); }
body.light-mode .account-header-email { color: var(--fg-3); }
body.light-mode .account-section {
    background: var(--bg-elev-2);
    border-color: var(--border);
  }
body.light-mode .account-section-title { color: var(--brand); }
body.light-mode .account-field label { color: var(--fg-2); }
body.light-mode .account-field input {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode .account-field input:focus { border-color: var(--brand); }
body.light-mode .account-field select {
    background: var(--bg-0);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode .account-field select:focus { border-color: var(--brand); }
body.light-mode .account-btn {
    background: var(--bg-elev);
    border-color: var(--border);
    color: var(--fg-1);
  }
body.light-mode .account-btn:hover {
    background: var(--bg-tint-2);
    border-color: var(--brand);
  }
body.light-mode .account-btn-primary {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--bg-0);
  }
body.light-mode .account-status { color: var(--fg-2); }
body.light-mode .account-danger-desc { color: var(--fg-2); }
body.light-mode .account-delete-confirm { border-top-color: var(--border); }
body.light-mode .account-delete-ack { color: var(--fg-2); }

/* ── "Send to agent" long-press affordance ────────────────────────────────
   The reverse of the App-control ability: on a still hold (500ms) the floating
   "Send to agent" button appears near the cursor. During the hold a subtle
   ripple ring grows at the touch point (300ms → 500ms) to signal the gesture
   is building. Clicking the button stages a context note (current view, the
   outlined container, selected text) at the front of the chat box. The
   container it will capture is outlined via .s2a-highlight. Right-click also
   works as a desktop fallback.
   DEACTIVATED (orphaned): the JS handler (ui/js/sendToAgent.js) was removed in the
   ui restructure and no JS/HTML currently creates .s2a-fab/.s2a-highlight, so this
   whole .s2a-* block is dead at present. REMOVE-WHEN: confirmed the send-to-agent
   FAB is not being re-added; otherwise re-wire a handler under ui/shared/js/.
   Design-system tokens only, so it tracks dark/light. */
.s2a-fab {
  position: fixed;
  z-index: 4000;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  background: var(--accent);
  color: var(--bg-0);
  border: none;
  border-radius: 999px;
  box-shadow: var(--shadow-lg);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  animation: s2a-fab-in 0.12s ease-out;
}
.s2a-fab:hover { background: var(--accent-hover); }
.s2a-fab .lucide { width: 15px; height: 15px; flex-shrink: 0; }
@keyframes s2a-fab-in {
  from { opacity: 0; transform: translateY(3px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

/* The container that will be captured — outlined while the button is up.
   outline (not box-shadow) so it's never clipped by an overflow ancestor and
   adds no layout shift. */
.s2a-highlight {
  outline: 2px solid var(--accent) !important;
  outline-offset: 1px;
  border-radius: 4px;
  transition: outline-color 0.12s;
}

/* Ripple ring that appears during the last ~200ms of the 500ms long-press
   hold, signaling the gesture is about to fire (appears at 280ms). Design-
   system --accent with low opacity so it works in dark and light mode. */
.s2a-ripple-ring {
  position: fixed;
  z-index: 3999;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 2.5px solid var(--accent);
  opacity: 0.55;
  pointer-events: none;
  animation: s2a-ripple-grow 0.32s ease-out forwards;
}
@keyframes s2a-ripple-grow {
  from { transform: scale(0.4); opacity: 0.55; }
  to   { transform: scale(1.4); opacity: 0; }
}

