/* banter — App-replica styles
 *
 * Renders the Banter macOS app (Sources/BanterApp/...) as HTML/CSS so we
 * can compose marketing animations from real component fragments. Every
 * `.banter-*` class is a 1:1 reproduction of a SwiftUI surface; spec lives
 * with the class.
 *
 * Naming: `.banter-<surface>[-<part>][.is-<state>]`. State classes are
 * the animation hooks — toggle `is-streaming`, `is-selected`, `is-active`
 * and the surface re-styles. Don't author state via inline style.
 *
 * Scope: each component is self-contained. CSS variables on a parent
 * scope cascade in (e.g. `--avatar-token: var(--p3)` lets one persona
 * override the default). No global selectors here — everything is
 * `.banter-*`.
 *
 * Source-of-truth references (line numbers from /Library/Projects/Banter):
 *   - Sidebar:        Sources/BanterApp/AppShell/GroupsAndDMsSidebar.swift
 *   - ChatView:       Sources/BanterApp/ChatView.swift
 *   - PersonaAvatar:  Sources/BanterApp/Avatars/PersonaAvatarView.swift
 *   - BrandTokens:    Sources/BanterApp/Brand/BrandTokens.swift
 *   - BrandTypography:Sources/BanterApp/Brand/BrandTypography.swift
 *
 * Dependencies: brand/tokens.css (palette + spacing) and brand/lg-tokens.css
 * (Liquid Glass material). This file layers on top.
 */

/* ─── system-font stack ───
   Banter uses SF Pro on macOS for body sans (BrandTypography.swift comment:
   "we deliberately diverge so the app feels native"). On the web we hit the
   same font on Apple devices via `-apple-system` / `BlinkMacSystemFont`,
   then degrade to Inter Tight (the marketing site's body sans). Using this
   stack ONLY inside `.banter-*` so the rest of the site keeps its current
   chain intact. */
:root {
  --banter-sans:
    -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro",
    "Inter Tight", system-ui, sans-serif;
  --banter-serif: "Cormorant Garamond", Georgia, serif;
  --banter-display: "DM Serif Display", "Cormorant Garamond", Georgia, serif;
  --banter-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  /* Apple New York screen-tuned serif — used for sidebar persona names and
     other ≤17pt italic moments where Cormorant Light Italic loses
     legibility. The web fallback is Cormorant since New York isn't on the
     web font universe. */
  --banter-ui-serif: ui-serif, "New York", "Cormorant Garamond", Georgia, serif;

  /* Semantic warning / error tone — used for `failed` system lines,
     down-vote feedback, mic-recording, over-budget counters. The
     brand's `--primary-deep` is tangerine-deep, not red, so we keep
     a dedicated warn token. Lift to tokens.css if other surfaces
     start needing it. Adaptive in dark mode via `[data-paper="ink"]`
     override below — slightly brighter so it stays legible against
     the dark paper. */
  --banter-warn: var(--banter-warn);
}
[data-paper="ink"] {
  --banter-warn: #F08A6A;       /* a1 Coral — same hue family, brighter */
}

/* ───────────────────────────────────────────────────────────
   1. Window chrome
   ───────────────────────────────────────────────────────────
   The macOS 26 window. Paper canvas, soft drop shadow, hairline
   border, ~10pt corner radius. Traffic lights bare on paper at
   top-left (no titlebar capsule — chrome is the toolbar items
   themselves, per HIG and observed Banter behavior). */
.banter-window {
  /* Re-bind the foundation tokens to the brand's true app values
     inside the replica. The website's `--paper` is normally the brand
     Paper (`:root` default) — but a future page or section could
     override it (e.g. inside a marketing scene that uses a darker
     paper for contrast); this rebind keeps the app device frame at
     the canonical `BrandColor.paper` regardless. Persona accents
     (--p1…p6, --a1…a6) intentionally pass through — they're identity,
     not surface, and the app honors palette swaps the same way the
     website does.
     Dark mode: the app supports it; we mirror via the website's
     `[data-paper="ink"]` selector below, flipping these to the
     values BrandColor.adaptive resolves to in dark appearance. */
  --paper:      #F5EFE6;
  --paper-warm: #EFE6D8;
  --paper-cool: #FAF6EE;
  --ink:        #14110F;
  --ink-soft:   #2B2622;
  --ink-mute:   #6B625B;
  --rule:       rgba(20, 17, 15, 0.14);

  position: relative;
  background: var(--paper);
  border-radius: 12px;
  border: 1px solid var(--rule);
  /* Fixed-ink shadow: visible drop on cream paper in light mode,
     fades to imperceptible on dark paper. The earlier
     color-mix(--ink) stack inverted in dark mode and rendered as
     a cream halo around the window. */
  box-shadow: 0 12px 40px rgba(20, 17, 15, 0.10);
  overflow: hidden;
  font-family: var(--banter-sans);
  color: var(--ink);
  /* Default size — matches Banter's `defaultSize(width: 980, height: 700)`
     in BanterApp.swift. Override with width/height props on the wrapper. */
  width: 980px;
  height: 700px;
  /* Mobile: cap to viewport so the window doesn't push the page wider
     than the screen. The 980×700 desktop default still applies above
     the breakpoint; below it, width: 100% of parent and the height
     scales by aspect to keep proportions sensible. The sidebar
     (280px) becomes a tighter fixed column on phones — see below. */
  max-width: 100%;
  display: grid;
  grid-template-columns: 280px 1fr;
  grid-template-rows: minmax(0, 1fr);
}
@media (max-width: 720px) {
  .banter-window {
    /* `!important` is required to beat the inline `width: 460px` /
       `width: 980px` props that callers pass for desktop sizing. Without
       it, the inline style wins and the window stays 460/980px wide on
       phone — which then forces the parent grid cell wider, which forces
       the SIBLING column wider, which makes prose paragraphs wrap at
       460px and clip past the 360px viewport edge. The whole TOKENS
       section overflow chain traces back to this single rule. */
    width: 100% !important;
    /* Reduce sidebar so the chat surface gets enough room on a 360px
     phone. Desktop: 280px / 1fr; phone: 96px / 1fr. */
    grid-template-columns: 96px 1fr;
  }
  .banter-window--detail-only {
    grid-template-columns: 1fr;
  }
  /* (PersonaCarousel mobile mode now renders the full panel —
     identity + voice + engine — stacked vertically inside a horizontal
     scroll-snap deck, so the previous voice/engine display:none rule
     was retired with the swipe redesign.) */
}
[data-paper="ink"] .banter-window {
  --paper:      #1A1714;
  --paper-warm: #221E1A;
  --paper-cool: #221E1A;
  --ink:        #F5EFE6;
  --ink-soft:   #D8D2C8;
  --ink-mute:   #8E867E;
  --rule:       rgba(245, 239, 230, 0.16);
}

/* Standalone app surfaces — kit showcases and animation scenes
   sometimes render a sidebar / chat area / composer / group-home
   fragment OUTSIDE a `.banter-window` wrapper. Apply the same
   brand-paper rebind so the fragment doesn't inherit the page's
   `[data-paper="…"]` variant. Inside a `.banter-window` these
   overrides are idempotent — the parent already provides the
   identical values. */
