/* =========================================================================
   agent-beautiful.css — the BridgeBI Agent, rebuilt on beautiful.css
   =========================================================================

   Loaded AFTER agent.css so it wins on equal specificity. It restyles seven
   surfaces of the analyst, each ported from the component study at
   https://beautiful-ui-five.vercel.app:

     1. SIDEBAR NAV      the sessions/memory column → a real workspace rail
     2. TASK ROWS        tool-call chips → capsules with a live state glyph
     3. THINKING         a bare shimmering line + "Pensó 4s" on collapse
     4. STREAMING TEXT   blur-tail + blinking caret while the answer lands
     5. INSIGHT CARDS    visuals → head / lede / delta grid / chart stage
     6. APPROVAL CARDS   the follow-up question as options you can answer
     7. PROMPT BAR       one pill: field, divider, actions, send

   Two rules held throughout:

   - NOTHING HERE TOUCHES A NUMBER. Every figure on this page came out of a
     tool that ran in the browser against the pool the dashboard classified.
     This file changes how those figures are framed, never which cell they
     came from.
   - THE GOLD RING STAYS. `.ai-chat-col`'s travelling gold edge and the send
     button's rim are the app's signature; the prompt bar is rebuilt AROUND
     them rather than over them.
   ========================================================================= */

/* =========================================================================
   1. SIDEBAR NAV — sessions + memory as a workspace rail
   -------------------------------------------------------------------------
   The reference sidebar's structure: an identity block on top, quick search,
   the primary action, then uppercase micro-cap section labels over the list.
   That order matters — the thing you do most (start a new question) sits
   above the history, not buried under it.
   ========================================================================= */

.ai-sessions {
  gap: 0;
  padding: 8px;
  border-radius: var(--bb-r-card);
  background: var(--bb-surface);
  box-shadow: var(--bb-sh-card);
}

/* Identity block: monogram + workspace name. Uses the app's own avatar
   language so the rail reads as part of BridgeBI, not a bolted-on panel. */