.banter-sidebar,
.banter-chat-area,
.banter-composer,
.banter-group-home {
  --paper:      #F5EFE6;
  --paper-warm: #EFE6D8;
  --paper-cool: #FAF6EE;
  --ink:        #14110F;
  --ink-soft:   #2B2622;
  --ink-mute:   #6B625B;
  --rule:       rgba(20, 17, 15, 0.14);
}
[data-paper="ink"] .banter-sidebar,
[data-paper="ink"] .banter-chat-area,
[data-paper="ink"] .banter-composer,
[data-paper="ink"] .banter-group-home {
  --paper:      #1A1714;
  --paper-warm: #221E1A;
  --paper-cool: #221E1A;
  --ink:        #F5EFE6;
  --ink-soft:   #D8D2C8;
  --ink-mute:   #8E867E;
  --rule:       rgba(245, 239, 230, 0.16);
}

.banter-window--detail-only {
  grid-template-columns: 1fr;
}

/* Traffic lights — bare on the paper at top-left. Sit ABOVE the
   sidebar; not inside any pill. */
.banter-traffic-lights {
  position: absolute;
  top: 14px;
  left: 16px;
  z-index: 10;
  display: inline-flex;
  gap: 8px;
  pointer-events: none;
}
.banter-traffic-lights span {
  width: 12px; height: 12px; border-radius: 50%;
  border: 0.5px solid color-mix(in oklab, var(--ink) 20%, transparent);
}
.banter-traffic-lights span:nth-child(1) { background: #FF5F56; }
.banter-traffic-lights span:nth-child(2) { background: #FFBD2E; }
.banter-traffic-lights span:nth-child(3) { background: #27C93F; }

/* ───────────────────────────────────────────────────────────
   2. Sidebar
   ───────────────────────────────────────────────────────────
   GroupsAndDMsSidebar.swift line 30: `List(selection: $selection)`
   with three `Section`s — "Groups", "Direct Messages", "Rooms".
   Each row is one of: groupRow(g), dmRow(dm), roomRow(ch),
   createRow(label,action). Selection highlight = cobalt
   (BrandColor.secondary, #1F4FBF) with cream foreground. */
.banter-sidebar {
  background: var(--paper);
  border-right: 0.5px solid color-mix(in oklab, var(--ink) 10%, transparent);
  padding: 44px 8px 12px;       /* 44pt top clear of traffic lights */
  overflow-y: auto;
  font-family: var(--banter-sans);
  font-size: 13px;
}

.banter-sidebar-toggle {
  position: absolute;
  top: 14px;
  right: -34px;                 /* sits in the chat-area top bar */
  width: 26px; height: 22px;
  border: none; background: transparent;
  border-radius: 6px;
  color: color-mix(in oklab, var(--ink) 60%, transparent);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  z-index: 11;
}

/* Section header: "Groups", "Direct Messages", "Rooms".
   System default for `Section("Title")` inside `List(.sidebar)`:
   small, secondary tertiary color, slight letter-spacing. */
.banter-sidebar-section-title {
  padding: 12px 12px 4px;
  font-size: 11px;
  font-weight: 400;
  color: color-mix(in oklab, var(--ink) 56%, transparent);
  text-transform: none;
  letter-spacing: 0;
}
.banter-sidebar-section-title:first-child { padding-top: 4px; }

/* Row — common shape for all three section row types. Selection
   uses cobalt fill (BrandColor.secondary) with cream foreground;
   the macOS sidebar default is a tinted highlight, which Banter
   gets by being inside a `List(.sidebar)`. */
.banter-sidebar-row {
  display: flex; align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 7px;
  cursor: pointer;
  user-select: none;
  color: var(--ink);
  position: relative;
}
.banter-sidebar-row:hover {
  background: color-mix(in oklab, var(--ink) 5%, transparent);
}
.banter-sidebar-row.is-selected {
  background: var(--secondary);
  color: var(--paper);
}
.banter-sidebar-row.is-selected .banter-sidebar-row-name,
.banter-sidebar-row.is-selected .banter-sidebar-row-meta,
.banter-sidebar-row.is-selected .banter-sidebar-row-count,
.banter-sidebar-row.is-selected .banter-sidebar-row-symbol,
.banter-sidebar-row.is-selected .banter-sidebar-row-handle,
.banter-sidebar-row.is-selected .banter-sidebar-dm-name,
.banter-sidebar-row.is-selected .banter-sidebar-dm-role,
.banter-sidebar-row.is-selected .banter-sidebar-dm-bullet {
  color: var(--paper);
}
.banter-sidebar-row.is-selected .banter-sidebar-row-handle,
.banter-sidebar-row.is-selected .banter-sidebar-dm-role {
  /* Mono / role captions stay legible against the cobalt fill;
     don't full-knock them or they read at the same weight as the
     name. */
  opacity: 0.85;
}

/* Group glyph — 22pt rounded-rect with cast-color fill + white SF
   symbol. GroupsAndDMsSidebar.swift lines 200-219 (groupGlyph). */
.banter-group-glyph {
  width: 22px; height: 22px;
  border-radius: 5px;
  background: var(--banter-glyph-color, var(--p1));
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600;
  flex-shrink: 0;
}

/* Active-group cue — bold name + small tangerine dot. Selection
   and active-group are independent: selection is "what the chat
   pane shows"; active group is "which workspace owns the
   sidebar's lower sections." */
.banter-sidebar-row.is-active-group .banter-sidebar-row-name {
  font-weight: 600;
}
.banter-sidebar-active-dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--primary);
  flex-shrink: 0;
}
.banter-sidebar-row.is-selected .banter-sidebar-active-dot {
  /* on the cobalt fill, cream-ink the dot so it remains visible
     but doesn't compete with the white text. */
  background: var(--paper);
}

.banter-sidebar-row-name {
  flex: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 13px;
}
.banter-sidebar-row-count {
  font-family: var(--banter-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: color-mix(in oklab, var(--ink) 40%, transparent);
}

/* Room row — `#` glyph (gray) + name + count.
   GroupsAndDMsSidebar.swift lines 221-238. */
.banter-sidebar-row-symbol {
  width: 22px;
  text-align: center;
  font-size: 14px;
  font-weight: 400;
  color: color-mix(in oklab, var(--ink) 56%, transparent);
  flex-shrink: 0;
}

/* DM row — avatar + serif italic name + mono "@handle · role"
   GroupsAndDMsSidebar.swift lines 156-198 (dmRow). */
.banter-sidebar-dm {
  align-items: flex-start;
  padding: 7px 10px;
}
.banter-sidebar-dm-name {
  font-family: var(--banter-ui-serif);
  font-style: italic;
  font-weight: 500;
  font-size: 14px;
  line-height: 1.15;
  color: var(--ink);
}
.banter-sidebar-row-handle {
  font-family: var(--banter-mono);
  font-size: 11px;
  color: color-mix(in oklab, var(--ink) 40%, transparent);
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.banter-sidebar-dm-meta {
  display: flex; align-items: center; gap: 4px;
  margin-top: 2px;
  white-space: nowrap; overflow: hidden;
}
.banter-sidebar-dm-bullet {
  color: color-mix(in oklab, var(--ink) 30%, transparent);
  font-size: 8px;
}
.banter-sidebar-dm-role {
  font-size: 11px;
  color: color-mix(in oklab, var(--ink) 56%, transparent);
}
.banter-sidebar-dm-stack {
  display: flex; flex-direction: column;
  flex: 1; min-width: 0;
  /* Compensates the `.padding(.top, 2)` Banter applies in dmRow. */
  padding-top: 1px;
}

/* "Add …" footer row for each section. */
.banter-sidebar-row-create {
  color: color-mix(in oklab, var(--ink) 48%, transparent);
}
.banter-sidebar-row-create .banter-sidebar-row-symbol,
.banter-sidebar-row-create .banter-sidebar-row-name {
  color: inherit;
}

/* ───────────────────────────────────────────────────────────
   3. Persona avatar
   ───────────────────────────────────────────────────────────
   PersonaAvatarView.swift — circle filled with cast color (or
   complementary), white SF symbol or initial inside. Sizes used
   in app: 22pt (sidebar), 28pt (chat), 36pt (intake form), 26pt
   (members popover). Default 28. */
.banter-avatar {
  --avatar-size: 28px;
  --avatar-color: var(--p1);
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: 50%;
  background: var(--avatar-color);
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: calc(var(--avatar-size) * 0.5);
  font-weight: 600;
  font-family: var(--banter-sans);
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
}
.banter-avatar.is-ink {
  /* p6 — adaptive ink slot. On dark backgrounds, the foreground
     color flips. We approximate: when the avatar IS the ink
     slot, use `--paper` for the foreground glyph so it knocks
     out cleanly. */
  color: var(--paper);
}
.banter-avatar svg { width: 60%; height: 60%; stroke-width: 2; }
.banter-avatar.is-photo,
.banter-avatar.is-face {
  background: transparent;
}
.banter-avatar.is-photo img,
.banter-avatar.is-face img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* Cast-color helpers — use these on the avatar element to
   override `--avatar-color`. Applied as `.banter-avatar--p3` etc.
   ColorToken.swift / BrandColor.swift cast. */
.banter-avatar--p1 { --avatar-color: var(--p1); }
.banter-avatar--p2 { --avatar-color: var(--p2); }
.banter-avatar--p3 { --avatar-color: var(--p3); }
.banter-avatar--p4 { --avatar-color: var(--p4); }
.banter-avatar--p5 { --avatar-color: var(--p5); }
.banter-avatar--p6 { --avatar-color: var(--ink); }
.banter-avatar--a1 { --avatar-color: var(--a1); }
.banter-avatar--a2 { --avatar-color: var(--a2); }
.banter-avatar--a3 { --avatar-color: var(--a3); }
.banter-avatar--a4 { --avatar-color: var(--a4); }
.banter-avatar--a5 { --avatar-color: var(--a5); }
.banter-avatar--a6 { --avatar-color: var(--a6); }

/* ───────────────────────────────────────────────────────────
   4. Chat header (toolbar)
   ───────────────────────────────────────────────────────────
   ChatView.swift lines 1554-1592. Native macOS 26 toolbar with
   Liquid Glass treatment auto-applied. Items: backButton at
   .navigation; roomSettings + members at .primaryAction (clustered
   as one Liquid Glass pill). Title `#name`, subtitle `groupName`. */
.banter-chat-area {
  position: relative;
  background: var(--paper);
  display: flex; flex-direction: column;
  min-width: 0;       /* allow flex shrink */
  height: 100%;
}
.banter-chat-toolbar {
  position: relative;
  display: flex; align-items: center;
  padding: 14px 20px;
  gap: 14px;
  z-index: 5;
}
.banter-chat-toolbar-back {
  width: 38px; height: 38px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--paper) 80%, white);
  border: 0.5px solid color-mix(in oklab, var(--ink) 8%, transparent);
  box-shadow:
    inset 0 0.5px 0 color-mix(in oklab, white 60%, transparent),
    0 1px 2px color-mix(in oklab, var(--ink) 8%, transparent);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink);
  cursor: pointer;
  flex-shrink: 0;
}
.banter-chat-title-block {
  display: flex; flex-direction: column; gap: 1px;
  flex: 1; min-width: 0;
}
.banter-chat-title {
  font-family: var(--banter-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.banter-chat-subtitle {
  font-family: var(--banter-sans);
  font-size: 11px;
  color: color-mix(in oklab, var(--ink) 50%, transparent);
}
.banter-chat-toolbar-actions {
  display: inline-flex; align-items: center;
  gap: 0;
  padding: 6px 10px;
  background: color-mix(in oklab, var(--paper) 80%, white);
  border-radius: 999px;
  border: 0.5px solid color-mix(in oklab, var(--ink) 8%, transparent);
  box-shadow:
    inset 0 0.5px 0 color-mix(in oklab, white 60%, transparent),
    0 1px 2px color-mix(in oklab, var(--ink) 8%, transparent);
}
.banter-chat-toolbar-action {
  width: 28px; height: 26px;
  border: none; background: transparent;
  border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink);
  cursor: pointer;
}
.banter-chat-toolbar-action:hover {
  background: color-mix(in oklab, var(--ink) 6%, transparent);
}

/* ───────────────────────────────────────────────────────────
   5. Chat body (transcript + bubbles)
   ───────────────────────────────────────────────────────────
   ChatView.swift — `ChatTypography.maxTextColumnWidth = 720`,
   `avatarSize = 28`, `rowSpacing = 18`, `contentColumnWidth = 820`
   (lines 3520-3531). Persona bubble: avatar + mono credit line +
   serif body. User bubble: ink fill, paper text, right-aligned. */
.banter-chat-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 24px 0;
  display: flex; flex-direction: column;
  gap: 18px;       /* ChatTypography.rowSpacing */
}

.banter-chat-row {
  max-width: 820px;
  width: 100%;
  margin: 0 auto;
}

/* Panel inline-reply variant — Reddit-canonical inline-nested
   threading. Reactions to a just-revealed answer render under the
   reveal with a 2px left-edge accent rule + indent. Mirrors
   `ChatView.bubble(for:)` in the Banter app: 8px from edge, 2px
   rule, 16px content indent. The rule extends the full row height
   (align-items: stretch + align-self: stretch on the rule column)
   so a multi-line reaction reads as one indented unit. */
.banter-chat-row--reaction {
  display: flex;
  align-items: stretch;
  gap: 0;
  padding-left: 8px;
}
.banter-chat-row--reaction > .banter-chat-row__rule {
  flex: 0 0 2px;
  width: 2px;
  background: color-mix(in oklab, var(--ink) 18%, transparent);
  border-radius: 1px;
  align-self: stretch;
}
.banter-chat-row--reaction > .banter-chat-row__inner {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 16px;
}

/* Persona bubble — avatar + (name + body + feedback) column. */
.banter-bubble-persona {
  display: flex; align-items: flex-start;
  gap: 10px;
}
.banter-bubble-persona .banter-avatar { margin-top: 2px; }
.banter-bubble-persona-stack {
  display: flex; flex-direction: column;
  gap: 3px;
  max-width: 720px;
  flex: 1; min-width: 0;
}
.banter-bubble-credit {
  font-family: var(--banter-mono);
  font-size: 11px;
  /* Persona-color set via inline style or `--credit-color` parent
     custom property. ChatView.swift line 2444 — mono in persona
     accent color "speaker credit, not heading". */
  color: var(--banter-credit-color, var(--p1));
}
.banter-bubble-text {
  font-family: var(--banter-serif);
  font-size: 20px;          /* SwiftUI `.title3` ≈ 20pt at default */
  line-height: 1.45;
  color: color-mix(in oklab, var(--ink) 86%, transparent);
  padding: 12px 16px;
}
.banter-bubble-text--user {
  font-family: var(--banter-sans);   /* user keeps system sans */
  color: var(--paper);
}