.ai-rail-id {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 6px 9px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--bb-line);
}
.ai-rail-mark {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 30px; width: 30px; height: 30px;
  border-radius: var(--bb-r-control);
  background: linear-gradient(150deg, var(--bb-accent), #17407F);
  color: #fff; font: 700 12px/1 inherit; letter-spacing: .02em;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .10), 0 1px 2px rgba(13, 42, 94, .25);
}
.ai-rail-idtext { min-width: 0; }
.ai-rail-idtext b {
  display: block; font: 600 12.5px/1.25 inherit; color: var(--bb-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ai-rail-idtext span { display: block; font-size: 10.5px; color: var(--bb-ink-3); }

/* Quick search over the conversation titles. The `/` key badge is the
   reference's affordance — it teaches the shortcut without a tooltip. */
.ai-rail-search {
  display: flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 9px; margin-bottom: 8px;
  border-radius: var(--bb-r-control);
  background: var(--bb-field);
  box-shadow: var(--bb-sh-inset);
}
.ai-rail-search svg { width: 14px; height: 14px; color: var(--bb-ink-3); flex: none; max-width: none; }
/* The input fills the pill's full height on purpose: at its natural 17px, the
   14px of padding above and below it were dead pixels — you aimed at the search
   box, hit the wrapper, and nothing focused. */
.ai-rail-search input {
  flex: 1; min-width: 0; align-self: stretch; height: 100%;
  border: 0; background: transparent; outline: 0;
  color: var(--bb-ink); font: 400 12px/1 inherit;
}
.ai-rail-search input::placeholder { color: var(--bb-ink-3); }
.ai-rail-search kbd {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: var(--bb-r-chip);
  background: var(--bb-surface); color: var(--bb-ink-3);
  box-shadow: var(--bb-sh-hairline);
  font: 500 10.5px/1 'JetBrains Mono', ui-monospace, monospace;
}

/* Primary action, reference-style: accent-coloured label on the left, a
   filled circular plus on the right. It reads as a verb, not a button. */
.ai-new-chat {
  justify-content: space-between;
  padding: 8px 8px 8px 10px !important;
  border: 0 !important;
  border-radius: var(--bb-r-control) !important;
  background: transparent !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  color: var(--bb-accent-ink) !important;
  font-weight: 600 !important;
  box-shadow: none !important;
}
.ai-new-chat:hover { background: var(--bb-hover) !important; transform: none !important; }
/* `text-align: left` because the bare `button` rule centres its text, and a
   full-width label in a space-between row then floats in the middle with the
   plus stranded at the edge. The label is a verb; it starts where the list
   below it starts. */
.ai-new-chat span { flex: 1; order: 1; text-align: left; }
.ai-new-chat svg {
  order: 2; box-sizing: content-box;
  width: 13px !important; height: 13px !important; padding: 5px;
  border-radius: 50%;
  background: var(--bb-accent); color: #fff;
  transition: transform var(--bb-t-base) var(--bb-ease-strong);
}
html[data-bb-theme="dark"] .ai-new-chat svg { color: #08111F; }
.ai-new-chat:hover svg { transform: rotate(90deg); }

/* Micro-cap section label. `.ai-rail-label` is new markup; `.ai-memory-head`
   already had this treatment and just gets the tightened metrics. */
.ai-rail-label,
.ai-memory-head {
  padding: 7px 8px 5px !important;
  font-size: 10.5px !important;
  font-weight: 650 !important;
  letter-spacing: .045em !important;
  color: var(--bb-ink-3) !important;
}
.ai-rail-label {
  display: flex; align-items: center; gap: 6px;
  text-transform: uppercase; line-height: 1;
}
.ai-rail-label .n {
  margin-left: auto;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums; letter-spacing: 0;
}

/* Session rows: an 8px pill, and the ACTIVE one carries a surface + hairline
   rather than a flat tint. A tint alone loses the row the moment the sidebar
   itself is tinted — which is exactly what happens in dark mode. */
.ai-session { border-radius: var(--bb-r-control); }
.ai-session:hover { background: var(--bb-hover); }
.ai-session.is-active {
  background: var(--bb-hover); box-shadow: var(--bb-sh-hairline);
}
.ai-session.is-active .ai-session-title { color: var(--bb-ink); }
.ai-session-title { color: var(--bb-ink); font-size: 12.5px; }
.ai-session-time {
  color: var(--bb-ink-3); font-size: 10px;
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums;
}
/* Gold left rail on the active conversation — the same marker the app's own
   sidebar uses for the active page, so "where am I" reads the same in both. */
.ai-session.is-active .ai-session-open { position: relative; }
.ai-session.is-active .ai-session-open::before {
  content: ''; position: absolute; left: 3px; top: 9px; bottom: 9px; width: 2.5px;
  border-radius: 2px; background: var(--bb-gold);
}

.ai-session-list { gap: 2px; }
.ai-session-list::-webkit-scrollbar { display: none; }
.ai-session-list { scrollbar-width: none; }
.ai-session-empty { color: var(--bb-ink-3); }

.ai-memory { border-top-color: var(--bb-line); }
.ai-memory-count {
  background: var(--bb-gold-tint) !important; color: var(--bb-gold-ink) !important;
  border-radius: var(--bb-r-chip) !important;
}

/* =========================================================================
   2. TASK ROWS — one capsule per tool call
   -------------------------------------------------------------------------
   Replaces the mono chip with a left-glyph capsule: rotating ring while the
   tool runs, green check when it returns, red when it fails. A user watching
   evidence being gathered is a user who trusts the answer; a spinner tells
   them nothing about what is happening.
   ========================================================================= */

.bb-agent-tool {
  align-items: center; flex-wrap: nowrap; gap: 10px;
  max-width: 100%;
  padding: 8px 12px 8px 8px;
  border: 0; border-left: 0;
  border-radius: 99px;
  background: var(--bb-surface);
  box-shadow: var(--bb-sh-card);
  font-family: inherit; font-size: 12.5px;
  animation: bb-fade-up var(--bb-t-slow) var(--bb-ease-strong) both;
}

/* The tick svg becomes the glyph disc. Styling the existing element instead
   of replacing it means a cached agent.js still renders something sane. */
.bb-agent-tool-tick {
  box-sizing: content-box;
  width: 13px !important; height: 13px !important; padding: 5px;
  border-radius: 50%;
  background: var(--bb-green); color: #fff !important;
  flex: none; max-width: none;
}
.bb-agent-tool.is-running .bb-agent-tool-tick {
  background: var(--bb-field); color: transparent !important; position: relative;
}
.bb-agent-tool.is-running .bb-agent-tool-tick::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid transparent; border-top-color: var(--bb-accent);
  animation: bb-spin .8s linear infinite;
}
.bb-agent-tool.is-failed .bb-agent-tool-tick { background: var(--bb-red); }

.bb-agent-tool-name {
  flex: 0 1 auto; min-width: 0;
  font-family: inherit !important; font-size: 12.5px !important; font-weight: 500 !important;
  color: var(--bb-ink) !important;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bb-agent-tool-args {
  flex: 1 1 auto; min-width: 0; text-align: right;
  color: var(--bb-ink-3); opacity: 1 !important;
  font-size: 11.5px; word-break: normal !important;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Status capsule on the right end, added by agent.js. */
.bb-agent-tool .bb-status { margin-left: 2px; }

/* Folded into the steps drawer they lose the capsule and become a compact
   list — a stack of pills inside a drawer is visual noise. */
.bb-agent-tool.is-folded {
  padding: 5px 8px; border-radius: var(--bb-r-chip);
  background: var(--bb-inset); box-shadow: none;
}
.bb-agent-tool.is-folded .bb-agent-tool-tick { padding: 3px; width: 10px !important; height: 10px !important; }
.bb-agent-tool.is-folded .bb-status { display: none; }

.bb-agent-steps-body { gap: 4px; }

/* =========================================================================
   3. THINKING — a line, not a badge
   -------------------------------------------------------------------------
   The reference's thinking state has no container at all: sparkle, shimmering
   label, elapsed seconds. A pill around it makes the wait feel like a status
   chip you have to acknowledge; a bare line feels like the model murmuring.
   ========================================================================= */

.bb-agent-thinking {
  gap: 9px;
  padding: 5px 2px !important;
  border: 0 !important; border-radius: 0 !important;
  background: none !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  font-size: 13px !important;
}
.bb-agent-thinking-label {
  /* Re-declared against the theme tokens so the shimmer survives dark mode —
     agent.css hardcodes its gradient stops for the light palette. */
  background: linear-gradient(100deg,
    var(--bb-ink-3) 26%, var(--bb-gold) 44%, var(--bb-ink) 52%, var(--bb-ink-3) 70%) !important;
  background-size: 220% 100% !important;
  -webkit-background-clip: text !important; background-clip: text !important;
  -webkit-text-fill-color: transparent; color: transparent !important;
}
.bb-agent-thinking .bb-agent-elapsed {
  color: var(--bb-ink-3);
  font: 400 12px/1 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

/* Collapsed trace: "✦ Pensó 4s · consultó 3 fuentes". Chevron rotates DOWN
   when open (the reference's direction) — a caret that turns sideways reads
   as "next", one that turns down reads as "unfolded". */
.bb-agent-steps-head {
  gap: 7px;
  padding: 5px 9px !important;
  border: 0 !important; border-radius: var(--bb-r-control) !important;
  background: transparent !important;
  color: var(--bb-ink-2) !important;
  font-size: 12.5px !important; font-weight: 500;
  transition: background-color var(--bb-t-fast) ease-out, color var(--bb-t-fast) ease-out !important;
}
.bb-agent-steps-head:hover { background: var(--bb-hover) !important; color: var(--bb-ink) !important; }
.bb-agent-steps-caret { order: 3; margin-left: 2px; color: var(--bb-ink-3); }
.bb-agent-steps.is-open .bb-agent-steps-caret { transform: rotate(90deg); }
.bb-agent-steps-head .bb-agent-spark { order: 1; width: 14px; height: 14px; flex: none; max-width: none; color: var(--bb-gold); }
.bb-agent-steps-head > span { order: 2; }

/* =========================================================================
   4. STREAMING TEXT
   -------------------------------------------------------------------------
   The answer is revealed progressively: settled text is crisp, the newest
   words carry a blur-and-fade tail, and a caret blinks until the reveal ends.

   BE PRECISE ABOUT WHAT THIS IS. The provider call is `stream: false` —
   agent.js has the complete answer before the first word appears. This is a
   REVEAL, not token streaming. It is worth having anyway: reading starts at
   the top while the rest arrives, which is the actual benefit of streaming
   for a paragraph-length answer. It must never be described in the UI as the
   model "still writing", because it is not.
   ========================================================================= */

.bb-agent-msg.is-revealing .bb-reveal-tail {
  filter: blur(1.6px);
  -webkit-mask-image: linear-gradient(90deg, #000 20%, rgba(0, 0, 0, .2));
          mask-image: linear-gradient(90deg, #000 20%, rgba(0, 0, 0, .2));
}
.bb-agent-msg .bb-caret {
  display: inline-block; width: 2px; height: 1.05em; margin-left: 2px;
  vertical-align: text-bottom; translate: 0 -.5px;
  border-radius: 1px; background: var(--bb-accent);
  animation: bb-caret 1s step-end infinite;
}

/* A reader who scrolls up mid-reveal has opted out of following along; the
   reveal keeps going but stops yanking the viewport (handled in agent.js).
   Reduced motion skips the animation entirely and prints the answer at once. */
@media (prefers-reduced-motion: reduce) {
  .bb-agent-msg.is-revealing .bb-reveal-tail { filter: none; -webkit-mask-image: none; mask-image: none; }
  .bb-agent-msg .bb-caret { animation: none; }
}

/* =========================================================================
   5. INSIGHT CARDS — the visuals
   -------------------------------------------------------------------------
   `.bb-viz` gets the reference's insight-card frame: hairline-first elevation,
   a head that carries the title and (when a turn produced several) a pager,
   and a recessed chart stage with a scrub cursor and a real tooltip instead
   of the browser's `title` bubble.
   ========================================================================= */

.bb-viz {
  padding: 14px;
  border: 0 !important;
  border-radius: var(--bb-r-card) !important;
  background: var(--bb-surface) !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  box-shadow: var(--bb-sh-card) !important;
  animation: bb-fade-up var(--bb-t-slow) var(--bb-ease-strong) both;
}

.bb-viz-head { align-items: center; gap: 8px; margin-bottom: 10px; }
.bb-viz-title { font-size: 14px !important; color: var(--bb-ink) !important; }
.bb-viz-sub { font-size: 11.5px !important; color: var(--bb-ink-3) !important; }

/* Pager, rendered only when a single turn produced more than one visual.
   Sits hard right so the title never shifts as you page. */
.bb-viz-pager { display: inline-flex; align-items: center; gap: 2px; margin-left: auto; flex: none; }
.bb-viz-pager .bb-viz-count {
  margin-right: 4px; color: var(--bb-ink-3);
  font: 500 11.5px/1 'JetBrains Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums;
}

/* Delta block: label with a series dot, big signed percent, figure under it. */
.bb-viz-metrics { gap: 6px 22px; }
.bb-viz-metric b { font-size: 15px !important; }
.bb-viz-hero b { font-size: 26px !important; letter-spacing: -.02em !important; }
.bb-viz-hero span { color: var(--bb-ink-3) !important; letter-spacing: .05em; }
.bb-viz-hero.is-up b   { color: var(--bb-green) !important; }
.bb-viz-hero.is-down b { color: var(--bb-red) !important; }

/* Chart stage: the plot is recessed into its own well, which separates the
   data surface from the card that frames it. */
.bb-viz-trend {
  border-radius: var(--bb-r-control);
  background: var(--bb-canvas);
  box-shadow: var(--bb-sh-hairline);
  overflow: hidden;
}
.bb-viz-stage-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 10px;
  border-bottom: 1px solid var(--bb-line);
  color: var(--bb-ink-3); font-size: 11.5px;
}
.bb-viz-stage-head .bb-viz-stagetag {
  padding: 3px 7px; border-radius: var(--bb-r-chip);
  background: var(--bb-field); color: var(--bb-ink-2);
  font: 500 10.5px/1 inherit; letter-spacing: .03em; text-transform: uppercase;
}
.bb-viz-plot { height: 150px; padding: 10px 10px 2px; }
.bb-viz-trend-x { padding: 0 10px 8px; margin-top: 2px; color: var(--bb-ink-3); }

/* Dots stay as hit targets but go invisible until their point is scrubbed —
   a line with 30 permanent dots on it reads as a bead necklace. */
.bb-viz-dot {
  width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px;
  background: var(--bb-gold);
  box-shadow: 0 0 0 1.6px var(--bb-surface);
  opacity: 0; transition: opacity var(--bb-t-fast) ease-out, transform var(--bb-t-fast) var(--bb-ease-strong);
}
.bb-viz-dot.is-hot { opacity: 1; transform: scale(1.15); }
.bb-viz-plot:hover .bb-viz-dot { opacity: .35; }
.bb-viz-plot:hover .bb-viz-dot.is-hot { opacity: 1; }

.bb-viz-cursor {
  position: absolute; top: 10px; bottom: 8px; width: 1px; z-index: 4;
  background: var(--bb-ink); opacity: .26; pointer-events: none;
  display: none;
}
.bb-viz-plot.is-scrubbing .bb-viz-cursor { display: block; }

.bb-viz-tip {
  position: absolute; top: 4px; z-index: 5;
  transform: translateX(-50%); pointer-events: none;
  min-width: 120px; padding: 8px 10px;
  border: 1px solid var(--bb-tip-line); border-radius: var(--bb-r-card);
  background: var(--bb-tip-bg); color: var(--bb-tip-fg);
  box-shadow: var(--bb-sh-overlay);
  font-size: 12px; white-space: nowrap;
  display: none;
}
.bb-viz-plot.is-scrubbing .bb-viz-tip { display: block; }
.bb-viz-tip .t { display: block; margin-bottom: 5px; color: var(--bb-tip-muted); font-size: 11px; }
.bb-viz-tip .r { display: flex; align-items: center; justify-content: space-between; gap: 14px; line-height: 1.6; }
.bb-viz-tip .r span { display: inline-flex; align-items: center; gap: 6px; }
.bb-viz-tip .r i { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.bb-viz-tip .r b {
  color: var(--bb-tip-muted); font-weight: 500;
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums;
}

/* Cards inside a visual pick up the same hairline elevation. */
.bb-viz-card {
  border: 0 !important; border-radius: var(--bb-r-control) !important;
  background: var(--bb-surface) !important;
  box-shadow: var(--bb-sh-card) !important;
}
.bb-viz-card.is-askable:hover { box-shadow: var(--bb-sh-raised) !important; transform: translateY(-2px); }
.bb-viz-card-name { color: var(--bb-ink) !important; }
.bb-viz-card-sub  { color: var(--bb-ink-3) !important; }
.bb-viz-track { background: var(--bb-field); }
.bb-viz-row-label { color: var(--bb-ink-2) !important; }
.bb-viz-row-val { color: var(--bb-ink) !important; }
.bb-viz-row-end { color: var(--bb-ink-3) !important; }

/* =========================================================================
   6. APPROVAL CARDS — the follow-up, answerable in place
   -------------------------------------------------------------------------
   The old follow-ups were a scrolling strip of chips. The reference's
   approval card is better for this job because it states the QUESTION, then
   the options, then a free-text row for the answer that was not offered —
   which is the honest shape, since the three suggestions are a guess.

   `.bb-agent-suggest` keeps its class so agent.js's existing handler still
   finds it; only the layout changes.
   ========================================================================= */

.bb-agent-suggest {
  display: block; overflow: visible;
  padding: 0 18px 10px;
}

.bb-approve {
  /* `flex: none` IS LOAD-BEARING. `.bb-agent-log` is a column flex container
     that overflows, and `overflow: hidden` here sets this box's automatic
     minimum size to 0 — so the default `flex-shrink: 1` crushed the card to
     ZERO HEIGHT whenever the transcript was taller than the column. It was in
     the DOM, `getBoundingClientRect().height` read 0, and nothing errored: the
     follow-ups simply did not exist on any conversation long enough to scroll.

     Same family as the KPI `min-width: auto` clip. Every box in this file that
     pairs `overflow: hidden` with life as a flex child needs the same guard. */
  flex: none;
  /* Sized and aligned to the ASSISTANT's column, not the full log width.
     Full-bleed, the card's right-aligned category tags ended up ~700px from
     the option text they label, which reads as two unrelated columns. Capped,
     the tag sits beside its row and the card reads as the agent offering
     something — the same side every other thing it says comes from. */
  align-self: flex-start;
  width: 100%; max-width: 560px;
  border-radius: var(--bb-r-card);
  background: var(--bb-surface);
  box-shadow: var(--bb-sh-card);
  overflow: hidden;
  animation: bb-fade-up var(--bb-t-slow) var(--bb-ease-strong) both;
}

/* ONE 14px gutter down the whole card. The header text, each option's radio,
   and the input all start on it — so the eye tracks a single left edge instead
   of three that are two pixels apart. */
.bb-approve-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 10px 8px 14px;
}
/* A question, set as a question. Micro-caps are for section LABELS; uppercasing
   "¿Qué sigue?" made the header compete with the options it introduces. */
.bb-approve-q { min-width: 0; font: 600 13.5px/1.35 inherit; color: var(--bb-ink); }
.bb-approve-opts { padding: 0 6px 6px; }

/* An option row is a radio in behaviour but a button in markup — it fires the
   question immediately, so a two-step "select then submit" would be a lie. */
/* 38px rows. The old 8px padding gave ~31px, which is under the comfortable
   floor for a click target that sits in a stack of three — they read as a
   checklist to scan rather than three things to press. */
.bb-approve-opt {
  display: flex; align-items: center; gap: 11px; width: 100%;
  min-height: 38px; padding: 8px; border: 0; border-radius: var(--bb-r-control);
  background: transparent; color: var(--bb-ink);
  font: 450 13px/1.4 inherit; text-align: left; cursor: pointer;
  transition: background-color var(--bb-t-fast) ease-out, color var(--bb-t-fast) ease-out;
}
.bb-approve-opt:hover { background: var(--bb-hover); }
.bb-approve-opt:active { background: var(--bb-hover-2); }
.bb-approve-opt::before {
  content: ''; flex: 0 0 16px; width: 16px; height: 16px;
  border: 1.5px solid var(--bb-line-strong); border-radius: 50%;
  transition: border-color var(--bb-t-fast) ease-out, box-shadow var(--bb-t-fast) ease-out;
}
.bb-approve-opt:hover::before {
  border-color: var(--bb-accent);
  box-shadow: inset 0 0 0 3px var(--bb-surface), inset 0 0 0 9px var(--bb-accent);
}
/* The tag HUGS its own text instead of being right-aligned. Right-aligning three
   tags of different widths leaves their left edges ragged, which builds a second
   crooked column down the card — the thing that made this look disorganised.
   Hugging, each tag reads as belonging to the question in front of it, and the
   leftover space collects harmlessly on the right. */
.bb-approve-opt > span:first-of-type { flex: 0 1 auto; min-width: 0; }
.bb-approve-opt .bb-approve-why {
  flex: none; margin-right: auto;
  padding: 3px 7px; border-radius: var(--bb-r-chip);
  background: var(--bb-field); color: var(--bb-ink-3);
  font: 500 10.5px/1 inherit; letter-spacing: .02em;
  opacity: .75; transition: opacity var(--bb-t-fast) ease-out, color var(--bb-t-fast) ease-out;
}
.bb-approve-opt:hover .bb-approve-why { opacity: 1; color: var(--bb-ink-2); }

.bb-approve-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px 10px 14px;
  border-top: 1px solid var(--bb-line); background: var(--bb-inset);
}
.bb-approve-foot input {
  flex: 1; min-width: 0; height: 32px; padding: 0 11px;
  border: 0; border-radius: var(--bb-r-control);
  background: var(--bb-surface); box-shadow: var(--bb-sh-hairline);
  color: var(--bb-ink); font: 400 12.5px/1 inherit; outline: 0;
}
.bb-approve-foot input::placeholder { color: var(--bb-ink-3); }
.bb-approve-foot input:focus { box-shadow: var(--bb-sh-hairline), var(--bb-sh-focus); }
.bb-approve-send {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 32px; height: 32px;   /* matches the input it sits beside */
  border: 0; border-radius: var(--bb-r-control);
  background: var(--bb-accent); color: #fff; cursor: pointer;
  transition: transform var(--bb-t-fast) var(--bb-ease-strong), background-color var(--bb-t-fast) ease-out;
}
html[data-bb-theme="dark"] .bb-approve-send { color: #08111F; }
.bb-approve-send:hover  { background: color-mix(in srgb, var(--bb-accent) 88%, #000); }
.bb-approve-send:active { transform: scale(.94); }
.bb-approve-send svg { width: 14px; height: 14px; flex: none; max-width: none; }

.bb-approve-dismiss {
  flex: none; width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: var(--bb-r-chip);
  background: transparent; color: var(--bb-ink-3); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background-color var(--bb-t-fast) ease-out, color var(--bb-t-fast) ease-out;
}
.bb-approve-dismiss:hover { background: var(--bb-hover); color: var(--bb-ink); }
.bb-approve-dismiss svg { width: 13px; height: 13px; flex: none; max-width: none; }

/* Empty-state suggestion cards get the same hairline elevation. */
.bb-agent-card {
  border: 0 !important; border-radius: var(--bb-r-card) !important;
  background: var(--bb-surface) !important;
  backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
  box-shadow: var(--bb-sh-card) !important;
}
.bb-agent-card:hover { box-shadow: var(--bb-sh-raised) !important; }
.bb-agent-card-title { color: var(--bb-ink) !important; }
.bb-agent-card-sub   { color: var(--bb-ink-3) !important; }

/* =========================================================================
   7. PROMPT BAR — field, divider, actions, send, in one pill
   -------------------------------------------------------------------------
   The form becomes ONE control instead of a textarea sitting next to a
   button. The gold send button keeps its rim animation; it is simply inset
   into the pill now, and the row beneath it carries the affordances that
   were previously invisible (what the agent can currently see, and the
   Enter/Shift+Enter contract).
   ========================================================================= */

.bb-agent-form {
  display: block;
  padding: 10px 18px calc(16px + env(safe-area-inset-bottom, 0px));
}
.bb-agent-bar {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 7px 7px 7px 12px;
  border-radius: 18px;
  background: var(--bb-surface);
  box-shadow: var(--bb-sh-card);
  transition: box-shadow var(--bb-t-base) ease-out;
}
.bb-agent-form:focus-within .bb-agent-bar { box-shadow: var(--bb-sh-raised); }

#bbAgentInput {
  flex: 1; min-width: 0;
  padding: 10px 0 !important;
  border: 0 !important; background: transparent !important;
  box-shadow: none !important; outline: 0 !important;
  color: var(--bb-ink); font: 400 13.5px/1.5 inherit;
  resize: none;
}
#bbAgentInput::placeholder { color: var(--bb-ink-3); }

/* The send button drops from 44px to 38px: inside a pill, 44 leaves no
   breathing room and the rim animation clips against the container edge. */
#bbAgentSend {
  width: 38px !important; height: 38px !important;
  border-radius: 13px !important;
}

/* Hint row under the bar: what it can see on the left, key contract right. */
.bb-agent-hints {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 4px 0;
  color: var(--bb-ink-3); font-size: 11px;
}
.bb-agent-hints .bb-agent-hint-scope {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bb-agent-hints .bb-agent-hint-scope i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--bb-green); flex: none;
}
.bb-agent-hints .bb-agent-hint-keys { margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 5px; }
.bb-agent-hints kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 4px;
  background: var(--bb-field); color: var(--bb-ink-3);
  font: 500 10px/1 'JetBrains Mono', ui-monospace, monospace;
}

/* =========================================================================
   8. MESSAGE BUBBLES + LOG
   -------------------------------------------------------------------------
   Left/right asymmetry stays (it is how you tell who said what), but both
   sides move onto the token surfaces so the column works in dark mode.
   ========================================================================= */

/* ---- bubble metrics ------------------------------------------------------
   The old bubble was 11px/14px padding around 13.5px text on a 1.55 line-height
   (~21px lines), with 8px between paragraphs. That is the actual defect: the gap
   BETWEEN paragraphs was the same as the gap to the EDGE, so the text read as
   pressed against the walls and the paragraph break stopped registering as one.

   The fix is a hierarchy, not just "more padding":

       outer padding  16px   >   paragraph gap  10px   >   line gap  ~8px

   Each level of separation is visibly larger than the one inside it, which is
   what makes a block of text read as sitting IN something.

   Measure is capped in `ch`, not `%`. At 13.5px in a 1,000px column, 78% is
   ~110 characters per line — nearly double the 55–75ch where the eye reliably
   finds the start of the next line. `min()` keeps the percentage as the floor on
   narrow screens. */
.bb-agent-msg {
  max-width: min(78%, 68ch) !important;
  padding: 16px 18px !important;
  line-height: 1.62 !important;
}
.bb-agent-msg p { margin: 0 0 10px !important; }
.bb-agent-msg p:last-child { margin-bottom: 0 !important; }
.bb-agent-msg ul, .bb-agent-msg ol { margin: 8px 0 10px !important; padding-left: 20px !important; }
.bb-agent-msg li { margin-bottom: 5px !important; }
.bb-agent-msg li:last-child { margin-bottom: 0 !important; }
.bb-agent-msg h4 { margin: 16px 0 8px !important; }
.bb-agent-msg h4:first-child { margin-top: 0 !important; }
.bb-agent-msg > :first-child { margin-top: 0 !important; }
.bb-agent-msg > :last-child { margin-bottom: 0 !important; }

/* Inline number chips sit ON the text baseline, so their own padding eats into
   the line gap. A hair of horizontal margin keeps them from touching the words
   either side, and the taller line-height above gives them vertical room. */
.bb-agent-msg code {
  padding: 2px 6px !important;
  margin: 0 1px;
  border-radius: var(--bb-r-chip) !important;
}

.bb-agent-assistant {
  background: var(--bb-surface) !important;
  color: var(--bb-ink) !important;
  box-shadow: var(--bb-sh-card);
  border-radius: 14px 14px 14px 5px !important;
}
.bb-agent-user {
  /* The user's own words are short — a question, not a report — so this side
     wraps to its content instead of reserving the assistant's measure. */
  max-width: min(78%, 52ch) !important;
  background: var(--bb-accent) !important;
  color: #fff !important;
  border-radius: 14px 14px 5px 14px !important;
  box-shadow: 0 1px 2px rgba(13, 42, 94, .18);
}
html[data-bb-theme="dark"] .bb-agent-user { color: #08111F !important; }
.bb-agent-user strong, .bb-agent-user code { color: inherit !important; background: rgba(255, 255, 255, .16) !important; }

.bb-agent-msg strong { color: var(--bb-ink); }
.bb-agent-msg code   { background: var(--bb-field) !important; color: var(--bb-accent-ink) !important; }
.bb-agent-msg h4     { color: var(--bb-ink) !important; }
.bb-agent-msg.bb-agent-error {
  border: 0 !important;
  background: var(--bb-red-tint) !important;
  color: var(--bb-ink) !important;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--bb-red) 30%, transparent);
}

.ai-title { color: var(--bb-ink) !important; }

/* Chat column surface. The gold ring lives on ::before and is untouched. */
.ai-chat-col { background: var(--bb-surface); border-radius: 20px; }
.ai-chat-col > #bbAgentChat, .ai-chat-col > .bb-agent-host { border-radius: 18.5px; }

/* In dark mode the ring's resting hairline is nearly invisible against the
   page, so it borrows the token line instead of its hardcoded navy. */
html[data-bb-theme="dark"] .ai-chat-col::before { background: var(--bb-line-strong); }

/* ---- the frosted panels, in the dark -----------------------------------
   agent.css frosts both the floating panel and the in-page chat with a WHITE
   gradient over a backdrop blur. Frost is a light-mode idea: white at 88%
   opacity over a dark page is a pale slab, which is what made the chat column
   read as a lit rectangle inside a dark app.

   The dark equivalent keeps the mechanism (translucent + blur, so the page
   still shows through as texture) and only changes what colour is being
   diffused — the surface token instead of white. The inset top highlight
   inverts too: a white 90% inset line is a glare seam on a dark panel. */
html[data-bb-theme="dark"] #bbAgentPanel {
  background: linear-gradient(160deg, rgba(19, 28, 46, .94), rgba(14, 22, 38, .88));
  border-color: var(--bb-line-strong);
  color: var(--bb-ink);
}
html[data-bb-theme="dark"] #bbAgentChat.in-page {
  background: linear-gradient(160deg, rgba(19, 28, 46, .92), rgba(14, 22, 38, .84));
  box-shadow: inset 0 1px 1.5px 0 rgba(255, 255, 255, .05);
}

/* The context strip states what the agent can currently see — the window, the
   filters, the counting mode. Its values are `--navy`, which in dark mode sits
   a few percent off the page itself. It is the one row on this page that must
   never be hard to read: a misread window means a misread number. */
html[data-bb-theme="dark"] .ai-ctx-v { color: var(--bb-ink); }
html[data-bb-theme="dark"] .ai-ctx-k { color: var(--bb-ink-3); }

/* Same problem on the visuals: the neutral hero (no up/down) and the metric
   values are navy. Only the signed variants were re-coloured above. */
html[data-bb-theme="dark"] .bb-viz-hero b,
html[data-bb-theme="dark"] .bb-viz-metric b,
html[data-bb-theme="dark"] .bb-viz-title,
html[data-bb-theme="dark"] .bb-viz-serp b { color: var(--bb-ink); }
html[data-bb-theme="dark"] .bb-viz-hero.is-up b   { color: var(--bb-green); }
html[data-bb-theme="dark"] .bb-viz-hero.is-down b { color: var(--bb-red); }

/* The SERP tile — a Search ad's creative IS its text — kept its LIGHT gradient in
   dark mode while the rule above turned its headline into light ink. Light on
   light: the one creative that is nothing but words became the one you could not
   read. Darken the tile with it, and use Google's own dark-mode link blue so it
   still reads as a search ad rather than as body copy. */
html[data-bb-theme="dark"] .bb-viz-thumb.is-serp {
  background: linear-gradient(160deg, #16203A, #111A2E);
  border-bottom-color: rgba(255, 255, 255, 0.08);
}
html[data-bb-theme="dark"] .bb-viz-serp b { color: #8AB4F8; }
html[data-bb-theme="dark"] .bb-viz-serp i { color: var(--bb-ink-3); }

/* The panel header's three buttons carry a resting surface so they read as
   buttons before you hover (agent.css). That surface is a NAVY tint, which on a
   dark panel is the same colour as the panel — the buttons went back to being
   invisible boxes in dark mode, which is the exact bug the tint was added to
   fix. Same idea, inverted ink. */
html[data-bb-theme="dark"] .bb-agent-act {
  background: rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.11);
  color: var(--bb-ink-2);
}
html[data-bb-theme="dark"] .bb-agent-act:hover {
  background: rgba(255, 255, 255, 0.14);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.20);
  color: var(--bb-ink);
}
html[data-bb-theme="dark"] #bbAgentClose:hover {
  background: rgba(230, 120, 120, 0.20);
  box-shadow: inset 0 0 0 1px rgba(230, 120, 120, 0.34);
  color: #F0A9A9;
}
html[data-bb-theme="dark"] .bb-agent-act:focus-visible {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.20), var(--bb-sh-focus);
}