.banter-bubble-feedback {
  display: inline-flex; gap: 8px;
  margin-left: 16px;
  margin-top: -4px;
  font-size: 12px;
  color: color-mix(in oklab, var(--ink) 50%, transparent);
}
.banter-bubble-feedback button {
  background: transparent; border: none;
  color: inherit; cursor: pointer;
  padding: 2px;
  display: inline-flex; align-items: center; justify-content: center;
}
.banter-bubble-feedback .is-good.is-active { color: var(--primary); }
.banter-bubble-feedback .is-bad.is-active  { color: var(--banter-warn); }

/* User bubble — right-aligned ink pill. ChatView.swift lines
   2416-2431. ~75% column max-width, 14pt corner radius,
   `.title3` font, paper foreground. */
.banter-bubble-user {
  display: flex;
  justify-content: flex-end;
}
.banter-bubble-user-pill {
  background: var(--ink);
  color: var(--paper);
  font-family: var(--banter-sans);
  font-size: 20px;
  line-height: 1.4;
  padding: 12px 16px;
  border-radius: 14px;
  max-width: 540px;     /* 720 * 0.75 */
  text-align: left;
  white-space: pre-wrap;
}

/* System notice line — held / suppressed / failed / truncated /
   notice. ChatView.swift lines 2572-2620. Dim chrome by default;
   red for failed, slightly larger. */
.banter-bubble-system {
  display: flex; align-items: center;
  gap: 6px;
  margin-left: 38px;        /* avatar gutter + spacing */
  font-size: 11px;
  color: color-mix(in oklab, var(--ink) 45%, transparent);
}
.banter-bubble-system.is-failed {
  color: var(--banter-warn);
  font-size: 12px;
}
.banter-bubble-system .banter-bubble-system-glyph {
  display: inline-flex; align-items: center;
}

/* ───────────────────────────────────────────────────────────
   6. Streaming primitives
   ───────────────────────────────────────────────────────────
   ChatView.swift `streamingBubble` (lines 2658-2686): same shape
   as a persona bubble but with `· streaming…` suffix on the
   credit line, and ThinkingDots in the body until reveal. */
.banter-thinking-dots {
  display: inline-flex; align-items: center; gap: 4px;
  height: 28px;
  padding: 8px 0;
}
.banter-thinking-dots span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: color-mix(in oklab, var(--ink) 40%, transparent);
  animation: banter-thinking-pulse 1100ms ease-in-out infinite;
}
.banter-thinking-dots span:nth-child(2) { animation-delay: 160ms; }
.banter-thinking-dots span:nth-child(3) { animation-delay: 320ms; }
@keyframes banter-thinking-pulse {
  0%, 100% { opacity: 0.35; transform: translateY(0); }
  50%      { opacity: 1;    transform: translateY(-2px); }
}

/* Typing indicator — used for multi-persona group rooms in lieu of
   per-persona streaming bubbles. ChatView.swift lines 2693-2719. */
.banter-typing-indicator {
  display: flex; align-items: center;
  gap: 10px;
  padding: 4px 0 4px 38px;       /* avatar gutter */
  font-family: var(--banter-mono);
  font-size: 11px;
  color: color-mix(in oklab, var(--ink) 50%, transparent);
}

/* BlurRevealText — per-word blur reveal. The app has its own
   SwiftUI animation; on the web we simulate per-word fade with
   stagger. Apply `.is-revealing` to (re)trigger the animation. */
.banter-blur-reveal {
  display: inline;
}
.banter-blur-reveal-word {
  display: inline-block;
  filter: blur(6px);
  opacity: 0;
  transform: translateY(2px);
  animation: banter-blur-reveal-in 360ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
  /* `--word-delay-ms` set per-word inline so siblings stagger. */
  animation-delay: var(--word-delay-ms, 0ms);
  margin-right: 0.25em;
}
@keyframes banter-blur-reveal-in {
  to { filter: blur(0); opacity: 1; transform: translateY(0); }
}

/* ───────────────────────────────────────────────────────────
   7. Composer
   ───────────────────────────────────────────────────────────
   ChatView.swift `inputBar` (lines 2845-2910). VStack: optional
   attachment chip row, then text field, then composerToolRow.
   Background: `brandGlass(.extendedChrome)` — translucent paper.
   Text field: `.quaternary` fill, 10pt corner radius. */
.banter-composer {
  background: color-mix(in oklab, var(--paper) 88%, white);
  border-top: 0.5px solid color-mix(in oklab, var(--ink) 6%, transparent);
  padding: 16px 24px;
  display: flex; flex-direction: column;
  gap: 10px;
}