/* =========================================================================
   9. KEYBOARD — every control says where you are
   -------------------------------------------------------------------------
   The analyst is a keyboard surface: you type a question, Tab to a card, Enter
   to open it. Before this block the focus ring was the browser default where a
   control had a background and NOTHING where it did not, so tabbing through the
   rail moved an invisible cursor. One ring, one token (`--bb-sh-focus`, which
   carries a page-coloured inner stop so it reads on tinted surfaces too), on
   every interactive element of the page and the panel.

   `:focus-visible`, never `:focus` — a mouse click must not leave a ring behind.
   ========================================================================= */

#pageAi :is(button, a[href], [role="button"], summary):focus-visible,
#bbAgentPanel :is(button, a[href], [role="button"], summary):focus-visible,
#bbAgentChat :is(button, a[href], [role="button"], summary):focus-visible,
.bb-det-sheet :is(button, a[href], [role="button"]):focus-visible {
  outline: 0;
  box-shadow: var(--bb-sh-focus);
  /* The ring is drawn outside the box; a clipped ancestor would shave it. */
  position: relative;
  z-index: 2;
}
/* Controls that already own a box-shadow have to re-state it, or focusing them
   drops their elevation for as long as they hold focus. */
.bb-agent-act:focus-visible {
  box-shadow: inset 0 0 0 1px rgba(13, 42, 94, 0.16), var(--bb-sh-focus);
}
.bb-agent-card:focus-visible,
.bb-viz-card:focus-visible {
  box-shadow: var(--bb-sh-raised), var(--bb-sh-focus) !important;
}
.ai-session:has(:focus-visible) { background: var(--bb-hover); }
/* The text inputs take the ring on the WRAPPER, since the input itself is a
   borderless field inside a styled pill and a ring around bare text reads as a
   glitch. */
.ai-rail-search:has(input:focus-visible) {
  box-shadow: var(--bb-sh-inset), var(--bb-sh-focus);
}
.ai-rail-search input:focus-visible,
#bbAgentInput:focus-visible { outline: 0; box-shadow: none; }
/* Skip-the-ring exception: the send button paints its own animated rim, and a
   second ring on top of it reads as two borders. It gets a brighter rim. */
#bbAgentSend:focus-visible { box-shadow: var(--bb-sh-focus) !important; }

/* =========================================================================
   10. RESPONSIVE
   ========================================================================= */

@media (max-width: 900px) {
  .ai-rail-search { display: none; }
  .ai-rail-id { padding-bottom: 7px; }
}
@media (max-width: 620px) {
  .bb-agent-tool { border-radius: var(--bb-r-card); }
  .bb-agent-tool-args { display: none; }
  .bb-agent-hints .bb-agent-hint-keys { display: none; }
  .bb-viz-plot { height: 130px; }
}