.banter-composer-attachments {
  display: flex; flex-wrap: wrap;
  gap: 6px;
}
.banter-attach-chip {
  display: inline-flex; align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: color-mix(in oklab, var(--ink) 6%, transparent);
  border-radius: 999px;
  font-size: 11px;
}
.banter-attach-chip-leading {
  width: 16px; height: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  color: color-mix(in oklab, var(--ink) 60%, transparent);
}
.banter-attach-chip-leading.is-image {
  border-radius: 3px;
  overflow: hidden;
  background: color-mix(in oklab, var(--ink) 12%, transparent);
}
.banter-attach-chip-leading.is-image img {
  width: 100%; height: 100%; object-fit: cover;
}
.banter-attach-chip-name {
  max-width: 240px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.banter-attach-chip-bytes {
  font-family: var(--banter-mono);
  font-size: 10px;
  color: color-mix(in oklab, var(--ink) 40%, transparent);
}
.banter-attach-chip-remove {
  width: 16px; height: 16px;
  border: none; background: transparent;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: color-mix(in oklab, var(--ink) 50%, transparent);
  cursor: pointer;
  font-weight: 700; font-size: 10px;
}

.banter-composer-textfield {
  background: color-mix(in oklab, var(--ink) 5%, transparent);
  border-radius: 10px;
  padding: 10px 12px;
  font-family: var(--banter-sans);
  font-size: 13px;
  color: var(--ink);
  border: none; outline: none;
  width: 100%;
  min-height: 24px;
  resize: none;
}
.banter-composer-textfield::placeholder {
  color: color-mix(in oklab, var(--ink) 40%, transparent);
}

.banter-composer-toolrow {
  display: flex; align-items: center;
  gap: 6px;
}

/* Composer chip — used for profile / mode / reply-style picker.
   ChatView.swift lines 3248-3399 (composerProfileChip / Mode /
   ReplyStyleChip). Caption font, `chevron.down` glyph trailing. */
.banter-composer-chip {
  display: inline-flex; align-items: center;
  gap: 4px;
  padding: 4px 10px;
  background: color-mix(in oklab, var(--ink) 5%, transparent);
  border-radius: 7px;
  border: none;
  font-family: var(--banter-sans);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
}
.banter-composer-chip:hover {
  background: color-mix(in oklab, var(--ink) 8%, transparent);
}
.banter-composer-chip-chevron {
  color: color-mix(in oklab, var(--ink) 40%, transparent);
  font-size: 10px;
}

.banter-composer-icon-button {
  width: 28px; height: 28px;
  border: none; background: transparent;
  border-radius: 6px;
  color: color-mix(in oklab, var(--ink) 70%, transparent);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.banter-composer-icon-button:hover {
  background: color-mix(in oklab, var(--ink) 6%, transparent);
}
.banter-composer-icon-button.is-attach {
  /* `+` button — tinted primary in the app. */
  color: var(--ink);
}
.banter-composer-icon-button.is-mic-recording { color: var(--banter-warn); }

/* Send / stop button — circular tangerine. ChatView.swift
   lines 3136-3149. */
.banter-composer-send {
  width: 26px; height: 26px;
  border: none; border-radius: 50%;
  background: var(--primary);
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}
.banter-composer-send:disabled,
.banter-composer-send.is-disabled {
  background: color-mix(in oklab, var(--ink) 25%, transparent);
  cursor: not-allowed;
}
.banter-composer-send.is-stop { background: var(--primary); }

.banter-composer-token-counter {
  font-family: var(--banter-mono);
  font-size: 10px;
  color: color-mix(in oklab, var(--ink) 40%, transparent);
}
.banter-composer-token-counter.is-over { color: var(--banter-warn); }

/* ───────────────────────────────────────────────────────────
   8. Scroll-to-bottom floating chip
   ───────────────────────────────────────────────────────────
   The little arrow-down disc that appears when the user scrolls
   up. ChatView visible when `!isAtBottom`. */
.banter-scroll-to-bottom {
  position: absolute;
  bottom: 96px;
  left: 50%;
  transform: translateX(-50%);
  width: 30px; height: 30px;
  border-radius: 50%;
  background: color-mix(in oklab, var(--paper) 88%, white);
  border: 0.5px solid color-mix(in oklab, var(--ink) 10%, transparent);
  box-shadow:
    inset 0 0.5px 0 color-mix(in oklab, white 60%, transparent),
    0 4px 12px -4px color-mix(in oklab, var(--ink) 28%, transparent);
  display: inline-flex; align-items: center; justify-content: center;
  color: color-mix(in oklab, var(--ink) 60%, transparent);
  cursor: pointer;
  z-index: 4;
}

/* ───────────────────────────────────────────────────────────
   9. Group home page (the orange "Default" splash)
   ───────────────────────────────────────────────────────────
   GroupRoomsView — the page shown when the user clicks a group
   in the sidebar. Wordmark top-right, big icon + display-serif
   group name + mono "X personas · Y rooms · Z DMs", then Rooms +
   DMs lists below. */
.banter-group-home {
  flex: 1;
  overflow-y: auto;
  padding: 56px 56px 24px;
  display: flex; flex-direction: column;
  gap: 32px;
  position: relative;
}
.banter-group-home-wordmark {
  position: absolute; top: 18px; right: 24px;
  font-family: var(--banter-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ink);
  letter-spacing: -0.04em;
}
.banter-group-home-wordmark::after {
  content: ".";
  color: var(--primary);
}
.banter-group-home-hero {
  display: flex; align-items: center;
  gap: 24px;
}
.banter-group-home-hero .banter-group-glyph {
  width: 76px; height: 76px;
  border-radius: 14px;
  font-size: 36px;
}
.banter-group-home-title {
  font-family: var(--banter-display);
  font-size: 56px;
  line-height: 0.95;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.banter-group-home-meta {
  font-family: var(--banter-mono);
  font-size: 13px;
  color: color-mix(in oklab, var(--ink) 48%, transparent);
  margin-top: 6px;
  letter-spacing: 0.01em;
}
.banter-group-home-section {
  display: flex; flex-direction: column;
  gap: 8px;
}
.banter-group-home-section-title {
  font-family: var(--banter-serif);
  font-style: italic;
  font-size: 20px;
  color: color-mix(in oklab, var(--ink) 86%, transparent);
}
.banter-group-home-card {
  display: flex; align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: color-mix(in oklab, var(--ink) 5%, transparent);
  border-radius: 10px;
  cursor: pointer;
}
.banter-group-home-card:hover {
  background: color-mix(in oklab, var(--ink) 8%, transparent);
}
.banter-group-home-card .banter-sidebar-row-symbol {
  width: auto;
  font-size: 16px;
  font-weight: 500;
  color: color-mix(in oklab, var(--ink) 50%, transparent);
}
.banter-group-home-card-title {
  font-weight: 600;
  font-size: 17px;
}
.banter-group-home-card-meta {
  margin-left: auto;
  font-family: var(--banter-mono);
  font-size: 12px;
  color: color-mix(in oklab, var(--ink) 48%, transparent);
}
.banter-group-home-card-chev {
  margin-left: 8px;
  color: color-mix(in oklab, var(--ink) 40%, transparent);
}

/* ───────────────────────────────────────────────────────────
   10. Aspirational marker
   ───────────────────────────────────────────────────────────
   For components designed for the marketing site that don't yet
   exist in the app (per-persona Knowledge Library, persistent
   memory facts panel). Adds a small ribbon so neither we nor the
   marketing/animation team forgets the gap. */
.banter-aspirational {
  position: relative;
}
.banter-aspirational::after {
  content: "Aspirational · not shipped";
  position: absolute;
  top: 6px; right: 6px;
  font-family: var(--banter-mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 6px;
  background: color-mix(in oklab, var(--primary) 20%, transparent);
  color: var(--primary-deep, var(--primary));
  border-radius: 4px;
  pointer-events: none;
  z-index: 9;
}

/* ──────────────────────────────────────────────────────────
   Block 2 (v0.4): sheets, forms, popovers
   Sources:
     - PersonaIntakeForm:           Sources/BanterApp/AppShell/PersonaIntakeForm.swift
     - PersonaEditorView:           Sources/BanterApp/Settings/PersonaEditorView.swift
     - MembersChipPopover:          Sources/BanterApp/AppShell/MembersChipPopover.swift
     - UserIdentityOnboardingView:  Sources/BanterApp/Settings/UserIdentitySettingsView.swift:163
   ────────────────────────────────────────────────────────── */

/* Sheet — modal-presented body. The kit renders without the host
   window's chrome around it; in-app these float on a translucent
   scrim. */
.banter-sheet {
  width: 100%;
  max-width: 520px;
  background: var(--paper);
  border: 1px solid color-mix(in oklab, var(--ink) 10%, transparent);
  border-radius: 14px;
  box-shadow: 0 24px 60px color-mix(in oklab, var(--ink) 22%, transparent);
  display: flex; flex-direction: column;
  overflow: hidden;
  font-family: var(--banter-sans);
}
.banter-sheet-title {
  padding: 14px 18px;
  border-bottom: 1px solid var(--rule);
  font-size: 14px; font-weight: 600;
  color: var(--ink);
}
.banter-sheet-body {
  padding: 16px 18px;
  display: flex; flex-direction: column; gap: 14px;
  min-height: 280px;
}
.banter-sheet-footer {
  padding: 12px 18px;
  border-top: 1px solid var(--rule);
  display: flex; justify-content: flex-end; gap: 8px;
}

/* Buttons. Ghost = paper, primary = ink fill on tangerine, destructive
   = red text on a paper background. */
.banter-btn-primary,
.banter-btn-ghost,
.banter-btn-destructive {
  border: 0;
  border-radius: 8px;
  padding: 7px 14px;
  font-family: var(--banter-sans); font-size: 12px; font-weight: 500;
  cursor: pointer;
  line-height: 1.3;
}
.banter-btn-primary    { background: var(--primary); color: var(--paper); }
.banter-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.banter-btn-ghost      { background: transparent; color: var(--primary); border: 1px solid color-mix(in oklab, var(--primary) 30%, transparent); }
.banter-btn-destructive{ background: transparent; color: var(--banter-warn); border: 1px solid color-mix(in oklab, var(--banter-warn) 30%, transparent); }

/* Segmented picker — the SwiftUI .pickerStyle(.segmented) shape.
   Single primary fill on the selected segment. */
.banter-segmented {
  display: inline-flex;
  background: var(--paper-cool);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
  /* Subtle hairline + drop shadow so the picker reads as a raised
     pill against the page rather than melting into paper-cool. */
  border: 1px solid color-mix(in oklab, var(--ink) 8%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in oklab, white 35%, transparent),
    0 1px 2px color-mix(in oklab, var(--ink) 6%, transparent);
}
.banter-segmented-item {
  flex: 1; text-align: center;
  padding: 6px 12px;
  border-radius: 999px;
  font-family: var(--banter-sans); font-size: 11px; font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
  transition: background 200ms ease, color 200ms ease;
}
.banter-segmented-item.is-selected {
  background: var(--primary);
  color: var(--paper);
}
/* Inert mode — no `onSelect` (or explicit `inert` prop). The picker
   labels a static state; the cursor matches that fact. */
.banter-segmented.is-inert .banter-segmented-item { cursor: default; }

/* Form section — `Section { … } header: Text(…) footer: Text(…)`. */
.banter-form-section { display: flex; flex-direction: column; gap: 8px; }
.banter-form-section-header {
  font-family: var(--banter-sans); font-size: 13px; font-weight: 600;
  color: var(--ink);
  display: flex; align-items: center; gap: 6px;
}
.banter-form-section-group {
  background: var(--paper-cool);
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;
}
.banter-form-section-group.is-disabled { opacity: 0.55; pointer-events: none; }
.banter-form-section-footer {
  font-family: var(--banter-sans); font-size: 12px;
  color: var(--ink-mute); line-height: 1.45;
  padding: 0 2px;
}

/* Form row — `LabeledContent("Label") { value/control }`. Single-line
   rows put the label on the left and the value flush-right (the
   SwiftUI Form default). Multiline rows stack: label on top, body
   below at full width. Padding + bottom rule give the section card
   its rhythm. */
.banter-form-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--rule);
  min-height: 40px;
}
.banter-form-row.is-multiline {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 12px 16px 14px;
}
.banter-form-row:last-child { border-bottom: 0; }
.banter-form-row-label {
  flex: 0 0 auto;
  font-family: var(--banter-sans); font-size: 13px;
  color: var(--ink); font-weight: 500;
}
.banter-form-row-value {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  font-family: var(--banter-sans); font-size: 13px;
  color: var(--ink);
  text-align: right;
  min-width: 0;
}
.banter-form-row.is-multiline .banter-form-row-value {
  justify-content: flex-start;
  text-align: left;
  color: var(--ink-soft);
  line-height: 1.55;
  white-space: pre-wrap;
}

/* Color token picker — six swatches + a "no color" prohibition. */
.banter-color-picker { display: inline-flex; gap: 6px; align-items: center; }
.banter-color-swatch {
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 1px solid color-mix(in oklab, var(--ink) 12%, transparent);
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.banter-color-swatch.is-selected {
  box-shadow: 0 0 0 1.5px var(--primary);
  transform: scale(1.04);
}
.banter-color-swatch.is-none {
  background: transparent;
  position: relative;
  border-color: var(--ink-mute);
  color: var(--ink-mute);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px;
}
.banter-color-swatch.is-none::after {
  content: "";
  position: absolute;
  inset: 2px;
  border: 1px solid currentColor;
  border-radius: 50%;
  background: linear-gradient(45deg,
    transparent 47%, currentColor 47%, currentColor 53%, transparent 53%);
}

/* Slider row — inline label + track + value. Mirrors
   `LabeledContent("Max output tokens") { Slider(…); Text("\(value)") }`
   where the slider takes the remaining width. */
.banter-slider {
  position: relative;
  width: 140px; height: 6px;
  background: color-mix(in oklab, var(--ink) 10%, transparent);
  border-radius: 999px;
}
.banter-slider-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: var(--primary);
  border-radius: 999px;
  transition: width 700ms cubic-bezier(0.4, 0, 0.2, 1);
}
.banter-slider-thumb {
  position: absolute; top: -3px;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--paper);
  border: 1.5px solid var(--primary);
  box-shadow: 0 1px 3px color-mix(in oklab, var(--ink) 18%, transparent);
  transition: left 700ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Onboarding sheet — matches UserIdentityOnboardingView. 480pt fixed
   width, 32pt padding, BrandSpacing.s5 (24pt) vertical gap. */
.banter-onboarding {
  width: 480px;
  background: var(--paper);
  padding: 32px;
  border: 1px solid var(--rule);
  border-radius: 14px;
  box-shadow: 0 24px 60px color-mix(in oklab, var(--ink) 22%, transparent);
  display: flex; flex-direction: column; gap: 24px;
  font-family: var(--banter-sans);
}
.banter-onboarding-greeting { display: flex; flex-direction: column; gap: 6px; }
.banter-onboarding-headline {
  font-family: var(--banter-serif); font-style: italic; font-size: 28px;
  line-height: 1.05; color: var(--ink);
}
.banter-onboarding-prompt { font-size: 14px; color: var(--ink-soft); }
.banter-onboarding-caption { font-size: 12px; color: var(--ink-mute); line-height: 1.5; }
.banter-onboarding-actions { display: flex; justify-content: flex-end; }

/* Chrome-style inline label + value field — the SwiftUI
   ChromedShortField. Label sits inside the field chrome above the
   value, label rendered as small mono caps. */
.banter-form-stack { display: flex; flex-direction: column; gap: 10px; }
.banter-shortfield {
  background: var(--paper-cool);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 8px 12px;
  display: flex; flex-direction: column; gap: 2px;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.banter-shortfield.is-focused {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 15%, transparent);
}
.banter-shortfield-label {
  font-family: var(--banter-mono); font-size: 9px;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-mute);
}
.banter-shortfield-value {
  font-family: var(--banter-sans); font-size: 14px;
  color: var(--ink);
  display: flex; align-items: center; gap: 2px;
  min-height: 18px;
}
.banter-shortfield-placeholder { color: var(--ink-mute); }
.banter-shortfield-cursor {
  display: inline-block;
  width: 1.5px; height: 14px;
  background: var(--ink);
  margin-left: 2px;
  animation: banter-cursor-blink 1.08s steps(2, jump-none) infinite;
}
@keyframes banter-cursor-blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }

/* Members chip popover — toolbar popover with a header, persona
   accordion, and per-persona settings/memory tabs when expanded. */
.banter-popover {
  width: 360px;
  background: var(--paper);
  border: 1px solid color-mix(in oklab, var(--ink) 12%, transparent);
  border-radius: 12px;
  box-shadow: 0 18px 40px color-mix(in oklab, var(--ink) 22%, transparent);
  font-family: var(--banter-sans);
  overflow: hidden;
}
.banter-popover-header {
  padding: 12px 14px;
  border-bottom: 1px solid var(--rule);
  display: flex; justify-content: space-between; align-items: baseline;
}
.banter-popover-title {
  font-size: 13px; font-weight: 600; color: var(--ink);
}
.banter-popover-meta {
  font-family: var(--banter-mono); font-size: 10px;
  color: var(--ink-mute); letter-spacing: 0.06em;
}
.banter-popover-list {
  display: flex; flex-direction: column;
  max-height: 480px; overflow-y: auto;
}
.banter-popover-row {
  border-top: 1px solid var(--rule);
  padding: 10px 14px;
  display: flex; flex-direction: column; gap: 8px;
}
.banter-popover-row:first-child { border-top: 0; }
.banter-popover-row-head {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer;
}
.banter-popover-row-name {
  font-family: var(--banter-serif); font-style: italic; font-size: 16px;
  color: var(--ink);
}
.banter-popover-row-handle {
  font-family: var(--banter-mono); font-size: 10px;
  color: var(--ink-mute);
}
.banter-popover-row-chev {
  margin-left: auto; color: var(--ink-mute);
  transition: transform 200ms ease;
}
.banter-popover-row.is-expanded .banter-popover-row-chev {
  transform: rotate(90deg);
}
.banter-popover-row-body {
  padding: 4px 0 2px;
  display: flex; flex-direction: column; gap: 10px;
}
.banter-popover-tabs { display: flex; gap: 4px; }
.banter-popover-tab {
  flex: 1; text-align: center;
  font-family: var(--banter-sans); font-size: 11px; font-weight: 500;
  padding: 5px 8px; border-radius: 999px;
  background: var(--paper-cool); color: var(--ink-soft);
  cursor: pointer;
}
.banter-popover-tab.is-selected { background: var(--ink); color: var(--paper); }
.banter-popover-tabs.is-inert .banter-popover-tab { cursor: default; }

.banter-memory-row {
  display: flex; flex-direction: column; gap: 4px;
  padding: 8px 10px;
  background: var(--paper-cool);
  border-radius: 8px;
  border: 1px solid var(--rule);
}
.banter-memory-meta {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--banter-mono); font-size: 9px;
  color: var(--ink-mute); letter-spacing: 0.06em; text-transform: uppercase;
}
.banter-memory-badge {
  background: color-mix(in oklab, var(--primary) 14%, transparent);
  color: var(--primary);
  padding: 1px 5px; border-radius: 3px;
  font-weight: 600;
}
.banter-memory-badge.is-extractor { background: color-mix(in oklab, var(--p2) 14%, transparent); color: var(--p2); }
.banter-memory-text {
  font-family: var(--banter-sans); font-size: 12px; color: var(--ink);
  line-height: 1.4;
}

/* Editor — full-pane Form layout, used when the persona editor is
   the main content of a window (PersonaEditorView). */
.banter-editor {
  display: flex; flex-direction: column; gap: 18px;
  padding: 24px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 14px;
  font-family: var(--banter-sans);
}
.banter-editor-empty {
  padding: 14px;
  font-size: 12px; color: var(--ink-mute); line-height: 1.4;
}
.banter-editor-token-meter {
  display: flex; justify-content: space-between; align-items: center;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-family: var(--banter-mono); font-size: 10px;
  color: var(--ink-mute); letter-spacing: 0.06em;
}

/* Archetype tile inside AddPersonasSheet's archetypes panel. */
.banter-archetype-tile {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  background: var(--paper-cool);
  border: 1px solid var(--rule);
  border-radius: 10px;
  cursor: pointer;
}
.banter-archetype-tile.is-selected {
  background: color-mix(in oklab, var(--primary) 8%, var(--paper-cool));
  border-color: var(--primary);
}
.banter-archetype-tile-name {
  font-size: 12px; font-weight: 600; color: var(--ink);
}
.banter-archetype-tile-tag {
  font-family: var(--banter-mono); font-size: 9px; color: var(--ink-mute);
  margin-top: 1px; letter-spacing: 0.04em;
}
.banter-archetype-tile-radio {
  margin-left: auto;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1.5px solid var(--ink-mute);
}
.banter-archetype-tile.is-selected .banter-archetype-tile-radio {
  background: var(--primary);
  border-color: var(--primary);
  box-shadow: inset 0 0 0 2.5px var(--paper);
}

/* ──────────────────────────────────────────────────────────
   Block 3 (v0.5) — Room editor primitives
   Sources:
     - RoomEditorView:  Sources/BanterApp/AppShell/RoomEditorView.swift
   ────────────────────────────────────────────────────────── */

/* Menu picker — SwiftUI .pickerStyle(.menu). Renders the current
   selection as a value with a right-side chevron. The kit's pickers
   are read-only representations; live state is driven by the
   surrounding section's React state. */
.banter-menu-picker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
  font-family: var(--banter-sans); font-size: 13px;
  color: var(--ink);
  min-width: 120px;
  justify-content: space-between;
}
.banter-menu-picker-chevron {
  display: inline-flex; flex-direction: column;
  font-size: 8px; color: var(--ink-mute); line-height: 0.75;
}

/* Stepper — value + up/down chevrons stacked vertically. */
.banter-stepper {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.banter-stepper-value {
  font-family: var(--banter-mono); font-size: 13px;
  color: var(--ink); min-width: 32px; text-align: right;
}
.banter-stepper-buttons {
  display: inline-flex; flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: 5px; overflow: hidden;
  background: var(--paper);
}
.banter-stepper-buttons span {
  width: 18px; height: 11px;
  display: flex; align-items: center; justify-content: center;
  font-size: 8px; color: var(--ink-mute);
  cursor: pointer;
}
.banter-stepper-buttons span:first-child { border-bottom: 1px solid var(--rule); }

/* Toggle switch — iOS-style pill. */
.banter-toggle {
  width: 32px; height: 18px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--ink) 16%, transparent);
  position: relative;
  cursor: pointer;
  transition: background 200ms ease;
  flex-shrink: 0;
}
.banter-toggle::after {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 1px 3px color-mix(in oklab, var(--ink) 30%, transparent);
  transition: transform 200ms ease;
}
.banter-toggle.is-on { background: var(--p3); }
.banter-toggle.is-on::after { transform: translateX(14px); }

/* Checkbox — SwiftUI .toggleStyle(.checkbox). */
.banter-checkbox {
  width: 16px; height: 16px;
  border-radius: 4px;
  border: 1px solid color-mix(in oklab, var(--ink) 30%, transparent);
  background: var(--paper);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
}
.banter-checkbox.is-checked {
  background: var(--p2);
  border-color: var(--p2);
  color: var(--paper);
  font-size: 11px; font-weight: 700;
  line-height: 1;
}
.banter-checkbox.is-checked::after { content: "✓"; }

/* Multi-line text area — Rules of Engagement. */
.banter-textarea {
  width: 100%;
  min-height: 80px;
  padding: 10px 12px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
  font-family: var(--banter-sans); font-size: 13px;
  color: var(--ink);
  line-height: 1.5;
  white-space: pre-wrap;
}
.banter-textarea-placeholder { color: var(--ink-mute); }
.banter-textarea-footer {
  display: flex; align-items: baseline; gap: 6px;
  margin-top: 4px;
  font-family: var(--banter-mono); font-size: 10px;
  color: var(--ink-mute); letter-spacing: 0.04em;
}
.banter-textarea-footer.is-over { color: var(--banter-warn); }

/* Member row — checkbox + avatar + name/handle. */
.banter-member-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--rule);
  min-height: 44px;
}
.banter-member-row:last-child { border-bottom: 0; }
.banter-member-row-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.banter-member-row-name {
  font-family: var(--banter-serif); font-style: italic; font-size: 14px;
  color: var(--ink); line-height: 1.1;
}
.banter-member-row-meta {
  font-family: var(--banter-mono); font-size: 11px;
  color: var(--ink-mute);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Knowledge row — file icon + name/meta + remove. */
.banter-knowledge-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--rule);
}
.banter-knowledge-row:last-child { border-bottom: 0; }
.banter-knowledge-row-icon {
  width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink-mute); flex-shrink: 0;
}
.banter-knowledge-row-text { flex: 1; min-width: 0; }
.banter-knowledge-row-name {
  font-family: var(--banter-sans); font-size: 13px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.banter-knowledge-row-meta {
  font-family: var(--banter-mono); font-size: 10px;
  color: var(--ink-mute); margin-top: 2px;
}
.banter-knowledge-row-remove {
  background: transparent; border: 0;
  color: var(--ink-mute); cursor: pointer;
  font-size: 14px; padding: 4px;
}
.banter-knowledge-row-remove:hover { color: var(--banter-warn); }

/* Section group with a label-on-the-right header (e.g. Members count,
   Knowledge token meter). */
.banter-form-section-header.has-meta {
  display: flex; align-items: baseline;
  justify-content: space-between;
  width: 100%;
}
.banter-form-section-header-meta {
  font-family: var(--banter-mono); font-size: 11px;
  color: var(--ink-mute); letter-spacing: 0.04em;
  font-weight: 400;
}

/* ──────────────────────────────────────────────────────────
   Token Ledger
   Source: Sources/BanterApp/Settings/TokenLedgerView.swift
   The ledger is a dark-leaning surface in the app — it ships
   with the standard `data-paper` flip, so var(--paper) /
   var(--ink) carry it. Most of the row chrome is bespoke:
   the in-app row is a VStack of name + dollar on top, metric
   strip below, with a soft hover background + chevron drill.
   ────────────────────────────────────────────────────────── */

/* Scope pill — breadcrumb capsule shown above the filters. */
.banter-ledger-scope {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  background: color-mix(in oklab, var(--ink) 6%, transparent);
  border: 1px solid var(--rule);
  border-radius: 10px;
  font-family: var(--banter-sans);
  font-size: 13px;
  color: var(--ink);
  width: 100%;
  flex-wrap: wrap;
}
.banter-ledger-scope-step {
  display: inline-flex; align-items: center;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit; color: inherit;
  cursor: pointer;
}
.banter-ledger-scope-step.is-mute   { color: var(--ink-mute); }
.banter-ledger-scope-step.is-active { color: var(--ink); font-weight: 500; }
.banter-ledger-scope-step:hover     { color: var(--primary); }
.banter-ledger-scope-sep {
  color: var(--ink-mute);
  font-family: var(--banter-mono);
}

/* Eyebrow label that sits above each ledger group. Uppercase
   mono, tight letter-spacing — same posture as the in-app
   `Text("SCOPE").font(.caption2.smallCaps())` callouts. */
.banter-ledger-eyebrow {
  font-family: var(--banter-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  color: var(--ink-mute);
  text-transform: uppercase;
}

/* Drill row card — a single Group / Persona / Room entry. The
   in-app version is a VStack inside a RoundedRectangle that
   tints on hover/expand. */
.banter-ledger-row {
  display: flex; flex-direction: column; gap: 8px;
  padding: 14px 16px;
  background: color-mix(in oklab, var(--ink) 4%, transparent);
  border: 1px solid var(--rule);
  border-radius: 10px;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
.banter-ledger-row:hover { background: color-mix(in oklab, var(--ink) 7%, transparent); }
.banter-ledger-row.is-expanded {
  background: color-mix(in oklab, var(--ink) 7%, transparent);
  border-color: color-mix(in oklab, var(--primary) 35%, var(--rule));
}
.banter-ledger-row + .banter-ledger-row { margin-top: -1px; } /* collapse adjacent borders into a hairline divider feel */

/* Top line: name + handle on the left, cost + chevron on the right. */
.banter-ledger-row-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px;
}
.banter-ledger-row-name {
  font-family: var(--banter-sans);
  font-size: 15px; font-weight: 600;
  color: var(--ink);
}
.banter-ledger-row-handle {
  font-family: var(--banter-mono);
  font-size: 12px;
  color: var(--ink-mute);
  margin-left: 6px;
}
.banter-ledger-row-cost {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-family: var(--banter-mono);
  font-size: 14px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.banter-ledger-row-cost.is-secondary { color: var(--ink-mute); }
.banter-ledger-row-chev {
  font-family: var(--banter-mono);
  color: var(--ink-mute);
  font-size: 12px;
  transition: transform 160ms ease;
}
.banter-ledger-row.is-expanded .banter-ledger-row-chev { transform: rotate(180deg); }

/* Metric strip — `calls 2  input 2540  output 0  cache —`. Mono
   small with muted gray for the labels and ink for the numbers. */
.banter-ledger-metrics {
  display: flex; flex-wrap: wrap;
  gap: 4px 14px;
  font-family: var(--banter-mono);
  font-size: 11px;
  color: var(--ink-mute);
}
.banter-ledger-metric { display: inline-flex; gap: 4px; align-items: baseline; }
.banter-ledger-metric strong {
  color: var(--ink);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

/* Outcome chip — `h30 s1` style pill that surfaces when a row
   has held / suppressed / failed / truncated turns. Persona
   warm tint — uses the ochre accent so it stands out from the
   neutral metrics. */
.banter-ledger-outcome-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  font-family: var(--banter-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: color-mix(in oklab, var(--p5) 80%, var(--ink));
  background: color-mix(in oklab, var(--p5) 15%, transparent);
  border: 1px solid color-mix(in oklab, var(--p5) 30%, transparent);
}

/* Per-model breakdown — surfaces inside an expanded row.
   Same row shape as the parent metric strip but with a
   provider tag + model id on the left. */
.banter-ledger-breakdown {
  display: flex; flex-direction: column; gap: 6px;
  padding-top: 10px;
  margin-top: 4px;
  border-top: 1px solid var(--rule);
}
.banter-ledger-breakdown-title {
  font-family: var(--banter-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--ink-mute);
  text-transform: uppercase;
}
.banter-ledger-breakdown-row {
  display: flex; gap: 12px; align-items: baseline;
  font-family: var(--banter-mono);
  font-size: 11px;
  color: var(--ink-mute);
}
.banter-ledger-breakdown-row .provider { color: var(--ink-mute); }
.banter-ledger-breakdown-row .model    { color: var(--ink); flex: 1 1 auto; min-width: 0; }
.banter-ledger-breakdown-row .calls,
.banter-ledger-breakdown-row .toks,
.banter-ledger-breakdown-row .cost {
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.banter-ledger-breakdown-row .cost { color: var(--ink); font-weight: 500; }

/* The "75 rows w/o usage" warn callout in the totals — orange
   to match the in-app `.foregroundStyle(.orange)` posture. */
.banter-ledger-warn {
  font-family: var(--banter-mono);
  font-size: 11px;
  color: color-mix(in oklab, var(--p1) 75%, var(--ink));
  margin-left: 8px;
}

/* The Token Ledger window in the kit fits within a BanterWindow
   detail-only frame; scope/filters/groups stack vertically with
   a generous top inset that clears the traffic lights. */
.banter-ledger {
  display: flex; flex-direction: column;
  gap: 28px;
  padding: 56px 24px 32px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none; /* Firefox — fully hidden by default */
}
.banter-ledger::-webkit-scrollbar {
  width: 6px;
  height: 0;
}
.banter-ledger::-webkit-scrollbar-track {
  background: transparent;
}
.banter-ledger::-webkit-scrollbar-thumb {
  background: transparent;
  border-radius: 3px;
  transition: background 220ms ease;
}
.banter-ledger.is-scrolling::-webkit-scrollbar-thumb,
.banter-ledger:hover::-webkit-scrollbar-thumb {
  background: color-mix(in oklab, var(--ink) 28%, transparent);
}
.banter-ledger-section {
  display: flex; flex-direction: column; gap: 10px;
}
